Entity data query builder improvements + initial tests
This commit is contained in:
parent
3acae2fbf2
commit
9460b09eef
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.common.data.query;
|
package org.thingsboard.server.common.data.query;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@ -22,6 +23,12 @@ public class EntityDataPageLink {
|
|||||||
|
|
||||||
private final int pageSize;
|
private final int pageSize;
|
||||||
private final int page;
|
private final int page;
|
||||||
|
private final String textSearch;
|
||||||
private final EntityDataSortOrder sortOrder;
|
private final EntityDataSortOrder sortOrder;
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public EntityDataPageLink nextPageLink() {
|
||||||
|
return new EntityDataPageLink(this.pageSize, this.page+1, this.textSearch, this.sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.common.data.query;
|
package org.thingsboard.server.common.data.query;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -41,4 +42,9 @@ public class EntityDataQuery extends EntityCountQuery {
|
|||||||
this.latestValues = latestValues;
|
this.latestValues = latestValues;
|
||||||
this.keyFilters = keyFilters;
|
this.keyFilters = keyFilters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public EntityDataQuery next() {
|
||||||
|
return new EntityDataQuery(getEntityFilter(), pageLink.nextPageLink(), entityFields, latestValues, keyFilters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,6 @@ import lombok.Data;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class EntityKey {
|
public class EntityKey {
|
||||||
private EntityKeyType type;
|
private final EntityKeyType type;
|
||||||
private String key;
|
private final String key;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,35 +18,24 @@ package org.thingsboard.server.dao.sql.query;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import org.thingsboard.server.common.data.DataConstants;
|
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.UUIDConverter;
|
import org.thingsboard.server.common.data.UUIDConverter;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
|
||||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.page.PageData;
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
import org.thingsboard.server.common.data.query.AssetTypeFilter;
|
import org.thingsboard.server.common.data.query.AssetTypeFilter;
|
||||||
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
|
|
||||||
import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
|
|
||||||
import org.thingsboard.server.common.data.query.DeviceTypeFilter;
|
import org.thingsboard.server.common.data.query.DeviceTypeFilter;
|
||||||
import org.thingsboard.server.common.data.query.EntityCountQuery;
|
import org.thingsboard.server.common.data.query.EntityCountQuery;
|
||||||
import org.thingsboard.server.common.data.query.EntityData;
|
import org.thingsboard.server.common.data.query.EntityData;
|
||||||
import org.thingsboard.server.common.data.query.EntityDataPageLink;
|
import org.thingsboard.server.common.data.query.EntityDataPageLink;
|
||||||
import org.thingsboard.server.common.data.query.EntityDataQuery;
|
import org.thingsboard.server.common.data.query.EntityDataQuery;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityDataSortOrder;
|
||||||
import org.thingsboard.server.common.data.query.EntityFilter;
|
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.EntityKeyType;
|
||||||
import org.thingsboard.server.common.data.query.EntityListFilter;
|
import org.thingsboard.server.common.data.query.EntityListFilter;
|
||||||
import org.thingsboard.server.common.data.query.EntityNameFilter;
|
import org.thingsboard.server.common.data.query.EntityNameFilter;
|
||||||
import org.thingsboard.server.common.data.query.EntityViewTypeFilter;
|
import org.thingsboard.server.common.data.query.EntityViewTypeFilter;
|
||||||
import org.thingsboard.server.common.data.query.FilterPredicateType;
|
|
||||||
import org.thingsboard.server.common.data.query.KeyFilter;
|
|
||||||
import org.thingsboard.server.common.data.query.KeyFilterPredicate;
|
|
||||||
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
|
|
||||||
import org.thingsboard.server.common.data.query.SingleEntityFilter;
|
import org.thingsboard.server.common.data.query.SingleEntityFilter;
|
||||||
import org.thingsboard.server.common.data.query.StringFilterPredicate;
|
|
||||||
import org.thingsboard.server.common.data.query.TsValue;
|
|
||||||
import org.thingsboard.server.dao.util.SqlDao;
|
import org.thingsboard.server.dao.util.SqlDao;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
@ -57,7 +46,7 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -66,25 +55,6 @@ import java.util.stream.Collectors;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
||||||
|
|
||||||
private static final Map<String, String> entityFieldColumnMap = new HashMap<>();
|
|
||||||
static {
|
|
||||||
entityFieldColumnMap.put("createdTime", "id");
|
|
||||||
entityFieldColumnMap.put("name", "name");
|
|
||||||
entityFieldColumnMap.put("type", "type");
|
|
||||||
entityFieldColumnMap.put("label", "label");
|
|
||||||
entityFieldColumnMap.put("firstName", "first_name");
|
|
||||||
entityFieldColumnMap.put("lastName", "last_name");
|
|
||||||
entityFieldColumnMap.put("email", "email");
|
|
||||||
entityFieldColumnMap.put("title", "title");
|
|
||||||
entityFieldColumnMap.put("country", "country");
|
|
||||||
entityFieldColumnMap.put("state", "state");
|
|
||||||
entityFieldColumnMap.put("city", "city");
|
|
||||||
entityFieldColumnMap.put("address", "address");
|
|
||||||
entityFieldColumnMap.put("address2", "address2");
|
|
||||||
entityFieldColumnMap.put("zip", "zip");
|
|
||||||
entityFieldColumnMap.put("phone", "phone");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Map<EntityType, String> entityTableMap = new HashMap<>();
|
private static final Map<EntityType, String> entityTableMap = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
entityTableMap.put(EntityType.ASSET, "asset");
|
entityTableMap.put(EntityType.ASSET, "asset");
|
||||||
@ -112,210 +82,77 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
@Override
|
@Override
|
||||||
public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
|
public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
|
||||||
EntityType entityType = resolveEntityType(query.getEntityFilter());
|
EntityType entityType = resolveEntityType(query.getEntityFilter());
|
||||||
|
EntityDataPageLink pageLink = query.getPageLink();
|
||||||
|
|
||||||
List<EntityKeyMapping> mappings = prepareKeyMapping(query);
|
List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(query);
|
||||||
|
|
||||||
List<EntityKeyMapping> selectionMapping = mappings.stream().filter(mapping -> mapping.isSelection())
|
List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest())
|
List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
List<EntityKeyMapping> latestSelectionMapping = selectionMapping.stream().filter(mapping -> mapping.isLatest())
|
List<EntityKeyMapping> latestSelectionMapping = selectionMapping.stream().filter(EntityKeyMapping::isLatest)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
List<EntityKeyMapping> filterMapping = mappings.stream().filter(mapping -> mapping.hasFilter())
|
List<EntityKeyMapping> filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest())
|
List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
List<EntityKeyMapping> latestFiltersMapping = filterMapping.stream().filter(mapping -> mapping.isLatest())
|
List<EntityKeyMapping> latestFiltersMapping = filterMapping.stream().filter(EntityKeyMapping::isLatest)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(mapping -> mapping.isLatest())
|
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
|
||||||
String entityWhereClause = this.buildEntityWhere(tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType);
|
String entityWhereClause = this.buildEntityWhere(tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType);
|
||||||
String latestJoins = this.buildLatestJoins(entityType, allLatestMappings);
|
String latestJoins = EntityKeyMapping.buildLatestJoins(entityType, allLatestMappings);
|
||||||
String latestFilters = this.buildLatestQuery(latestFiltersMapping);
|
String whereClause = this.buildWhere(selectionMapping, latestFiltersMapping, pageLink.getTextSearch());
|
||||||
|
String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping);
|
||||||
String countQuery = String.format("select count(*) from (select e.id from %s e where %s) entities %s",
|
|
||||||
entityTableMap.get(entityType), entityWhereClause, latestJoins);
|
|
||||||
if (!StringUtils.isEmpty(latestFilters)) {
|
|
||||||
countQuery = String.format("%s where %s", countQuery, latestFilters);
|
|
||||||
}
|
|
||||||
int totalElements = ((BigInteger)entityManager.createNativeQuery(countQuery)
|
|
||||||
.getSingleResult()).intValue();
|
|
||||||
|
|
||||||
String entityFieldsSelection = this.buildEntityFieldsSelection(entityFieldsSelectionMapping);
|
|
||||||
if (!StringUtils.isEmpty(entityFieldsSelection)) {
|
if (!StringUtils.isEmpty(entityFieldsSelection)) {
|
||||||
entityFieldsSelection = String.format("e.id, %s", entityFieldsSelection);
|
entityFieldsSelection = String.format("e.id, '%s', %s", entityType.name(), entityFieldsSelection);
|
||||||
} else {
|
} else {
|
||||||
entityFieldsSelection = "e.id";
|
entityFieldsSelection = String.format("e.id, '%s'", entityType.name());
|
||||||
}
|
}
|
||||||
String latestSelection = this.buildLatestSelections(latestSelectionMapping);
|
String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping);
|
||||||
String topSelection = "entities.*";
|
String selection = entityFieldsSelection;
|
||||||
if (!StringUtils.isEmpty(latestSelection)) {
|
if (!StringUtils.isEmpty(latestSelection)) {
|
||||||
topSelection = topSelection + ", " + latestSelection;
|
selection = entityFieldsSelection + ", " + latestSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
String dataQuery = String.format("select %s from (select %s from %s e where %s) entities %s", topSelection,
|
String fromClause = String.format("from (select %s from %s e where %s) entities %s",
|
||||||
entityFieldsSelection,
|
selection,
|
||||||
entityTableMap.get(entityType),
|
entityTableMap.get(entityType),
|
||||||
entityWhereClause,
|
entityWhereClause,
|
||||||
latestJoins);
|
latestJoins);
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(latestFilters)) {
|
if (!StringUtils.isEmpty(whereClause)) {
|
||||||
dataQuery = String.format("%s where %s", dataQuery, latestFilters);
|
fromClause = String.format("%s where %s", fromClause, whereClause);
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityDataPageLink pageLink = query.getPageLink();
|
int totalElements = ((BigInteger)entityManager.createNativeQuery(String.format("select count(*) %s", fromClause))
|
||||||
|
.getSingleResult()).intValue();
|
||||||
|
|
||||||
// TODO: order by
|
String dataQuery = String.format("select entities.* %s", fromClause);
|
||||||
|
|
||||||
|
EntityDataSortOrder sortOrder = pageLink.getSortOrder();
|
||||||
|
if (sortOrder != null) {
|
||||||
|
Optional<EntityKeyMapping> sortOrderMappingOpt = mappings.stream().filter(EntityKeyMapping::isSortOrder).findFirst();
|
||||||
|
if (sortOrderMappingOpt.isPresent()) {
|
||||||
|
EntityKeyMapping sortOrderMapping = sortOrderMappingOpt.get();
|
||||||
|
dataQuery = String.format("%s order by %s", dataQuery, sortOrderMapping.getValueAlias());
|
||||||
|
if (sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC) {
|
||||||
|
dataQuery += " asc";
|
||||||
|
} else {
|
||||||
|
dataQuery += " desc";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
int startIndex = pageLink.getPageSize() * pageLink.getPage();
|
int startIndex = pageLink.getPageSize() * pageLink.getPage();
|
||||||
if (pageLink.getPageSize() > 0) {
|
if (pageLink.getPageSize() > 0) {
|
||||||
dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex);
|
dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex);
|
||||||
}
|
}
|
||||||
List result = entityManager.createNativeQuery(dataQuery).getResultList();
|
List rows = entityManager.createNativeQuery(dataQuery).getResultList();
|
||||||
int totalPages = pageLink.getPageSize() > 0 ? (int)Math.ceil((float)totalElements / pageLink.getPageSize()) : 1;
|
return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements);
|
||||||
boolean hasNext = pageLink.getPageSize() > 0 && totalElements > startIndex + result.size();
|
|
||||||
List<EntityData> entitiesData = convertListToEntityData(result, entityType, selectionMapping);
|
|
||||||
return new PageData<>(entitiesData, totalPages, totalElements, hasNext);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<EntityData> convertListToEntityData(List<Object> result, EntityType entityType, List<EntityKeyMapping> selectionMapping) {
|
|
||||||
return result.stream().map(obj -> this.toEntityData(obj, entityType, selectionMapping)).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
private EntityData toEntityData(Object obj, EntityType entityType, List<EntityKeyMapping> selectionMapping) {
|
|
||||||
String id = obj instanceof String ? (String)obj : (String)((Object[]) obj)[0];
|
|
||||||
EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, UUIDConverter.fromString(id));
|
|
||||||
Map<EntityKeyType, Map<String, TsValue>> latest = new HashMap<>();
|
|
||||||
Map<String, TsValue[]> timeseries = new HashMap<>();
|
|
||||||
EntityData entityData = new EntityData(entityId, latest, timeseries);
|
|
||||||
for (EntityKeyMapping mapping: selectionMapping) {
|
|
||||||
Object value = ((Object[]) obj)[mapping.getIndex()];
|
|
||||||
// TODO:
|
|
||||||
}
|
|
||||||
return entityData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<EntityKeyMapping> prepareKeyMapping(EntityDataQuery query) {
|
|
||||||
List<EntityKey> entityFields = query.getEntityFields() != null ? query.getEntityFields() : Collections.emptyList();
|
|
||||||
List<EntityKey> latestValues = query.getLatestValues() != null ? query.getLatestValues() : Collections.emptyList();
|
|
||||||
Map<EntityKey, List<KeyFilter>> filters =
|
|
||||||
query.getKeyFilters() != null ?
|
|
||||||
query.getKeyFilters().stream().collect(Collectors.groupingBy(KeyFilter::getKey)) : Collections.emptyMap();
|
|
||||||
int index = 1;
|
|
||||||
List<EntityKeyMapping> mappings = new ArrayList<>();
|
|
||||||
for (EntityKey entityField : entityFields) {
|
|
||||||
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);
|
|
||||||
mappings.add(mapping);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
for (EntityKey latestField : latestValues) {
|
|
||||||
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);
|
|
||||||
mappings.add(mapping);
|
|
||||||
index +=2;
|
|
||||||
}
|
|
||||||
if (!filters.isEmpty()) {
|
|
||||||
for (EntityKey filterField : filters.keySet()) {
|
|
||||||
EntityKeyMapping mapping = new EntityKeyMapping();
|
|
||||||
mapping.setIndex(index);
|
|
||||||
mapping.setAlias(String.format("alias%s", index));
|
|
||||||
mapping.setKeyFilters(filters.get(filterField));
|
|
||||||
mapping.setLatest(!filterField.getType().equals(EntityKeyType.ENTITY_FIELD));
|
|
||||||
mapping.setSelection(false);
|
|
||||||
mapping.setEntityKey(filterField);
|
|
||||||
mappings.add(mapping);
|
|
||||||
index +=1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mappings;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildEntityFieldsSelection(List<EntityKeyMapping> entityFieldsSelectionMapping) {
|
|
||||||
return entityFieldsSelectionMapping.stream().map(mapping -> {
|
|
||||||
String column = entityFieldColumnMap.get(mapping.getEntityKey().getKey());
|
|
||||||
return String.format("e.%s as %s", column, mapping.getAlias());
|
|
||||||
}).collect(
|
|
||||||
Collectors.joining(", "));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildLatestSelections(List<EntityKeyMapping> latestSelectionMapping) {
|
|
||||||
return latestSelectionMapping.stream().map(mapping -> this.buildLatestSelection(mapping))
|
|
||||||
.collect(
|
|
||||||
Collectors.joining(", "));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildLatestSelection(EntityKeyMapping mapping) {
|
|
||||||
if (mapping.getEntityKey().getType().equals(EntityKeyType.TIME_SERIES)) {
|
|
||||||
return buildTimeseriesSelection(mapping);
|
|
||||||
} else {
|
|
||||||
return buildAttributeSelection(mapping);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildAttributeSelection(EntityKeyMapping mapping) {
|
|
||||||
String alias = mapping.getAlias();
|
|
||||||
String attrValAlias = alias + "_value";
|
|
||||||
String attrTsAlias = alias + "_ts";
|
|
||||||
String attrTsSelection = String.format("%s.last_update_ts as %s", alias, attrTsAlias);
|
|
||||||
String attrValSelection =
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildTimeseriesSelection(EntityKeyMapping mapping) {
|
|
||||||
// TODO:
|
|
||||||
String alias = mapping.getAlias();
|
|
||||||
String attrValAlias = alias + "_value";
|
|
||||||
String attrTsAlias = alias + "_ts";
|
|
||||||
return String.format("(select 1) as %s, (select '') as %s", attrTsAlias, attrValAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildLatestJoins(EntityType entityType, List<EntityKeyMapping> latestMappings) {
|
|
||||||
return latestMappings.stream().map(mapping -> this.buildLatestJoin(entityType, mapping)).collect(
|
|
||||||
Collectors.joining(" "));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildLatestJoin(EntityType entityType, EntityKeyMapping mapping) {
|
|
||||||
String join = mapping.hasFilter() ? "left join" : "left outer join";
|
|
||||||
if (mapping.getEntityKey().getType().equals(EntityKeyType.TIME_SERIES)) {
|
|
||||||
// TODO:
|
|
||||||
throw new RuntimeException("Not implemented!");
|
|
||||||
} else {
|
|
||||||
String alias = mapping.getAlias();
|
|
||||||
String query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type='%s' AND %s.attribute_key='%s'",
|
|
||||||
join, alias, alias, alias, entityType.name(), alias, mapping.getEntityKey().getKey());
|
|
||||||
if (!mapping.getEntityKey().getType().equals(EntityKeyType.ATTRIBUTE)) {
|
|
||||||
String scope;
|
|
||||||
if (mapping.getEntityKey().getType().equals(EntityKeyType.CLIENT_ATTRIBUTE)) {
|
|
||||||
scope = DataConstants.CLIENT_SCOPE;
|
|
||||||
} else if (mapping.getEntityKey().getType().equals(EntityKeyType.SHARED_ATTRIBUTE)) {
|
|
||||||
scope = DataConstants.SHARED_SCOPE;
|
|
||||||
} else {
|
|
||||||
scope = DataConstants.SERVER_SCOPE;
|
|
||||||
}
|
|
||||||
query = String.format("%s AND %s.attribute_type=%s", query, alias, scope);
|
|
||||||
}
|
|
||||||
return query;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildEntityWhere(TenantId tenantId,
|
private String buildEntityWhere(TenantId tenantId,
|
||||||
@ -326,7 +163,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
String permissionQuery = this.buildPermissionQuery(tenantId, customerId, entityType);
|
String permissionQuery = this.buildPermissionQuery(tenantId, customerId, entityType);
|
||||||
String entityFilterQuery = this.buildEntityFilterQuery(entityFilter);
|
String entityFilterQuery = this.buildEntityFilterQuery(entityFilter);
|
||||||
if (!entityFieldsFilters.isEmpty()) {
|
if (!entityFieldsFilters.isEmpty()) {
|
||||||
String entityFieldsQuery = this.buildEntityFieldsQuery(entityFieldsFilters);
|
String entityFieldsQuery = EntityKeyMapping.buildQuery(entityFieldsFilters);
|
||||||
return String.join(" and ", permissionQuery, entityFilterQuery, entityFieldsQuery);
|
return String.join(" and ", permissionQuery, entityFilterQuery, entityFieldsQuery);
|
||||||
} else {
|
} else {
|
||||||
return String.join(" and ", permissionQuery, entityFilterQuery);
|
return String.join(" and ", permissionQuery, entityFilterQuery);
|
||||||
@ -334,9 +171,9 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String buildPermissionQuery(TenantId tenantId, CustomerId customerId, EntityType entityType) {
|
private String buildPermissionQuery(TenantId tenantId, CustomerId customerId, EntityType entityType) {
|
||||||
String permissionQuery = String.format("e.tenant_id=%s", UUIDConverter.fromTimeUUID(tenantId.getId()));
|
String permissionQuery = String.format("e.tenant_id='%s'", UUIDConverter.fromTimeUUID(tenantId.getId()));
|
||||||
if (entityType != EntityType.TENANT && entityType != EntityType.CUSTOMER) {
|
if (entityType != EntityType.TENANT && entityType != EntityType.CUSTOMER) {
|
||||||
permissionQuery = String.format("%s and e.customerId=%s", permissionQuery, UUIDConverter.fromTimeUUID(customerId.getId()));
|
permissionQuery = String.format("%s and e.customer_id='%s'", permissionQuery, UUIDConverter.fromTimeUUID(customerId.getId()));
|
||||||
}
|
}
|
||||||
return permissionQuery;
|
return permissionQuery;
|
||||||
}
|
}
|
||||||
@ -358,151 +195,41 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildLatestQuery(List<EntityKeyMapping> latestFilters) {
|
private String buildWhere(List<EntityKeyMapping> selectionMapping, List<EntityKeyMapping> latestFiltersMapping, String searchText) {
|
||||||
List<String> latestQueries = new ArrayList<>();
|
String latestFilters = EntityKeyMapping.buildQuery(latestFiltersMapping);
|
||||||
for (EntityKeyMapping mapping: latestFilters) {
|
String textSearchQuery = this.buildTextSearchQuery(selectionMapping, searchText);
|
||||||
latestQueries.addAll(mapping.getKeyFilters().stream().map(keyFilter ->
|
if (!StringUtils.isEmpty(latestFilters) && !StringUtils.isEmpty(textSearchQuery)) {
|
||||||
this.buildKeyQuery(mapping.getAlias(), keyFilter))
|
return String.join(" AND ", latestFilters, textSearchQuery);
|
||||||
.collect(Collectors.toList()));
|
} else if (!StringUtils.isEmpty(latestFilters)) {
|
||||||
}
|
return latestFilters;
|
||||||
return latestQueries.stream().collect(Collectors.joining(" AND "));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildEntityFieldsQuery(List<EntityKeyMapping> entityFieldsFilters) {
|
|
||||||
return entityFieldsFilters.stream().flatMap(mapping -> mapping.getKeyFilters().stream())
|
|
||||||
.map(keyFilter -> this.buildKeyQuery("e", keyFilter)).collect(
|
|
||||||
Collectors.joining(" AND ")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildKeyQuery(String alias, KeyFilter keyFilter) {
|
|
||||||
return this.buildPredicateQuery(alias, keyFilter.getKey(), keyFilter.getPredicate());
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildPredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) {
|
|
||||||
if (predicate.getType().equals(FilterPredicateType.COMPLEX)) {
|
|
||||||
return this.buildComplexPredicateQuery(alias, key, (ComplexFilterPredicate)predicate);
|
|
||||||
} else {
|
} else {
|
||||||
return this.buildSimplePredicateQuery(alias, key, predicate);
|
return textSearchQuery;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildComplexPredicateQuery(String alias, EntityKey key, ComplexFilterPredicate predicate) {
|
private String buildTextSearchQuery(List<EntityKeyMapping> selectionMapping, String searchText) {
|
||||||
return predicate.getPredicates().stream()
|
if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) {
|
||||||
.map(keyFilterPredicate -> this.buildPredicateQuery(alias, key, keyFilterPredicate)).collect(Collectors.joining(
|
String lowerSearchText = searchText.toLowerCase() + "%";
|
||||||
" " + predicate.getOperation().name() + " "
|
List<String> searchPredicates = selectionMapping.stream().map(mapping -> String.format("LOWER(%s) LIKE '%s'",
|
||||||
));
|
mapping.getValueAlias(), lowerSearchText)).collect(Collectors.toList());
|
||||||
}
|
return String.format("(%s)", String.join(" or ", searchPredicates));
|
||||||
|
|
||||||
private String buildSimplePredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) {
|
|
||||||
if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
|
|
||||||
if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
|
||||||
String column = entityFieldColumnMap.get(key.getKey());
|
|
||||||
return this.buildNumericPredicateQuery(alias + "." + column, (NumericFilterPredicate)predicate);
|
|
||||||
} else {
|
|
||||||
String longQuery = this.buildNumericPredicateQuery(alias + ".long_v", (NumericFilterPredicate)predicate);
|
|
||||||
String doubleQuery = this.buildNumericPredicateQuery(alias + ".dbl_v", (NumericFilterPredicate)predicate);
|
|
||||||
return String.format("(%s or %s)", longQuery, doubleQuery);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
String column;
|
return null;
|
||||||
if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
|
||||||
column = entityFieldColumnMap.get(key.getKey());
|
|
||||||
} else {
|
|
||||||
column = predicate.getType().equals(FilterPredicateType.STRING) ? "str_v" : "bool_v";
|
|
||||||
}
|
|
||||||
String field = alias + "." + column;
|
|
||||||
if (predicate.getType().equals(FilterPredicateType.STRING)) {
|
|
||||||
return this.buildStringPredicateQuery(field, (StringFilterPredicate)predicate);
|
|
||||||
} else {
|
|
||||||
return this.buildBooleanPredicateQuery(field, (BooleanFilterPredicate)predicate);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildStringPredicateQuery(String field, StringFilterPredicate stringFilterPredicate) {
|
|
||||||
String operationField = field;
|
|
||||||
String value = stringFilterPredicate.getValue();
|
|
||||||
String stringOperationQuery = "";
|
|
||||||
if (stringFilterPredicate.isIgnoreCase()) {
|
|
||||||
value.toLowerCase();
|
|
||||||
operationField = String.format("lower(%s)", operationField);
|
|
||||||
}
|
|
||||||
switch (stringFilterPredicate.getOperation()) {
|
|
||||||
case EQUAL:
|
|
||||||
stringOperationQuery = String.format("%s = '%s'", operationField, value);
|
|
||||||
break;
|
|
||||||
case NOT_EQUAL:
|
|
||||||
stringOperationQuery = String.format("%s != '%s'", operationField, value);
|
|
||||||
break;
|
|
||||||
case STARTS_WITH:
|
|
||||||
stringOperationQuery = String.format("%s like '%s%'", operationField, value);
|
|
||||||
break;
|
|
||||||
case ENDS_WITH:
|
|
||||||
stringOperationQuery = String.format("%s like '%%s'", operationField, value);
|
|
||||||
break;
|
|
||||||
case CONTAINS:
|
|
||||||
stringOperationQuery = String.format("%s like '%%s%'", operationField, value);
|
|
||||||
break;
|
|
||||||
case NOT_CONTAINS:
|
|
||||||
stringOperationQuery = String.format("%s not like '%%s%'", operationField, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return String.format("(%s is not null and %s)", field, stringOperationQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildNumericPredicateQuery(String field, NumericFilterPredicate numericFilterPredicate) {
|
|
||||||
double value = numericFilterPredicate.getValue();
|
|
||||||
String numericOperationQuery = "";
|
|
||||||
switch (numericFilterPredicate.getOperation()) {
|
|
||||||
case EQUAL:
|
|
||||||
numericOperationQuery = String.format("%s = %s", field, value);
|
|
||||||
break;
|
|
||||||
case NOT_EQUAL:
|
|
||||||
numericOperationQuery = String.format("%s != '%s'", field, value);
|
|
||||||
break;
|
|
||||||
case GREATER:
|
|
||||||
numericOperationQuery = String.format("%s > %s", field, value);
|
|
||||||
break;
|
|
||||||
case GREATER_OR_EQUAL:
|
|
||||||
numericOperationQuery = String.format("%s >= %s", field, value);
|
|
||||||
break;
|
|
||||||
case LESS:
|
|
||||||
numericOperationQuery = String.format("%s < %s", field, value);
|
|
||||||
break;
|
|
||||||
case LESS_OR_EQUAL:
|
|
||||||
numericOperationQuery = String.format("%s <= %s", field, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return String.format("(%s is not null and %s)", field, numericOperationQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildBooleanPredicateQuery(String field,
|
|
||||||
BooleanFilterPredicate booleanFilterPredicate) {
|
|
||||||
boolean value = booleanFilterPredicate.isValue();
|
|
||||||
String booleanOperationQuery = "";
|
|
||||||
switch (booleanFilterPredicate.getOperation()) {
|
|
||||||
case EQUAL:
|
|
||||||
booleanOperationQuery = String.format("%s = %s", field, value);
|
|
||||||
break;
|
|
||||||
case NOT_EQUAL:
|
|
||||||
booleanOperationQuery = String.format("%s != %s", field, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return String.format("(%s is not null and %s)", field, booleanOperationQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String singleEntityQuery(SingleEntityFilter filter) {
|
private String singleEntityQuery(SingleEntityFilter filter) {
|
||||||
return String.format("e.id=%s", UUIDConverter.fromTimeUUID(filter.getSingleEntity().getId()));
|
return String.format("e.id='%s'", UUIDConverter.fromTimeUUID(filter.getSingleEntity().getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String entityListQuery(EntityListFilter filter) {
|
private String entityListQuery(EntityListFilter filter) {
|
||||||
return String.format("e.id in (%s)",
|
return String.format("e.id in (%s)",
|
||||||
filter.getEntityList().stream().map(UUID::fromString).map(UUIDConverter::fromTimeUUID).collect(Collectors.joining(",")));
|
filter.getEntityList().stream().map(UUID::fromString).map(UUIDConverter::fromTimeUUID)
|
||||||
|
.map(s -> String.format("'%s'", s)).collect(Collectors.joining(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String entityNameQuery(EntityNameFilter filter) {
|
private String entityNameQuery(EntityNameFilter filter) {
|
||||||
return String.format("lower(e.searchText) like lower(concat(%s, '%'))", filter.getEntityNameFilter());
|
return String.format("lower(e.search_text) like lower(concat(%s, '%%'))", filter.getEntityNameFilter());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String typeQuery(EntityFilter filter) {
|
private String typeQuery(EntityFilter filter) {
|
||||||
@ -524,7 +251,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
default:
|
default:
|
||||||
throw new RuntimeException("Not supported!");
|
throw new RuntimeException("Not supported!");
|
||||||
}
|
}
|
||||||
return String.format("e.type = %s and lower(e.searchText) like lower(concat(%s, '%'))", type, name);
|
return String.format("e.type = '%s' and lower(e.search_text) like lower(concat('%s', '%%'))", type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityType resolveEntityType(EntityFilter entityFilter) {
|
private EntityType resolveEntityType(EntityFilter entityFilter) {
|
||||||
|
|||||||
@ -0,0 +1,101 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2020 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.dao.sql.query;
|
||||||
|
|
||||||
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
|
import org.thingsboard.server.common.data.UUIDConverter;
|
||||||
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
|
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||||
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityData;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityDataPageLink;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityKey;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityKeyType;
|
||||||
|
import org.thingsboard.server.common.data.query.TsValue;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class EntityDataAdapter {
|
||||||
|
|
||||||
|
public static PageData<EntityData> createEntityData(EntityDataPageLink pageLink,
|
||||||
|
List<EntityKeyMapping> selectionMapping,
|
||||||
|
List<Object[]> rows,
|
||||||
|
int totalElements) {
|
||||||
|
int totalPages = pageLink.getPageSize() > 0 ? (int)Math.ceil((float)totalElements / pageLink.getPageSize()) : 1;
|
||||||
|
int startIndex = pageLink.getPageSize() * pageLink.getPage();
|
||||||
|
boolean hasNext = pageLink.getPageSize() > 0 && totalElements > startIndex + rows.size();
|
||||||
|
List<EntityData> entitiesData = convertListToEntityData(rows, selectionMapping);
|
||||||
|
return new PageData<>(entitiesData, totalPages, totalElements, hasNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<EntityData> convertListToEntityData(List<Object[]> result, List<EntityKeyMapping> selectionMapping) {
|
||||||
|
return result.stream().map(row -> toEntityData(row, selectionMapping)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static EntityData toEntityData(Object[] row, List<EntityKeyMapping> selectionMapping) {
|
||||||
|
String id = (String)row[0];
|
||||||
|
EntityType entityType = EntityType.valueOf((String)row[1]);
|
||||||
|
EntityId entityId = EntityIdFactory.getByTypeAndUuid(entityType, UUIDConverter.fromString(id));
|
||||||
|
Map<EntityKeyType, Map<String, TsValue>> latest = new HashMap<>();
|
||||||
|
Map<String, TsValue[]> timeseries = new HashMap<>();
|
||||||
|
EntityData entityData = new EntityData(entityId, latest, timeseries);
|
||||||
|
for (EntityKeyMapping mapping: selectionMapping) {
|
||||||
|
EntityKey entityKey = mapping.getEntityKey();
|
||||||
|
Object value = row[mapping.getIndex()];
|
||||||
|
String strValue;
|
||||||
|
long ts;
|
||||||
|
if (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||||
|
strValue = value != null ? value.toString() : null;
|
||||||
|
ts = System.currentTimeMillis();
|
||||||
|
} else {
|
||||||
|
strValue = convertValue(value);
|
||||||
|
Object tsObject = row[mapping.getIndex()+1];
|
||||||
|
ts = Long.parseLong(tsObject.toString());
|
||||||
|
}
|
||||||
|
TsValue tsValue = new TsValue(ts, strValue);
|
||||||
|
latest.computeIfAbsent(entityKey.getType(), entityKeyType -> new HashMap<>()).put(entityKey.getKey(), tsValue);
|
||||||
|
}
|
||||||
|
return entityData;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String convertValue(Object value) {
|
||||||
|
if (value != null) {
|
||||||
|
String strVal = value.toString();
|
||||||
|
// check number
|
||||||
|
if (strVal.length() > 0) {
|
||||||
|
try {
|
||||||
|
int intVal = Integer.parseInt(strVal);
|
||||||
|
return Integer.toString(intVal);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
double dblVal = Double.parseDouble(strVal);
|
||||||
|
if (!Double.isInfinite(dblVal)) {
|
||||||
|
return Double.toString(dblVal);
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strVal;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -16,21 +16,338 @@
|
|||||||
package org.thingsboard.server.dao.sql.query;
|
package org.thingsboard.server.dao.sql.query;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.DataConstants;
|
||||||
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
|
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
|
||||||
|
import org.thingsboard.server.common.data.query.ComplexFilterPredicate;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityDataQuery;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityDataSortOrder;
|
||||||
import org.thingsboard.server.common.data.query.EntityKey;
|
import org.thingsboard.server.common.data.query.EntityKey;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityKeyType;
|
||||||
|
import org.thingsboard.server.common.data.query.FilterPredicateType;
|
||||||
import org.thingsboard.server.common.data.query.KeyFilter;
|
import org.thingsboard.server.common.data.query.KeyFilter;
|
||||||
|
import org.thingsboard.server.common.data.query.KeyFilterPredicate;
|
||||||
|
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
|
||||||
|
import org.thingsboard.server.common.data.query.StringFilterPredicate;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class EntityKeyMapping {
|
public class EntityKeyMapping {
|
||||||
|
|
||||||
|
public static final Map<String, String> entityFieldColumnMap = new HashMap<>();
|
||||||
|
static {
|
||||||
|
entityFieldColumnMap.put("createdTime", "id");
|
||||||
|
entityFieldColumnMap.put("name", "name");
|
||||||
|
entityFieldColumnMap.put("type", "type");
|
||||||
|
entityFieldColumnMap.put("label", "label");
|
||||||
|
entityFieldColumnMap.put("firstName", "first_name");
|
||||||
|
entityFieldColumnMap.put("lastName", "last_name");
|
||||||
|
entityFieldColumnMap.put("email", "email");
|
||||||
|
entityFieldColumnMap.put("title", "title");
|
||||||
|
entityFieldColumnMap.put("country", "country");
|
||||||
|
entityFieldColumnMap.put("state", "state");
|
||||||
|
entityFieldColumnMap.put("city", "city");
|
||||||
|
entityFieldColumnMap.put("address", "address");
|
||||||
|
entityFieldColumnMap.put("address2", "address2");
|
||||||
|
entityFieldColumnMap.put("zip", "zip");
|
||||||
|
entityFieldColumnMap.put("phone", "phone");
|
||||||
|
}
|
||||||
|
|
||||||
private int index;
|
private int index;
|
||||||
private String alias;
|
private String alias;
|
||||||
private boolean isLatest;
|
private boolean isLatest;
|
||||||
private boolean isSelection;
|
private boolean isSelection;
|
||||||
|
private boolean isSortOrder;
|
||||||
private List<KeyFilter> keyFilters;
|
private List<KeyFilter> keyFilters;
|
||||||
private EntityKey entityKey;
|
private EntityKey entityKey;
|
||||||
|
|
||||||
public boolean hasFilter() {
|
public boolean hasFilter() {
|
||||||
return keyFilters != null && !keyFilters.isEmpty();
|
return keyFilters != null && !keyFilters.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getValueAlias() {
|
||||||
|
if (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||||
|
return alias;
|
||||||
|
} else {
|
||||||
|
return alias + "_value";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTsAlias() {
|
||||||
|
return alias + "_ts";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toSelection() {
|
||||||
|
if (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||||
|
String column = entityFieldColumnMap.get(entityKey.getKey());
|
||||||
|
return String.format("e.%s as %s", column, getValueAlias());
|
||||||
|
} else if (entityKey.getType().equals(EntityKeyType.TIME_SERIES)) {
|
||||||
|
return buildTimeseriesSelection();
|
||||||
|
} else {
|
||||||
|
return buildAttributeSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<String> toQueries() {
|
||||||
|
if (hasFilter()) {
|
||||||
|
String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias;
|
||||||
|
return keyFilters.stream().map(keyFilter ->
|
||||||
|
this.buildKeyQuery(keyAlias, keyFilter));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toLatestJoin(EntityType entityType) {
|
||||||
|
String join = hasFilter() ? "left join" : "left outer join";
|
||||||
|
if (entityKey.getType().equals(EntityKeyType.TIME_SERIES)) {
|
||||||
|
// TODO:
|
||||||
|
throw new RuntimeException("Not implemented!");
|
||||||
|
} else {
|
||||||
|
String query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type='%s' AND %s.attribute_key='%s'",
|
||||||
|
join, alias, alias, alias, entityType.name(), alias, entityKey.getKey());
|
||||||
|
if (!entityKey.getType().equals(EntityKeyType.ATTRIBUTE)) {
|
||||||
|
String scope;
|
||||||
|
if (entityKey.getType().equals(EntityKeyType.CLIENT_ATTRIBUTE)) {
|
||||||
|
scope = DataConstants.CLIENT_SCOPE;
|
||||||
|
} else if (entityKey.getType().equals(EntityKeyType.SHARED_ATTRIBUTE)) {
|
||||||
|
scope = DataConstants.SHARED_SCOPE;
|
||||||
|
} else {
|
||||||
|
scope = DataConstants.SERVER_SCOPE;
|
||||||
|
}
|
||||||
|
query = String.format("%s AND %s.attribute_type=%s", query, alias, scope);
|
||||||
|
}
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildSelections(List<EntityKeyMapping> mappings) {
|
||||||
|
return mappings.stream().map(EntityKeyMapping::toSelection).collect(
|
||||||
|
Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildLatestJoins(EntityType entityType, List<EntityKeyMapping> latestMappings) {
|
||||||
|
return latestMappings.stream().map(mapping -> mapping.toLatestJoin(entityType)).collect(
|
||||||
|
Collectors.joining(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String buildQuery(List<EntityKeyMapping> mappings) {
|
||||||
|
return mappings.stream().flatMap(EntityKeyMapping::toQueries).collect(
|
||||||
|
Collectors.joining(" AND "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<EntityKeyMapping> prepareKeyMapping(EntityDataQuery query) {
|
||||||
|
List<EntityKey> entityFields = query.getEntityFields() != null ? query.getEntityFields() : Collections.emptyList();
|
||||||
|
List<EntityKey> latestValues = query.getLatestValues() != null ? query.getLatestValues() : Collections.emptyList();
|
||||||
|
Map<EntityKey, List<KeyFilter>> filters =
|
||||||
|
query.getKeyFilters() != null ?
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
mappings.add(mapping);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
for (EntityKey latestField : latestValues) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
mappings.add(mapping);
|
||||||
|
index +=2;
|
||||||
|
}
|
||||||
|
if (!filters.isEmpty()) {
|
||||||
|
for (EntityKey filterField : filters.keySet()) {
|
||||||
|
EntityKeyMapping mapping = new EntityKeyMapping();
|
||||||
|
mapping.setIndex(index);
|
||||||
|
mapping.setAlias(String.format("alias%s", index));
|
||||||
|
mapping.setKeyFilters(filters.get(filterField));
|
||||||
|
mapping.setLatest(!filterField.getType().equals(EntityKeyType.ENTITY_FIELD));
|
||||||
|
mapping.setSelection(false);
|
||||||
|
mapping.setEntityKey(filterField);
|
||||||
|
mappings.add(mapping);
|
||||||
|
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), '') || " +
|
||||||
|
"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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildTimeseriesSelection() {
|
||||||
|
// TODO:
|
||||||
|
String attrValAlias = getValueAlias();
|
||||||
|
String attrTsAlias = getTsAlias();
|
||||||
|
return String.format("(select 1) as %s, (select '') as %s", attrTsAlias, attrValAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildKeyQuery(String alias, KeyFilter keyFilter) {
|
||||||
|
return this.buildPredicateQuery(alias, keyFilter.getKey(), keyFilter.getPredicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildPredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) {
|
||||||
|
if (predicate.getType().equals(FilterPredicateType.COMPLEX)) {
|
||||||
|
return this.buildComplexPredicateQuery(alias, key, (ComplexFilterPredicate)predicate);
|
||||||
|
} else {
|
||||||
|
return this.buildSimplePredicateQuery(alias, key, predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildComplexPredicateQuery(String alias, EntityKey key, ComplexFilterPredicate predicate) {
|
||||||
|
return predicate.getPredicates().stream()
|
||||||
|
.map(keyFilterPredicate -> this.buildPredicateQuery(alias, key, keyFilterPredicate)).collect(Collectors.joining(
|
||||||
|
" " + predicate.getOperation().name() + " "
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildSimplePredicateQuery(String alias, EntityKey key, KeyFilterPredicate predicate) {
|
||||||
|
if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
|
||||||
|
if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||||
|
String column = entityFieldColumnMap.get(key.getKey());
|
||||||
|
return this.buildNumericPredicateQuery(alias + "." + column, (NumericFilterPredicate)predicate);
|
||||||
|
} else {
|
||||||
|
String longQuery = this.buildNumericPredicateQuery(alias + ".long_v", (NumericFilterPredicate)predicate);
|
||||||
|
String doubleQuery = this.buildNumericPredicateQuery(alias + ".dbl_v", (NumericFilterPredicate)predicate);
|
||||||
|
return String.format("(%s or %s)", longQuery, doubleQuery);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String column;
|
||||||
|
if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||||
|
column = entityFieldColumnMap.get(key.getKey());
|
||||||
|
} else {
|
||||||
|
column = predicate.getType().equals(FilterPredicateType.STRING) ? "str_v" : "bool_v";
|
||||||
|
}
|
||||||
|
String field = alias + "." + column;
|
||||||
|
if (predicate.getType().equals(FilterPredicateType.STRING)) {
|
||||||
|
return this.buildStringPredicateQuery(field, (StringFilterPredicate)predicate);
|
||||||
|
} else {
|
||||||
|
return this.buildBooleanPredicateQuery(field, (BooleanFilterPredicate)predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildStringPredicateQuery(String field, StringFilterPredicate stringFilterPredicate) {
|
||||||
|
String operationField = field;
|
||||||
|
String value = stringFilterPredicate.getValue();
|
||||||
|
String stringOperationQuery = "";
|
||||||
|
if (stringFilterPredicate.isIgnoreCase()) {
|
||||||
|
value = value.toLowerCase();
|
||||||
|
operationField = String.format("lower(%s)", operationField);
|
||||||
|
}
|
||||||
|
switch (stringFilterPredicate.getOperation()) {
|
||||||
|
case EQUAL:
|
||||||
|
stringOperationQuery = String.format("%s = '%s'", operationField, value);
|
||||||
|
break;
|
||||||
|
case NOT_EQUAL:
|
||||||
|
stringOperationQuery = String.format("%s != '%s'", operationField, value);
|
||||||
|
break;
|
||||||
|
case STARTS_WITH:
|
||||||
|
stringOperationQuery = String.format("%s like '%s%%'", operationField, value);
|
||||||
|
break;
|
||||||
|
case ENDS_WITH:
|
||||||
|
stringOperationQuery = String.format("%s like '%%%s'", operationField, value);
|
||||||
|
break;
|
||||||
|
case CONTAINS:
|
||||||
|
stringOperationQuery = String.format("%s like '%%%s%%'", operationField, value);
|
||||||
|
break;
|
||||||
|
case NOT_CONTAINS:
|
||||||
|
stringOperationQuery = String.format("%s not like '%%%s%%'", operationField, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return String.format("(%s is not null and %s)", field, stringOperationQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildNumericPredicateQuery(String field, NumericFilterPredicate numericFilterPredicate) {
|
||||||
|
double value = numericFilterPredicate.getValue();
|
||||||
|
String numericOperationQuery = "";
|
||||||
|
switch (numericFilterPredicate.getOperation()) {
|
||||||
|
case EQUAL:
|
||||||
|
numericOperationQuery = String.format("%s = %s", field, value);
|
||||||
|
break;
|
||||||
|
case NOT_EQUAL:
|
||||||
|
numericOperationQuery = String.format("%s != '%s'", field, value);
|
||||||
|
break;
|
||||||
|
case GREATER:
|
||||||
|
numericOperationQuery = String.format("%s > %s", field, value);
|
||||||
|
break;
|
||||||
|
case GREATER_OR_EQUAL:
|
||||||
|
numericOperationQuery = String.format("%s >= %s", field, value);
|
||||||
|
break;
|
||||||
|
case LESS:
|
||||||
|
numericOperationQuery = String.format("%s < %s", field, value);
|
||||||
|
break;
|
||||||
|
case LESS_OR_EQUAL:
|
||||||
|
numericOperationQuery = String.format("%s <= %s", field, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return String.format("(%s is not null and %s)", field, numericOperationQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildBooleanPredicateQuery(String field,
|
||||||
|
BooleanFilterPredicate booleanFilterPredicate) {
|
||||||
|
boolean value = booleanFilterPredicate.isValue();
|
||||||
|
String booleanOperationQuery = "";
|
||||||
|
switch (booleanFilterPredicate.getOperation()) {
|
||||||
|
case EQUAL:
|
||||||
|
booleanOperationQuery = String.format("%s = %s", field, value);
|
||||||
|
break;
|
||||||
|
case NOT_EQUAL:
|
||||||
|
booleanOperationQuery = String.format("%s != %s", field, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return String.format("(%s is not null and %s)", field, booleanOperationQuery);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
@RunWith(ClasspathSuite.class)
|
@RunWith(ClasspathSuite.class)
|
||||||
@ClassnameFilters({
|
@ClassnameFilters({
|
||||||
"org.thingsboard.server.dao.service.*ServiceSqlTest"
|
"org.thingsboard.server.dao.service.*EntityServiceSqlTest"
|
||||||
})
|
})
|
||||||
public class SqlDaoServiceTestSuite {
|
public class SqlDaoServiceTestSuite {
|
||||||
|
|
||||||
|
|||||||
@ -33,9 +33,6 @@ import org.thingsboard.server.common.data.Event;
|
|||||||
import org.thingsboard.server.common.data.id.EntityId;
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UUIDBased;
|
import org.thingsboard.server.common.data.id.UUIDBased;
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentDescriptor;
|
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentScope;
|
|
||||||
import org.thingsboard.server.common.data.plugin.ComponentType;
|
|
||||||
import org.thingsboard.server.dao.alarm.AlarmService;
|
import org.thingsboard.server.dao.alarm.AlarmService;
|
||||||
import org.thingsboard.server.dao.asset.AssetService;
|
import org.thingsboard.server.dao.asset.AssetService;
|
||||||
import org.thingsboard.server.dao.audit.AuditLogLevelFilter;
|
import org.thingsboard.server.dao.audit.AuditLogLevelFilter;
|
||||||
@ -45,6 +42,7 @@ import org.thingsboard.server.dao.customer.CustomerService;
|
|||||||
import org.thingsboard.server.dao.dashboard.DashboardService;
|
import org.thingsboard.server.dao.dashboard.DashboardService;
|
||||||
import org.thingsboard.server.dao.device.DeviceCredentialsService;
|
import org.thingsboard.server.dao.device.DeviceCredentialsService;
|
||||||
import org.thingsboard.server.dao.device.DeviceService;
|
import org.thingsboard.server.dao.device.DeviceService;
|
||||||
|
import org.thingsboard.server.dao.entity.EntityService;
|
||||||
import org.thingsboard.server.dao.entityview.EntityViewService;
|
import org.thingsboard.server.dao.entityview.EntityViewService;
|
||||||
import org.thingsboard.server.dao.event.EventService;
|
import org.thingsboard.server.dao.event.EventService;
|
||||||
import org.thingsboard.server.dao.relation.RelationService;
|
import org.thingsboard.server.dao.relation.RelationService;
|
||||||
@ -94,6 +92,9 @@ public abstract class AbstractServiceTest {
|
|||||||
@Autowired
|
@Autowired
|
||||||
protected EntityViewService entityViewService;
|
protected EntityViewService entityViewService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected EntityService entityService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected DeviceCredentialsService deviceCredentialsService;
|
protected DeviceCredentialsService deviceCredentialsService;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2020 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.dao.service;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.thingsboard.server.common.data.Device;
|
||||||
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
|
import org.thingsboard.server.common.data.Tenant;
|
||||||
|
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.page.PageData;
|
||||||
|
import org.thingsboard.server.common.data.query.DeviceTypeFilter;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityCountQuery;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityData;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityDataPageLink;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityDataQuery;
|
||||||
|
import org.thingsboard.server.common.data.query.EntityDataSortOrder;
|
||||||
|
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 java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public abstract class BaseEntityServiceTest extends AbstractServiceTest {
|
||||||
|
|
||||||
|
private TenantId tenantId;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
Tenant tenant = new Tenant();
|
||||||
|
tenant.setTitle("My tenant");
|
||||||
|
Tenant savedTenant = tenantService.saveTenant(tenant);
|
||||||
|
Assert.assertNotNull(savedTenant);
|
||||||
|
tenantId = savedTenant.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
tenantService.deleteTenant(tenantId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCountEntitiesByQuery() {
|
||||||
|
List<Device> devices = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 97; 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceTypeFilter filter = new DeviceTypeFilter();
|
||||||
|
filter.setDeviceType("default");
|
||||||
|
filter.setDeviceNameFilter("");
|
||||||
|
|
||||||
|
EntityCountQuery countQuery = new EntityCountQuery(filter);
|
||||||
|
|
||||||
|
long count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
|
||||||
|
Assert.assertEquals(97, count);
|
||||||
|
|
||||||
|
filter.setDeviceType("unknown");
|
||||||
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
|
||||||
|
Assert.assertEquals(0, count);
|
||||||
|
|
||||||
|
filter.setDeviceType("default");
|
||||||
|
filter.setDeviceNameFilter("Device1");
|
||||||
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
|
||||||
|
Assert.assertEquals(11, count);
|
||||||
|
|
||||||
|
EntityListFilter entityListFilter = new EntityListFilter();
|
||||||
|
entityListFilter.setEntityType(EntityType.DEVICE);
|
||||||
|
entityListFilter.setEntityList(devices.stream().map(Device::getId).map(DeviceId::toString).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
countQuery = new EntityCountQuery(entityListFilter);
|
||||||
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
|
||||||
|
Assert.assertEquals(97, count);
|
||||||
|
|
||||||
|
deviceService.deleteDevicesByTenantId(tenantId);
|
||||||
|
count = entityService.countEntitiesByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), countQuery);
|
||||||
|
Assert.assertEquals(0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindEntityDataByQuery() {
|
||||||
|
List<Device> devices = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 97; 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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"));
|
||||||
|
|
||||||
|
EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, null, null);
|
||||||
|
PageData<EntityData> data = entityService.findEntityDataByQuery(tenantId, new CustomerId(CustomerId.NULL_UUID), query);
|
||||||
|
|
||||||
|
Assert.assertEquals(97, data.getTotalElements());
|
||||||
|
Assert.assertEquals(10, data.getTotalPages());
|
||||||
|
Assert.assertTrue(data.hasNext());
|
||||||
|
Assert.assertEquals(10, data.getData().size());
|
||||||
|
|
||||||
|
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(97, loadedEntities.size());
|
||||||
|
|
||||||
|
List<EntityId> loadedIds = loadedEntities.stream().map(EntityData::getEntityId).collect(Collectors.toList());
|
||||||
|
List<EntityId> deviceIds = devices.stream().map(Device::getId).collect(Collectors.toList());
|
||||||
|
|
||||||
|
Assert.assertEquals(deviceIds, loadedIds);
|
||||||
|
|
||||||
|
List<String> loadedNames = loadedEntities.stream().map(entityData ->
|
||||||
|
entityData.getLatest().get(EntityKeyType.ENTITY_FIELD).get("name").getValue()).collect(Collectors.toList());
|
||||||
|
List<String> deviceNames = devices.stream().map(Device::getName).collect(Collectors.toList());
|
||||||
|
|
||||||
|
Assert.assertEquals(deviceNames, loadedNames);
|
||||||
|
|
||||||
|
sortOrder = new EntityDataSortOrder(
|
||||||
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), EntityDataSortOrder.Direction.DESC
|
||||||
|
);
|
||||||
|
|
||||||
|
pageLink = new EntityDataPageLink(10, 0, "device1", sortOrder);
|
||||||
|
query = new EntityDataQuery(filter, pageLink, entityFields, null, null);
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2020 The Thingsboard Authors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.thingsboard.server.dao.service.sql;
|
||||||
|
|
||||||
|
import org.thingsboard.server.dao.service.BaseEntityServiceTest;
|
||||||
|
import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||||
|
|
||||||
|
@DaoSqlTest
|
||||||
|
public class EntityServiceSqlTest extends BaseEntityServiceTest {
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user