Improve data query
This commit is contained in:
parent
42bf0a37a2
commit
eebcccc6fd
@ -114,25 +114,23 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
||||
entityFieldsSelection = String.format("e.id, '%s'", entityType.name());
|
||||
}
|
||||
String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping);
|
||||
String selection = entityFieldsSelection;
|
||||
String topSelection = "entities.*";
|
||||
if (!StringUtils.isEmpty(latestSelection)) {
|
||||
selection = entityFieldsSelection + ", " + latestSelection;
|
||||
topSelection = topSelection + ", " + latestSelection;
|
||||
}
|
||||
|
||||
String fromClause = String.format("from (select %s from %s e where %s) entities %s",
|
||||
selection,
|
||||
String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result",
|
||||
topSelection,
|
||||
entityFieldsSelection,
|
||||
entityTableMap.get(entityType),
|
||||
entityWhereClause,
|
||||
latestJoins);
|
||||
|
||||
if (!StringUtils.isEmpty(whereClause)) {
|
||||
fromClause = String.format("%s where %s", fromClause, whereClause);
|
||||
}
|
||||
latestJoins,
|
||||
whereClause);
|
||||
|
||||
int totalElements = ((BigInteger)entityManager.createNativeQuery(String.format("select count(*) %s", fromClause))
|
||||
.getSingleResult()).intValue();
|
||||
|
||||
String dataQuery = String.format("select entities.* %s", fromClause);
|
||||
String dataQuery = String.format("select * %s", fromClause);
|
||||
|
||||
EntityDataSortOrder sortOrder = pageLink.getSortOrder();
|
||||
if (sortOrder != null) {
|
||||
@ -198,12 +196,18 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
||||
private String buildWhere(List<EntityKeyMapping> selectionMapping, List<EntityKeyMapping> latestFiltersMapping, String searchText) {
|
||||
String latestFilters = EntityKeyMapping.buildQuery(latestFiltersMapping);
|
||||
String textSearchQuery = this.buildTextSearchQuery(selectionMapping, searchText);
|
||||
String query;
|
||||
if (!StringUtils.isEmpty(latestFilters) && !StringUtils.isEmpty(textSearchQuery)) {
|
||||
return String.join(" AND ", latestFilters, textSearchQuery);
|
||||
query = String.join(" AND ", latestFilters, textSearchQuery);
|
||||
} else if (!StringUtils.isEmpty(latestFilters)) {
|
||||
return latestFilters;
|
||||
query = latestFilters;
|
||||
} else {
|
||||
return textSearchQuery;
|
||||
query = textSearchQuery;
|
||||
}
|
||||
if (!StringUtils.isEmpty(query)) {
|
||||
return String.format("where %s", query);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ public class EntityDataAdapter {
|
||||
Map<String, TsValue[]> timeseries = new HashMap<>();
|
||||
EntityData entityData = new EntityData(entityId, latest, timeseries);
|
||||
for (EntityKeyMapping mapping: selectionMapping) {
|
||||
if (!mapping.isIgnore()) {
|
||||
EntityKey entityKey = mapping.getEntityKey();
|
||||
Object value = row[mapping.getIndex()];
|
||||
String strValue;
|
||||
@ -71,6 +72,7 @@ public class EntityDataAdapter {
|
||||
TsValue tsValue = new TsValue(ts, strValue);
|
||||
latest.computeIfAbsent(entityKey.getType(), entityKeyType -> new HashMap<>()).put(entityKey.getKey(), tsValue);
|
||||
}
|
||||
}
|
||||
return entityData;
|
||||
}
|
||||
|
||||
@ -80,8 +82,8 @@ public class EntityDataAdapter {
|
||||
// check number
|
||||
if (strVal.length() > 0) {
|
||||
try {
|
||||
int intVal = Integer.parseInt(strVal);
|
||||
return Integer.toString(intVal);
|
||||
long longVal = Long.parseLong(strVal);
|
||||
return Long.toString(longVal);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
try {
|
||||
|
||||
@ -35,6 +35,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -65,6 +66,7 @@ public class EntityKeyMapping {
|
||||
private boolean isLatest;
|
||||
private boolean isSelection;
|
||||
private boolean isSortOrder;
|
||||
private boolean ignore = false;
|
||||
private List<KeyFilter> keyFilters;
|
||||
private EntityKey entityKey;
|
||||
|
||||
@ -151,43 +153,63 @@ public class EntityKeyMapping {
|
||||
query.getKeyFilters().stream().collect(Collectors.groupingBy(KeyFilter::getKey)) : Collections.emptyMap();
|
||||
EntityDataSortOrder sortOrder = query.getPageLink().getSortOrder();
|
||||
EntityKey sortOrderKey = sortOrder != null ? sortOrder.getKey() : null;
|
||||
EntityKeyMapping sortOrderMapping = null;
|
||||
int index = 2;
|
||||
List<EntityKeyMapping> mappings = new ArrayList<>();
|
||||
for (EntityKey entityField : entityFields) {
|
||||
List<EntityKeyMapping> entityFieldsMappings = entityFields.stream().map(
|
||||
key -> {
|
||||
EntityKeyMapping mapping = new EntityKeyMapping();
|
||||
mapping.setIndex(index);
|
||||
mapping.setAlias(String.format("alias%s", index));
|
||||
mapping.setKeyFilters(filters.remove(entityField));
|
||||
mapping.setLatest(false);
|
||||
mapping.setSelection(true);
|
||||
mapping.setEntityKey(entityField);
|
||||
if (entityField.equals(sortOrderKey)) {
|
||||
mapping.setSortOrder(true);
|
||||
sortOrderMapping = mapping;
|
||||
} else {
|
||||
mapping.setSortOrder(false);
|
||||
mapping.setEntityKey(key);
|
||||
return mapping;
|
||||
}
|
||||
mappings.add(mapping);
|
||||
index++;
|
||||
}
|
||||
for (EntityKey latestField : latestValues) {
|
||||
).collect(Collectors.toList());
|
||||
List<EntityKeyMapping> latestMappings = latestValues.stream().map(
|
||||
key -> {
|
||||
EntityKeyMapping mapping = new EntityKeyMapping();
|
||||
mapping.setIndex(index);
|
||||
mapping.setAlias(String.format("alias%s", index));
|
||||
mapping.setKeyFilters(filters.remove(latestField));
|
||||
mapping.setLatest(true);
|
||||
mapping.setSelection(true);
|
||||
mapping.setEntityKey(latestField);
|
||||
if (latestField.equals(sortOrderKey)) {
|
||||
mapping.setSortOrder(true);
|
||||
sortOrderMapping = mapping;
|
||||
} else {
|
||||
mapping.setSortOrder(false);
|
||||
mapping.setEntityKey(key);
|
||||
return mapping;
|
||||
}
|
||||
mappings.add(mapping);
|
||||
).collect(Collectors.toList());
|
||||
if (sortOrderKey != null) {
|
||||
Optional<EntityKeyMapping> existing;
|
||||
if (sortOrderKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||
existing =
|
||||
entityFieldsMappings.stream().filter(mapping -> mapping.entityKey.equals(sortOrderKey)).findFirst();
|
||||
} else {
|
||||
existing =
|
||||
latestMappings.stream().filter(mapping -> mapping.entityKey.equals(sortOrderKey)).findFirst();
|
||||
}
|
||||
if (existing.isPresent()) {
|
||||
existing.get().setSortOrder(true);
|
||||
} else {
|
||||
EntityKeyMapping sortOrderMapping = new EntityKeyMapping();
|
||||
sortOrderMapping.setLatest(!sortOrderKey.getType().equals(EntityKeyType.ENTITY_FIELD));
|
||||
sortOrderMapping.setSelection(true);
|
||||
sortOrderMapping.setEntityKey(sortOrderKey);
|
||||
sortOrderMapping.setSortOrder(true);
|
||||
sortOrderMapping.setIgnore(true);
|
||||
if (sortOrderKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||
entityFieldsMappings.add(sortOrderMapping);
|
||||
} else {
|
||||
latestMappings.add(sortOrderMapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<EntityKeyMapping> mappings = new ArrayList<>();
|
||||
mappings.addAll(entityFieldsMappings);
|
||||
mappings.addAll(latestMappings);
|
||||
for (EntityKeyMapping mapping : mappings) {
|
||||
mapping.setIndex(index);
|
||||
mapping.setAlias(String.format("alias%s", index));
|
||||
mapping.setKeyFilters(filters.remove(mapping.entityKey));
|
||||
if (mapping.getEntityKey().getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||
index++;
|
||||
} else {
|
||||
index +=2;
|
||||
}
|
||||
}
|
||||
if (!filters.isEmpty()) {
|
||||
for (EntityKey filterField : filters.keySet()) {
|
||||
EntityKeyMapping mapping = new EntityKeyMapping();
|
||||
@ -201,37 +223,28 @@ public class EntityKeyMapping {
|
||||
index +=1;
|
||||
}
|
||||
}
|
||||
if (sortOrderKey != null && sortOrderMapping == null) {
|
||||
sortOrderMapping = new EntityKeyMapping();
|
||||
sortOrderMapping.setIndex(index);
|
||||
sortOrderMapping.setAlias(String.format("alias%s", index));
|
||||
sortOrderMapping.setLatest(!sortOrderKey.getType().equals(EntityKeyType.ENTITY_FIELD));
|
||||
sortOrderMapping.setSelection(true);
|
||||
sortOrderMapping.setEntityKey(sortOrderKey);
|
||||
sortOrderMapping.setSortOrder(true);
|
||||
mappings.add(sortOrderMapping);
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
private String buildAttributeSelection() {
|
||||
String attrValAlias = getValueAlias();
|
||||
String attrTsAlias = getTsAlias();
|
||||
String attrTsSelection = String.format("%s.last_update_ts as %s", alias, attrTsAlias);
|
||||
String attrValSelection =
|
||||
String.format("coalesce(cast(%s.bool_v as varchar), '') || " +
|
||||
String.format("(coalesce(cast(%s.bool_v as varchar), '') || " +
|
||||
"coalesce(%s.str_v, '') || " +
|
||||
"coalesce(cast(%s.long_v as varchar), '') || " +
|
||||
"coalesce(cast(%s.dbl_v as varchar), '') || " +
|
||||
"coalesce(cast(%s.json_v as varchar), '')) as %s", alias, alias, alias, alias, alias, attrValAlias);
|
||||
return String.join(", ", attrTsSelection, attrValSelection);
|
||||
String attrTsSelection = String.format("%s.last_update_ts as %s", alias, attrTsAlias);
|
||||
return String.join(", ", attrValSelection, attrTsSelection);
|
||||
}
|
||||
|
||||
private String buildTimeseriesSelection() {
|
||||
// TODO:
|
||||
String attrValAlias = getValueAlias();
|
||||
String attrTsAlias = getTsAlias();
|
||||
return String.format("(select 1) as %s, (select '') as %s", attrTsAlias, attrValAlias);
|
||||
return String.format("(select '') as %s, (select 1) as %s", attrValAlias, attrTsAlias);
|
||||
}
|
||||
|
||||
private String buildKeyQuery(String alias, KeyFilter keyFilter) {
|
||||
|
||||
@ -15,10 +15,14 @@
|
||||
*/
|
||||
package org.thingsboard.server.dao.service;
|
||||
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.thingsboard.server.common.data.DataConstants;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
@ -26,6 +30,10 @@ import org.thingsboard.server.common.data.id.CustomerId;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
||||
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
|
||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||
import org.thingsboard.server.common.data.kv.LongDataEntry;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.query.DeviceTypeFilter;
|
||||
import org.thingsboard.server.common.data.query.EntityCountQuery;
|
||||
@ -37,14 +45,21 @@ import org.thingsboard.server.common.data.query.EntityFilter;
|
||||
import org.thingsboard.server.common.data.query.EntityKey;
|
||||
import org.thingsboard.server.common.data.query.EntityKeyType;
|
||||
import org.thingsboard.server.common.data.query.EntityListFilter;
|
||||
import org.thingsboard.server.common.data.query.KeyFilter;
|
||||
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
|
||||
import org.thingsboard.server.dao.attributes.AttributesService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class BaseEntityServiceTest extends AbstractServiceTest {
|
||||
|
||||
@Autowired
|
||||
private AttributesService attributesService;
|
||||
|
||||
private TenantId tenantId;
|
||||
|
||||
@Before
|
||||
@ -105,7 +120,7 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindEntityDataByQuery() {
|
||||
public void testSimpleFindEntityDataByQuery() {
|
||||
List<Device> devices = new ArrayList<>();
|
||||
for (int i = 0; i < 97; i++) {
|
||||
Device device = new Device();
|
||||
@ -162,5 +177,96 @@ public abstract class BaseEntityServiceTest extends AbstractServiceTest {
|
||||
data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
|
||||
Assert.assertEquals(11, data.getTotalElements());
|
||||
Assert.assertEquals("Device19", data.getData().get(0).getLatest().get(EntityKeyType.ENTITY_FIELD).get("name").getValue());
|
||||
|
||||
deviceService.deleteDevicesByTenantId(tenantId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindEntityDataByQueryWithAttributes() throws ExecutionException, InterruptedException {
|
||||
|
||||
List<Device> devices = new ArrayList<>();
|
||||
List<Long> temperatures = new ArrayList<>();
|
||||
List<Long> highTemperatures = new ArrayList<>();
|
||||
for (int i=0;i<67;i++) {
|
||||
Device device = new Device();
|
||||
device.setTenantId(tenantId);
|
||||
device.setName("Device"+i);
|
||||
device.setType("default");
|
||||
device.setLabel("testLabel"+(int)(Math.random()*1000));
|
||||
devices.add(deviceService.saveDevice(device));
|
||||
long temperature = (long)(Math.random()*100);
|
||||
temperatures.add(temperature);
|
||||
if (temperature > 45) {
|
||||
highTemperatures.add(temperature);
|
||||
}
|
||||
}
|
||||
|
||||
List<ListenableFuture<List<Void>>> attributeFutures = new ArrayList<>();
|
||||
for (int i=0;i<devices.size();i++) {
|
||||
Device device = devices.get(i);
|
||||
attributeFutures.add(saveLongAttribute(device.getId(), "temperature", temperatures.get(i), DataConstants.CLIENT_SCOPE));
|
||||
}
|
||||
Futures.successfulAsList(attributeFutures).get();
|
||||
|
||||
DeviceTypeFilter filter = new DeviceTypeFilter();
|
||||
filter.setDeviceType("default");
|
||||
filter.setDeviceNameFilter("");
|
||||
|
||||
EntityDataSortOrder sortOrder = new EntityDataSortOrder(
|
||||
new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
|
||||
);
|
||||
EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
|
||||
List<EntityKey> entityFields = Collections.singletonList(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"));
|
||||
List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
|
||||
|
||||
EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, null);
|
||||
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());
|
||||
}
|
||||
Assert.assertEquals(67, loadedEntities.size());
|
||||
List<String> loadedTemperatures = loadedEntities.stream().map(entityData ->
|
||||
entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
|
||||
List<String> deviceTemperatures = temperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
|
||||
Assert.assertEquals(deviceTemperatures, loadedTemperatures);
|
||||
|
||||
pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
|
||||
KeyFilter highTemperatureFilter = new KeyFilter();
|
||||
highTemperatureFilter.setKey(new EntityKey(EntityKeyType.ATTRIBUTE, "temperature"));
|
||||
NumericFilterPredicate predicate = new NumericFilterPredicate();
|
||||
predicate.setValue(45);
|
||||
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||
highTemperatureFilter.setPredicate(predicate);
|
||||
List<KeyFilter> keyFilters = Collections.singletonList(highTemperatureFilter);
|
||||
|
||||
query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFilters);
|
||||
|
||||
data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
|
||||
|
||||
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());
|
||||
}
|
||||
Assert.assertEquals(highTemperatures.size(), loadedEntities.size());
|
||||
|
||||
List<String> loadedHighTemperatures = loadedEntities.stream().map(entityData ->
|
||||
entityData.getLatest().get(EntityKeyType.ATTRIBUTE).get("temperature").getValue()).collect(Collectors.toList());
|
||||
List<String> deviceHighTemperatures = highTemperatures.stream().map(aLong -> Long.toString(aLong)).collect(Collectors.toList());
|
||||
|
||||
Assert.assertEquals(deviceHighTemperatures, loadedHighTemperatures);
|
||||
|
||||
deviceService.deleteDevicesByTenantId(tenantId);
|
||||
}
|
||||
|
||||
private ListenableFuture<List<Void>> saveLongAttribute(EntityId entityId, String key, long value, String scope) {
|
||||
KvEntry attrValue = new LongDataEntry(key, value);
|
||||
AttributeKvEntry attr = new BaseAttributeKvEntry(attrValue, 42L);
|
||||
return attributesService.save(SYSTEM_TENANT_ID, entityId, scope, Collections.singletonList(attr));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user