|
|
|
@ -15,10 +15,12 @@ |
|
|
|
*/ |
|
|
|
package org.thingsboard.server.dao.service; |
|
|
|
|
|
|
|
import com.google.common.collect.Lists; |
|
|
|
import com.google.common.util.concurrent.Futures; |
|
|
|
import com.google.common.util.concurrent.ListenableFuture; |
|
|
|
import org.apache.commons.lang3.RandomStringUtils; |
|
|
|
import org.apache.commons.lang3.RandomUtils; |
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.junit.After; |
|
|
|
import org.junit.Assert; |
|
|
|
import org.junit.Before; |
|
|
|
@ -35,6 +37,7 @@ import org.thingsboard.server.common.data.id.CustomerId; |
|
|
|
import org.thingsboard.server.common.data.id.DeviceId; |
|
|
|
import org.thingsboard.server.common.data.id.EdgeId; |
|
|
|
import org.thingsboard.server.common.data.id.EntityId; |
|
|
|
import org.thingsboard.server.common.data.id.IdBased; |
|
|
|
import org.thingsboard.server.common.data.id.TenantId; |
|
|
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
|
|
|
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
|
|
|
@ -75,8 +78,11 @@ import java.util.ArrayList; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.Comparator; |
|
|
|
import java.util.HashMap; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Random; |
|
|
|
import java.util.UUID; |
|
|
|
import java.util.concurrent.ExecutionException; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
import java.util.stream.Stream; |
|
|
|
@ -373,6 +379,108 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
|
|
|
deviceService.deleteDevicesByTenantId(tenantId); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testCountHierarchicalEntitiesByMultiRootQuery() throws InterruptedException { |
|
|
|
List<Asset> buildings = new ArrayList<>(); |
|
|
|
List<Asset> apartments = new ArrayList<>(); |
|
|
|
Map<String, Map<UUID, String>> entityNameByTypeMap = new HashMap<>(); |
|
|
|
Map<UUID, UUID> childParentRelationMap = new HashMap<>(); |
|
|
|
createMultiRootHierarchy(buildings, apartments, entityNameByTypeMap, childParentRelationMap); |
|
|
|
|
|
|
|
RelationsQueryFilter filter = new RelationsQueryFilter(); |
|
|
|
filter.setMultiRoot(true); |
|
|
|
filter.setMultiRootEntities(buildings.stream().map(IdBased::getId).collect(Collectors.toSet())); |
|
|
|
filter.setDirection(EntitySearchDirection.FROM); |
|
|
|
|
|
|
|
EntityCountQuery countQuery = new EntityCountQuery(filter); |
|
|
|
|
|
|
|
long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
|
|
|
Assert.assertEquals(63, count); |
|
|
|
|
|
|
|
filter.setFilters(Collections.singletonList(new RelationEntityTypeFilter("AptToHeat", Collections.singletonList(EntityType.DEVICE)))); |
|
|
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
|
|
|
Assert.assertEquals(27, count); |
|
|
|
|
|
|
|
filter.setMultiRootEntities(apartments.stream().map(IdBased::getId).collect(Collectors.toSet())); |
|
|
|
filter.setDirection(EntitySearchDirection.TO); |
|
|
|
filter.setFilters(Lists.newArrayList( |
|
|
|
new RelationEntityTypeFilter("buildingToApt", Collections.singletonList(EntityType.ASSET)), |
|
|
|
new RelationEntityTypeFilter("AptToEnergy", Collections.singletonList(EntityType.DEVICE)))); |
|
|
|
|
|
|
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery); |
|
|
|
Assert.assertEquals(9, count); |
|
|
|
|
|
|
|
deviceService.deleteDevicesByTenantId(tenantId); |
|
|
|
assetService.deleteAssetsByTenantId(tenantId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testMultiRootHierarchicalFindEntityDataWithAttributesByQuery() throws ExecutionException, InterruptedException { |
|
|
|
List<Asset> buildings = new ArrayList<>(); |
|
|
|
List<Asset> apartments = new ArrayList<>(); |
|
|
|
Map<String, Map<UUID, String>> entityNameByTypeMap = new HashMap<>(); |
|
|
|
Map<UUID, UUID> childParentRelationMap = new HashMap<>(); |
|
|
|
createMultiRootHierarchy(buildings, apartments, entityNameByTypeMap, childParentRelationMap); |
|
|
|
|
|
|
|
RelationsQueryFilter filter = new RelationsQueryFilter(); |
|
|
|
filter.setMultiRoot(true); |
|
|
|
filter.setMultiRootEntities(buildings.stream().map(IdBased::getId).collect(Collectors.toSet())); |
|
|
|
filter.setDirection(EntitySearchDirection.FROM); |
|
|
|
|
|
|
|
EntityDataSortOrder sortOrder = new EntityDataSortOrder( |
|
|
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC |
|
|
|
); |
|
|
|
EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder); |
|
|
|
List<EntityKey> entityFields = Lists.newArrayList( |
|
|
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), |
|
|
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "parentId"), |
|
|
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "type") |
|
|
|
); |
|
|
|
List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.ATTRIBUTE, "status")); |
|
|
|
|
|
|
|
KeyFilter onlineStatusFilter = new KeyFilter(); |
|
|
|
onlineStatusFilter.setKey(new EntityKey(EntityKeyType.ENTITY_FIELD, "name")); |
|
|
|
StringFilterPredicate predicate = new StringFilterPredicate(); |
|
|
|
predicate.setOperation(StringOperation.ENDS_WITH); |
|
|
|
predicate.setValue(FilterPredicateValue.fromString("_1")); |
|
|
|
onlineStatusFilter.setPredicate(predicate); |
|
|
|
List<KeyFilter> keyFilters = Collections.singletonList(onlineStatusFilter); |
|
|
|
|
|
|
|
EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFilters); |
|
|
|
PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query); |
|
|
|
List<EntityData> loadedEntities = new ArrayList<>(data.getData()); |
|
|
|
while (data.hasNext()) { |
|
|
|
query = query.next(); |
|
|
|
data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query); |
|
|
|
loadedEntities.addAll(data.getData()); |
|
|
|
} |
|
|
|
|
|
|
|
long expectedEntitiesCnt = entityNameByTypeMap.entrySet() |
|
|
|
.stream() |
|
|
|
.filter(e -> !e.getKey().equals("building")) |
|
|
|
.flatMap(e -> e.getValue().entrySet().stream()) |
|
|
|
.map(Map.Entry::getValue) |
|
|
|
.filter(e -> StringUtils.endsWith(e, "_1")) |
|
|
|
.count(); |
|
|
|
Assert.assertEquals(expectedEntitiesCnt, loadedEntities.size()); |
|
|
|
|
|
|
|
Map<UUID, UUID> actualRelations = new HashMap<>(); |
|
|
|
loadedEntities.forEach(ed -> { |
|
|
|
UUID parentId = UUID.fromString(ed.getLatest().get(EntityKeyType.ENTITY_FIELD).get("parentId").getValue()); |
|
|
|
UUID entityId = ed.getEntityId().getId(); |
|
|
|
Assert.assertEquals(childParentRelationMap.get(entityId), parentId); |
|
|
|
actualRelations.put(entityId, parentId); |
|
|
|
|
|
|
|
String entityType = ed.getLatest().get(EntityKeyType.ENTITY_FIELD).get("type").getValue(); |
|
|
|
String actualEntityName = ed.getLatest().get(EntityKeyType.ENTITY_FIELD).get("name").getValue(); |
|
|
|
String expectedEntityName = entityNameByTypeMap.get(entityType).get(entityId); |
|
|
|
Assert.assertEquals(expectedEntityName, actualEntityName); |
|
|
|
}); |
|
|
|
|
|
|
|
deviceService.deleteDevicesByTenantId(tenantId); |
|
|
|
assetService.deleteAssetsByTenantId(tenantId); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testHierarchicalFindDevicesWithAttributesByQuery() throws ExecutionException, InterruptedException { |
|
|
|
@ -1534,4 +1642,76 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest { |
|
|
|
BasicTsKvEntry timeseries = new BasicTsKvEntry(42L, telemetryValue); |
|
|
|
return timeseriesService.save(SYSTEM_TENANT_ID, entityId, timeseries); |
|
|
|
} |
|
|
|
|
|
|
|
private void createMultiRootHierarchy(List<Asset> buildings, List<Asset> apartments, |
|
|
|
Map<String, Map<UUID, String>> entityNameByTypeMap, |
|
|
|
Map<UUID, UUID> childParentRelationMap) throws InterruptedException { |
|
|
|
for (int k = 0; k < 3; k++) { |
|
|
|
Asset building = new Asset(); |
|
|
|
building.setTenantId(tenantId); |
|
|
|
building.setName("Building _" + k); |
|
|
|
building.setType("building"); |
|
|
|
building.setLabel("building label" + k); |
|
|
|
building = assetService.saveAsset(building); |
|
|
|
buildings.add(building); |
|
|
|
entityNameByTypeMap.computeIfAbsent(building.getType(), n -> new HashMap<>()).put(building.getId().getId(), building.getName()); |
|
|
|
|
|
|
|
for (int i = 0; i < 3; i++) { |
|
|
|
Asset asset = new Asset(); |
|
|
|
asset.setTenantId(tenantId); |
|
|
|
asset.setName("Apt " + k + "_" + i); |
|
|
|
asset.setType("apartment"); |
|
|
|
asset.setLabel("apartment " + i); |
|
|
|
asset = assetService.saveAsset(asset); |
|
|
|
//TO make sure devices have different created time
|
|
|
|
Thread.sleep(1); |
|
|
|
entityNameByTypeMap.computeIfAbsent(asset.getType(), n -> new HashMap<>()).put(asset.getId().getId(), asset.getName()); |
|
|
|
apartments.add(asset); |
|
|
|
EntityRelation er = new EntityRelation(); |
|
|
|
er.setFrom(building.getId()); |
|
|
|
er.setTo(asset.getId()); |
|
|
|
er.setType("buildingToApt"); |
|
|
|
er.setTypeGroup(RelationTypeGroup.COMMON); |
|
|
|
relationService.saveRelation(tenantId, er); |
|
|
|
childParentRelationMap.put(asset.getUuidId(), building.getUuidId()); |
|
|
|
for (int j = 0; j < 3; j++) { |
|
|
|
Device device = new Device(); |
|
|
|
device.setTenantId(tenantId); |
|
|
|
device.setName("Heat" + k + "_" + i + "_" + j); |
|
|
|
device.setType("heatmeter"); |
|
|
|
device.setLabel("heatmeter" + (int) (Math.random() * 1000)); |
|
|
|
device = deviceService.saveDevice(device); |
|
|
|
//TO make sure devices have different created time
|
|
|
|
Thread.sleep(1); |
|
|
|
entityNameByTypeMap.computeIfAbsent(device.getType(), n -> new HashMap<>()).put(device.getId().getId(), device.getName()); |
|
|
|
er = new EntityRelation(); |
|
|
|
er.setFrom(asset.getId()); |
|
|
|
er.setTo(device.getId()); |
|
|
|
er.setType("AptToHeat"); |
|
|
|
er.setTypeGroup(RelationTypeGroup.COMMON); |
|
|
|
relationService.saveRelation(tenantId, er); |
|
|
|
childParentRelationMap.put(device.getUuidId(), asset.getUuidId()); |
|
|
|
} |
|
|
|
|
|
|
|
for (int j = 0; j < 3; j++) { |
|
|
|
Device device = new Device(); |
|
|
|
device.setTenantId(tenantId); |
|
|
|
device.setName("Energy" + k + "_" + i + "_" + j); |
|
|
|
device.setType("energymeter"); |
|
|
|
device.setLabel("energymeter" + (int) (Math.random() * 1000)); |
|
|
|
device = deviceService.saveDevice(device); |
|
|
|
//TO make sure devices have different created time
|
|
|
|
Thread.sleep(1); |
|
|
|
entityNameByTypeMap.computeIfAbsent(device.getType(), n -> new HashMap<>()).put(device.getId().getId(), device.getName()); |
|
|
|
er = new EntityRelation(); |
|
|
|
er.setFrom(asset.getId()); |
|
|
|
er.setTo(device.getId()); |
|
|
|
er.setType("AptToEnergy"); |
|
|
|
er.setTypeGroup(RelationTypeGroup.COMMON); |
|
|
|
relationService.saveRelation(tenantId, er); |
|
|
|
childParentRelationMap.put(device.getUuidId(), asset.getUuidId()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|