diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 729a38fb14..e244c307ff 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -256,6 +256,8 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.info("Optimizing alarm relations..."); conn.createStatement().execute("DELETE from relation WHERE relation_type_group = 'ALARM' AND relation_type <> 'ALARM_ANY';"); + conn.createStatement().execute("DELETE from relation WHERE relation_type_group = 'ALARM' AND relation_type = 'ALARM_ANY' " + + "AND exists(SELECT * FROM alarm WHERE alarm.id = relation.to_id AND alarm.originator_id = relation.from_id)"); log.info("Alarm relations optimized."); for (String table : tables) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index 176da7b7b3..cfe9ca6840 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -47,26 +47,28 @@ public interface AlarmRepository extends CrudRepository { @Param("alarmType") String alarmType, Pageable pageable); - @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a, " + - "RelationEntity re " + - "WHERE a.tenantId = :tenantId " + - "AND (a.originatorId = :affectedEntityId or (a.id = re.toId " + - "AND re.relationTypeGroup = 'ALARM' AND re.toType = 'ALARM' " + + @Query(value = "SELECT new org.thingsboard.server.dao.model.sql.AlarmInfoEntity(a) FROM AlarmEntity a " + + "LEFT JOIN RelationEntity re ON a.id = re.toId " + + "AND re.relationTypeGroup = 'ALARM' " + + "AND re.toType = 'ALARM' " + "AND re.fromId = :affectedEntityId " + - "AND re.fromType = :affectedEntityType)) " + + "AND re.fromType = :affectedEntityType " + + "WHERE a.tenantId = :tenantId " + + "AND (a.originatorId = :affectedEntityId or re.fromId IS NOT NULL) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:alarmStatuses IS NULL OR a.status in :alarmStatuses) " + "AND (LOWER(a.type) LIKE LOWER(CONCAT(:searchText, '%'))" + "OR LOWER(a.severity) LIKE LOWER(CONCAT(:searchText, '%'))" + "OR LOWER(a.status) LIKE LOWER(CONCAT(:searchText, '%')))", - countQuery = "SELECT count(a) FROM AlarmEntity a, " + - "RelationEntity re " + - "WHERE a.tenantId = :tenantId " + - "AND (a.originatorId = :affectedEntityId or (a.id = re.toId " + - "AND re.relationTypeGroup = 'ALARM' AND re.toType = 'ALARM' " + + countQuery = "SELECT count(a) FROM AlarmEntity a " + + "LEFT JOIN RelationEntity re ON a.id = re.toId " + + "AND re.relationTypeGroup = 'ALARM' " + + "AND re.toType = 'ALARM' " + "AND re.fromId = :affectedEntityId " + - "AND re.fromType = :affectedEntityType)) " + + "AND re.fromType = :affectedEntityType " + + "WHERE a.tenantId = :tenantId " + + "AND (a.originatorId = :affectedEntityId or re.fromId IS NOT NULL) " + "AND (:startTime IS NULL OR a.createdTime >= :startTime) " + "AND (:endTime IS NULL OR a.createdTime <= :endTime) " + "AND (:alarmStatuses IS NULL OR a.status in :alarmStatuses) " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java index 4ac0c3be41..253c8e7ae4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultAlarmQueryRepository.java @@ -74,7 +74,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { " WHEN a.originator_type = " + EntityType.CUSTOMER.ordinal() + " THEN (select title from customer where id = a.originator_id)" + " WHEN a.originator_type = " + EntityType.USER.ordinal() + - " THEN (select CONCAT (first_name, ' ', last_name) from tb_user where id = a.originator_id)" + + " THEN (select email from tb_user where id = a.originator_id)" + " WHEN a.originator_type = " + EntityType.DASHBOARD.ordinal() + " THEN (select title from dashboard where id = a.originator_id)" + " WHEN a.originator_type = " + EntityType.ASSET.ordinal() + @@ -101,7 +101,7 @@ 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 = ", relation r"; + 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)"; @Autowired protected NamedParameterJdbcTemplate jdbcTemplate; @@ -111,6 +111,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { public PageData findAlarmDataByQueryForEntities(TenantId tenantId, CustomerId customerId, AlarmDataPageLink pageLink, Collection orderedEntityIds) { 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 "); @@ -118,22 +119,20 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { StringBuilder sortPart = new StringBuilder(" order by "); boolean addAnd = false; if (pageLink.isSearchPropagatedAlarms()) { - selectPart.append(" r.from_id as entity_id "); + 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 "); - //No need to check permissions if we select by originator. } 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()); - ctx.addUuidListParameter("entity_ids", orderedEntityIds.stream().map(EntityId::getId).collect(Collectors.toList())); if (pageLink.isSearchPropagatedAlarms()) { - wherePart.append(" and (a.originator_id in (:entity_ids) or (r.relation_type_group = 'ALARM' and r.relation_type = 'ALARM_ANY' and a.id = r.to_id and r.from_id in (:entity_ids)))"); + wherePart.append(" and (a.originator_id in (:entity_ids) or r.from_id IS NOT NULL)"); } else { addAndIfNeeded(wherePart, addAnd); addAnd = true; @@ -154,7 +153,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { } fromPart.append(" as e(id, priority)) e "); if (pageLink.isSearchPropagatedAlarms()) { - fromPart.append("on r.from_id = e.id"); + 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"); } @@ -219,12 +218,6 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { dataQuery = String.format("%s limit %s offset %s", dataQuery, pageLink.getPageSize(), startIndex); } List> rows = jdbcTemplate.queryForList(dataQuery, ctx); - log.error(dataQuery); - log.error("PARAMS:"); - for (String param : ctx.getParameterNames()) { - log.error("PARAM: {}, VALUE: {}", param, ctx.getValue(param)); - } - return AlarmDataAdapter.createAlarmData(pageLink, rows, totalElements, orderedEntityIds); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java index db87deab93..a6ef3935b0 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java @@ -24,7 +24,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - "org.thingsboard.server.dao.service.sql.AlarmServiceSqlTest" + "org.thingsboard.server.dao.service.sql.*SqlTest" }) public class SqlDaoServiceTestSuite { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java index 5c7396fb4f..98b0100caf 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java @@ -268,16 +268,29 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertEquals(1, customerAlarms.getData().size()); Assert.assertEquals(deviceAlarm, customerAlarms.getData().get(0)); + PageData alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder() + .affectedEntityId(tenantDevice.getId()) + .status(AlarmStatus.ACTIVE_UNACK).pageLink( + new TimePageLink(10, 0, "", + new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) + ).build()).get(); + Assert.assertNotNull(alarms.getData()); + Assert.assertEquals(1, alarms.getData().size()); + Assert.assertEquals(tenantAlarm, alarms.getData().get(0)); + } @Test public void testFindAlarmUsingAlarmDataQuery() throws ExecutionException, InterruptedException { AssetId parentId = new AssetId(Uuids.timeBased()); + AssetId parentId2 = new AssetId(Uuids.timeBased()); AssetId childId = new AssetId(Uuids.timeBased()); EntityRelation relation = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE); + EntityRelation relation2 = new EntityRelation(parentId2, childId, EntityRelation.CONTAINS_TYPE); Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation).get()); + Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation2).get()); long ts = System.currentTimeMillis(); Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId) @@ -292,7 +305,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { AlarmDataPageLink pageLink = new AlarmDataPageLink(); pageLink.setPage(0); - pageLink.setPageSize(1); + pageLink.setPageSize(10); pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime"))); pageLink.setStartTs(0L); @@ -308,7 +321,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertEquals(created, alarms.getData().get(0)); pageLink.setPage(0); - pageLink.setPageSize(1); + pageLink.setPageSize(10); pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"))); pageLink.setStartTs(0L); @@ -320,20 +333,22 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Collections.singletonList(childId)); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); - Assert.assertEquals(created, alarms.getData().get(0)); + Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); - // Check child relation + pageLink.setSearchPropagatedAlarms(true); + alarms = alarmService.findAlarmDataByQueryForEntities(tenantId, new CustomerId(CustomerId.NULL_UUID), pageLink, Collections.singletonList(childId)); Assert.assertNotNull(alarms.getData()); Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, new Alarm(alarms.getData().get(0))); + // Check child relation created.setPropagate(true); result = alarmService.createOrUpdateAlarm(created); created = result.getAlarm(); // Check child relation pageLink.setPage(0); - pageLink.setPageSize(1); + pageLink.setPageSize(10); pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime"))); pageLink.setStartTs(0L); @@ -349,7 +364,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { // Check parent relation pageLink.setPage(0); - pageLink.setPageSize(1); + pageLink.setPageSize(10); pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime"))); pageLink.setStartTs(0L); @@ -363,8 +378,38 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(created, alarms.getData().get(0)); + PageData alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() + .affectedEntityId(childId) + .status(AlarmStatus.ACTIVE_UNACK).pageLink( + new TimePageLink(10, 0, "", + new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) + ).build()).get(); + Assert.assertNotNull(alarmsInfoData.getData()); + Assert.assertEquals(1, alarmsInfoData.getData().size()); + Assert.assertEquals(created, alarmsInfoData.getData().get(0)); + + alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() + .affectedEntityId(parentId) + .status(AlarmStatus.ACTIVE_UNACK).pageLink( + new TimePageLink(10, 0, "", + new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) + ).build()).get(); + Assert.assertNotNull(alarmsInfoData.getData()); + Assert.assertEquals(1, alarmsInfoData.getData().size()); + Assert.assertEquals(created, alarmsInfoData.getData().get(0)); + + alarmsInfoData = alarmService.findAlarms(tenantId, AlarmQuery.builder() + .affectedEntityId(parentId2) + .status(AlarmStatus.ACTIVE_UNACK).pageLink( + new TimePageLink(10, 0, "", + new SortOrder("createdTime", SortOrder.Direction.DESC), 0L, System.currentTimeMillis()) + ).build()).get(); + Assert.assertNotNull(alarmsInfoData.getData()); + Assert.assertEquals(1, alarmsInfoData.getData().size()); + Assert.assertEquals(created, alarmsInfoData.getData().get(0)); + pageLink.setPage(0); - pageLink.setPageSize(1); + pageLink.setPageSize(10); pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ENTITY_FIELD, "createdTime"))); pageLink.setStartTs(0L); @@ -382,7 +427,7 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest { created = alarmService.findAlarmByIdAsync(tenantId, created.getId()).get(); pageLink.setPage(0); - pageLink.setPageSize(1); + pageLink.setPageSize(10); pageLink.setSortOrder(new EntityDataSortOrder(new EntityKey(EntityKeyType.ALARM_FIELD, "createdTime"))); pageLink.setStartTs(0L);