Commit ca435136899d715afee6ff549a588c269fe1bd9c

Authored by Andrii Shvaika
1 parent 42a629c6

Implementation of Hierarchical queries

... ... @@ -5,7 +5,7 @@
5 5 * you may not use this file except in compliance with the License.
6 6 * You may obtain a copy of the License at
7 7 *
8   - * http://www.apache.org/licenses/LICENSE-2.0
  8 + * http://www.apache.org/licenses/LICENSE-2.0
9 9 *
10 10 * Unless required by applicable law or agreed to in writing, software
11 11 * distributed under the License is distributed on an "AS IS" BASIS,
... ... @@ -20,11 +20,14 @@ import org.apache.commons.lang3.StringUtils;
20 20 import org.springframework.stereotype.Repository;
21 21 import org.thingsboard.server.common.data.EntityType;
22 22 import org.thingsboard.server.common.data.UUIDConverter;
  23 +import org.thingsboard.server.common.data.asset.Asset;
23 24 import org.thingsboard.server.common.data.id.CustomerId;
24 25 import org.thingsboard.server.common.data.id.EntityId;
25 26 import org.thingsboard.server.common.data.id.TenantId;
26 27 import org.thingsboard.server.common.data.page.PageData;
  28 +import org.thingsboard.server.common.data.query.AssetSearchQueryFilter;
27 29 import org.thingsboard.server.common.data.query.AssetTypeFilter;
  30 +import org.thingsboard.server.common.data.query.DeviceSearchQueryFilter;
28 31 import org.thingsboard.server.common.data.query.DeviceTypeFilter;
29 32 import org.thingsboard.server.common.data.query.EntityCountQuery;
30 33 import org.thingsboard.server.common.data.query.EntityData;
... ... @@ -35,6 +38,7 @@ import org.thingsboard.server.common.data.query.EntityFilter;
35 38 import org.thingsboard.server.common.data.query.EntityFilterType;
36 39 import org.thingsboard.server.common.data.query.EntityListFilter;
37 40 import org.thingsboard.server.common.data.query.EntityNameFilter;
  41 +import org.thingsboard.server.common.data.query.EntitySearchQueryFilter;
38 42 import org.thingsboard.server.common.data.query.EntityViewTypeFilter;
39 43 import org.thingsboard.server.common.data.query.RelationsQueryFilter;
40 44 import org.thingsboard.server.common.data.query.SingleEntityFilter;
... ... @@ -57,7 +61,7 @@ import java.util.stream.Collectors;
57 61 @Repository
58 62 @Slf4j
59 63 public class DefaultEntityQueryRepository implements EntityQueryRepository {
60   -
  64 + //TODO: rafactoring to protect from SQL injections;
61 65 private static final Map<EntityType, String> entityTableMap = new HashMap<>();
62 66
63 67 static {
... ... @@ -70,6 +74,22 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
70 74 entityTableMap.put(EntityType.TENANT, "tenant");
71 75 }
72 76
  77 + public static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" +
  78 + " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" +
  79 + " FROM relation" +
  80 + " WHERE $in_id = '%s' and $in_type = '%s' and relation_type_group = 'COMMON'" +
  81 + " UNION ALL" +
  82 + " SELECT r.from_id, r.from_type, r.to_id, r.to_type, r.relation_type, lvl + 1" +
  83 + " FROM relation r" +
  84 + " INNER JOIN related_entities re ON" +
  85 + " r.$in_id = re.$out_id and r.$in_type = re.$out_type and" +
  86 + " relation_type_group = 'COMMON' %s)" +
  87 + " SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" +
  88 + " from related_entities re" +
  89 + " %s ) entity";
  90 + public static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from");
  91 + public static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to");
  92 +
73 93 @PersistenceContext
74 94 private EntityManager entityManager;
75 95
... ... @@ -109,13 +129,19 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
109 129
110 130
111 131 String entityWhereClause = this.buildEntityWhere(tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType);
112   - String latestJoins = EntityKeyMapping.buildLatestJoins(entityType, allLatestMappings);
  132 + String latestJoins = EntityKeyMapping.buildLatestJoins(query.getEntityFilter(), entityType, allLatestMappings);
113 133 String whereClause = this.buildWhere(selectionMapping, latestFiltersMapping, pageLink.getTextSearch());
114 134 String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping);
  135 + String entityTypeStr;
  136 + if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) {
  137 + entityTypeStr = "e.entity_type";
  138 + } else {
  139 + entityTypeStr = "'" + entityType.name() + "'";
  140 + }
115 141 if (!StringUtils.isEmpty(entityFieldsSelection)) {
116   - entityFieldsSelection = String.format("e.id, '%s', %s", entityType.name(), entityFieldsSelection);
  142 + entityFieldsSelection = String.format("e.id, %s, %s", entityTypeStr, entityFieldsSelection);
117 143 } else {
118   - entityFieldsSelection = String.format("e.id, '%s'", entityType.name());
  144 + entityFieldsSelection = String.format("e.id, %s", entityTypeStr);
119 145 }
120 146 String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping);
121 147 String topSelection = "entities.*";
... ... @@ -175,16 +201,19 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
175 201 }
176 202
177 203 private String buildPermissionQuery(EntityFilter entityFilter, TenantId tenantId, CustomerId customerId, EntityType entityType) {
178   - if (entityFilter.getType().equals(EntityFilterType.RELATIONS_QUERY)) {
179   - return String.format("e.tenant_id='%s' and e.customer_id='%s'", UUIDConverter.fromTimeUUID(tenantId.getId()), UUIDConverter.fromTimeUUID(customerId.getId()));
180   - } else {
181   - if (entityType == EntityType.TENANT) {
182   - return String.format("e.id='%s'", UUIDConverter.fromTimeUUID(tenantId.getId()));
183   - } else if (entityType == EntityType.CUSTOMER) {
184   - return String.format("e.tenant_id='%s' and e.id='%s'", UUIDConverter.fromTimeUUID(tenantId.getId()), UUIDConverter.fromTimeUUID(customerId.getId()));
185   - } else {
  204 + switch (entityFilter.getType()) {
  205 + case RELATIONS_QUERY:
  206 + case DEVICE_SEARCH_QUERY:
  207 + case ASSET_SEARCH_QUERY:
186 208 return String.format("e.tenant_id='%s' and e.customer_id='%s'", UUIDConverter.fromTimeUUID(tenantId.getId()), UUIDConverter.fromTimeUUID(customerId.getId()));
187   - }
  209 + default:
  210 + if (entityType == EntityType.TENANT) {
  211 + return String.format("e.id='%s'", UUIDConverter.fromTimeUUID(tenantId.getId()));
  212 + } else if (entityType == EntityType.CUSTOMER) {
  213 + return String.format("e.tenant_id='%s' and e.id='%s'", UUIDConverter.fromTimeUUID(tenantId.getId()), UUIDConverter.fromTimeUUID(customerId.getId()));
  214 + } else {
  215 + return String.format("e.tenant_id='%s' and e.customer_id='%s'", UUIDConverter.fromTimeUUID(tenantId.getId()), UUIDConverter.fromTimeUUID(customerId.getId()));
  216 + }
188 217 }
189 218 }
190 219
... ... @@ -201,6 +230,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
201 230 case ENTITY_VIEW_TYPE:
202 231 return this.typeQuery(entityFilter);
203 232 case RELATIONS_QUERY:
  233 + case DEVICE_SEARCH_QUERY:
  234 + case ASSET_SEARCH_QUERY:
204 235 return "";
205 236 default:
206 237 throw new RuntimeException("Not implemented!");
... ... @@ -211,84 +242,42 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
211 242 switch (entityFilter.getType()) {
212 243 case RELATIONS_QUERY:
213 244 return relationQuery((RelationsQueryFilter) entityFilter);
  245 + case DEVICE_SEARCH_QUERY:
  246 + DeviceSearchQueryFilter deviceQuery = (DeviceSearchQueryFilter) entityFilter;
  247 + return entitySearchQuery(deviceQuery, EntityType.DEVICE, deviceQuery.getDeviceTypes());
  248 + case ASSET_SEARCH_QUERY:
  249 + AssetSearchQueryFilter assetQuery = (AssetSearchQueryFilter) entityFilter;
  250 + return entitySearchQuery(assetQuery, EntityType.ASSET, assetQuery.getAssetTypes());
214 251 default:
215 252 return entityTableMap.get(entityType);
216 253 }
217 254 }
218 255
219   - private String relationQuery(RelationsQueryFilter entityFilter) {
  256 + private String entitySearchQuery(EntitySearchQueryFilter entityFilter, EntityType entityType, List<String> types) {
220 257 EntityId rootId = entityFilter.getRootEntity();
221   - String lvlFilter = entityFilter.getMaxLevel() > 0 ? ("and lvl <= " + (entityFilter.getMaxLevel() - 1)) : "";
222   - //TODO: refactoring
223   - String selectFields = " select CASE\n" +
224   - " WHEN entity.entity_type = 'DEVICE'\n" +
225   - " THEN (select tenant_id from device where id = entity_id)\n" +
226   - " WHEN entity.entity_type = 'ASSET'\n" +
227   - " THEN (select tenant_id from asset where id = entity_id)\n" +
228   - " WHEN entity.entity_type = 'CUSTOMER'\n" +
229   - " THEN (select tenant_id from customer where id = entity_id)\n" +
230   - " WHEN entity.entity_type = 'TENANT'\n" +
231   - " THEN entity_id\n" +
232   - " END as tenant_id,\n" +
233   - " CASE\n" +
234   - " WHEN entity.entity_type = 'DEVICE'\n" +
235   - " THEN (select customer_id from device where id = entity_id)\n" +
236   - " WHEN entity.entity_type = 'ASSET'\n" +
237   - " THEN (select customer_id from asset where id = entity_id)\n" +
238   - " WHEN entity.entity_type = 'CUSTOMER'\n" +
239   - " THEN entity_id\n" +
240   - " WHEN entity.entity_type = 'TENANT'\n" +
241   - " THEN '1b21dd2138140008080808080808080'\n" +
242   - " END as customer_id,\n" +
243   - " entity.entity_id as id,\n" +
244   - " CASE\n" +
245   - " WHEN entity.entity_type = 'DEVICE'\n" +
246   - " THEN (select type from device where id = entity_id)\n" +
247   - " WHEN entity.entity_type = 'ASSET' THEN (select type from asset where id = entity_id)\n" +
248   - " ELSE entity.entity_type\n" +
249   - " END as type,\n" +
250   - " CASE\n" +
251   - " WHEN entity.entity_type = 'DEVICE'\n" +
252   - " THEN (select name from device where id = entity_id)\n" +
253   - " WHEN entity.entity_type = 'ASSET' THEN (select name from asset where id = entity_id)\n" +
254   - " WHEN entity.entity_type = 'CUSTOMER'\n" +
255   - " THEN (select title from customer where id = entity_id)\n" +
256   - " WHEN entity.entity_type = 'TENANT'\n" +
257   - " THEN (select title from tenant where id = entity_id)\n" +
258   - " ELSE entity.entity_type\n" +
259   - " END as name,\n" +
260   - " CASE\n" +
261   - " WHEN entity.entity_type = 'DEVICE'\n" +
262   - " THEN (select label from device where id = entity_id)\n" +
263   - " WHEN entity.entity_type = 'ASSET' THEN (select label from asset where id = entity_id)\n" +
264   - " WHEN entity.entity_type = 'CUSTOMER'\n" +
265   - " THEN (select title from customer where id = entity_id)\n" +
266   - " WHEN entity.entity_type = 'TENANT'\n" +
267   - " THEN (select title from tenant where id = entity_id)\n" +
268   - " ELSE entity.entity_type\n" +
269   - " END as label,\n" +
270   - " entity.entity_type as entity_type";
271   -
272   - String fromTemplate = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" +
273   - " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" +
274   - " FROM relation" +
275   - " WHERE $in_id = '%s' and $in_type = '%s' and relation_type_group = 'COMMON'" +
276   - " UNION ALL" +
277   - " SELECT r.from_id, r.from_type, r.to_id, r.to_type, r.relation_type, lvl + 1" +
278   - " FROM relation r" +
279   - " INNER JOIN related_entities re ON" +
280   - " r.$in_id = re.$out_id and r.$in_type = re.$out_type and" +
281   - " relation_type_group = 'COMMON' %s)" +
282   - " SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" +
283   - " from related_entities re" +
284   - " %s ) entity";
  258 + //TODO: fetch last level only.
  259 + String lvlFilter = getLvlFilter(entityFilter.getMaxLevel());
  260 + String selectFields = "SELECT tenant_id, customer_id, id, type, name, label FROM " + entityType.name() + " WHERE id in ( SELECT entity_id";
  261 + String from = getQueryTemplate(entityFilter.getDirection());
285 262
286   - String from;
287   - if (entityFilter.getDirection().equals(EntitySearchDirection.FROM)) {
288   - from = fromTemplate.replace("$in", "from").replace("$out", "to");
289   - } else {
290   - from = fromTemplate.replace("$in", "to").replace("$out", "from");
  263 + String whereFilter = " WHERE " + " re.relation_type = '" + entityFilter.getRelationType() + "'" +
  264 + " AND re.to_type = '" + entityType.name() + "'";
  265 + from = String.format(from, UUIDConverter.fromTimeUUID(rootId.getId()), rootId.getEntityType().name(), lvlFilter, whereFilter);
  266 + String query = "( " + selectFields + from + ")";
  267 + if (types != null && !types.isEmpty()) {
  268 + query += " and type in (" + types.stream().map(type -> "'" + type + "'").collect(Collectors.joining(", ")) + ")";
291 269 }
  270 + query += " )";
  271 + return query;
  272 + }
  273 +
  274 + private String relationQuery(RelationsQueryFilter entityFilter) {
  275 + EntityId rootId = entityFilter.getRootEntity();
  276 + String lvlFilter = getLvlFilter(entityFilter.getMaxLevel());
  277 + String selectFields = getSelectTenantId() + ", " + getSelectCustomerId() + ", " +
  278 + " entity.entity_id as id," + getSelectType() + ", " + getSelectName() + ", " +
  279 + getSelectLabel() + ", entity.entity_type as entity_type";
  280 + String from = getQueryTemplate(entityFilter.getDirection());
292 281
293 282 StringBuilder whereFilter;
294 283 if (entityFilter.getFilters() != null && !entityFilter.getFilters().isEmpty()) {
... ... @@ -302,13 +291,13 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
302 291 whereFilter.append(" AND ");
303 292 }
304 293 String relationType = etf.getRelationType();
305   - String types = etf.getEntityTypes().stream().map(type -> "'" + type + "'").collect(Collectors.joining(", "));
  294 + String entityTypes = etf.getEntityTypes().stream().map(type -> "'" + type + "'").collect(Collectors.joining(", "));
306 295 if (!single) {
307 296 whereFilter.append(" (");
308 297 }
309 298 whereFilter.append(" re.relation_type = '").append(relationType).append("' and re.")
310 299 .append(entityFilter.getDirection().equals(EntitySearchDirection.FROM) ? "to" : "from")
311   - .append("_type in (").append(types).append(")");
  300 + .append("_type in (").append(entityTypes).append(")");
312 301 if (!single) {
313 302 whereFilter.append(" )");
314 303 }
... ... @@ -320,6 +309,107 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
320 309 return "( " + selectFields + from + ")";
321 310 }
322 311
  312 + private String getLvlFilter(int maxLevel) {
  313 + return maxLevel > 0 ? ("and lvl <= " + (maxLevel - 1)) : "";
  314 + }
  315 +
  316 + private String getQueryTemplate(EntitySearchDirection direction) {
  317 + String from;
  318 + if (direction.equals(EntitySearchDirection.FROM)) {
  319 + from = HIERARCHICAL_FROM_QUERY_TEMPLATE;
  320 + } else {
  321 + from = HIERARCHICAL_TO_QUERY_TEMPLATE;
  322 + }
  323 + return from;
  324 + }
  325 +
  326 + private String getSelectTenantId() {
  327 + return "SELECT CASE" +
  328 + " WHEN entity.entity_type = 'TENANT' THEN entity_id" +
  329 + " WHEN entity.entity_type = 'CUSTOMER'" +
  330 + " THEN (select tenant_id from customer where id = entity_id)" +
  331 + " WHEN entity.entity_type = 'USER'" +
  332 + " THEN (select tenant_id from tb_user where id = entity_id)" +
  333 + " WHEN entity.entity_type = 'DASHBOARD'" +
  334 + " THEN (select tenant_id from dashboard where id = entity_id)" +
  335 + " WHEN entity.entity_type = 'ASSET'" +
  336 + " THEN (select tenant_id from asset where id = entity_id)" +
  337 + " WHEN entity.entity_type = 'DEVICE'" +
  338 + " THEN (select tenant_id from device where id = entity_id)" +
  339 + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
  340 + " THEN (select tenant_id from entity_view where id = entity_id)" +
  341 + " END as tenant_id";
  342 + }
  343 +
  344 + private String getSelectCustomerId() {
  345 + return "CASE" +
  346 + " WHEN entity.entity_type = 'TENANT'" +
  347 + " THEN '" + UUIDConverter.fromTimeUUID(TenantId.NULL_UUID) + "'" +
  348 + " WHEN entity.entity_type = 'CUSTOMER' THEN entity_id" +
  349 + " WHEN entity.entity_type = 'USER'" +
  350 + " THEN (select customer_id from tb_user where id = entity_id)" +
  351 + " WHEN entity.entity_type = 'DASHBOARD'" +
  352 + //TODO: parse assigned customers or use contains?
  353 + " THEN NULL" +
  354 + " WHEN entity.entity_type = 'ASSET'" +
  355 + " THEN (select customer_id from asset where id = entity_id)" +
  356 + " WHEN entity.entity_type = 'DEVICE'" +
  357 + " THEN (select customer_id from device where id = entity_id)" +
  358 + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
  359 + " THEN (select customer_id from entity_view where id = entity_id)" +
  360 + " END as customer_id";
  361 + }
  362 +
  363 + private String getSelectName() {
  364 + return " CASE" +
  365 + " WHEN entity.entity_type = 'TENANT'" +
  366 + " THEN (select title from tenant where id = entity_id)" +
  367 + " WHEN entity.entity_type = 'CUSTOMER' " +
  368 + " THEN (select title from customer where id = entity_id)" +
  369 + " WHEN entity.entity_type = 'USER'" +
  370 + " THEN (select CONCAT (first_name, ' ', last_name) from tb_user where id = entity_id)" +
  371 + " WHEN entity.entity_type = 'DASHBOARD'" +
  372 + " THEN (select title from dashboard where id = entity_id)" +
  373 + " WHEN entity.entity_type = 'ASSET'" +
  374 + " THEN (select name from asset where id = entity_id)" +
  375 + " WHEN entity.entity_type = 'DEVICE'" +
  376 + " THEN (select name from device where id = entity_id)" +
  377 + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
  378 + " THEN (select name from entity_view where id = entity_id)" +
  379 + " END as name";
  380 + }
  381 +
  382 + private String getSelectType() {
  383 + return " CASE" +
  384 + " WHEN entity.entity_type = 'USER'" +
  385 + " THEN (select authority from tb_user where id = entity_id)" +
  386 + " WHEN entity.entity_type = 'ASSET'" +
  387 + " THEN (select type from asset where id = entity_id)" +
  388 + " WHEN entity.entity_type = 'DEVICE'" +
  389 + " THEN (select type from device where id = entity_id)" +
  390 + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
  391 + " THEN (select type from entity_view where id = entity_id)" +
  392 + " ELSE entity.entity_type END as type";
  393 + }
  394 +
  395 + private String getSelectLabel() {
  396 + return " CASE" +
  397 + " WHEN entity.entity_type = 'TENANT'" +
  398 + " THEN (select title from tenant where id = entity_id)" +
  399 + " WHEN entity.entity_type = 'CUSTOMER' " +
  400 + " THEN (select title from customer where id = entity_id)" +
  401 + " WHEN entity.entity_type = 'USER'" +
  402 + " THEN (select CONCAT (first_name, ' ', last_name) from tb_user where id = entity_id)" +
  403 + " WHEN entity.entity_type = 'DASHBOARD'" +
  404 + " THEN (select title from dashboard where id = entity_id)" +
  405 + " WHEN entity.entity_type = 'ASSET'" +
  406 + " THEN (select label from asset where id = entity_id)" +
  407 + " WHEN entity.entity_type = 'DEVICE'" +
  408 + " THEN (select label from device where id = entity_id)" +
  409 + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
  410 + " THEN (select name from entity_view where id = entity_id)" +
  411 + " END as label";
  412 + }
323 413
324 414 private String buildWhere
325 415 (List<EntityKeyMapping> selectionMapping, List<EntityKeyMapping> latestFiltersMapping, String searchText) {
... ...
... ... @@ -22,6 +22,8 @@ import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
22 22 import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
23 23 import org.thingsboard.server.common.data.query.EntityDataQuery;
24 24 import org.thingsboard.server.common.data.query.EntityDataSortOrder;
  25 +import org.thingsboard.server.common.data.query.EntityFilter;
  26 +import org.thingsboard.server.common.data.query.EntityFilterType;
25 27 import org.thingsboard.server.common.data.query.EntityKey;
26 28 import org.thingsboard.server.common.data.query.EntityKeyType;
27 29 import org.thingsboard.server.common.data.query.FilterPredicateType;
... ... @@ -43,6 +45,7 @@ import java.util.stream.Stream;
43 45 public class EntityKeyMapping {
44 46
45 47 public static final Map<String, String> entityFieldColumnMap = new HashMap<>();
  48 +
46 49 static {
47 50 entityFieldColumnMap.put("createdTime", "id");
48 51 entityFieldColumnMap.put("name", "name");
... ... @@ -107,14 +110,20 @@ public class EntityKeyMapping {
107 110 }
108 111 }
109 112
110   - public String toLatestJoin(EntityType entityType) {
  113 + public String toLatestJoin(EntityFilter entityFilter, EntityType entityType) {
  114 + String entityTypeStr;
  115 + if (entityFilter.getType().equals(EntityFilterType.RELATIONS_QUERY)) {
  116 + entityTypeStr = "entities.entity_type";
  117 + } else {
  118 + entityTypeStr = "'" + entityType.name() + "'";
  119 + }
111 120 String join = hasFilter() ? "left join" : "left outer join";
112 121 if (entityKey.getType().equals(EntityKeyType.TIME_SERIES)) {
113 122 // TODO:
114 123 throw new RuntimeException("Not implemented!");
115 124 } else {
116   - String query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type='%s' AND %s.attribute_key='%s'",
117   - join, alias, alias, alias, entityType.name(), alias, entityKey.getKey());
  125 + String query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key='%s'",
  126 + join, alias, alias, alias, entityTypeStr, alias, entityKey.getKey());
118 127 if (!entityKey.getType().equals(EntityKeyType.ATTRIBUTE)) {
119 128 String scope;
120 129 if (entityKey.getType().equals(EntityKeyType.CLIENT_ATTRIBUTE)) {
... ... @@ -135,8 +144,8 @@ public class EntityKeyMapping {
135 144 Collectors.joining(", "));
136 145 }
137 146
138   - public static String buildLatestJoins(EntityType entityType, List<EntityKeyMapping> latestMappings) {
139   - return latestMappings.stream().map(mapping -> mapping.toLatestJoin(entityType)).collect(
  147 + public static String buildLatestJoins(EntityFilter entityFilter, EntityType entityType, List<EntityKeyMapping> latestMappings) {
  148 + return latestMappings.stream().map(mapping -> mapping.toLatestJoin(entityFilter, entityType)).collect(
140 149 Collectors.joining(" "));
141 150 }
142 151
... ... @@ -207,7 +216,7 @@ public class EntityKeyMapping {
207 216 if (mapping.getEntityKey().getType().equals(EntityKeyType.ENTITY_FIELD)) {
208 217 index++;
209 218 } else {
210   - index +=2;
  219 + index += 2;
211 220 }
212 221 }
213 222 if (!filters.isEmpty()) {
... ... @@ -220,7 +229,7 @@ public class EntityKeyMapping {
220 229 mapping.setSelection(false);
221 230 mapping.setEntityKey(filterField);
222 231 mappings.add(mapping);
223   - index +=1;
  232 + index += 1;
224 233 }
225 234 }
226 235
... ... @@ -253,7 +262,7 @@ public class EntityKeyMapping {
253 262
254 263 private String buildPredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) {
255 264 if (predicate.getType().equals(FilterPredicateType.COMPLEX)) {
256   - return this.buildComplexPredicateQuery(alias, key, (ComplexFilterPredicate)predicate);
  265 + return this.buildComplexPredicateQuery(alias, key, (ComplexFilterPredicate) predicate);
257 266 } else {
258 267 return this.buildSimplePredicateQuery(alias, key, predicate);
259 268 }
... ... @@ -270,10 +279,10 @@ public class EntityKeyMapping {
270 279 if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
271 280 if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
272 281 String column = entityFieldColumnMap.get(key.getKey());
273   - return this.buildNumericPredicateQuery(alias + "." + column, (NumericFilterPredicate)predicate);
  282 + return this.buildNumericPredicateQuery(alias + "." + column, (NumericFilterPredicate) predicate);
274 283 } else {
275   - String longQuery = this.buildNumericPredicateQuery(alias + ".long_v", (NumericFilterPredicate)predicate);
276   - String doubleQuery = this.buildNumericPredicateQuery(alias + ".dbl_v", (NumericFilterPredicate)predicate);
  284 + String longQuery = this.buildNumericPredicateQuery(alias + ".long_v", (NumericFilterPredicate) predicate);
  285 + String doubleQuery = this.buildNumericPredicateQuery(alias + ".dbl_v", (NumericFilterPredicate) predicate);
277 286 return String.format("(%s or %s)", longQuery, doubleQuery);
278 287 }
279 288 } else {
... ... @@ -285,9 +294,9 @@ public class EntityKeyMapping {
285 294 }
286 295 String field = alias + "." + column;
287 296 if (predicate.getType().equals(FilterPredicateType.STRING)) {
288   - return this.buildStringPredicateQuery(field, (StringFilterPredicate)predicate);
  297 + return this.buildStringPredicateQuery(field, (StringFilterPredicate) predicate);
289 298 } else {
290   - return this.buildBooleanPredicateQuery(field, (BooleanFilterPredicate)predicate);
  299 + return this.buildBooleanPredicateQuery(field, (BooleanFilterPredicate) predicate);
291 300 }
292 301 }
293 302 }
... ...
... ... @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.Device;
27 27 import org.thingsboard.server.common.data.EntityType;
28 28 import org.thingsboard.server.common.data.Tenant;
29 29 import org.thingsboard.server.common.data.asset.Asset;
  30 +import org.thingsboard.server.common.data.asset.AssetSearchQuery;
30 31 import org.thingsboard.server.common.data.id.CustomerId;
31 32 import org.thingsboard.server.common.data.id.DeviceId;
32 33 import org.thingsboard.server.common.data.id.EntityId;
... ... @@ -36,6 +37,8 @@ import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
36 37 import org.thingsboard.server.common.data.kv.KvEntry;
37 38 import org.thingsboard.server.common.data.kv.LongDataEntry;
38 39 import org.thingsboard.server.common.data.page.PageData;
  40 +import org.thingsboard.server.common.data.query.AssetSearchQueryFilter;
  41 +import org.thingsboard.server.common.data.query.DeviceSearchQueryFilter;
39 42 import org.thingsboard.server.common.data.query.DeviceTypeFilter;
40 43 import org.thingsboard.server.common.data.query.EntityCountQuery;
41 44 import org.thingsboard.server.common.data.query.EntityData;
... ... @@ -55,6 +58,7 @@ import org.thingsboard.server.common.data.relation.RelationTypeGroup;
55 58 import org.thingsboard.server.dao.attributes.AttributesService;
56 59
57 60 import java.util.ArrayList;
  61 +import java.util.Arrays;
58 62 import java.util.Collections;
59 63 import java.util.List;
60 64 import java.util.concurrent.ExecutionException;
... ... @@ -128,6 +132,280 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
128 132 public void testCountHierarchicalEntitiesByQuery() {
129 133 List<Asset> assets = new ArrayList<>();
130 134 List<Device> devices = new ArrayList<>();
  135 + createTestHierarchy(assets, devices, new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
  136 +
  137 + RelationsQueryFilter filter = new RelationsQueryFilter();
  138 + filter.setRootEntity(tenantId);
  139 + filter.setDirection(EntitySearchDirection.FROM);
  140 +
  141 + EntityCountQuery countQuery = new EntityCountQuery(filter);
  142 +
  143 + long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  144 + Assert.assertEquals(30, count);
  145 +
  146 + filter.setFilters(Collections.singletonList(new EntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE))));
  147 + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  148 + Assert.assertEquals(25, count);
  149 +
  150 + filter.setRootEntity(devices.get(0).getId());
  151 + filter.setDirection(EntitySearchDirection.TO);
  152 + filter.setFilters(Collections.singletonList(new EntityTypeFilter("Manages", Collections.singletonList(EntityType.TENANT))));
  153 + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  154 + Assert.assertEquals(1, count);
  155 +
  156 + DeviceSearchQueryFilter filter2 = new DeviceSearchQueryFilter();
  157 + filter2.setRootEntity(tenantId);
  158 + filter2.setDirection(EntitySearchDirection.FROM);
  159 + filter2.setRelationType("Contains");
  160 +
  161 + countQuery = new EntityCountQuery(filter2);
  162 +
  163 + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  164 + Assert.assertEquals(25, count);
  165 +
  166 + filter2.setDeviceTypes(Arrays.asList("default0", "default1"));
  167 + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  168 + Assert.assertEquals(10, count);
  169 +
  170 + filter2.setRootEntity(devices.get(0).getId());
  171 + filter2.setDirection(EntitySearchDirection.TO);
  172 + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  173 + Assert.assertEquals(0, count);
  174 +
  175 + AssetSearchQueryFilter filter3 = new AssetSearchQueryFilter();
  176 + filter3.setRootEntity(tenantId);
  177 + filter3.setDirection(EntitySearchDirection.FROM);
  178 + filter3.setRelationType("Manages");
  179 +
  180 + countQuery = new EntityCountQuery(filter3);
  181 +
  182 + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  183 + Assert.assertEquals(5, count);
  184 +
  185 + filter3.setAssetTypes(Arrays.asList("type0", "type1"));
  186 + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  187 + Assert.assertEquals(2, count);
  188 +
  189 + filter3.setRootEntity(devices.get(0).getId());
  190 + filter3.setDirection(EntitySearchDirection.TO);
  191 + count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
  192 + Assert.assertEquals(0, count);
  193 + }
  194 +
  195 + @Test
  196 + public void testHierarchicalFindEntityDataWithAttributesByQuery() throws ExecutionException, InterruptedException {
  197 + List<Asset> assets = new ArrayList<>();
  198 + List<Device> devices = new ArrayList<>();
  199 + List<Long> temperatures = new ArrayList<>();
  200 + List<Long> highTemperatures = new ArrayList<>();
  201 + createTestHierarchy(assets, devices, new ArrayList<>(), new ArrayList<>(), temperatures, highTemperatures);
  202 +
  203 + List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>();
  204 + for (int i = 0; i < devices.size(); i++) {
  205 + Device device = devices.get(i);
  206 + attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), DataConstants.CLIENT_SCOPE));
  207 + }
  208 + Futures.successfulAsList(attributeFutures).get();
  209 +
  210 + RelationsQueryFilter filter = new RelationsQueryFilter();
  211 + filter.setRootEntity(tenantId);
  212 + filter.setDirection(EntitySearchDirection.FROM);
  213 + filter.setFilters(Collections.singletonList(new EntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE))));
  214 +
  215 + EntityDataSortOrder sortOrder = new EntityDataSortOrder(
  216 + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
  217 + );
  218 + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
  219 + List<EntityKey> entityFields = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"));
  220 + List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
  221 +
  222 + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, null);
  223 + PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  224 + List<EntityData> loadedEntities = new ArrayList<>(data.getData());
  225 + while (data.hasNext()) {
  226 + query = query.next();
  227 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  228 + loadedEntities.addAll(data.getData());
  229 + }
  230 + Assert.assertEquals(25, loadedEntities.size());
  231 + List<String> loadedTemperatures = loadedEntities.stream().map(entityData ->
  232 + entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  233 + List<String> deviceTemperatures = temperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  234 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  235 +
  236 + pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
  237 + KeyFilter highTemperatureFilter = new KeyFilter();
  238 + highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
  239 + NumericFilterPredicate predicate = new NumericFilterPredicate();
  240 + predicate.setValue(45);
  241 + predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
  242 + highTemperatureFilter.setPredicate(predicate);
  243 + List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
  244 +
  245 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFilters);
  246 +
  247 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  248 +
  249 + loadedEntities = new ArrayList<>(data.getData());
  250 + while (data.hasNext()) {
  251 + query = query.next();
  252 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  253 + loadedEntities.addAll(data.getData());
  254 + }
  255 + Assert.assertEquals(highTemperatures.size(), loadedEntities.size());
  256 +
  257 + List<String> loadedHighTemperatures = loadedEntities.stream().map(entityData ->
  258 + entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  259 + List<String> deviceHighTemperatures = highTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  260 +
  261 + Assert.assertEquals(deviceHighTemperatures, loadedHighTemperatures);
  262 +
  263 + deviceService.deleteDevicesByTenantId(tenantId);
  264 + }
  265 +
  266 + @Test
  267 + public void testHierarchicalFindDevicesWithAttributesByQuery() throws ExecutionException, InterruptedException {
  268 + List<Asset> assets = new ArrayList<>();
  269 + List<Device> devices = new ArrayList<>();
  270 + List<Long> temperatures = new ArrayList<>();
  271 + List<Long> highTemperatures = new ArrayList<>();
  272 + createTestHierarchy(assets, devices, new ArrayList<>(), new ArrayList<>(), temperatures, highTemperatures);
  273 +
  274 + List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>();
  275 + for (int i = 0; i < devices.size(); i++) {
  276 + Device device = devices.get(i);
  277 + attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), DataConstants.CLIENT_SCOPE));
  278 + }
  279 + Futures.successfulAsList(attributeFutures).get();
  280 +
  281 + DeviceSearchQueryFilter filter = new DeviceSearchQueryFilter();
  282 + filter.setRootEntity(tenantId);
  283 + filter.setDirection(EntitySearchDirection.FROM);
  284 + filter.setRelationType("Contains");
  285 +
  286 + EntityDataSortOrder sortOrder = new EntityDataSortOrder(
  287 + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
  288 + );
  289 + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
  290 + List<EntityKey> entityFields = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"));
  291 + List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
  292 +
  293 + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, null);
  294 + PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  295 + List<EntityData> loadedEntities = new ArrayList<>(data.getData());
  296 + while (data.hasNext()) {
  297 + query = query.next();
  298 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  299 + loadedEntities.addAll(data.getData());
  300 + }
  301 + Assert.assertEquals(25, loadedEntities.size());
  302 + List<String> loadedTemperatures = loadedEntities.stream().map(entityData ->
  303 + entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  304 + List<String> deviceTemperatures = temperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  305 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  306 +
  307 + pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
  308 + KeyFilter highTemperatureFilter = new KeyFilter();
  309 + highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
  310 + NumericFilterPredicate predicate = new NumericFilterPredicate();
  311 + predicate.setValue(45);
  312 + predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
  313 + highTemperatureFilter.setPredicate(predicate);
  314 + List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
  315 +
  316 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFilters);
  317 +
  318 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  319 +
  320 + loadedEntities = new ArrayList<>(data.getData());
  321 + while (data.hasNext()) {
  322 + query = query.next();
  323 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  324 + loadedEntities.addAll(data.getData());
  325 + }
  326 + Assert.assertEquals(highTemperatures.size(), loadedEntities.size());
  327 +
  328 + List<String> loadedHighTemperatures = loadedEntities.stream().map(entityData ->
  329 + entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  330 + List<String> deviceHighTemperatures = highTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  331 +
  332 + Assert.assertEquals(deviceHighTemperatures, loadedHighTemperatures);
  333 +
  334 + deviceService.deleteDevicesByTenantId(tenantId);
  335 + }
  336 +
  337 + @Test
  338 + public void testHierarchicalFindAssetsWithAttributesByQuery() throws ExecutionException, InterruptedException {
  339 + List<Asset> assets = new ArrayList<>();
  340 + List<Device> devices = new ArrayList<>();
  341 + List<Long> consumptions = new ArrayList<>();
  342 + List<Long> highConsumptions = new ArrayList<>();
  343 + createTestHierarchy(assets, devices, consumptions, highConsumptions, new ArrayList<>(), new ArrayList<>());
  344 +
  345 + List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>();
  346 + for (int i = 0; i < assets.size(); i++) {
  347 + Asset asset = assets.get(i);
  348 + attributeFutures.add(saveLongAttribute(asset.getId(), "consumption", consumptions.get(i), DataConstants.SERVER_SCOPE));
  349 + }
  350 + Futures.successfulAsList(attributeFutures).get();
  351 +
  352 + AssetSearchQueryFilter filter = new AssetSearchQueryFilter();
  353 + filter.setRootEntity(tenantId);
  354 + filter.setDirection(EntitySearchDirection.FROM);
  355 + filter.setRelationType("Manages");
  356 +
  357 + EntityDataSortOrder sortOrder = new EntityDataSortOrder(
  358 + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
  359 + );
  360 + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
  361 + List<EntityKey> entityFields = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"));
  362 + List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.ATTRIBUTE, "consumption"));
  363 +
  364 + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, null);
  365 + PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  366 + List<EntityData> loadedEntities = new ArrayList<>(data.getData());
  367 + while (data.hasNext()) {
  368 + query = query.next();
  369 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  370 + loadedEntities.addAll(data.getData());
  371 + }
  372 + Assert.assertEquals(5, loadedEntities.size());
  373 + List<String> loadedTemperatures = loadedEntities.stream().map(entityData ->
  374 + entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("consumption").getValue()).collect(Collectors.toList());
  375 + List<String> deviceTemperatures = consumptions.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  376 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  377 +
  378 + pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
  379 + KeyFilter highTemperatureFilter = new KeyFilter();
  380 + highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "consumption"));
  381 + NumericFilterPredicate predicate = new NumericFilterPredicate();
  382 + predicate.setValue(50);
  383 + predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
  384 + highTemperatureFilter.setPredicate(predicate);
  385 + List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
  386 +
  387 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFilters);
  388 +
  389 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  390 +
  391 + loadedEntities = new ArrayList<>(data.getData());
  392 + while (data.hasNext()) {
  393 + query = query.next();
  394 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  395 + loadedEntities.addAll(data.getData());
  396 + }
  397 + Assert.assertEquals(highConsumptions.size(), loadedEntities.size());
  398 +
  399 + List<String> loadedHighTemperatures = loadedEntities.stream().map(entityData ->
  400 + entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("consumption").getValue()).collect(Collectors.toList());
  401 + List<String> deviceHighTemperatures = highConsumptions.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  402 +
  403 + Assert.assertEquals(deviceHighTemperatures, loadedHighTemperatures);
  404 +
  405 + deviceService.deleteDevicesByTenantId(tenantId);
  406 + }
  407 +
  408 + private void createTestHierarchy(List<Asset> assets, List<Device> devices, List<Long> consumptions, List<Long> highConsumptions, List<Long> temperatures, List<Long> highTemperatures) {
131 409 for (int i = 0; i < 5; i++) {
132 410 Asset asset = new Asset();
133 411 asset.setTenantId(tenantId);
... ... @@ -142,11 +420,16 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
142 420 er.setType("Manages");
143 421 er.setTypeGroup(RelationTypeGroup.COMMON);
144 422 relationService.saveRelation(tenantId, er);
  423 + long consumption = (long) (Math.random() * 100);
  424 + consumptions.add(consumption);
  425 + if (consumption > 50) {
  426 + highConsumptions.add(consumption);
  427 + }
145 428 for (int j = 0; j < 5; j++) {
146 429 Device device = new Device();
147 430 device.setTenantId(tenantId);
148 431 device.setName("A" + i + "Device" + j);
149   - device.setType("default");
  432 + device.setType("default" + j);
150 433 device.setLabel("testLabel" + (int) (Math.random() * 1000));
151 434 device = deviceService.saveDevice(device);
152 435 devices.add(device);
... ... @@ -156,27 +439,13 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
156 439 er.setType("Contains");
157 440 er.setTypeGroup(RelationTypeGroup.COMMON);
158 441 relationService.saveRelation(tenantId, er);
  442 + long temperature = (long) (Math.random() * 100);
  443 + temperatures.add(temperature);
  444 + if (temperature > 45) {
  445 + highTemperatures.add(temperature);
  446 + }
159 447 }
160 448 }
161   -
162   - RelationsQueryFilter filter = new RelationsQueryFilter();
163   - filter.setRootEntity(tenantId);
164   - filter.setDirection(EntitySearchDirection.FROM);
165   -
166   - EntityCountQuery countQuery = new EntityCountQuery(filter);
167   -
168   - long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
169   - Assert.assertEquals(30, count);
170   -
171   - filter.setFilters(Collections.singletonList(new EntityTypeFilter("Contains", Collections.singletonList(EntityType.DEVICE))));
172   - count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
173   - Assert.assertEquals(25, count);
174   -
175   - filter.setRootEntity(devices.get(0).getId());
176   - filter.setDirection(EntitySearchDirection.TO);
177   - filter.setFilters(Collections.singletonList(new EntityTypeFilter("Manages", Collections.singletonList(EntityType.TENANT))));
178   - count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
179   - Assert.assertEquals(1, count);
180 449 }
181 450
182 451 @Test
... ...