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 BEGIN
DROP INDEX IF EXISTS idx_alarm_originator_alarm_type; 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_event_type_entity_id;
DROP INDEX IF EXISTS idx_relation_to_id; DROP INDEX IF EXISTS idx_relation_to_id;
DROP INDEX IF EXISTS idx_relation_from_id; DROP INDEX IF EXISTS idx_relation_from_id;
@ -93,7 +94,8 @@ CREATE OR REPLACE PROCEDURE create_all_idx()
$$ $$
BEGIN 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_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_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_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); 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); int dynamicQueryInvocationCntValue = stats.getDynamicQueryInvocationCnt().getAndSet(0);
long dynamicQueryInvocationTimeValue = stats.getDynamicQueryTimeSpent().getAndSet(0); long dynamicQueryInvocationTimeValue = stats.getDynamicQueryTimeSpent().getAndSet(0);
long dynamicQueryCnt = subscriptionsBySessionId.values().stream().map(Map::values).count(); 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 = [{}], " + log.info("Stats: regularQueryInvocationCnt = [{}], regularQueryInvocationTime = [{}], " +
"dynamicQueryCnt = [{}] dynamicQueryInvocationCnt = [{}], dynamicQueryInvocationTime = [{}], " + "dynamicQueryCnt = [{}] dynamicQueryInvocationCnt = [{}], dynamicQueryInvocationTime = [{}], " +
"alarmQueryInvocationCnt = [{}], alarmQueryInvocationTime = [{}]", "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 org.thingsboard.server.service.state.DeviceStateService;
import java.util.UUID; 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; import java.util.concurrent.locks.ReentrantLock;
/** /**
@ -92,7 +95,7 @@ public class DefaultTransportApiService implements TransportApiService {
@Autowired @Autowired
protected TbClusterService tbClusterService; protected TbClusterService tbClusterService;
private ReentrantLock deviceCreationLock = new ReentrantLock(); private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
@Override @Override
public ListenableFuture<TbProtoQueueMsg<TransportApiResponseMsg>> handle(TbProtoQueueMsg<TransportApiRequestMsg> tbProtoQueueMsg) { 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())); DeviceId gatewayId = new DeviceId(new UUID(requestMsg.getGatewayIdMSB(), requestMsg.getGatewayIdLSB()));
ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId); ListenableFuture<Device> gatewayFuture = deviceService.findDeviceByIdAsync(TenantId.SYS_TENANT_ID, gatewayId);
return Futures.transform(gatewayFuture, gateway -> { return Futures.transform(gatewayFuture, gateway -> {
Lock deviceCreationLock = deviceCreationLocks.computeIfAbsent(requestMsg.getDeviceName(), id -> new ReentrantLock());
deviceCreationLock.lock(); deviceCreationLock.lock();
try { try {
Device device = deviceService.findDeviceByTenantIdAndName(gateway.getTenantId(), requestMsg.getDeviceName()); 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.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository; 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.EntityType;
import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmSeverity;
@ -53,7 +56,6 @@ import java.util.stream.Collectors;
public class DefaultAlarmQueryRepository implements AlarmQueryRepository { public class DefaultAlarmQueryRepository implements AlarmQueryRepository {
private static final Map<String, String> alarmFieldColumnMap = new HashMap<>(); private static final Map<String, String> alarmFieldColumnMap = new HashMap<>();
private static final List<String> uniqueAlarmFields = new ArrayList<>();
static { static {
alarmFieldColumnMap.put("createdTime", ModelConstants.CREATED_TIME_PROPERTY); 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_id", ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY);
alarmFieldColumnMap.put("originator_type", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY); alarmFieldColumnMap.put("originator_type", ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY);
alarmFieldColumnMap.put("originator", "originator_name"); 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() + " WHEN a.originator_type = " + EntityType.TENANT.ordinal() +
" THEN (select title from tenant where id = a.originator_id)" + " THEN (select title from tenant where id = a.originator_id)" +
" WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + " 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)" + " THEN (select name from entity_view where id = a.originator_id)" +
" END as originator_name"; " 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.created_time as created_time," +
" a.ack_ts as ack_ts," + " a.ack_ts as ack_ts," +
" a.clear_ts as clear_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.propagate_relation_types as propagate_relation_types, " +
" a.type as type," + SELECT_ORIGINATOR_NAME + ", "; " 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 final NamedParameterJdbcTemplate jdbcTemplate;
protected NamedParameterJdbcTemplate jdbcTemplate; private final TransactionTemplate transactionTemplate;
public DefaultAlarmQueryRepository(NamedParameterJdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.transactionTemplate = transactionTemplate;
}
@Override @Override
public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, public PageData<AlarmData> findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId,
AlarmDataQuery query, Collection<EntityId> orderedEntityIds) { AlarmDataQuery query, Collection<EntityId> orderedEntityIds) {
AlarmDataPageLink pageLink = query.getPageLink(); return transactionTemplate.execute(status -> {
QueryContext ctx = new QueryContext(); AlarmDataPageLink pageLink = query.getPageLink();
ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList())); QueryContext ctx = new QueryContext();
ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList()));
StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION); StringBuilder selectPart = new StringBuilder(FIELDS_SELECTION);
StringBuilder fromPart = new StringBuilder(" from alarm a "); StringBuilder fromPart = new StringBuilder(" from alarm a ");
StringBuilder wherePart = new StringBuilder(" where "); StringBuilder wherePart = new StringBuilder(" where ");
StringBuilder sortPart = new StringBuilder(" order by "); StringBuilder sortPart = new StringBuilder(" order by ");
boolean addAnd = false; 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());
if (pageLink.isSearchPropagatedAlarms()) { if (pageLink.isSearchPropagatedAlarms()) {
wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)"); selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id ");
} else { fromPart.append(JOIN_RELATIONS);
addAndIfNeeded(wherePart, addAnd); wherePart.append(buildPermissionsQuery(tenantId, customerId, ctx));
addAnd = true; addAnd = true;
wherePart.append(" a.originator_id in (:entity_ids)"); } else {
selectPart.append(" a.originator_id as entity_id ");
} }
} else { EntityDataSortOrder sortOrder = pageLink.getSortOrder();
fromPart.append(" left join (select * from (VALUES"); if (sortOrder != null && sortOrder.getKey().getType().equals(EntityKeyType.ALARM_FIELD)) {
int entityIdIdx = 0; String sortOrderKey = sortOrder.getKey().getKey();
int lastEntityIdIdx = orderedEntityIds.size() - 1; sortPart.append(alarmFieldColumnMap.getOrDefault(sortOrderKey, sortOrderKey))
for (EntityId entityId : orderedEntityIds) { .append(" ").append(sortOrder.getDirection().name());
fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")"); if (pageLink.isSearchPropagatedAlarms()) {
if (entityIdIdx != lastEntityIdIdx) { wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)");
fromPart.append(",");
} else { } 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 { } 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) { long startTs;
addAndIfNeeded(wherePart, addAnd); long endTs;
addAnd = true; if (pageLink.getTimeWindow() > 0) {
ctx.addLongParameter("startTime", startTs); endTs = System.currentTimeMillis();
wherePart.append("a.created_time >= :startTime"); startTs = endTs - pageLink.getTimeWindow();
} } else {
startTs = pageLink.getStartTs();
endTs = pageLink.getEndTs();
}
if (endTs > 0) { if (startTs > 0) {
addAndIfNeeded(wherePart, addAnd); addAndIfNeeded(wherePart, addAnd);
addAnd = true; addAnd = true;
ctx.addLongParameter("endTime", endTs); ctx.addLongParameter("startTime", startTs);
wherePart.append("a.created_time <= :endTime"); wherePart.append("a.created_time >= :startTime");
} }
if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) { if (endTs > 0) {
addAndIfNeeded(wherePart, addAnd); addAndIfNeeded(wherePart, addAnd);
addAnd = true; addAnd = true;
ctx.addStringListParameter("alarmTypes", pageLink.getTypeList()); ctx.addLongParameter("endTime", endTs);
wherePart.append("a.type in (:alarmTypes)"); wherePart.append("a.created_time <= :endTime");
} }
if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) { if (pageLink.getTypeList() != null && !pageLink.getTypeList().isEmpty()) {
addAndIfNeeded(wherePart, addAnd); addAndIfNeeded(wherePart, addAnd);
addAnd = true; addAnd = true;
ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList())); ctx.addStringListParameter("alarmTypes", pageLink.getTypeList());
wherePart.append("a.severity in (:alarmSeverities)"); wherePart.append("a.type in (:alarmTypes)");
} }
if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) { if (pageLink.getSeverityList() != null && !pageLink.getSeverityList().isEmpty()) {
Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList());
if (!statusSet.isEmpty()) {
addAndIfNeeded(wherePart, addAnd); addAndIfNeeded(wherePart, addAnd);
addAnd = true; addAnd = true;
ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList())); ctx.addStringListParameter("alarmSeverities", pageLink.getSeverityList().stream().map(AlarmSeverity::name).collect(Collectors.toList()));
wherePart.append(" a.status in (:alarmStatuses)"); wherePart.append("a.severity in (:alarmSeverities)");
} }
}
String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch()); if (pageLink.getStatusList() != null && !pageLink.getStatusList().isEmpty()) {
String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString(); Set<AlarmStatus> statusSet = toStatusSet(pageLink.getStatusList());
if (!textSearchQuery.isEmpty()) { if (!statusSet.isEmpty()) {
mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery); addAndIfNeeded(wherePart, addAnd);
} addAnd = true;
String countQuery = mainQuery; ctx.addStringListParameter("alarmStatuses", statusSet.stream().map(AlarmStatus::name).collect(Collectors.toList()));
int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) from (%s) result", countQuery), ctx, Integer.class); 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(); String dataQuery = mainQuery + sortPart;
if (pageLink.getPageSize() > 0) {
dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); int startIndex = pageLink.getPageSize() * pageLink.getPage();
} if (pageLink.getPageSize() > 0) {
List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx); dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex);
return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements, orderedEntityIds); }
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) { 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.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository; 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.EntityType;
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.EntityId;
@ -58,37 +61,36 @@ import java.util.stream.Collectors;
@Repository @Repository
@Slf4j @Slf4j
public class DefaultEntityQueryRepository implements EntityQueryRepository { public class DefaultEntityQueryRepository implements EntityQueryRepository {
//TODO: rafactoring to protect from SQL injections;
private static final Map<EntityType, String> entityTableMap = new HashMap<>(); 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"; " 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"; " 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 tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select address2 from customer where id = entity_id) END as address2"; " 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 tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select address from customer where id = entity_id) END as address"; " 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 tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select city from customer where id = entity_id) END as city"; " 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 tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select state from customer where id = entity_id) END as state"; " 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 tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select country from customer where id = entity_id) END as country"; " 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 tenant where id = entity_id) WHEN entity.entity_type = 'CUSTOMER' " +
" THEN (select title from customer where id = entity_id) END as title"; " 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"; " 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"; " 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"; " 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'" + " WHEN entity.entity_type = 'TENANT'" +
" THEN (select email from tenant where id = entity_id)" + " THEN (select email from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' " + " WHEN entity.entity_type = 'CUSTOMER' " +
@ -96,7 +98,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'USER'" + " WHEN entity.entity_type = 'USER'" +
" THEN (select email from tb_user where id = entity_id)" + " THEN (select email from tb_user where id = entity_id)" +
" END as email"; " END as email";
public static final String SELECT_CUSTOMER_ID = "CASE" + private static final String SELECT_CUSTOMER_ID = "CASE" +
" WHEN entity.entity_type = 'TENANT'" + " WHEN entity.entity_type = 'TENANT'" +
" THEN UUID('" + TenantId.NULL_UUID + "')" + " THEN UUID('" + TenantId.NULL_UUID + "')" +
" WHEN entity.entity_type = 'CUSTOMER' THEN entity_id" + " WHEN entity.entity_type = 'CUSTOMER' THEN entity_id" +
@ -112,7 +114,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select customer_id from entity_view where id = entity_id)" + " THEN (select customer_id from entity_view where id = entity_id)" +
" END as customer_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 = 'TENANT' THEN entity_id" +
" WHEN entity.entity_type = 'CUSTOMER'" + " WHEN entity.entity_type = 'CUSTOMER'" +
" THEN (select tenant_id from customer where id = entity_id)" + " 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'" + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select tenant_id from entity_view where id = entity_id)" + " THEN (select tenant_id from entity_view where id = entity_id)" +
" END as tenant_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'" + " WHEN entity.entity_type = 'TENANT'" +
" THEN (select created_time from tenant where id = entity_id)" + " THEN (select created_time from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' " + " WHEN entity.entity_type = 'CUSTOMER' " +
@ -143,7 +145,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select created_time from entity_view where id = entity_id)" + " THEN (select created_time from entity_view where id = entity_id)" +
" END as created_time"; " END as created_time";
public static final String SELECT_NAME = " CASE" + private static final String SELECT_NAME = " CASE" +
" WHEN entity.entity_type = 'TENANT'" + " WHEN entity.entity_type = 'TENANT'" +
" THEN (select title from tenant where id = entity_id)" + " THEN (select title from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' " + " WHEN entity.entity_type = 'CUSTOMER' " +
@ -159,7 +161,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select name from entity_view where id = entity_id)" + " THEN (select name from entity_view where id = entity_id)" +
" END as name"; " END as name";
public static final String SELECT_TYPE = " CASE" + private static final String SELECT_TYPE = " CASE" +
" WHEN entity.entity_type = 'USER'" + " WHEN entity.entity_type = 'USER'" +
" THEN (select authority from tb_user where id = entity_id)" + " THEN (select authority from tb_user where id = entity_id)" +
" WHEN entity.entity_type = 'ASSET'" + " WHEN entity.entity_type = 'ASSET'" +
@ -169,7 +171,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
" WHEN entity.entity_type = 'ENTITY_VIEW'" + " WHEN entity.entity_type = 'ENTITY_VIEW'" +
" THEN (select type from entity_view where id = entity_id)" + " THEN (select type from entity_view where id = entity_id)" +
" ELSE entity.entity_type END as type"; " 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'" + " WHEN entity.entity_type = 'TENANT'" +
" THEN (select title from tenant where id = entity_id)" + " THEN (select title from tenant where id = entity_id)" +
" WHEN entity.entity_type = 'CUSTOMER' " + " WHEN entity.entity_type = 'CUSTOMER' " +
@ -196,7 +198,7 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository {
entityTableMap.put(EntityType.TENANT, "tenant"); 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" + " SELECT from_id, from_type, to_id, to_type, relation_type, 1 as lvl" +
" FROM relation" + " FROM relation" +
" WHERE $in_id = :relation_root_id and $in_type = :relation_root_type and relation_type_group = 'COMMON'" + " 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" + " SELECT re.$out_id entity_id, re.$out_type entity_type, re.lvl lvl" +
" from related_entities re" + " from related_entities re" +
" %s ) entity"; " %s ) entity";
public static final String HIERARCHICAL_TO_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "to").replace("$out", "from"); private 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_FROM_QUERY_TEMPLATE = HIERARCHICAL_QUERY_TEMPLATE.replace("$in", "from").replace("$out", "to");
@Autowired private final NamedParameterJdbcTemplate jdbcTemplate;
protected NamedParameterJdbcTemplate jdbcTemplate; private final TransactionTemplate transactionTemplate;
public DefaultEntityQueryRepository(NamedParameterJdbcTemplate jdbcTemplate, TransactionTemplate transactionTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.transactionTemplate = transactionTemplate;
}
@Override @Override
public long countEntitiesByQuery(TenantId tenantId, CustomerId customerId, EntityCountQuery query) { 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(addEntityTableQuery(ctx, query.getEntityFilter(), entityType));
ctx.append(" e where "); ctx.append(" e where ");
ctx.append(buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), Collections.emptyList(), entityType)); 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 @Override
public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) { public PageData<EntityData> findEntityDataByQuery(TenantId tenantId, CustomerId customerId, EntityDataQuery query) {
QueryContext ctx = new QueryContext(); return transactionTemplate.execute(status -> {
EntityType entityType = resolveEntityType(query.getEntityFilter()); QueryContext ctx = new QueryContext();
EntityDataPageLink pageLink = query.getPageLink(); EntityType entityType = resolveEntityType(query.getEntityFilter());
EntityDataPageLink pageLink = query.getPageLink();
List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(query);
List<EntityKeyMapping> mappings = EntityKeyMapping.prepareKeyMapping(query);
List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection)
.collect(Collectors.toList()); List<EntityKeyMapping> selectionMapping = mappings.stream().filter(EntityKeyMapping::isSelection)
List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest()) .collect(Collectors.toList());
.collect(Collectors.toList()); List<EntityKeyMapping> entityFieldsSelectionMapping = selectionMapping.stream().filter(mapping -> !mapping.isLatest())
List<EntityKeyMapping> latestSelectionMapping = selectionMapping.stream().filter(EntityKeyMapping::isLatest) .collect(Collectors.toList());
.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> filterMapping = mappings.stream().filter(EntityKeyMapping::hasFilter)
List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest()) .collect(Collectors.toList());
.collect(Collectors.toList()); List<EntityKeyMapping> entityFieldsFiltersMapping = filterMapping.stream().filter(mapping -> !mapping.isLatest())
List<EntityKeyMapping> latestFiltersMapping = filterMapping.stream().filter(EntityKeyMapping::isLatest) .collect(Collectors.toList());
.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()); 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 entityWhereClause = DefaultEntityQueryRepository.this.buildEntityWhere(ctx, tenantId, customerId, query.getEntityFilter(), entityFieldsFiltersMapping, entityType);
String whereClause = this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType); String latestJoins = EntityKeyMapping.buildLatestJoins(ctx, query.getEntityFilter(), entityType, allLatestMappings);
String textSearchQuery = this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch()); String whereClause = DefaultEntityQueryRepository.this.buildWhere(ctx, latestFiltersMapping, query.getEntityFilter().getType(), entityType);
String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType); String textSearchQuery = DefaultEntityQueryRepository.this.buildTextSearchQuery(ctx, selectionMapping, pageLink.getTextSearch());
String entityTypeStr; String entityFieldsSelection = EntityKeyMapping.buildSelections(entityFieldsSelectionMapping, query.getEntityFilter().getType(), entityType);
if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) { String entityTypeStr;
entityTypeStr = "e.entity_type"; if (query.getEntityFilter().getType().equals(EntityFilterType.RELATIONS_QUERY)) {
} else { entityTypeStr = "e.entity_type";
entityTypeStr = "'" + entityType.name() + "'"; } else {
} entityTypeStr = "'" + entityType.name() + "'";
if (!StringUtils.isEmpty(entityFieldsSelection)) { }
entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection); if (!StringUtils.isEmpty(entityFieldsSelection)) {
} else { entityFieldsSelection = String.format("e.id id, %s entity_type, %s", entityTypeStr, entityFieldsSelection);
entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr); } else {
} entityFieldsSelection = String.format("e.id id, %s entity_type", entityTypeStr);
String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping, query.getEntityFilter().getType(), entityType); }
String topSelection = "entities.*"; String latestSelection = EntityKeyMapping.buildSelections(latestSelectionMapping, query.getEntityFilter().getType(), entityType);
if (!StringUtils.isEmpty(latestSelection)) { String topSelection = "entities.*";
topSelection = topSelection + ", " + latestSelection; 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", String fromClause = String.format("from (select %s from (select %s from %s e where %s) entities %s %s) result %s",
topSelection, topSelection,
entityFieldsSelection, entityFieldsSelection,
addEntityTableQuery(ctx, query.getEntityFilter(), entityType), addEntityTableQuery(ctx, query.getEntityFilter(), entityType),
entityWhereClause, entityWhereClause,
latestJoins, latestJoins,
whereClause, whereClause,
textSearchQuery); textSearchQuery);
int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class); int totalElements = jdbcTemplate.queryForObject(String.format("select count(*) %s", fromClause), ctx, Integer.class);
String dataQuery = String.format("select * %s", fromClause); String dataQuery = String.format("select * %s", fromClause);
EntityDataSortOrder sortOrder = pageLink.getSortOrder(); EntityDataSortOrder sortOrder = pageLink.getSortOrder();
if (sortOrder != null) { if (sortOrder != null) {
Optional<EntityKeyMapping> sortOrderMappingOpt = mappings.stream().filter(EntityKeyMapping::isSortOrder).findFirst(); Optional<EntityKeyMapping> sortOrderMappingOpt = mappings.stream().filter(EntityKeyMapping::isSortOrder).findFirst();
if (sortOrderMappingOpt.isPresent()) { if (sortOrderMappingOpt.isPresent()) {
EntityKeyMapping sortOrderMapping = sortOrderMappingOpt.get(); EntityKeyMapping sortOrderMapping = sortOrderMappingOpt.get();
dataQuery = String.format("%s order by %s", dataQuery, sortOrderMapping.getValueAlias()); dataQuery = String.format("%s order by %s", dataQuery, sortOrderMapping.getValueAlias());
if (sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC) { if (sortOrder.getDirection() == EntityDataSortOrder.Direction.ASC) {
dataQuery += " asc"; dataQuery += " asc";
} else { } else {
dataQuery += " desc"; 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<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx);
List<Map<String, Object>> rows = jdbcTemplate.queryForList(dataQuery, ctx); return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements);
return EntityDataAdapter.createEntityData(pageLink, selectionMapping, rows, totalElements); });
} }
private String buildEntityWhere(QueryContext ctx, 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_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_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> </mat-cell>
</ng-container> </ng-container>
<mat-header-row [ngClass]="{'mat-row-select': enableSelection}" *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> <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), 'mat-selected': alarmsDatasource.isSelected(alarm),
'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm)}" 'tb-current-entity': alarmsDatasource.isCurrentAlarm(alarm)}"
*matRowDef="let alarm; columns: displayedColumns;" *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.title = this.utils.customTranslation(dataKey.label, dataKey.label);
dataKey.def = 'def' + this.columns.length; dataKey.def = 'def' + this.columns.length;
const keySettings: TableWidgetDataKeySettings = dataKey.settings; 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.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings);
this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx'); this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, alarm, ctx');
this.columnWidth[dataKey.def] = getColumnWidth(keySettings); this.columnWidth[dataKey.def] = getColumnWidth(keySettings);
@ -863,7 +868,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) { loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) {
this.dataLoading = true; this.dataLoading = true;
this.clear(); // this.clear();
this.appliedPageLink = pageLink; this.appliedPageLink = pageLink;
this.appliedSortOrderLabel = sortOrderLabel; this.appliedSortOrderLabel = sortOrderLabel;
this.subscription.subscribeForAlarms(pageLink, keyFilters); 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> </mat-cell>
</ng-container> </ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></mat-header-row> <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;" *matRowDef="let entity; columns: displayedColumns;"
(click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row> (click)="onRowClick($event, entity)" (dblclick)="onRowClick($event, entity, true)"></mat-row>
</table> </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 { sortItems } from '@shared/models/page/page-link';
import { entityFields } from '@shared/models/entity.models'; import { entityFields } from '@shared/models/entity.models';
import { DatePipe } from '@angular/common'; import { DatePipe } from '@angular/common';
import { alarmFields } from '@shared/models/alarm.models';
interface EntitiesTableWidgetSettings extends TableWidgetSettings { interface EntitiesTableWidgetSettings extends TableWidgetSettings {
entitiesTitle: string; entitiesTitle: string;
@ -348,6 +349,13 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label); dataKey.title = this.utils.customTranslation(dataKey.label, dataKey.label);
dataKey.def = 'def' + this.columns.length; dataKey.def = 'def' + this.columns.length;
const keySettings: TableWidgetDataKeySettings = dataKey.settings; 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.stylesInfo[dataKey.def] = getCellStyleInfo(keySettings);
this.contentsInfo[dataKey.def] = getCellContentInfo(keySettings, 'value, entity, ctx'); 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[]) { loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) {
this.dataLoading = true; this.dataLoading = true;
this.clear(); // this.clear();
this.appliedPageLink = pageLink; this.appliedPageLink = pageLink;
this.appliedSortOrderLabel = sortOrderLabel; this.appliedSortOrderLabel = sortOrderLabel;
this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters); this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters);

Loading…
Cancel
Save