Browse Source

Merge remote-tracking branch 'origin/master'

pull/3104/head
Andrii Shvaika 6 years ago
parent
commit
933c502ce3
  1. 6
      application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql
  2. 2
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java
  3. 6
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  4. 214
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java
  5. 209
      dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java
  6. 4
      dao/src/main/resources/sql/schema-entities-idx.sql
  7. 2
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html
  8. 9
      ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts
  9. 2
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html
  10. 10
      ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts

6
application/src/main/data/upgrade/3.0.1/schema_update_to_uuid.sql

@ -75,7 +75,8 @@ CREATE OR REPLACE PROCEDURE drop_all_idx()
$$
BEGIN
DROP INDEX IF EXISTS idx_alarm_originator_alarm_type;
DROP INDEX IF EXISTS idx_alarm_originator_alarm_time;
DROP INDEX IF EXISTS idx_alarm_originator_created_time;
DROP INDEX IF EXISTS idx_alarm_tenant_created_time;
DROP INDEX IF EXISTS idx_event_type_entity_id;
DROP INDEX IF EXISTS idx_relation_to_id;
DROP INDEX IF EXISTS idx_relation_from_id;
@ -93,7 +94,8 @@ CREATE OR REPLACE PROCEDURE create_all_idx()
$$
BEGIN
CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_time ON alarm(originator_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_relation_to_id ON relation(relation_type_group, to_type, to_id);
CREATE INDEX IF NOT EXISTS idx_relation_from_id ON relation(relation_type_group, from_type, from_id);

2
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java

@ -286,7 +286,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
int dynamicQueryInvocationCntValue = stats.getDynamicQueryInvocationCnt().getAndSet(0);
long dynamicQueryInvocationTimeValue = stats.getDynamicQueryTimeSpent().getAndSet(0);
long dynamicQueryCnt = subscriptionsBySessionId.values().stream().map(Map::values).count();
if (regularQueryInvocationCntValue > 0 || dynamicQueryInvocationCntValue > 0 || dynamicQueryCnt > 0) {
if (regularQueryInvocationCntValue > 0 || dynamicQueryInvocationCntValue > 0 || dynamicQueryCnt > 0 || alarmQueryInvocationCntValue > 0) {
log.info("Stats: regularQueryInvocationCnt = [{}], regularQueryInvocationTime = [{}], " +
"dynamicQueryCnt = [{}] dynamicQueryInvocationCnt = [{}], dynamicQueryInvocationTime = [{}], " +
"alarmQueryInvocationCnt = [{}], alarmQueryInvocationTime = [{}]",

6
application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java

@ -58,6 +58,9 @@ import org.thingsboard.server.service.queue.TbClusterService;
import org.thingsboard.server.service.state.DeviceStateService;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
@ -92,7 +95,7 @@ public class DefaultTransportApiService implements TransportApiService {
@Autowired
protected TbClusterService tbClusterService;
private ReentrantLock deviceCreationLock = new ReentrantLock();
private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
@Override
public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) {
@ -125,6 +128,7 @@ public class DefaultTransportApiService implements TransportApiService {
DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB()));
ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId);
return Futures.transform(gatewayFuture, gateway -> {
Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(requestMsg.getDeviceName(), id -> new ReentrantLock());
deviceCreationLock.lock();
try {
Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName());

214
dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java

@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
@ -53,7 +56,6 @@ import java.util.stream.Collectors;
public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
private static final Map<String, String> alarmFieldColumnMap = new HashMap<>();
private static final List<String> uniqueAlarmFields = new ArrayList<>();
static {
alarmFieldColumnMap.put("createdTime", ModelConstants.CREATED_TIME_PROPERTY);
@ -72,11 +74,9 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
alarmFieldColumnMap.put("originator_id", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY);
alarmFieldColumnMap.put("originator_type", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY);
alarmFieldColumnMap.put("originator", "originator_name");
uniqueAlarmFields.addAll(new HashSet<>(alarmFieldColumnMap.values()));
}
public static final String SELECT_ORIGINATOR_NAME = " CASE" +
private static final String SELECT_ORIGINATOR_NAME = " CASE" +
" WHEN a.originator_type = " + EntityType.TENANT.ordinal() +
" THEN (select title from tenant where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() +
@ -93,7 +93,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
" THEN (select name from entity_view where id = a.originator_id)" +
" END as originator_name";
public static final String FIELDS_SELECTION = "select a.id as id," +
private static final String FIELDS_SELECTION = "select a.id as id," +
" a.created_time as created_time," +
" a.ack_ts as ack_ts," +
" a.clear_ts as clear_ts," +
@ -109,130 +109,136 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
" a.propagate_relation_types as propagate_relation_types, " +
" a.type as type," + SELECT_ORIGINATOR_NAME + ", ";
public static final String JOIN_RELATIONS = "left join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id and r.from_id in (:entity_ids)";
private static final String JOIN_RELATIONS = "left join relation r on r.relation_type_group = 'ALARM' and r.relation_type = 'ANY' and a.id = r.to_id and r.from_id in (:entity_ids)";
@Autowired
protected NamedParameterJdbcTemplate jdbcTemplate;
protected final NamedParameterJdbcTemplate jdbcTemplate;
private final TransactionTemplate transactionTemplate;
public DefaultAlarmQueryRepository(NamedParameterJdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.transactionTemplate = transactionTemplate;
}
@Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
AlarmDataPageLink pageLink = query.getPageLink();
QueryContext ctx = new QueryContext();
ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList()));
return transactionTemplate.execute(status -> {
AlarmDataPageLink pageLink = query.getPageLink();
QueryContext ctx = new QueryContext();
ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList()));
StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION);
StringBuilder fromPart = new StringBuilder(" from alarm a ");
StringBuilder wherePart = new StringBuilder(" where ");
StringBuilder sortPart = new StringBuilder(" order by ");
boolean addAnd = false;
if (pageLink.isSearchPropagatedAlarms()) {
selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id ");
fromPart.append(JOIN_RELATIONS);
wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx));
addAnd = true;
} else {
selectPart.append(" a.originator_id as entity_id ");
}
EntityDataSortOrder sortOrder = pageLink.getSortOrder();
if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) {
String sortOrderKey = sortOrder.getKey().getKey();
sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey))
.append(" ").append(sortOrder.getDirection().name());
StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION);
StringBuilder fromPart = new StringBuilder(" from alarm a ");
StringBuilder wherePart = new StringBuilder(" where ");
StringBuilder sortPart = new StringBuilder(" order by ");
boolean addAnd = false;
if (pageLink.isSearchPropagatedAlarms()) {
wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)");
} else {
addAndIfNeeded(wherePart, addAnd);
selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id ");
fromPart.append(JOIN_RELATIONS);
wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx));
addAnd = true;
wherePart.append(" a.originator_id in (:entity_ids)");
} else {
selectPart.append(" a.originator_id as entity_id ");
}
} else {
fromPart.append(" left join (select * from (VALUES");
int entityIdIdx = 0;
int lastEntityIdIdx = orderedEntityIds.size() - 1;
for (EntityId entityId : orderedEntityIds) {
fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")");
if (entityIdIdx != lastEntityIdIdx) {
fromPart.append(",");
EntityDataSortOrder sortOrder = pageLink.getSortOrder();
if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) {
String sortOrderKey = sortOrder.getKey().getKey();
sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey))
.append(" ").append(sortOrder.getDirection().name());
if (pageLink.isSearchPropagatedAlarms()) {
wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)");
} else {
fromPart.append(")");
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
wherePart.append(" a.originator_id in (:entity_ids)");
}
entityIdIdx++;
}
fromPart.append(" as e(id, priority)) e ");
if (pageLink.isSearchPropagatedAlarms()) {
fromPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)");
} else {
fromPart.append("on a.originator_id = e.id");
fromPart.append(" left join (select * from (VALUES");
int entityIdIdx = 0;
int lastEntityIdIdx = orderedEntityIds.size() - 1;
for (EntityId entityId : orderedEntityIds) {
fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")");
if (entityIdIdx != lastEntityIdIdx) {
fromPart.append(",");
} else {
fromPart.append(")");
}
entityIdIdx++;
}
fromPart.append(" as e(id, priority)) e ");
if (pageLink.isSearchPropagatedAlarms()) {
fromPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)");
} else {
fromPart.append("on a.originator_id = e.id");
}
sortPart.append("e.priority");
}
sortPart.append("e.priority");
}
long startTs;
long endTs;
if (pageLink.getTimeWindow() > 0) {
endTs = System.currentTimeMillis();
startTs = endTs - pageLink.getTimeWindow();
} else {
startTs = pageLink.getStartTs();
endTs = pageLink.getEndTs();
}
if (startTs > 0) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addLongParameter("startTime", startTs);
wherePart.append("a.created_time >= :startTime");
}
long startTs;
long endTs;
if (pageLink.getTimeWindow() > 0) {
endTs = System.currentTimeMillis();
startTs = endTs - pageLink.getTimeWindow();
} else {
startTs = pageLink.getStartTs();
endTs = pageLink.getEndTs();
}
if (endTs > 0) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addLongParameter("endTime", endTs);
wherePart.append("a.created_time <= :endTime");
}
if (startTs > 0) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addLongParameter("startTime", startTs);
wherePart.append("a.created_time >= :startTime");
}
if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addStringListParameter("alarmTypes", pageLink.getTypeList());
wherePart.append("a.type in (:alarmTypes)");
}
if (endTs > 0) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addLongParameter("endTime", endTs);
wherePart.append("a.created_time <= :endTime");
}
if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList()));
wherePart.append("a.severity in (:alarmSeverities)");
}
if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addStringListParameter("alarmTypes", pageLink.getTypeList());
wherePart.append("a.type in (:alarmTypes)");
}
if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) {
Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList());
if (!statusSet.isEmpty()) {
if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList()));
wherePart.append(" a.status in (:alarmStatuses)");
ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList()));
wherePart.append("a.severity in (:alarmSeverities)");
}
}
String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch());
String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString();
if (!textSearchQuery.isEmpty()) {
mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery);
}
String countQuery = mainQuery;
int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) from (%s) result", countQuery), ctx, Integer.class);
if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) {
Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList());
if (!statusSet.isEmpty()) {
addAndIfNeeded(wherePart, addAnd);
addAnd = true;
ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList()));
wherePart.append(" a.status in (:alarmStatuses)");
}
}
String dataQuery = mainQuery + sortPart;
String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch());
String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString();
if (!textSearchQuery.isEmpty()) {
mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery);
}
String countQuery = mainQuery;
int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) from (%s) result", countQuery), ctx, Integer.class);
int startIndex = pageLink.getPageSize() * pageLink.getPage();
if (pageLink.getPageSize() > 0) {
dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex);
}
List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx);
return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements, orderedEntityIds);
String dataQuery = mainQuery + sortPart;
int startIndex = pageLink.getPageSize() * pageLink.getPage();
if (pageLink.getPageSize() > 0) {
dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex);
}
List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx);
return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements, orderedEntityIds);
});
}
private String buildTextSearchQuery(QueryContext ctx, List<EntityKey> selectionMapping, String searchText) {

209
dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java

@ -20,6 +20,9 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
@ -58,37 +61,36 @@ import java.util.stream.Collectors;
@Repository
@Slf4j
public class DefaultEntityQueryRepository implements EntityQueryRepository {
//TODO: rafactoring to protect from SQL injections;
private static final Map<EntityType, String> entityTableMap = new HashMap<>();
public static final String SELECT_PHONE = " CASE WHEN entity.entity_type = 'TENANT' THEN (select phone from tenant where id = entity_id)" +
private static final String SELECT_PHONE = " CASE WHEN entity.entity_type = 'TENANT' THEN (select phone from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' THEN (select phone from customer where id = entity_id) END as phone";
public static final String SELECT_ZIP = " CASE WHEN entity.entity_type = 'TENANT' THEN (select zip from tenant where id = entity_id)" +
private static final String SELECT_ZIP = " CASE WHEN entity.entity_type = 'TENANT' THEN (select zip from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' THEN (select zip from customer where id = entity_id) END as zip";
public static final String SELECT_ADDRESS_2 = " CASE WHEN entity.entity_type = 'TENANT'" +
private static final String SELECT_ADDRESS_2 = " CASE WHEN entity.entity_type = 'TENANT'" +
" THEN (select address2 from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select address2 from customer where id = entity_id) END as address2";
public static final String SELECT_ADDRESS = " CASE WHEN entity.entity_type = 'TENANT'" +
private static final String SELECT_ADDRESS = " CASE WHEN entity.entity_type = 'TENANT'" +
" THEN (select address from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select address from customer where id = entity_id) END as address";
public static final String SELECT_CITY = " CASE WHEN entity.entity_type = 'TENANT'" +
private static final String SELECT_CITY = " CASE WHEN entity.entity_type = 'TENANT'" +
" THEN (select city from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select city from customer where id = entity_id) END as city";
public static final String SELECT_STATE = " CASE WHEN entity.entity_type = 'TENANT'" +
private static final String SELECT_STATE = " CASE WHEN entity.entity_type = 'TENANT'" +
" THEN (select state from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select state from customer where id = entity_id) END as state";
public static final String SELECT_COUNTRY = " CASE WHEN entity.entity_type = 'TENANT'" +
private static final String SELECT_COUNTRY = " CASE WHEN entity.entity_type = 'TENANT'" +
" THEN (select country from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select country from customer where id = entity_id) END as country";
public static final String SELECT_TITLE = " CASE WHEN entity.entity_type = 'TENANT'" +
private static final String SELECT_TITLE = " CASE WHEN entity.entity_type = 'TENANT'" +
" THEN (select title from tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select title from customer where id = entity_id) END as title";
public static final String SELECT_LAST_NAME = " CASE WHEN entity.entity_type = 'USER'" +
private static final String SELECT_LAST_NAME = " CASE WHEN entity.entity_type = 'USER'" +
" THEN (select last_name from tb_user where id = entity_id) END as last_name";
public static final String SELECT_FIRST_NAME = " CASE WHEN entity.entity_type = 'USER'" +
private static final String SELECT_FIRST_NAME = " CASE WHEN entity.entity_type = 'USER'" +
" THEN (select first_name from tb_user where id = entity_id) END as first_name";
public static final String SELECT_REGION = " CASE WHEN entity.entity_type = 'TENANT'" +
private static final String SELECT_REGION = " CASE WHEN entity.entity_type = 'TENANT'" +
" THEN (select region from tenant where id = entity_id) END as region";
public static final String SELECT_EMAIL = " CASE" +
private static final String SELECT_EMAIL = " CASE" +
" WHEN entity.entity_type = 'TENANT'" +
" THEN (select email from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' " +
@ -96,7 +98,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'USER'" +
" THEN (select email from tb_user where id = entity_id)" +
" END as email";
public static final String SELECT_CUSTOMER_ID = "CASE" +
private static final String SELECT_CUSTOMER_ID = "CASE" +
" WHEN entity.entity_type = 'TENANT'" +
" THEN UUID('" + TenantId.NULL_UUID + "')" +
" WHEN entity.entity_type = 'CUSTOMER' THEN entity_id" +
@ -112,7 +114,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select customer_id from entity_view where id = entity_id)" +
" END as customer_id";
public static final String SELECT_TENANT_ID = "SELECT CASE" +
private static final String SELECT_TENANT_ID = "SELECT CASE" +
" WHEN entity.entity_type = 'TENANT' THEN entity_id" +
" WHEN entity.entity_type = 'CUSTOMER'" +
" THEN (select tenant_id from customer where id = entity_id)" +
@ -127,7 +129,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select tenant_id from entity_view where id = entity_id)" +
" END as tenant_id";
public static final String SELECT_CREATED_TIME = " CASE" +
private static final String SELECT_CREATED_TIME = " CASE" +
" WHEN entity.entity_type = 'TENANT'" +
" THEN (select created_time from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' " +
@ -143,7 +145,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select created_time from entity_view where id = entity_id)" +
" END as created_time";
public static final String SELECT_NAME = " CASE" +
private static final String SELECT_NAME = " CASE" +
" WHEN entity.entity_type = 'TENANT'" +
" THEN (select title from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' " +
@ -159,7 +161,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select name from entity_view where id = entity_id)" +
" END as name";
public static final String SELECT_TYPE = " CASE" +
private static final String SELECT_TYPE = " CASE" +
" WHEN entity.entity_type = 'USER'" +
" THEN (select authority from tb_user where id = entity_id)" +
" WHEN entity.entity_type = 'ASSET'" +
@ -169,7 +171,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select type from entity_view where id = entity_id)" +
" ELSE entity.entity_type END as type";
public static final String SELECT_LABEL = " CASE" +
private static final String SELECT_LABEL = " CASE" +
" WHEN entity.entity_type = 'TENANT'" +
" THEN (select title from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' " +
@ -196,7 +198,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
entityTableMap.put(EntityType.TENANT, "tenant");
}
public static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" +
private static final String HIERARCHICAL_QUERY_TEMPLATE = " FROM (WITH RECURSIVE related_entities(from_id, from_type, to_id, to_type, relation_type, lvl) AS (" +
" SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" +
" FROM relation" +
" WHERE $in_id = :relation_root_id and $in_type = :relation_root_type and relation_type_group = 'COMMON'" +
@ -209,11 +211,16 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" +
" from related_entities re" +
" %s ) entity";
public static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from");
public static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to");
private static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from");
private static final String HIERARCHICAL_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to");
@Autowired
protected NamedParameterJdbcTemplate jdbcTemplate;
private final NamedParameterJdbcTemplate jdbcTemplate;
private final TransactionTemplate transactionTemplate;
public DefaultEntityQueryRepository(NamedParameterJdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.transactionTemplate = transactionTemplate;
}
@Override
public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) {
@ -223,89 +230,91 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
ctx.append(addEntityTableQuery(ctx, query.getEntityFilter(), entityType));
ctx.append(" e where ");
ctx.append(buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), Collections.emptyList(), entityType));
return jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class);
return transactionTemplate.execute(status -> jdbcTemplate.queryForObject(ctx.getQuery(), ctx, Long.class));
}
@Override
public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
QueryContext ctx = new QueryContext();
EntityType entityType = resolveEntityType(query.getEntityFilter());
EntityDataPageLink pageLink = query.getPageLink();
List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(query);
List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection)
.collect(Collectors.toList());
List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest())
.collect(Collectors.toList());
List<EntityKeyMapping> latestSelectionMapping = selectionMapping.stream().filter(EntityKeyMapping::isLatest)
.collect(Collectors.toList());
List<EntityKeyMapping> filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter)
.collect(Collectors.toList());
List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest())
.collect(Collectors.toList());
List<EntityKeyMapping> latestFiltersMapping = filterMapping.stream().filter(EntityKeyMapping::isLatest)
.collect(Collectors.toList());
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest)
.collect(Collectors.toList());
String entityWhereClause = this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType);
String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings);
String whereClause = this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType);
String textSearchQuery = this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch());
String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType);
String entityTypeStr;
if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) {
entityTypeStr = "e.entity_type";
} else {
entityTypeStr = "'" + entityType.name() + "'";
}
if (!StringUtils.isEmpty(entityFieldsSelection)) {
entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection);
} else {
entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr);
}
String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping, query.getEntityFilter().getType(), entityType);
String topSelection = "entities.*";
if (!StringUtils.isEmpty(latestSelection)) {
topSelection = topSelection + ", " + latestSelection;
}
return transactionTemplate.execute(status -> {
QueryContext ctx = new QueryContext();
EntityType entityType = resolveEntityType(query.getEntityFilter());
EntityDataPageLink pageLink = query.getPageLink();
List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(query);
List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection)
.collect(Collectors.toList());
List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest())
.collect(Collectors.toList());
List<EntityKeyMapping> latestSelectionMapping = selectionMapping.stream().filter(EntityKeyMapping::isLatest)
.collect(Collectors.toList());
List<EntityKeyMapping> filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter)
.collect(Collectors.toList());
List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest())
.collect(Collectors.toList());
List<EntityKeyMapping> latestFiltersMapping = filterMapping.stream().filter(EntityKeyMapping::isLatest)
.collect(Collectors.toList());
List<EntityKeyMapping> allLatestMappings = mappings.stream().filter(EntityKeyMapping::isLatest)
.collect(Collectors.toList());
String entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType);
String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings);
String whereClause = DefaultEntityQueryRepository.this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType);
String textSearchQuery = DefaultEntityQueryRepository.this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch());
String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType);
String entityTypeStr;
if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) {
entityTypeStr = "e.entity_type";
} else {
entityTypeStr = "'" + entityType.name() + "'";
}
if (!StringUtils.isEmpty(entityFieldsSelection)) {
entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection);
} else {
entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr);
}
String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping, query.getEntityFilter().getType(), entityType);
String topSelection = "entities.*";
if (!StringUtils.isEmpty(latestSelection)) {
topSelection = topSelection + ", " + latestSelection;
}
String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result %s",
topSelection,
entityFieldsSelection,
addEntityTableQuery(ctx, query.getEntityFilter(), entityType),
entityWhereClause,
latestJoins,
whereClause,
textSearchQuery);
int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class);
String dataQuery = String.format("select * %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";
String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result %s",
topSelection,
entityFieldsSelection,
addEntityTableQuery(ctx, query.getEntityFilter(), entityType),
entityWhereClause,
latestJoins,
whereClause,
textSearchQuery);
int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class);
String dataQuery = String.format("select * %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();
if (pageLink.getPageSize() > 0) {
dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex);
}
List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx);
return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements);
int startIndex = pageLink.getPageSize() * pageLink.getPage();
if (pageLink.getPageSize() > 0) {
dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex);
}
List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx);
return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements);
});
}
private String buildEntityWhere(QueryContext ctx,

4
dao/src/main/resources/sql/schema-entities-idx.sql

@ -16,7 +16,9 @@
CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_type ON alarm(originator_id, type, start_ts DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_originator_alarm_time ON alarm(originator_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_originator_created_time ON alarm(originator_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_alarm_tenant_created_time ON alarm(tenant_id, created_time DESC);
CREATE INDEX IF NOT EXISTS idx_event_type_entity_id ON event(tenant_id, event_type, entity_type, entity_id);

2
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.html

@ -119,7 +119,7 @@
</mat-cell>
</ng-container>
<mat-header-row [ngClass]="{'mat-row-select': enableSelection}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [ngClass]="{'mat-row-select': enableSelection,
<mat-row [fxShow]="!alarmsDatasource.dataLoading" [ngClass]="{'mat-row-select': enableSelection,
'mat-selected': alarmsDatasource.isSelected(alarm),
'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm)}"
*matRowDef="let alarm; columns: displayedColumns;"

9
ui-ngx/src/app/modules/home/components/widget/lib/alarms-table-widget.component.ts

@ -368,7 +368,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label);
dataKey.def = 'def' + this.columns.length;
const keySettings: TableWidgetDataKeySettings = dataKey.settings;
if (dataKey.type === DataKeyType.alarm && !isDefined(keySettings.columnWidth)) {
const alarmField = alarmFields[dataKey.name];
if (alarmField && alarmField.time) {
keySettings.columnWidth = '120px';
}
}
this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings);
this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx');
this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
@ -863,7 +868,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) {
this.dataLoading = true;
this.clear();
// this.clear();
this.appliedPageLink = pageLink;
this.appliedSortOrderLabel = sortOrderLabel;
this.subscription.subscribeForAlarms(pageLink, keyFilters);

2
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.html

@ -82,7 +82,7 @@
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row>
<mat-row [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity)}"
<mat-row [fxShow]="!entityDatasource.dataLoading" [ngClass]="{'tb-current-entity': entityDatasource.isCurrentEntity(entity)}"
*matRowDef="let entity; columns: displayedColumns;"
(click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row>
</table>

10
ui-ngx/src/app/modules/home/components/widget/lib/entities-table-widget.component.ts

@ -87,6 +87,7 @@ import {
import { sortItems } from '@shared/models/page/page-link';
import { entityFields } from '@shared/models/entity.models';
import { DatePipe } from '@angular/common';
import { alarmFields } from '@shared/models/alarm.models';
interface EntitiesTableWidgetSettings extends TableWidgetSettings {
entitiesTitle: string;
@ -348,6 +349,13 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label);
dataKey.def = 'def' + this.columns.length;
const keySettings: TableWidgetDataKeySettings = dataKey.settings;
if (dataKey.type === DataKeyType.entityField &&
!isDefined(keySettings.columnWidth) || keySettings.columnWidth === '0px') {
const entityField = entityFields[dataKey.name];
if (entityField && entityField.time) {
keySettings.columnWidth = '120px';
}
}
this.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings);
this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx');
@ -595,7 +603,7 @@ class EntityDatasource implements DataSource<EntityData> {
loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) {
this.dataLoading = true;
this.clear();
// this.clear();
this.appliedPageLink = pageLink;
this.appliedSortOrderLabel = sortOrderLabel;
this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters);

Loading…
Cancel
Save