Merge pull request #10454 from dashevchenko/ownerName
Added ownerName ENTITY_FIELD to EntityDataQuery
This commit is contained in:
commit
ab607ed710
@ -52,6 +52,7 @@ import org.thingsboard.server.common.data.query.EntityTypeFilter;
|
|||||||
import org.thingsboard.server.common.data.query.FilterPredicateValue;
|
import org.thingsboard.server.common.data.query.FilterPredicateValue;
|
||||||
import org.thingsboard.server.common.data.query.KeyFilter;
|
import org.thingsboard.server.common.data.query.KeyFilter;
|
||||||
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
|
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
|
||||||
|
import org.thingsboard.server.common.data.query.StringFilterPredicate;
|
||||||
import org.thingsboard.server.common.data.query.TsValue;
|
import org.thingsboard.server.common.data.query.TsValue;
|
||||||
import org.thingsboard.server.common.data.queue.QueueStats;
|
import org.thingsboard.server.common.data.queue.QueueStats;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
import org.thingsboard.server.common.data.security.Authority;
|
||||||
@ -642,4 +643,166 @@ public class EntityQueryControllerTest extends AbstractControllerTest {
|
|||||||
Assert.assertEquals(97, count.longValue());
|
Assert.assertEquals(97, count.longValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindDevicesCountByOwnerNameAndOwnerType() throws Exception {
|
||||||
|
loginTenantAdmin();
|
||||||
|
int numOfDevices = 8;
|
||||||
|
|
||||||
|
for (int i = 0; i < numOfDevices; i++) {
|
||||||
|
Device device = new Device();
|
||||||
|
String name = "Device" + i;
|
||||||
|
device.setName(name);
|
||||||
|
device.setType("default");
|
||||||
|
|
||||||
|
Device savedDevice = doPost("/api/device?accessToken=" + name, device, Device.class);
|
||||||
|
JsonNode content = JacksonUtil.toJsonNode("{\"alarmActiveTime\": 1" + i + "}");
|
||||||
|
doPost("/api/plugins/telemetry/" + EntityType.DEVICE.name() + "/" + savedDevice.getUuidId() + "/SERVER_SCOPE", content)
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceTypeFilter filter = new DeviceTypeFilter();
|
||||||
|
filter.setDeviceTypes(List.of("default"));
|
||||||
|
filter.setDeviceNameFilter("");
|
||||||
|
|
||||||
|
KeyFilter activeAlarmTimeFilter = getServerAttributeNumericGreaterThanKeyFilter("alarmActiveTime", 5);
|
||||||
|
KeyFilter activeAlarmTimeToLongFilter = getServerAttributeNumericGreaterThanKeyFilter("alarmActiveTime", 30);
|
||||||
|
KeyFilter tenantOwnerNameFilter = getEntityFieldStringEqualToKeyFilter("ownerName", TEST_TENANT_NAME);
|
||||||
|
KeyFilter wrongOwnerNameFilter = getEntityFieldStringEqualToKeyFilter("ownerName", "wrongName");
|
||||||
|
KeyFilter tenantOwnerTypeFilter = getEntityFieldStringEqualToKeyFilter("ownerType", "TENANT");
|
||||||
|
KeyFilter customerOwnerTypeFilter = getEntityFieldStringEqualToKeyFilter("ownerType", "CUSTOMER");
|
||||||
|
|
||||||
|
// all devices with ownerName = TEST TENANT
|
||||||
|
EntityCountQuery query = new EntityCountQuery(filter, List.of(activeAlarmTimeFilter, tenantOwnerNameFilter));
|
||||||
|
checkEntitiesCount(query, numOfDevices);
|
||||||
|
|
||||||
|
// all devices with ownerName = TEST TENANT
|
||||||
|
EntityCountQuery activeAlarmTimeToLongQuery = new EntityCountQuery(filter, List.of(activeAlarmTimeToLongFilter, tenantOwnerNameFilter));
|
||||||
|
checkEntitiesCount(activeAlarmTimeToLongQuery, 0);
|
||||||
|
|
||||||
|
// all devices with wrong ownerName
|
||||||
|
EntityCountQuery wrongTenantNameQuery = new EntityCountQuery(filter, List.of(activeAlarmTimeFilter, wrongOwnerNameFilter));
|
||||||
|
checkEntitiesCount(wrongTenantNameQuery, 0);
|
||||||
|
|
||||||
|
// all devices with owner type = TENANT
|
||||||
|
EntityCountQuery tenantEntitiesQuery = new EntityCountQuery(filter, List.of(activeAlarmTimeFilter, tenantOwnerTypeFilter));
|
||||||
|
checkEntitiesCount(tenantEntitiesQuery, numOfDevices);
|
||||||
|
|
||||||
|
// all devices with owner type = CUSTOMER
|
||||||
|
EntityCountQuery customerEntitiesQuery = new EntityCountQuery(filter, List.of(activeAlarmTimeFilter, customerOwnerTypeFilter));
|
||||||
|
checkEntitiesCount(customerEntitiesQuery, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindDevicesByOwnerNameAndOwnerType() throws Exception {
|
||||||
|
loginTenantAdmin();
|
||||||
|
int numOfDevices = 3;
|
||||||
|
|
||||||
|
for (int i = 0; i < numOfDevices; i++) {
|
||||||
|
Device device = new Device();
|
||||||
|
String name = "Device" + i;
|
||||||
|
device.setName(name);
|
||||||
|
device.setType("default");
|
||||||
|
|
||||||
|
Device savedDevice = doPost("/api/device?accessToken=" + name, device, Device.class);
|
||||||
|
JsonNode content = JacksonUtil.toJsonNode("{\"alarmActiveTime\": 1" + i + "}");
|
||||||
|
doPost("/api/plugins/telemetry/" + EntityType.DEVICE.name() + "/" + savedDevice.getUuidId() + "/SERVER_SCOPE", content)
|
||||||
|
.andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceTypeFilter filter = new DeviceTypeFilter();
|
||||||
|
filter.setDeviceTypes(List.of("default"));
|
||||||
|
filter.setDeviceNameFilter("");
|
||||||
|
|
||||||
|
KeyFilter activeAlarmTimeFilter = getServerAttributeNumericGreaterThanKeyFilter("alarmActiveTime", 5);
|
||||||
|
KeyFilter tenantOwnerNameFilter = getEntityFieldStringEqualToKeyFilter("ownerName", TEST_TENANT_NAME);
|
||||||
|
KeyFilter wrongOwnerNameFilter = getEntityFieldStringEqualToKeyFilter("ownerName", "wrongName");
|
||||||
|
KeyFilter tenantOwnerTypeFilter = getEntityFieldStringEqualToKeyFilter("ownerType", "TENANT");
|
||||||
|
KeyFilter customerOwnerTypeFilter = getEntityFieldStringEqualToKeyFilter("ownerType", "CUSTOMER");
|
||||||
|
|
||||||
|
EntityDataSortOrder sortOrder = new EntityDataSortOrder(
|
||||||
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"), EntityDataSortOrder.Direction.ASC
|
||||||
|
);
|
||||||
|
EntityDataPageLink pageLink = new EntityDataPageLink(10, 0, null, sortOrder);
|
||||||
|
List<EntityKey> entityFields = List.of(new EntityKey(EntityKeyType.ENTITY_FIELD, "name"), new EntityKey(EntityKeyType.ENTITY_FIELD, "ownerName"),
|
||||||
|
new EntityKey(EntityKeyType.ENTITY_FIELD, "ownerType"));
|
||||||
|
List<EntityKey> latestValues = Collections.singletonList(new EntityKey(EntityKeyType.ATTRIBUTE, "alarmActiveTime"));
|
||||||
|
|
||||||
|
// all devices with ownerName = TEST TENANT
|
||||||
|
EntityDataQuery query = new EntityDataQuery(filter, pageLink, entityFields, latestValues, List.of(activeAlarmTimeFilter, tenantOwnerNameFilter));
|
||||||
|
checkEntitiesByQuery(query, numOfDevices, TEST_TENANT_NAME, "TENANT");
|
||||||
|
|
||||||
|
// all devices with wrong ownerName
|
||||||
|
EntityDataQuery wrongTenantNameQuery = new EntityDataQuery(filter, pageLink, entityFields, latestValues, List.of(activeAlarmTimeFilter, wrongOwnerNameFilter));
|
||||||
|
checkEntitiesByQuery(wrongTenantNameQuery, 0, null, null);
|
||||||
|
|
||||||
|
// all devices with owner type = TENANT
|
||||||
|
EntityDataQuery tenantEntitiesQuery = new EntityDataQuery(filter, pageLink, entityFields, latestValues, List.of(activeAlarmTimeFilter, tenantOwnerTypeFilter));
|
||||||
|
checkEntitiesByQuery(tenantEntitiesQuery, numOfDevices, TEST_TENANT_NAME, "TENANT");
|
||||||
|
|
||||||
|
// all devices with owner type = CUSTOMER
|
||||||
|
EntityDataQuery customerEntitiesQuery = new EntityDataQuery(filter, pageLink, entityFields, latestValues, List.of(activeAlarmTimeFilter, customerOwnerTypeFilter));
|
||||||
|
checkEntitiesByQuery(customerEntitiesQuery, 0, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkEntitiesByQuery(EntityDataQuery query, int expectedNumOfDevices, String expectedOwnerName, String expectedOwnerType) throws Exception {
|
||||||
|
Awaitility.await()
|
||||||
|
.alias("data by query")
|
||||||
|
.atMost(30, TimeUnit.SECONDS)
|
||||||
|
.until(() -> {
|
||||||
|
var data = doPostWithTypedResponse("/api/entitiesQuery/find", query, new TypeReference<PageData<EntityData>>() {});
|
||||||
|
var loadedEntities = new ArrayList<>(data.getData());
|
||||||
|
return loadedEntities.size() == expectedNumOfDevices;
|
||||||
|
});
|
||||||
|
if (expectedNumOfDevices == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var data = doPostWithTypedResponse("/api/entitiesQuery/find", query, new TypeReference<PageData<EntityData>>() {});
|
||||||
|
var loadedEntities = new ArrayList<>(data.getData());
|
||||||
|
|
||||||
|
Assert.assertEquals(expectedNumOfDevices, loadedEntities.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < expectedNumOfDevices; i++) {
|
||||||
|
var entity = loadedEntities.get(i);
|
||||||
|
String name = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("name", new TsValue(0, "Invalid")).getValue();
|
||||||
|
String ownerName = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("ownerName", new TsValue(0, "Invalid")).getValue();
|
||||||
|
String ownerType = entity.getLatest().get(EntityKeyType.ENTITY_FIELD).getOrDefault("ownerType", new TsValue(0, "Invalid")).getValue();
|
||||||
|
String alarmActiveTime = entity.getLatest().get(EntityKeyType.ATTRIBUTE).getOrDefault("alarmActiveTime", new TsValue(0, "-1")).getValue();
|
||||||
|
|
||||||
|
Assert.assertEquals("Device" + i, name);
|
||||||
|
Assert.assertEquals( expectedOwnerName, ownerName);
|
||||||
|
Assert.assertEquals( expectedOwnerType, ownerType);
|
||||||
|
Assert.assertEquals("1" + i, alarmActiveTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkEntitiesCount(EntityCountQuery query, int expectedNumOfDevices) {
|
||||||
|
Awaitility.await()
|
||||||
|
.alias("count by query")
|
||||||
|
.atMost(30, TimeUnit.SECONDS)
|
||||||
|
.until(() -> {
|
||||||
|
var count = doPost("/api/entitiesQuery/count", query, Integer.class);
|
||||||
|
return count == expectedNumOfDevices;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyFilter getEntityFieldStringEqualToKeyFilter(String keyName, String value) {
|
||||||
|
KeyFilter tenantOwnerNameFilter = new KeyFilter();
|
||||||
|
tenantOwnerNameFilter.setKey(new EntityKey(EntityKeyType.ENTITY_FIELD, keyName));
|
||||||
|
StringFilterPredicate ownerNamePredicate = new StringFilterPredicate();
|
||||||
|
ownerNamePredicate.setValue(FilterPredicateValue.fromString(value));
|
||||||
|
ownerNamePredicate.setOperation(StringFilterPredicate.StringOperation.EQUAL);
|
||||||
|
tenantOwnerNameFilter.setPredicate(ownerNamePredicate);
|
||||||
|
return tenantOwnerNameFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyFilter getServerAttributeNumericGreaterThanKeyFilter(String attribute, int value) {
|
||||||
|
KeyFilter numericFilter = new KeyFilter();
|
||||||
|
numericFilter.setKey(new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, attribute));
|
||||||
|
NumericFilterPredicate predicate = new NumericFilterPredicate();
|
||||||
|
predicate.setValue(FilterPredicateValue.fromDouble(value));
|
||||||
|
predicate.setOperation(NumericFilterPredicate.NumericOperation.GREATER);
|
||||||
|
numericFilter.setPredicate(predicate);
|
||||||
|
return numericFilter;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -359,7 +359,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
|
|
||||||
List<EntityKeyMapping> filterMapping = mappings.stream().filter(EntityKeyMapping::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() && mapping.getEntityKeyColumn() != null)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest)
|
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest)
|
||||||
@ -367,6 +367,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
|
|
||||||
|
|
||||||
String entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, query.getEntityFilter(), entityFieldsFiltersMapping);
|
String entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, query.getEntityFilter(), entityFieldsFiltersMapping);
|
||||||
|
String aliasWhereQuery = DefaultEntityQueryRepository.this.buildAliasWhereQuery(ctx, query.getEntityFilter(), selectionMapping, "");
|
||||||
String latestJoinsCnt = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings, true);
|
String latestJoinsCnt = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings, true);
|
||||||
String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType);
|
String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType);
|
||||||
String entityTypeStr;
|
String entityTypeStr;
|
||||||
@ -387,7 +388,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
addEntityTableQuery(ctx, query.getEntityFilter()),
|
addEntityTableQuery(ctx, query.getEntityFilter()),
|
||||||
entityWhereClause,
|
entityWhereClause,
|
||||||
latestJoinsCnt,
|
latestJoinsCnt,
|
||||||
"");
|
aliasWhereQuery);
|
||||||
|
|
||||||
String countQuery = String.format("select count(id) %s", fromClauseCount);
|
String countQuery = String.format("select count(id) %s", fromClauseCount);
|
||||||
|
|
||||||
@ -418,7 +419,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, customerId, entityType, ignorePermissionCheck));
|
QueryContext ctx = new QueryContext(new QuerySecurityContext(tenantId, customerId, entityType, ignorePermissionCheck));
|
||||||
EntityDataPageLink pageLink = query.getPageLink();
|
EntityDataPageLink pageLink = query.getPageLink();
|
||||||
|
|
||||||
List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(query);
|
List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(entityType, query);
|
||||||
|
|
||||||
List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection)
|
List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@ -429,7 +430,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
|
|
||||||
List<EntityKeyMapping> filterMapping = mappings.stream().filter(EntityKeyMapping::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() && mapping.getEntityKeyColumn() != null)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest)
|
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest)
|
||||||
@ -439,7 +440,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
String entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, query.getEntityFilter(), entityFieldsFiltersMapping);
|
String entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, query.getEntityFilter(), entityFieldsFiltersMapping);
|
||||||
String latestJoinsCnt = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings, true);
|
String latestJoinsCnt = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings, true);
|
||||||
String latestJoinsData = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings, false);
|
String latestJoinsData = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings, false);
|
||||||
String textSearchQuery = DefaultEntityQueryRepository.this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch());
|
String aliasWhereQuery = DefaultEntityQueryRepository.this.buildAliasWhereQuery(ctx, query.getEntityFilter(), selectionMapping, pageLink.getTextSearch());
|
||||||
String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType);
|
String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType);
|
||||||
String entityTypeStr;
|
String entityTypeStr;
|
||||||
if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) {
|
if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) {
|
||||||
@ -465,7 +466,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
addEntityTableQuery(ctx, query.getEntityFilter()),
|
addEntityTableQuery(ctx, query.getEntityFilter()),
|
||||||
entityWhereClause,
|
entityWhereClause,
|
||||||
latestJoinsCnt,
|
latestJoinsCnt,
|
||||||
textSearchQuery);
|
aliasWhereQuery);
|
||||||
|
|
||||||
String fromClauseData = String.format("from (select %s from (select %s from %s e where %s) entities %s ) result %s",
|
String fromClauseData = String.format("from (select %s from (select %s from %s e where %s) entities %s ) result %s",
|
||||||
topSelection,
|
topSelection,
|
||||||
@ -473,7 +474,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
addEntityTableQuery(ctx, query.getEntityFilter()),
|
addEntityTableQuery(ctx, query.getEntityFilter()),
|
||||||
entityWhereClause,
|
entityWhereClause,
|
||||||
latestJoinsData,
|
latestJoinsData,
|
||||||
textSearchQuery);
|
aliasWhereQuery);
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(pageLink.getTextSearch())) {
|
if (!StringUtils.isEmpty(pageLink.getTextSearch())) {
|
||||||
//Unfortunately, we need to sacrifice performance in case of full text search, because it is applied to all joined records.
|
//Unfortunately, we need to sacrifice performance in case of full text search, because it is applied to all joined records.
|
||||||
@ -800,6 +801,21 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
return from;
|
return from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String buildAliasWhereQuery(QueryContext ctx, EntityFilter entityFilter, List<EntityKeyMapping> selectionMapping, String searchText) {
|
||||||
|
List<EntityKeyMapping> aliasFiltersMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest() && mapping.getEntityKeyColumn() == null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
String entityFieldsQuery = EntityKeyMapping.buildQuery(ctx, aliasFiltersMapping, entityFilter.getType());
|
||||||
|
String searchTextQuery = buildTextSearchQuery(ctx, selectionMapping, searchText);
|
||||||
|
String result = "";
|
||||||
|
if (!entityFieldsQuery.isEmpty()) {
|
||||||
|
result += " where (" + entityFieldsQuery + ")";
|
||||||
|
}
|
||||||
|
if (!searchTextQuery.isEmpty()) {
|
||||||
|
result += (result.isEmpty() ? " where ": " and ") + "(" + searchTextQuery + ") ";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private String buildTextSearchQuery(QueryContext ctx, List<EntityKeyMapping> selectionMapping, String searchText) {
|
private String buildTextSearchQuery(QueryContext ctx, List<EntityKeyMapping> selectionMapping, String searchText) {
|
||||||
if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) {
|
if (!StringUtils.isEmpty(searchText) && !selectionMapping.isEmpty()) {
|
||||||
String sqlSearchText = "%" + searchText + "%";
|
String sqlSearchText = "%" + searchText + "%";
|
||||||
@ -811,7 +827,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
|
|||||||
} else {
|
} else {
|
||||||
searchAliasesExpression = searchAliases.get(0);
|
searchAliasesExpression = searchAliases.get(0);
|
||||||
}
|
}
|
||||||
return String.format(" WHERE %s ILIKE :%s", searchAliasesExpression, "lowerSearchTextParam");
|
return String.format(" %s ILIKE :%s", searchAliasesExpression, "lowerSearchTextParam");
|
||||||
} else {
|
} else {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,6 +38,7 @@ import org.thingsboard.server.dao.model.ModelConstants;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -48,6 +49,8 @@ import java.util.stream.Collectors;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static org.thingsboard.server.common.data.StringUtils.splitByCommaWithoutQuotes;
|
import static org.thingsboard.server.common.data.StringUtils.splitByCommaWithoutQuotes;
|
||||||
|
import static org.thingsboard.server.common.data.id.EntityId.NULL_UUID;
|
||||||
|
import static org.thingsboard.server.dao.sql.query.DefaultEntityQueryRepository.resolveEntityType;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class EntityKeyMapping {
|
public class EntityKeyMapping {
|
||||||
@ -55,6 +58,7 @@ public class EntityKeyMapping {
|
|||||||
private static final Map<EntityType, Set<String>> allowedEntityFieldMap = new HashMap<>();
|
private static final Map<EntityType, Set<String>> allowedEntityFieldMap = new HashMap<>();
|
||||||
private static final Map<String, String> entityFieldColumnMap = new HashMap<>();
|
private static final Map<String, String> entityFieldColumnMap = new HashMap<>();
|
||||||
private static final Map<EntityType, Map<String, String>> aliases = new HashMap<>();
|
private static final Map<EntityType, Map<String, String>> aliases = new HashMap<>();
|
||||||
|
private static final Map<EntityType, Map<String, String>> propertiesFunctions = new EnumMap<>(EntityType.class);
|
||||||
|
|
||||||
public static final String CREATED_TIME = "createdTime";
|
public static final String CREATED_TIME = "createdTime";
|
||||||
public static final String ENTITY_TYPE = "entityType";
|
public static final String ENTITY_TYPE = "entityType";
|
||||||
@ -77,6 +81,18 @@ public class EntityKeyMapping {
|
|||||||
public static final String RELATED_PARENT_ID = "parentId";
|
public static final String RELATED_PARENT_ID = "parentId";
|
||||||
public static final String QUEUE_NAME = "queueName";
|
public static final String QUEUE_NAME = "queueName";
|
||||||
public static final String SERVICE_ID = "serviceId";
|
public static final String SERVICE_ID = "serviceId";
|
||||||
|
public static final String OWNER_NAME = "ownerName";
|
||||||
|
public static final String OWNER_TYPE = "ownerType";
|
||||||
|
public static final String OWNER_NAME_SELECT_QUERY = "case when e.customer_id = '" + NULL_UUID + "' " +
|
||||||
|
"then (select title from tenant where id = e.tenant_id) " +
|
||||||
|
"else (select title from customer where id = e.customer_id) end";
|
||||||
|
public static final String OWNER_TYPE_SELECT_QUERY = "case when e.customer_id = '" + NULL_UUID + "' " +
|
||||||
|
"then 'TENANT' " +
|
||||||
|
"else 'CUSTOMER' end";
|
||||||
|
public static final Map<String, String> ownerPropertiesFunctions = Map.of(
|
||||||
|
OWNER_NAME, OWNER_NAME_SELECT_QUERY,
|
||||||
|
OWNER_TYPE, OWNER_TYPE_SELECT_QUERY
|
||||||
|
);
|
||||||
|
|
||||||
public static final List<String> typedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, ADDITIONAL_INFO);
|
public static final List<String> typedEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME, TYPE, ADDITIONAL_INFO);
|
||||||
public static final List<String> widgetEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME);
|
public static final List<String> widgetEntityFields = Arrays.asList(CREATED_TIME, ENTITY_TYPE, NAME);
|
||||||
@ -145,6 +161,12 @@ public class EntityKeyMapping {
|
|||||||
aliases.put(EntityType.ENTITY_VIEW, commonEntityAliases);
|
aliases.put(EntityType.ENTITY_VIEW, commonEntityAliases);
|
||||||
aliases.put(EntityType.WIDGETS_BUNDLE, commonEntityAliases);
|
aliases.put(EntityType.WIDGETS_BUNDLE, commonEntityAliases);
|
||||||
|
|
||||||
|
propertiesFunctions.put(EntityType.DEVICE, ownerPropertiesFunctions);
|
||||||
|
propertiesFunctions.put(EntityType.ASSET, ownerPropertiesFunctions);
|
||||||
|
propertiesFunctions.put(EntityType.ENTITY_VIEW, ownerPropertiesFunctions);
|
||||||
|
propertiesFunctions.put(EntityType.USER, ownerPropertiesFunctions);
|
||||||
|
propertiesFunctions.put(EntityType.DASHBOARD, ownerPropertiesFunctions);
|
||||||
|
|
||||||
Map<String, String> userEntityAliases = new HashMap<>();
|
Map<String, String> userEntityAliases = new HashMap<>();
|
||||||
userEntityAliases.put(TITLE, EMAIL);
|
userEntityAliases.put(TITLE, EMAIL);
|
||||||
userEntityAliases.put(LABEL, EMAIL);
|
userEntityAliases.put(LABEL, EMAIL);
|
||||||
@ -155,6 +177,7 @@ public class EntityKeyMapping {
|
|||||||
private int index;
|
private int index;
|
||||||
private String alias;
|
private String alias;
|
||||||
private boolean isLatest;
|
private boolean isLatest;
|
||||||
|
private String entityKeyColumn;
|
||||||
private boolean isSelection;
|
private boolean isSelection;
|
||||||
private boolean isSearchable;
|
private boolean isSearchable;
|
||||||
private boolean isSortOrder;
|
private boolean isSortOrder;
|
||||||
@ -184,12 +207,14 @@ public class EntityKeyMapping {
|
|||||||
if (entityKey.getKey().equals("entityType") && !filterType.equals(EntityFilterType.RELATIONS_QUERY)) {
|
if (entityKey.getKey().equals("entityType") && !filterType.equals(EntityFilterType.RELATIONS_QUERY)) {
|
||||||
return String.format("'%s' as %s", entityType.name(), getValueAlias());
|
return String.format("'%s' as %s", entityType.name(), getValueAlias());
|
||||||
} else {
|
} else {
|
||||||
Set<String> existingEntityFields = getExistingEntityFields(filterType, entityType);
|
if (getEntityKeyColumn() != null) {
|
||||||
String alias = getEntityFieldAlias(filterType, entityType);
|
return String.format("cast(e.%s as varchar) as %s", getEntityKeyColumn(), getValueAlias());
|
||||||
if (existingEntityFields.contains(alias)) {
|
|
||||||
String column = entityFieldColumnMap.get(alias);
|
|
||||||
return String.format("cast(e.%s as varchar) as %s", column, getValueAlias());
|
|
||||||
} else {
|
} else {
|
||||||
|
Map<String, String> entityPropertiesFunctions = propertiesFunctions.get(entityType);
|
||||||
|
String entityFieldAlias = getEntityFieldAlias(filterType, entityType);
|
||||||
|
if (entityPropertiesFunctions != null && entityPropertiesFunctions.containsKey(entityFieldAlias)) {
|
||||||
|
return String.format("%s as %s", entityPropertiesFunctions.get(entityFieldAlias), getValueAlias());
|
||||||
|
}
|
||||||
return String.format("'' as %s", getValueAlias());
|
return String.format("'' as %s", getValueAlias());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,7 +264,7 @@ public class EntityKeyMapping {
|
|||||||
|
|
||||||
public Stream<String> toQueries(QueryContext ctx, EntityFilterType filterType) {
|
public Stream<String> toQueries(QueryContext ctx, EntityFilterType filterType) {
|
||||||
if (hasFilter()) {
|
if (hasFilter()) {
|
||||||
String keyAlias = entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) ? "e" : alias;
|
String keyAlias = (entityKey.getType().equals(EntityKeyType.ENTITY_FIELD) && getEntityKeyColumn() != null) ? "e" : alias;
|
||||||
return keyFilters.stream().map(keyFilter ->
|
return keyFilters.stream().map(keyFilter ->
|
||||||
this.buildKeyQuery(ctx, keyAlias, keyFilter, filterType));
|
this.buildKeyQuery(ctx, keyAlias, keyFilter, filterType));
|
||||||
} else {
|
} else {
|
||||||
@ -319,7 +344,9 @@ public class EntityKeyMapping {
|
|||||||
.collect(Collectors.joining(" AND "));
|
.collect(Collectors.joining(" AND "));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<EntityKeyMapping> prepareKeyMapping(EntityDataQuery query) {
|
public static List<EntityKeyMapping> prepareKeyMapping(EntityType entityType, EntityDataQuery query) {
|
||||||
|
EntityFilterType entityFilterType = query.getEntityFilter().getType();
|
||||||
|
|
||||||
List<EntityKey> entityFields = query.getEntityFields() != null ? query.getEntityFields() : Collections.emptyList();
|
List<EntityKey> entityFields = query.getEntityFields() != null ? query.getEntityFields() : Collections.emptyList();
|
||||||
List<EntityKey> latestValues = query.getLatestValues() != null ? query.getLatestValues() : Collections.emptyList();
|
List<EntityKey> latestValues = query.getLatestValues() != null ? query.getLatestValues() : Collections.emptyList();
|
||||||
Map<EntityKey, List<KeyFilter>> filters =
|
Map<EntityKey, List<KeyFilter>> filters =
|
||||||
@ -335,6 +362,7 @@ public class EntityKeyMapping {
|
|||||||
mapping.setSelection(true);
|
mapping.setSelection(true);
|
||||||
mapping.setSearchable(!key.getKey().equals(ADDITIONAL_INFO));
|
mapping.setSearchable(!key.getKey().equals(ADDITIONAL_INFO));
|
||||||
mapping.setEntityKey(key);
|
mapping.setEntityKey(key);
|
||||||
|
mapping.setEntityKeyColumn(entityType, entityFilterType);
|
||||||
return mapping;
|
return mapping;
|
||||||
}
|
}
|
||||||
).collect(Collectors.toList());
|
).collect(Collectors.toList());
|
||||||
@ -366,6 +394,7 @@ public class EntityKeyMapping {
|
|||||||
sortOrderMapping.setEntityKey(sortOrderKey);
|
sortOrderMapping.setEntityKey(sortOrderKey);
|
||||||
sortOrderMapping.setSortOrder(true);
|
sortOrderMapping.setSortOrder(true);
|
||||||
sortOrderMapping.setIgnore(true);
|
sortOrderMapping.setIgnore(true);
|
||||||
|
sortOrderMapping.setEntityKeyColumn(entityType, entityFilterType);
|
||||||
if (sortOrderKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
if (sortOrderKey.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||||
entityFieldsMappings.add(sortOrderMapping);
|
entityFieldsMappings.add(sortOrderMapping);
|
||||||
} else {
|
} else {
|
||||||
@ -393,8 +422,9 @@ public class EntityKeyMapping {
|
|||||||
mapping.setAlias(String.format("alias%s", index));
|
mapping.setAlias(String.format("alias%s", index));
|
||||||
mapping.setKeyFilters(filters.get(filterField));
|
mapping.setKeyFilters(filters.get(filterField));
|
||||||
mapping.setLatest(!filterField.getType().equals(EntityKeyType.ENTITY_FIELD));
|
mapping.setLatest(!filterField.getType().equals(EntityKeyType.ENTITY_FIELD));
|
||||||
mapping.setSelection(false);
|
|
||||||
mapping.setEntityKey(filterField);
|
mapping.setEntityKey(filterField);
|
||||||
|
mapping.setEntityKeyColumn(entityType, entityFilterType);
|
||||||
|
mapping.setSelection(mapping.getEntityKeyColumn() == null);
|
||||||
mappings.add(mapping);
|
mappings.add(mapping);
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
@ -403,7 +433,18 @@ public class EntityKeyMapping {
|
|||||||
return mappings;
|
return mappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setEntityKeyColumn(EntityType entityType, EntityFilterType entityFilterType) {
|
||||||
|
Set<String> existingEntityFields = getExistingEntityFields(entityFilterType, entityType);
|
||||||
|
String entityFieldAlias = getEntityFieldAlias(entityFilterType, entityType);
|
||||||
|
if (existingEntityFields.contains(entityFieldAlias)) {
|
||||||
|
entityKeyColumn = entityFieldColumnMap.get(entityFieldAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static List<EntityKeyMapping> prepareEntityCountKeyMapping(EntityCountQuery query) {
|
public static List<EntityKeyMapping> prepareEntityCountKeyMapping(EntityCountQuery query) {
|
||||||
|
EntityType entityType = resolveEntityType(query.getEntityFilter());
|
||||||
|
EntityFilterType entityFilterType = query.getEntityFilter().getType();
|
||||||
|
|
||||||
Map<EntityKey, List<KeyFilter>> filters =
|
Map<EntityKey, List<KeyFilter>> filters =
|
||||||
query.getKeyFilters() != null ?
|
query.getKeyFilters() != null ?
|
||||||
query.getKeyFilters().stream().collect(Collectors.groupingBy(KeyFilter::getKey)) : Collections.emptyMap();
|
query.getKeyFilters().stream().collect(Collectors.groupingBy(KeyFilter::getKey)) : Collections.emptyMap();
|
||||||
@ -416,8 +457,9 @@ public class EntityKeyMapping {
|
|||||||
mapping.setAlias(String.format("alias%s", index));
|
mapping.setAlias(String.format("alias%s", index));
|
||||||
mapping.setKeyFilters(filters.get(filterField));
|
mapping.setKeyFilters(filters.get(filterField));
|
||||||
mapping.setLatest(!filterField.getType().equals(EntityKeyType.ENTITY_FIELD));
|
mapping.setLatest(!filterField.getType().equals(EntityKeyType.ENTITY_FIELD));
|
||||||
mapping.setSelection(false);
|
|
||||||
mapping.setEntityKey(filterField);
|
mapping.setEntityKey(filterField);
|
||||||
|
mapping.setEntityKeyColumn(entityType, entityFilterType);
|
||||||
|
mapping.setSelection(mapping.getEntityKeyColumn() == null);
|
||||||
mappings.add(mapping);
|
mappings.add(mapping);
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
@ -494,30 +536,20 @@ public class EntityKeyMapping {
|
|||||||
private String buildSimplePredicateQuery(QueryContext ctx, String alias, EntityKey key,
|
private String buildSimplePredicateQuery(QueryContext ctx, String alias, EntityKey key,
|
||||||
KeyFilterPredicate predicate, EntityFilterType filterType) {
|
KeyFilterPredicate predicate, EntityFilterType filterType) {
|
||||||
if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
if (key.getType().equals(EntityKeyType.ENTITY_FIELD)) {
|
||||||
Set<String> existingEntityFields = getExistingEntityFields(filterType, ctx.getEntityType());
|
String field = (getEntityKeyColumn() != null) ? alias + "." + getEntityKeyColumn() : alias;
|
||||||
String entityFieldAlias = getEntityFieldAlias(filterType, ctx.getEntityType());
|
if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
|
||||||
String column = null;
|
return this.buildNumericPredicateQuery(ctx, field, (NumericFilterPredicate) predicate);
|
||||||
if (existingEntityFields.contains(entityFieldAlias)) {
|
} else if (predicate.getType().equals(FilterPredicateType.STRING)) {
|
||||||
column = entityFieldColumnMap.get(entityFieldAlias);
|
if (key.getKey().equals("entityType") && !filterType.equals(EntityFilterType.RELATIONS_QUERY)) {
|
||||||
}
|
field = ctx.getEntityType().toString();
|
||||||
if (column != null) {
|
return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate)
|
||||||
String field = alias + "." + column;
|
.replace("lower(" + field, "lower('" + field + "'")
|
||||||
if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
|
.replace(field + " ", "'" + field + "' ");
|
||||||
return this.buildNumericPredicateQuery(ctx, field, (NumericFilterPredicate) predicate);
|
|
||||||
} else if (predicate.getType().equals(FilterPredicateType.STRING)) {
|
|
||||||
if (key.getKey().equals("entityType") && !filterType.equals(EntityFilterType.RELATIONS_QUERY)) {
|
|
||||||
field = ctx.getEntityType().toString();
|
|
||||||
return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate)
|
|
||||||
.replace("lower(" + field, "lower('" + field + "'")
|
|
||||||
.replace(field + " ", "'" + field + "' ");
|
|
||||||
} else {
|
|
||||||
return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate);
|
return this.buildStringPredicateQuery(ctx, field, (StringFilterPredicate) predicate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return this.buildBooleanPredicateQuery(ctx, field, (BooleanFilterPredicate) predicate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
|
if (predicate.getType().equals(FilterPredicateType.NUMERIC)) {
|
||||||
|
|||||||
@ -766,6 +766,8 @@ export class EntityService {
|
|||||||
entityFieldKeys.push(entityFields.firstName.keyName);
|
entityFieldKeys.push(entityFields.firstName.keyName);
|
||||||
entityFieldKeys.push(entityFields.lastName.keyName);
|
entityFieldKeys.push(entityFields.lastName.keyName);
|
||||||
entityFieldKeys.push(entityFields.phone.keyName);
|
entityFieldKeys.push(entityFields.phone.keyName);
|
||||||
|
entityFieldKeys.push(entityFields.ownerName.keyName);
|
||||||
|
entityFieldKeys.push(entityFields.ownerType.keyName);
|
||||||
break;
|
break;
|
||||||
case EntityType.TENANT:
|
case EntityType.TENANT:
|
||||||
case EntityType.CUSTOMER:
|
case EntityType.CUSTOMER:
|
||||||
@ -782,6 +784,8 @@ export class EntityService {
|
|||||||
case EntityType.ENTITY_VIEW:
|
case EntityType.ENTITY_VIEW:
|
||||||
entityFieldKeys.push(entityFields.name.keyName);
|
entityFieldKeys.push(entityFields.name.keyName);
|
||||||
entityFieldKeys.push(entityFields.type.keyName);
|
entityFieldKeys.push(entityFields.type.keyName);
|
||||||
|
entityFieldKeys.push(entityFields.ownerName.keyName);
|
||||||
|
entityFieldKeys.push(entityFields.ownerType.keyName);
|
||||||
break;
|
break;
|
||||||
case EntityType.DEVICE:
|
case EntityType.DEVICE:
|
||||||
case EntityType.EDGE:
|
case EntityType.EDGE:
|
||||||
@ -789,9 +793,13 @@ export class EntityService {
|
|||||||
entityFieldKeys.push(entityFields.name.keyName);
|
entityFieldKeys.push(entityFields.name.keyName);
|
||||||
entityFieldKeys.push(entityFields.type.keyName);
|
entityFieldKeys.push(entityFields.type.keyName);
|
||||||
entityFieldKeys.push(entityFields.label.keyName);
|
entityFieldKeys.push(entityFields.label.keyName);
|
||||||
|
entityFieldKeys.push(entityFields.ownerName.keyName);
|
||||||
|
entityFieldKeys.push(entityFields.ownerType.keyName);
|
||||||
break;
|
break;
|
||||||
case EntityType.DASHBOARD:
|
case EntityType.DASHBOARD:
|
||||||
entityFieldKeys.push(entityFields.title.keyName);
|
entityFieldKeys.push(entityFields.title.keyName);
|
||||||
|
entityFieldKeys.push(entityFields.ownerName.keyName);
|
||||||
|
entityFieldKeys.push(entityFields.ownerType.keyName);
|
||||||
break;
|
break;
|
||||||
case EntityType.API_USAGE_STATE:
|
case EntityType.API_USAGE_STATE:
|
||||||
entityFieldKeys.push(entityFields.name.keyName);
|
entityFieldKeys.push(entityFields.name.keyName);
|
||||||
|
|||||||
@ -171,6 +171,16 @@ export const entityFields: {[fieldName: string]: EntityField} = {
|
|||||||
keyName: 'serviceId',
|
keyName: 'serviceId',
|
||||||
name: 'entity-field.service-id',
|
name: 'entity-field.service-id',
|
||||||
value: 'serviceId'
|
value: 'serviceId'
|
||||||
|
},
|
||||||
|
ownerName: {
|
||||||
|
keyName: 'ownerName',
|
||||||
|
name: 'entity-field.owner-name',
|
||||||
|
value: 'ownerName'
|
||||||
|
},
|
||||||
|
ownerType: {
|
||||||
|
keyName: 'ownerType',
|
||||||
|
name: 'entity-field.owner-type',
|
||||||
|
value: 'ownerType'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -2273,7 +2273,9 @@
|
|||||||
"phone": "Phone",
|
"phone": "Phone",
|
||||||
"label": "Label",
|
"label": "Label",
|
||||||
"queue-name": "Queue name",
|
"queue-name": "Queue name",
|
||||||
"service-id": "Service Id"
|
"service-id": "Service Id",
|
||||||
|
"owner-name": "Owner name",
|
||||||
|
"owner-type": "Owner type"
|
||||||
},
|
},
|
||||||
"entity-view": {
|
"entity-view": {
|
||||||
"entity-view": "Entity view",
|
"entity-view": "Entity view",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user