Commit 5a4cb62317051bb7aace61fd4f5da62be9a63d57

Authored by zbeacon
Committed by Andrew Shvayka
1 parent 47c85fdc

Added tests for EntityMapping

... ... @@ -15,12 +15,14 @@
15 15 */
16 16 package org.thingsboard.server.dao.service;
17 17
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.databind.JsonMappingException;
  20 +import com.fasterxml.jackson.databind.ObjectMapper;
18 21 import com.google.common.util.concurrent.Futures;
19 22 import com.google.common.util.concurrent.ListenableFuture;
20 23 import org.junit.After;
21 24 import org.junit.Assert;
22 25 import org.junit.Before;
23   -import org.junit.Ignore;
24 26 import org.junit.Test;
25 27 import org.springframework.beans.factory.annotation.Autowired;
26 28 import org.thingsboard.server.common.data.DataConstants;
... ... @@ -28,42 +30,23 @@ import org.thingsboard.server.common.data.Device;
28 30 import org.thingsboard.server.common.data.EntityType;
29 31 import org.thingsboard.server.common.data.Tenant;
30 32 import org.thingsboard.server.common.data.asset.Asset;
31   -import org.thingsboard.server.common.data.asset.AssetSearchQuery;
32   -import org.thingsboard.server.common.data.id.CustomerId;
33   -import org.thingsboard.server.common.data.id.DeviceId;
34   -import org.thingsboard.server.common.data.id.EntityId;
35   -import org.thingsboard.server.common.data.id.TenantId;
36   -import org.thingsboard.server.common.data.kv.AttributeKvEntry;
37   -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
38   -import org.thingsboard.server.common.data.kv.KvEntry;
39   -import org.thingsboard.server.common.data.kv.LongDataEntry;
  33 +import org.thingsboard.server.common.data.id.*;
  34 +import org.thingsboard.server.common.data.kv.*;
40 35 import org.thingsboard.server.common.data.page.PageData;
41   -import org.thingsboard.server.common.data.query.AssetSearchQueryFilter;
42   -import org.thingsboard.server.common.data.query.DeviceSearchQueryFilter;
43   -import org.thingsboard.server.common.data.query.DeviceTypeFilter;
44   -import org.thingsboard.server.common.data.query.EntityCountQuery;
45   -import org.thingsboard.server.common.data.query.EntityData;
46   -import org.thingsboard.server.common.data.query.EntityDataPageLink;
47   -import org.thingsboard.server.common.data.query.EntityDataQuery;
48   -import org.thingsboard.server.common.data.query.EntityDataSortOrder;
49   -import org.thingsboard.server.common.data.query.EntityKey;
50   -import org.thingsboard.server.common.data.query.EntityKeyType;
51   -import org.thingsboard.server.common.data.query.EntityListFilter;
52   -import org.thingsboard.server.common.data.query.FilterPredicateValue;
53   -import org.thingsboard.server.common.data.query.KeyFilter;
54   -import org.thingsboard.server.common.data.query.NumericFilterPredicate;
55   -import org.thingsboard.server.common.data.query.RelationsQueryFilter;
  36 +import org.thingsboard.server.common.data.query.*;
56 37 import org.thingsboard.server.common.data.relation.EntityRelation;
57 38 import org.thingsboard.server.common.data.relation.EntitySearchDirection;
58 39 import org.thingsboard.server.common.data.relation.EntityTypeFilter;
59 40 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
  41 +import org.thingsboard.server.common.data.rule.RuleChain;
  42 +import org.thingsboard.server.common.data.rule.RuleChainMetaData;
  43 +import org.thingsboard.server.common.data.rule.RuleNode;
60 44 import org.thingsboard.server.dao.attributes.AttributesService;
  45 +import org.thingsboard.server.dao.model.sqlts.ts.TsKvEntity;
  46 +import org.thingsboard.server.dao.rule.RuleChainService;
  47 +import org.thingsboard.server.dao.timeseries.TimeseriesService;
61 48
62   -import java.util.ArrayList;
63   -import java.util.Arrays;
64   -import java.util.Collections;
65   -import java.util.Comparator;
66   -import java.util.List;
  49 +import java.util.*;
67 50 import java.util.concurrent.ExecutionException;
68 51 import java.util.stream.Collectors;
69 52
... ... @@ -72,6 +55,9 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
72 55 @Autowired
73 56 private AttributesService attributesService;
74 57
  58 + @Autowired
  59 + private TimeseriesService timeseriesService;
  60 +
75 61 private TenantId tenantId;
76 62
77 63 @Before
... ... @@ -88,6 +74,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
88 74 tenantService.deleteTenant(tenantId);
89 75 }
90 76
  77 +
91 78 @Test
92 79 public void testCountEntitiesByQuery() throws InterruptedException {
93 80 List<Device> devices = new ArrayList<>();
... ... @@ -131,6 +118,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
131 118 Assert.assertEquals(0, count);
132 119 }
133 120
  121 +
134 122 @Test
135 123 public void testCountHierarchicalEntitiesByQuery() throws InterruptedException {
136 124 List<Asset> assets = new ArrayList<>();
... ... @@ -195,6 +183,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
195 183 Assert.assertEquals(0, count);
196 184 }
197 185
  186 +
198 187 @Test
199 188 public void testHierarchicalFindEntityDataWithAttributesByQuery() throws ExecutionException, InterruptedException {
200 189 List<Asset> assets = new ArrayList<>();
... ... @@ -266,6 +255,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
266 255 deviceService.deleteDevicesByTenantId(tenantId);
267 256 }
268 257
  258 +
269 259 @Test
270 260 public void testHierarchicalFindDevicesWithAttributesByQuery() throws ExecutionException, InterruptedException {
271 261 List<Asset> assets = new ArrayList<>();
... ... @@ -338,6 +328,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
338 328 deviceService.deleteDevicesByTenantId(tenantId);
339 329 }
340 330
  331 +
341 332 @Test
342 333 public void testHierarchicalFindAssetsWithAttributesByQuery() throws ExecutionException, InterruptedException {
343 334 List<Asset> assets = new ArrayList<>();
... ... @@ -456,6 +447,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
456 447 }
457 448 }
458 449
  450 +
459 451 @Test
460 452 public void testSimpleFindEntityDataByQuery() throws InterruptedException {
461 453 List<Device> devices = new ArrayList<>();
... ... @@ -526,6 +518,8 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
526 518 @Test
527 519 public void testFindEntityDataByQueryWithAttributes() throws ExecutionException, InterruptedException {
528 520
  521 + List<EntityKeyType> attributesEntityTypes = new ArrayList<>(Arrays.asList(EntityKeyType.CLIENT_ATTRIBUTE, EntityKeyType.SHARED_ATTRIBUTE, EntityKeyType.SERVER_ATTRIBUTE));
  522 +
529 523 List<Device> devices = new ArrayList<>();
530 524 List<Long> temperatures = new ArrayList<>();
531 525 List<Long> highTemperatures = new ArrayList<>();
... ... @@ -548,6 +542,108 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
548 542 List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>();
549 543 for (int i = 0; i < devices.size(); i++) {
550 544 Device device = devices.get(i);
  545 + for (String currentScope : DataConstants.allScopes()) {
  546 + attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), currentScope));
  547 + }
  548 + }
  549 + Futures.successfulAsList(attributeFutures).get();
  550 +
  551 + DeviceTypeFilter filter = new DeviceTypeFilter();
  552 + filter.setDeviceType("default");
  553 + filter.setDeviceNameFilter("");
  554 +
  555 + EntityDataSortOrder sortOrder = new EntityDataSortOrder(
  556 + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
  557 + );
  558 + EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
  559 + List<EntityKey> entityFields = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"));
  560 + for (EntityKeyType currentAttributeKeyType : attributesEntityTypes) {
  561 + List<EntityKey> latestValues = Collections.singletonList(new EntityKey(currentAttributeKeyType, "temperature"));
  562 + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, null);
  563 + PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  564 + List<EntityData> loadedEntities = new ArrayList<>(data.getData());
  565 + while (data.hasNext()) {
  566 + query = query.next();
  567 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  568 + loadedEntities.addAll(data.getData());
  569 + }
  570 + Assert.assertEquals(67, loadedEntities.size());
  571 + List<String> loadedTemperatures = new ArrayList<>();
  572 + for (Device device : devices) {
  573 + loadedTemperatures.add(loadedEntities.stream().filter(entityData -> entityData.getEntityId().equals(device.getId())).findFirst().orElse(null)
  574 + .getLatest().get(currentAttributeKeyType).get("temperature").getValue());
  575 + }
  576 + List<String> deviceTemperatures = temperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  577 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  578 +
  579 + pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
  580 + KeyFilter highTemperatureFilter = createNumericKeyFilter("temperature", currentAttributeKeyType, NumericFilterPredicate.NumericOperation.GREATER, 45);
  581 + List<KeyFilter> keyFiltersHighTemperature = Collections.singletonList(highTemperatureFilter);
  582 +
  583 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersHighTemperature);
  584 +
  585 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  586 +
  587 + loadedEntities = new ArrayList<>(data.getData());
  588 +
  589 + while (data.hasNext()) {
  590 + query = query.next();
  591 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  592 + loadedEntities.addAll(data.getData());
  593 + }
  594 + Assert.assertEquals(highTemperatures.size(), loadedEntities.size());
  595 +
  596 + List<String> loadedHighTemperatures = loadedEntities.stream().map(entityData ->
  597 + entityData.getLatest().get(currentAttributeKeyType).get("temperature").getValue()).collect(Collectors.toList());
  598 + List<String> deviceHighTemperatures = highTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  599 +
  600 + Assert.assertEquals(deviceHighTemperatures, loadedHighTemperatures);
  601 +
  602 + }
  603 + deviceService.deleteDevicesByTenantId(tenantId);
  604 + }
  605 +
  606 + @Test
  607 + public void testBuildNumericPredicateQueryOperations() throws ExecutionException, InterruptedException{
  608 +
  609 + List<Device> devices = new ArrayList<>();
  610 + List<Long> temperatures = new ArrayList<>();
  611 + List<Long> equalTemperatures = new ArrayList<>();
  612 + List<Long> notEqualTemperatures = new ArrayList<>();
  613 + List<Long> greaterTemperatures = new ArrayList<>();
  614 + List<Long> greaterOrEqualTemperatures = new ArrayList<>();
  615 + List<Long> lessTemperatures = new ArrayList<>();
  616 + List<Long> lessOrEqualTemperatures = new ArrayList<>();
  617 +
  618 + for (int i = 0; i < 10; i++) {
  619 + Device device = new Device();
  620 + device.setTenantId(tenantId);
  621 + device.setName("Device" + i);
  622 + device.setType("default");
  623 + device.setLabel("testLabel" + (int) (Math.random() * 1000));
  624 + devices.add(deviceService.saveDevice(device));
  625 + //TO make sure devices have different created time
  626 + Thread.sleep(1);
  627 + long temperature = (long) (Math.random() * 100);
  628 + temperatures.add(temperature);
  629 + if (temperature == 45) {
  630 + greaterOrEqualTemperatures.add(temperature);
  631 + lessOrEqualTemperatures.add(temperature);
  632 + equalTemperatures.add(temperature);
  633 + } else if (temperature > 45) {
  634 + greaterTemperatures.add(temperature);
  635 + greaterOrEqualTemperatures.add(temperature);
  636 + notEqualTemperatures.add(temperature);
  637 + } else {
  638 + lessTemperatures.add(temperature);
  639 + lessOrEqualTemperatures.add(temperature);
  640 + notEqualTemperatures.add(temperature);
  641 + }
  642 + }
  643 +
  644 + List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>();
  645 + for (int i = 0; i < devices.size(); i++) {
  646 + Device device = devices.get(i);
551 647 attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), DataConstants.CLIENT_SCOPE));
552 648 }
553 649 Futures.successfulAsList(attributeFutures).get();
... ... @@ -559,9 +655,155 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
559 655 EntityDataSortOrder sortOrder = new EntityDataSortOrder(
560 656 new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
561 657 );
  658 +
  659 + List<EntityKey> entityFields = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"));
  660 + List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, "temperature"));
  661 +
  662 + KeyFilter greaterTemperatureFilter = createNumericKeyFilter("temperature", EntityKeyType.CLIENT_ATTRIBUTE, NumericFilterPredicate.NumericOperation.GREATER, 45);
  663 + List<KeyFilter> keyFiltersGreaterTemperature = Collections.singletonList(greaterTemperatureFilter);
  664 +
  665 + KeyFilter greaterOrEqualTemperatureFilter = createNumericKeyFilter("temperature", EntityKeyType.CLIENT_ATTRIBUTE, NumericFilterPredicate.NumericOperation.GREATER_OR_EQUAL, 45);
  666 + List<KeyFilter> keyFiltersGreaterOrEqualTemperature = Collections.singletonList(greaterOrEqualTemperatureFilter);
  667 +
  668 + KeyFilter lessTemperatureFilter = createNumericKeyFilter("temperature", EntityKeyType.CLIENT_ATTRIBUTE, NumericFilterPredicate.NumericOperation.LESS, 45);
  669 + List<KeyFilter> keyFiltersLessTemperature = Collections.singletonList(lessTemperatureFilter);
  670 +
  671 + KeyFilter lessOrEqualTemperatureFilter = createNumericKeyFilter("temperature", EntityKeyType.CLIENT_ATTRIBUTE, NumericFilterPredicate.NumericOperation.LESS_OR_EQUAL, 45);
  672 + List<KeyFilter> keyFiltersLessOrEqualTemperature = Collections.singletonList(lessOrEqualTemperatureFilter);
  673 +
  674 + KeyFilter equalTemperatureFilter = createNumericKeyFilter("temperature", EntityKeyType.CLIENT_ATTRIBUTE, NumericFilterPredicate.NumericOperation.EQUAL, 45);
  675 + List<KeyFilter> keyFiltersEqualTemperature = Collections.singletonList(equalTemperatureFilter);
  676 +
  677 + KeyFilter notEqualTemperatureFilter = createNumericKeyFilter("temperature", EntityKeyType.CLIENT_ATTRIBUTE, NumericFilterPredicate.NumericOperation.NOT_EQUAL, 45);
  678 + List<KeyFilter> keyFiltersNotEqualTemperature = Collections.singletonList(notEqualTemperatureFilter);
  679 +
  680 + //Greater Operation
  681 +
  682 + EntityDataPageLink pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  683 + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersGreaterTemperature);
  684 + PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  685 + List<EntityData> loadedEntities = getLoadedEntities(data, query);
  686 + Assert.assertEquals(greaterTemperatures.size(), loadedEntities.size());
  687 +
  688 + List<String> loadedTemperatures = loadedEntities.stream().map(entityData ->
  689 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  690 + List<String> deviceTemperatures = greaterTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  691 +
  692 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  693 +
  694 + //Greater or equal Operation
  695 +
  696 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  697 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersGreaterOrEqualTemperature);
  698 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  699 + loadedEntities = getLoadedEntities(data, query);
  700 + Assert.assertEquals(greaterOrEqualTemperatures.size(), loadedEntities.size());
  701 +
  702 + loadedTemperatures = loadedEntities.stream().map(entityData ->
  703 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  704 + deviceTemperatures = greaterOrEqualTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  705 +
  706 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  707 +
  708 + //Less Operation
  709 +
  710 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  711 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersLessTemperature);
  712 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  713 + loadedEntities = getLoadedEntities(data, query);
  714 + Assert.assertEquals(lessTemperatures.size(), loadedEntities.size());
  715 +
  716 + loadedTemperatures = loadedEntities.stream().map(entityData ->
  717 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  718 + deviceTemperatures = lessTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  719 +
  720 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  721 +
  722 + //Less or equal Operation
  723 +
  724 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  725 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersLessOrEqualTemperature);
  726 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  727 + loadedEntities = getLoadedEntities(data, query);
  728 + Assert.assertEquals(lessOrEqualTemperatures.size(), loadedEntities.size());
  729 +
  730 + loadedTemperatures = loadedEntities.stream().map(entityData ->
  731 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  732 + deviceTemperatures = lessOrEqualTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  733 +
  734 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  735 +
  736 + //Equal Operation
  737 +
  738 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  739 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersEqualTemperature);
  740 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  741 + loadedEntities = getLoadedEntities(data, query);
  742 + Assert.assertEquals(equalTemperatures.size(), loadedEntities.size());
  743 +
  744 + loadedTemperatures = loadedEntities.stream().map(entityData ->
  745 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  746 + deviceTemperatures = equalTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  747 +
  748 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  749 +
  750 + //Not equal Operation
  751 +
  752 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  753 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersNotEqualTemperature);
  754 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  755 + loadedEntities = getLoadedEntities(data, query);
  756 + Assert.assertEquals(notEqualTemperatures.size(), loadedEntities.size());
  757 +
  758 + loadedTemperatures = loadedEntities.stream().map(entityData ->
  759 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
  760 + deviceTemperatures = notEqualTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  761 +
  762 + Assert.assertEquals(deviceTemperatures, loadedTemperatures);
  763 +
  764 +
  765 + deviceService.deleteDevicesByTenantId(tenantId);
  766 + }
  767 +
  768 + @Test
  769 + public void testFindEntityDataByQueryWithTimeseries() throws ExecutionException, InterruptedException {
  770 +
  771 + List<Device> devices = new ArrayList<>();
  772 + List<Double> temperatures = new ArrayList<>();
  773 + List<Double> highTemperatures = new ArrayList<>();
  774 + for (int i = 0; i < 67; i++) {
  775 + Device device = new Device();
  776 + device.setTenantId(tenantId);
  777 + device.setName("Device" + i);
  778 + device.setType("default");
  779 + device.setLabel("testLabel" + (int) (Math.random() * 1000));
  780 + devices.add(deviceService.saveDevice(device));
  781 + //TO make sure devices have different created time
  782 + Thread.sleep(1);
  783 + double temperature = (double) (Math.random() * 100.0);
  784 + temperatures.add(temperature);
  785 + if (temperature > 45.0) {
  786 + highTemperatures.add(temperature);
  787 + }
  788 + }
  789 +
  790 + List<ListenableFuture<List<Void>>> timeseriesFutures = new ArrayList<>();
  791 + for (int i = 0; i < devices.size(); i++) {
  792 + Device device = devices.get(i);
  793 + timeseriesFutures.add(saveLongTimeseries(device.getId(), "temperature", temperatures.get(i)));
  794 + }
  795 + Futures.successfulAsList(timeseriesFutures).get();
  796 +
  797 + DeviceTypeFilter filter = new DeviceTypeFilter();
  798 + filter.setDeviceType("default");
  799 + filter.setDeviceNameFilter("");
  800 +
  801 + EntityDataSortOrder sortOrder = new EntityDataSortOrder(
  802 + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
  803 + );
562 804 EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
563 805 List<EntityKey> entityFields = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"));
564   - List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
  806 + List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
565 807
566 808 EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, null);
567 809 PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
... ... @@ -576,14 +818,14 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
576 818 List<String> loadedTemperatures = new ArrayList<>();
577 819 for (Device device : devices) {
578 820 loadedTemperatures.add(loadedEntities.stream().filter(entityData -> entityData.getEntityId().equals(device.getId())).findFirst().orElse(null)
579   - .getLatest().get(EntityKeyType.ATTRIBUTE).get("temperature").getValue());
  821 + .getLatest().get(EntityKeyType.TIME_SERIES).get("temperature").getValue());
580 822 }
581   - List<String> deviceTemperatures = temperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  823 + List<String> deviceTemperatures = temperatures.stream().map(aDouble -> Double.toString(aDouble)).collect(Collectors.toList());
582 824 Assert.assertEquals(deviceTemperatures, loadedTemperatures);
583 825
584 826 pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
585 827 KeyFilter highTemperatureFilter = new KeyFilter();
586   - highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
  828 + highTemperatureFilter.setKey(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
587 829 NumericFilterPredicate predicate = new NumericFilterPredicate();
588 830 predicate.setValue(FilterPredicateValue.fromDouble(45));
589 831 predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
... ... @@ -603,17 +845,306 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
603 845 Assert.assertEquals(highTemperatures.size(), loadedEntities.size());
604 846
605 847 List<String> loadedHighTemperatures = loadedEntities.stream().map(entityData ->
606   - entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
607   - List<String> deviceHighTemperatures = highTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
  848 + entityData.getLatest().get(EntityKeyType.TIME_SERIES).get("temperature").getValue()).collect(Collectors.toList());
  849 + List<String> deviceHighTemperatures = highTemperatures.stream().map(aDouble -> Double.toString(aDouble)).collect(Collectors.toList());
608 850
609 851 Assert.assertEquals(deviceHighTemperatures, loadedHighTemperatures);
610 852
611 853 deviceService.deleteDevicesByTenantId(tenantId);
612 854 }
613 855
  856 + @Test
  857 + public void testBuildStringPredicateQueryOperations() throws ExecutionException, InterruptedException{
  858 +
  859 + List<Device> devices = new ArrayList<>();
  860 + List<String> attributeStrings = new ArrayList<>();
  861 + List<String> equalStrings = new ArrayList<>();
  862 + List<String> notEqualStrings = new ArrayList<>();
  863 + List<String> startsWithStrings = new ArrayList<>();
  864 + List<String> endsWithStrings = new ArrayList<>();
  865 + List<String> containsStrings = new ArrayList<>();
  866 + List<String> notContainsStrings = new ArrayList<>();
  867 +
  868 + for (int i = 0; i < 10; i++) {
  869 + Device device = new Device();
  870 + device.setTenantId(tenantId);
  871 + device.setName("Device" + i);
  872 + device.setType("default");
  873 + device.setLabel("testLabel" + (int) (Math.random() * 1000));
  874 + devices.add(deviceService.saveDevice(device));
  875 + //TO make sure devices have different created time
  876 + Thread.sleep(1);
  877 + List<StringFilterPredicate.StringOperation> operationValues= Arrays.asList(StringFilterPredicate.StringOperation.values());
  878 + StringFilterPredicate.StringOperation operation = operationValues.get(new Random().nextInt(operationValues.size()));
  879 + String operationName = operation.name();
  880 + attributeStrings.add(operationName);
  881 + switch(operation){
  882 + case EQUAL:
  883 + equalStrings.add(operationName);
  884 + notContainsStrings.add(operationName);
  885 + notEqualStrings.add(operationName);
  886 + break;
  887 + case NOT_EQUAL:
  888 + notContainsStrings.add(operationName);
  889 + break;
  890 + case STARTS_WITH:
  891 + notEqualStrings.add(operationName);
  892 + startsWithStrings.add(operationName);
  893 + endsWithStrings.add(operationName);
  894 + notContainsStrings.add(operationName);
  895 + break;
  896 + case ENDS_WITH:
  897 + notEqualStrings.add(operationName);
  898 + endsWithStrings.add(operationName);
  899 + notContainsStrings.add(operationName);
  900 + break;
  901 + case CONTAINS:
  902 + notEqualStrings.add(operationName);
  903 + notContainsStrings.add(operationName);
  904 + containsStrings.add(operationName);
  905 + break;
  906 + case NOT_CONTAINS:
  907 + notEqualStrings.add(operationName);
  908 + containsStrings.add(operationName);
  909 + break;
  910 + }
  911 + }
  912 +
  913 + List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>();
  914 + for (int i = 0; i < devices.size(); i++) {
  915 + Device device = devices.get(i);
  916 + attributeFutures.add(saveStringAttribute(device.getId(), "attributeString", attributeStrings.get(i), DataConstants.CLIENT_SCOPE));
  917 + }
  918 + Futures.successfulAsList(attributeFutures).get();
  919 +
  920 + DeviceTypeFilter filter = new DeviceTypeFilter();
  921 + filter.setDeviceType("default");
  922 + filter.setDeviceNameFilter("");
  923 +
  924 + EntityDataSortOrder sortOrder = new EntityDataSortOrder(
  925 + new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.DESC
  926 + );
  927 +
  928 + List<EntityKey> entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), new EntityKey(EntityKeyType.ENTITY_FIELD, "deviceType"));
  929 + List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, "attributeString"));
  930 +
  931 + List<KeyFilter> keyFiltersEqualString = createStringKeyFilters("attributeString", EntityKeyType.CLIENT_ATTRIBUTE, StringFilterPredicate.StringOperation.EQUAL, "equal");
  932 +
  933 + List<KeyFilter> keyFiltersNotEqualString = createStringKeyFilters("attributeString", EntityKeyType.CLIENT_ATTRIBUTE, StringFilterPredicate.StringOperation.NOT_EQUAL, "NOT_EQUAL");
  934 +
  935 + List<KeyFilter> keyFiltersStartsWithString = createStringKeyFilters("attributeString", EntityKeyType.CLIENT_ATTRIBUTE, StringFilterPredicate.StringOperation.STARTS_WITH, "starts_");
  936 +
  937 + List<KeyFilter> keyFiltersEndsWithString = createStringKeyFilters("attributeString", EntityKeyType.CLIENT_ATTRIBUTE, StringFilterPredicate.StringOperation.ENDS_WITH, "_WITH");
  938 +
  939 + List<KeyFilter> keyFiltersContainsString = createStringKeyFilters("attributeString", EntityKeyType.CLIENT_ATTRIBUTE, StringFilterPredicate.StringOperation.CONTAINS, "contains");
  940 +
  941 + List<KeyFilter> keyFiltersNotContainsString = createStringKeyFilters("attributeString", EntityKeyType.CLIENT_ATTRIBUTE, StringFilterPredicate.StringOperation.NOT_CONTAINS, "NOT_CONTAINS");
  942 +
  943 + List<KeyFilter> deviceTypeFilters = createStringKeyFilters("entityType", EntityKeyType.ENTITY_FIELD, StringFilterPredicate.StringOperation.EQUAL, "EQUAL");
  944 +
  945 + // Equal Operation
  946 +
  947 + EntityDataPageLink pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  948 + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersEqualString);
  949 + PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  950 + List<EntityData> loadedEntities = getLoadedEntities(data, query);
  951 + Assert.assertEquals(equalStrings.size(), loadedEntities.size());
  952 +
  953 + List<String> loadedStrings = loadedEntities.stream().map(entityData ->
  954 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("attributeString").getValue()).collect(Collectors.toList());
  955 +
  956 + Assert.assertTrue(listEqualWithoutOrder(equalStrings, loadedStrings));
  957 +
  958 + // Not equal Operation
  959 +
  960 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  961 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersNotEqualString);
  962 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  963 + loadedEntities = getLoadedEntities(data, query);
  964 + Assert.assertEquals(notEqualStrings.size(), loadedEntities.size());
  965 +
  966 + loadedStrings = loadedEntities.stream().map(entityData ->
  967 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("attributeString").getValue()).collect(Collectors.toList());
  968 +
  969 + Assert.assertTrue(listEqualWithoutOrder(notEqualStrings, loadedStrings));
  970 +
  971 + // Starts with Operation
  972 +
  973 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  974 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersStartsWithString);
  975 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  976 + loadedEntities = getLoadedEntities(data, query);
  977 + Assert.assertEquals(startsWithStrings.size(), loadedEntities.size());
  978 +
  979 + loadedStrings = loadedEntities.stream().map(entityData ->
  980 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("attributeString").getValue()).collect(Collectors.toList());
  981 +
  982 + Assert.assertTrue(listEqualWithoutOrder(startsWithStrings, loadedStrings));
  983 +
  984 + // Ends with Operation
  985 +
  986 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  987 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersEndsWithString);
  988 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  989 + loadedEntities = getLoadedEntities(data, query);
  990 + Assert.assertEquals(endsWithStrings.size(), loadedEntities.size());
  991 +
  992 + loadedStrings = loadedEntities.stream().map(entityData ->
  993 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("attributeString").getValue()).collect(Collectors.toList());
  994 +
  995 + Assert.assertTrue(listEqualWithoutOrder(endsWithStrings, loadedStrings));
  996 +
  997 + // Contains Operation
  998 +
  999 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  1000 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersContainsString);
  1001 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  1002 + loadedEntities = getLoadedEntities(data, query);
  1003 + Assert.assertEquals(containsStrings.size(), loadedEntities.size());
  1004 +
  1005 + loadedStrings = loadedEntities.stream().map(entityData ->
  1006 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("attributeString").getValue()).collect(Collectors.toList());
  1007 +
  1008 + Assert.assertTrue(listEqualWithoutOrder(containsStrings, loadedStrings));
  1009 +
  1010 + // Not contains Operation
  1011 +
  1012 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  1013 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFiltersNotContainsString);
  1014 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  1015 + loadedEntities = getLoadedEntities(data, query);
  1016 + Assert.assertEquals(notContainsStrings.size(), loadedEntities.size());
  1017 +
  1018 + loadedStrings = loadedEntities.stream().map(entityData ->
  1019 + entityData.getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("attributeString").getValue()).collect(Collectors.toList());
  1020 +
  1021 + Assert.assertTrue(listEqualWithoutOrder(notContainsStrings, loadedStrings));
  1022 +
  1023 + // Device type filters Operation
  1024 +
  1025 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  1026 + query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, deviceTypeFilters);
  1027 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  1028 + loadedEntities = getLoadedEntities(data, query);
  1029 + Assert.assertEquals(devices.size(), loadedEntities.size());
  1030 +
  1031 + deviceService.deleteDevicesByTenantId(tenantId);
  1032 + }
  1033 +
  1034 + @Test
  1035 + public void testBuildSimplePredicateQueryOperations() throws JsonMappingException, JsonProcessingException, ExecutionException, InterruptedException{
  1036 +
  1037 + List<Device> devices = new ArrayList<>();
  1038 +
  1039 + for (int i = 0; i < 10; i++) {
  1040 + Device device = new Device();
  1041 + device.setTenantId(tenantId);
  1042 + device.setName("Device" + i);
  1043 + device.setType("default");
  1044 + device.setLabel("testLabel" + (int) (Math.random() * 1000));
  1045 + devices.add(deviceService.saveDevice(device));
  1046 + //TO make sure devices have different created time
  1047 + Thread.sleep(1);
  1048 + }
  1049 +
  1050 + DeviceTypeFilter filter = new DeviceTypeFilter();
  1051 + filter.setDeviceType("default");
  1052 + filter.setDeviceNameFilter("");
  1053 +
  1054 + EntityDataSortOrder sortOrder = new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), EntityDataSortOrder.Direction.DESC);
  1055 +
  1056 + List<KeyFilter> deviceTypeFilters = createStringKeyFilters("entityType", EntityKeyType.ENTITY_FIELD, StringFilterPredicate.StringOperation.EQUAL, "EQUAL");
  1057 +
  1058 + KeyFilter createdTimeFilter = createNumericKeyFilter("createdTime", EntityKeyType.ENTITY_FIELD, NumericFilterPredicate.NumericOperation.GREATER, 1L);
  1059 + List<KeyFilter> createdTimeFilters = Collections.singletonList(createdTimeFilter);
  1060 +
  1061 + List<KeyFilter> nameFilters = createStringKeyFilters("name", EntityKeyType.ENTITY_FIELD, StringFilterPredicate.StringOperation.CONTAINS, "Device");
  1062 +
  1063 + List<EntityKey> entityFields = Arrays.asList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"),
  1064 + new EntityKey(EntityKeyType.ENTITY_FIELD, "type"));
  1065 +
  1066 + // Device type filters
  1067 +
  1068 + EntityDataPageLink pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  1069 + EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, null, deviceTypeFilters);
  1070 + PageData data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  1071 + List<EntityData> loadedEntities = getLoadedEntities(data, query);
  1072 + Assert.assertEquals(devices.size(), loadedEntities.size());
  1073 +
  1074 + // Device create time filters
  1075 +
  1076 + pageLink = new EntityDataPageLink(100, 0, null, sortOrder);
  1077 + query = new EntityDataQuery(filter, pageLink, entityFields, null, createdTimeFilters);
  1078 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  1079 + loadedEntities = getLoadedEntities(data, query);
  1080 + Assert.assertEquals(devices.size(), loadedEntities.size());
  1081 +
  1082 + // Device name filters
  1083 +
  1084 + pageLink = new EntityDataPageLink(100, 0, null, null);
  1085 + query = new EntityDataQuery(filter, pageLink, entityFields, null, nameFilters);
  1086 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  1087 + loadedEntities = getLoadedEntities(data, query);
  1088 + Assert.assertEquals(devices.size(), loadedEntities.size());
  1089 +
  1090 + deviceService.deleteDevicesByTenantId(tenantId);
  1091 + }
  1092 +
  1093 + private Boolean listEqualWithoutOrder(List<String> A, List<String> B) {
  1094 + return A.containsAll(B) && B.containsAll(A);
  1095 + }
  1096 +
  1097 + private List<EntityData> getLoadedEntities(PageData data, EntityDataQuery query) {
  1098 + List<EntityData> loadedEntities = new ArrayList<>(data.getData());
  1099 +
  1100 + while (data.hasNext()) {
  1101 + query = query.next();
  1102 + data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
  1103 + loadedEntities.addAll(data.getData());
  1104 + }
  1105 + return loadedEntities;
  1106 + }
  1107 +
  1108 + private List<KeyFilter> createStringKeyFilters(String key, EntityKeyType keyType, StringFilterPredicate.StringOperation operation, String value){
  1109 + KeyFilter filter = new KeyFilter();
  1110 + filter.setKey(new EntityKey(keyType, key));
  1111 + StringFilterPredicate predicate = new StringFilterPredicate();
  1112 + predicate.setValue(FilterPredicateValue.fromString(value));
  1113 + predicate.setOperation(operation);
  1114 + predicate.setIgnoreCase(true);
  1115 + filter.setPredicate(predicate);
  1116 + return Collections.singletonList(filter);
  1117 + }
  1118 +
  1119 + private KeyFilter createNumericKeyFilter(String key, EntityKeyType keyType, NumericFilterPredicate.NumericOperation operation, double value){
  1120 + KeyFilter filter = new KeyFilter();
  1121 + filter.setKey(new EntityKey(keyType, key));
  1122 + NumericFilterPredicate predicate = new NumericFilterPredicate();
  1123 + predicate.setValue(FilterPredicateValue.fromDouble(value));
  1124 + predicate.setOperation(operation);
  1125 + filter.setPredicate(predicate);
  1126 +
  1127 + return filter;
  1128 + }
  1129 +
614 1130 private ListenableFuture<List<Void>> saveLongAttribute(EntityId entityId, String key, long value, String scope) {
615 1131 KvEntry attrValue = new LongDataEntry(key, value);
616 1132 AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L);
617 1133 return attributesService.save(SYSTEM_TENANT_ID, entityId, scope, Collections.singletonList(attr));
618 1134 }
  1135 +
  1136 + private ListenableFuture<List<Void>> saveStringAttribute(EntityId entityId, String key, String value, String scope) {
  1137 + KvEntry attrValue = new StringDataEntry(key, value);
  1138 + AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L);
  1139 + return attributesService.save(SYSTEM_TENANT_ID, entityId, scope, Collections.singletonList(attr));
  1140 + }
  1141 +
  1142 + private ListenableFuture<List<Void>> saveLongTimeseries(EntityId entityId, String key, Double value) {
  1143 + TsKvEntity tsKv = new TsKvEntity();
  1144 + tsKv.setStrKey(key);
  1145 + tsKv.setDoubleValue(value);
  1146 + KvEntry telemetryValue = new DoubleDataEntry(key, value);
  1147 + BasicTsKvEntry timeseries = new BasicTsKvEntry(42L, telemetryValue);
  1148 + return timeseriesService.save(SYSTEM_TENANT_ID, entityId, timeseries);
  1149 + }
619 1150 }
... ...