thingsboard-memoizeit

Merge pull request #165 from thingsboard/feature/TB-63 TB-63:

6/12/2017 12:31:43 PM

Details

diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
index 7a2c273..19774bb 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java
@@ -22,10 +22,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.thingsboard.server.common.data.Customer;
 import org.thingsboard.server.common.data.Event;
-import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmId;
-import org.thingsboard.server.common.data.alarm.AlarmQuery;
-import org.thingsboard.server.common.data.alarm.AlarmStatus;
+import org.thingsboard.server.common.data.alarm.*;
 import org.thingsboard.server.common.data.asset.Asset;
 import org.thingsboard.server.common.data.id.*;
 import org.thingsboard.server.common.data.page.TextPageData;
@@ -103,24 +100,31 @@ public class AlarmController extends BaseController {
     @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
     @RequestMapping(value = "/alarm/{entityType}/{entityId}", method = RequestMethod.GET)
     @ResponseBody
-    public TimePageData<Alarm> getAlarms(
+    public TimePageData<AlarmInfo> getAlarms(
             @PathVariable("entityType") String strEntityType,
             @PathVariable("entityId") String strEntityId,
+            @RequestParam(required = false) String searchStatus,
             @RequestParam(required = false) String status,
             @RequestParam int limit,
             @RequestParam(required = false) Long startTime,
             @RequestParam(required = false) Long endTime,
             @RequestParam(required = false, defaultValue = "false") boolean ascOrder,
-            @RequestParam(required = false) String offset
+            @RequestParam(required = false) String offset,
+            @RequestParam(required = false) Boolean fetchOriginator
     ) throws ThingsboardException {
         checkParameter("EntityId", strEntityId);
         checkParameter("EntityType", strEntityType);
         EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId);
+        AlarmSearchStatus alarmSearchStatus = StringUtils.isEmpty(searchStatus) ? null : AlarmSearchStatus.valueOf(searchStatus);
         AlarmStatus alarmStatus = StringUtils.isEmpty(status) ? null : AlarmStatus.valueOf(status);
+        if (alarmSearchStatus != null && alarmStatus != null) {
+            throw new ThingsboardException("Invalid alarms search query: Both parameters 'searchStatus' " +
+                    "and 'status' can't be specified at the same time!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
+        }
         checkEntityId(entityId);
         try {
             TimePageLink pageLink = createPageLink(limit, startTime, endTime, ascOrder, offset);
-            return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmStatus)).get());
+            return checkNotNull(alarmService.findAlarms(new AlarmQuery(entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator)).get());
         } catch (Exception e) {
             throw handleException(e);
         }
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
index 09695e5..9c6f998 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
@@ -53,6 +53,21 @@ public class Alarm extends BaseData<AlarmId> implements HasName {
         super(id);
     }
 
+    public Alarm(Alarm alarm) {
+        super(alarm.getId());
+        this.tenantId = alarm.getTenantId();
+        this.type = alarm.getType();
+        this.originator = alarm.getOriginator();
+        this.severity = alarm.getSeverity();
+        this.status = alarm.getStatus();
+        this.startTs = alarm.getStartTs();
+        this.endTs = alarm.getEndTs();
+        this.ackTs = alarm.getAckTs();
+        this.clearTs = alarm.getClearTs();
+        this.details = alarm.getDetails();
+        this.propagate = alarm.isPropagate();
+    }
+
     @Override
     @JsonProperty(access = JsonProperty.Access.READ_ONLY)
     public String getName() {
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java
new file mode 100644
index 0000000..ef24f1c
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmInfo.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.thingsboard.server.common.data.alarm;
+
+public class AlarmInfo extends Alarm {
+
+    private static final long serialVersionUID = 2807343093519543363L;
+
+    private String originatorName;
+
+    public AlarmInfo() {
+        super();
+    }
+
+    public AlarmInfo(Alarm alarm) {
+        super(alarm);
+    }
+
+    public String getOriginatorName() {
+        return originatorName;
+    }
+
+    public void setOriginatorName(String originatorName) {
+        this.originatorName = originatorName;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
+
+        AlarmInfo alarmInfo = (AlarmInfo) o;
+
+        return originatorName != null ? originatorName.equals(alarmInfo.originatorName) : alarmInfo.originatorName == null;
+
+    }
+
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = 31 * result + (originatorName != null ? originatorName.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
index 00ca6c3..55019c8 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmQuery.java
@@ -32,6 +32,8 @@ public class AlarmQuery {
 
     private EntityId affectedEntityId;
     private TimePageLink pageLink;
+    private AlarmSearchStatus searchStatus;
     private AlarmStatus status;
+    private Boolean fetchOriginator;
 
 }
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java
new file mode 100644
index 0000000..c09b1fd
--- /dev/null
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmSearchStatus.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright © 2016-2017 The Thingsboard Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.thingsboard.server.common.data.alarm;
+
+public enum AlarmSearchStatus {
+
+    ANY, ACTIVE, CLEARED, ACK, UNACK
+
+}
diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java
index 0f1b346..a8704b2 100644
--- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java
+++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/AlarmStatus.java
@@ -30,4 +30,13 @@ public enum AlarmStatus {
         return this == CLEARED_ACK || this == CLEARED_UNACK;
     }
 
+    public AlarmSearchStatus getClearSearchStatus() {
+        return this.isCleared() ? AlarmSearchStatus.CLEARED : AlarmSearchStatus.ACTIVE;
+    }
+
+    public AlarmSearchStatus getAckSearchStatus() {
+        return this.isAck() ? AlarmSearchStatus.ACK : AlarmSearchStatus.UNACK;
+    }
+
+
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
index 9fdd92e..289d5c9 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
 
 import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
 import org.thingsboard.server.common.data.alarm.AlarmQuery;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -37,5 +38,5 @@ public interface AlarmDao extends Dao<AlarmEntity> {
 
     AlarmEntity save(Alarm alarm);
 
-    ListenableFuture<List<Alarm>> findAlarms(AlarmQuery query);
+    ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query);
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDaoImpl.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDaoImpl.java
index 1362e6b..72fbae5 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDaoImpl.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDaoImpl.java
@@ -17,6 +17,7 @@ package org.thingsboard.server.dao.alarm;
 
 import com.datastax.driver.core.querybuilder.QueryBuilder;
 import com.datastax.driver.core.querybuilder.Select;
+import com.google.common.base.Function;
 import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -25,7 +26,9 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
 import org.thingsboard.server.common.data.alarm.AlarmQuery;
+import org.thingsboard.server.common.data.alarm.AlarmSearchStatus;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.id.TenantId;
 import org.thingsboard.server.common.data.relation.EntityRelation;
@@ -94,15 +97,25 @@ public class AlarmDaoImpl extends AbstractModelDao<AlarmEntity> implements Alarm
     }
 
     @Override
-    public ListenableFuture<List<Alarm>> findAlarms(AlarmQuery query) {
-        log.trace("Try to find alarms by entity [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getStatus(), query.getPageLink());
+    public ListenableFuture<List<AlarmInfo>> findAlarms(AlarmQuery query) {
+        log.trace("Try to find alarms by entity [{}], searchStatus [{}], status [{}] and pageLink [{}]", query.getAffectedEntityId(), query.getSearchStatus(), query.getStatus(), query.getPageLink());
         EntityId affectedEntity = query.getAffectedEntityId();
-        String relationType = query.getStatus() == null ? BaseAlarmService.ALARM_RELATION : BaseAlarmService.ALARM_RELATION_PREFIX + query.getStatus().name();
+        String searchStatusName;
+        if (query.getSearchStatus() == null && query.getStatus() == null) {
+            searchStatusName = AlarmSearchStatus.ANY.name();
+        } else if (query.getSearchStatus() != null) {
+            searchStatusName = query.getSearchStatus().name();
+        } else {
+            searchStatusName = query.getStatus().name();
+        }
+        String relationType = BaseAlarmService.ALARM_RELATION_PREFIX + searchStatusName;
         ListenableFuture<List<EntityRelation>> relations = relationDao.findRelations(affectedEntity, relationType, RelationTypeGroup.ALARM, EntityType.ALARM, query.getPageLink());
-        return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<Alarm>>) input -> {
-            List<ListenableFuture<Alarm>> alarmFutures = new ArrayList<>(input.size());
+        return Futures.transform(relations, (AsyncFunction<List<EntityRelation>, List<AlarmInfo>>) input -> {
+            List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
             for (EntityRelation relation : input) {
-                alarmFutures.add(findAlarmByIdAsync(relation.getTo().getId()));
+                alarmFutures.add(Futures.transform(
+                        findAlarmByIdAsync(relation.getTo().getId()), (Function<Alarm, AlarmInfo>)
+                                alarm1 -> new AlarmInfo(alarm1)));
             }
             return Futures.successfulAsList(alarmFutures);
         });
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
index 5399d9d..fb8a80d 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
@@ -18,6 +18,7 @@ package org.thingsboard.server.dao.alarm;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.thingsboard.server.common.data.alarm.Alarm;
 import org.thingsboard.server.common.data.alarm.AlarmId;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
 import org.thingsboard.server.common.data.alarm.AlarmQuery;
 import org.thingsboard.server.common.data.page.TimePageData;
 
@@ -34,6 +35,6 @@ public interface AlarmService {
 
     ListenableFuture<Alarm> findAlarmByIdAsync(AlarmId alarmId);
 
-    ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query);
+    ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query);
 
 }
diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
index 3981f64..d152d96 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
@@ -17,22 +17,21 @@ package org.thingsboard.server.dao.alarm;
 
 
 import com.google.common.base.Function;
+import com.google.common.util.concurrent.AsyncFunction;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
-import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmId;
-import org.thingsboard.server.common.data.alarm.AlarmQuery;
-import org.thingsboard.server.common.data.alarm.AlarmStatus;
+import org.thingsboard.server.common.data.alarm.*;
 import org.thingsboard.server.common.data.id.EntityId;
 import org.thingsboard.server.common.data.page.TimePageData;
 import org.thingsboard.server.common.data.relation.EntityRelation;
 import org.thingsboard.server.common.data.relation.RelationTypeGroup;
 import org.thingsboard.server.dao.entity.AbstractEntityService;
 import org.thingsboard.server.dao.entity.BaseEntityService;
+import org.thingsboard.server.dao.entity.EntityService;
 import org.thingsboard.server.dao.exception.DataValidationException;
 import org.thingsboard.server.dao.model.*;
 import org.thingsboard.server.dao.relation.EntityRelationsQuery;
@@ -45,6 +44,7 @@ import org.thingsboard.server.dao.tenant.TenantDao;
 import javax.annotation.Nullable;
 import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -59,7 +59,6 @@ import static org.thingsboard.server.dao.service.Validator.*;
 public class BaseAlarmService extends AbstractEntityService implements AlarmService {
 
     public static final String ALARM_RELATION_PREFIX = "ALARM_";
-    public static final String ALARM_RELATION = "ALARM_ANY";
 
     @Autowired
     private AlarmDao alarmDao;
@@ -70,6 +69,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
     @Autowired
     private RelationService relationService;
 
+    @Autowired
+    private EntityService entityService;
+
     protected ExecutorService readResultsProcessingExecutor;
 
     @PostConstruct
@@ -116,11 +118,9 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
         query.setParameters(new RelationsSearchParameters(saved.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
         List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
         for (EntityId parentId : parentEntities) {
-            createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM));
-            createRelation(new EntityRelation(parentId, saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM));
+            createAlarmRelation(parentId, saved.getId(), saved.getStatus(), true);
         }
-        createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION, RelationTypeGroup.ALARM));
-        createRelation(new EntityRelation(alarm.getOriginator(), saved.getId(), ALARM_RELATION_PREFIX + saved.getStatus().name(), RelationTypeGroup.ALARM));
+        createAlarmRelation(alarm.getOriginator(), saved.getId(), saved.getStatus(), true);
         return saved;
     }
 
@@ -199,12 +199,27 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
     }
 
     @Override
-    public ListenableFuture<TimePageData<Alarm>> findAlarms(AlarmQuery query) {
-        ListenableFuture<List<Alarm>> alarms = alarmDao.findAlarms(query);
-        return Futures.transform(alarms, new Function<List<Alarm>, TimePageData<Alarm>>() {
+    public ListenableFuture<TimePageData<AlarmInfo>> findAlarms(AlarmQuery query) {
+        ListenableFuture<List<AlarmInfo>> alarms = alarmDao.findAlarms(query);
+        if (query.getFetchOriginator() != null && query.getFetchOriginator().booleanValue()) {
+            alarms = Futures.transform(alarms, (AsyncFunction<List<AlarmInfo>, List<AlarmInfo>>) input -> {
+                List<ListenableFuture<AlarmInfo>> alarmFutures = new ArrayList<>(input.size());
+                for (AlarmInfo alarmInfo : input) {
+                    alarmFutures.add(Futures.transform(
+                            entityService.fetchEntityNameAsync(alarmInfo.getOriginator()), (Function<String, AlarmInfo>)
+                                    originatorName -> {
+                                        alarmInfo.setOriginatorName(originatorName);
+                                        return alarmInfo;
+                                    }
+                    ));
+                }
+                return Futures.successfulAsList(alarmFutures);
+             });
+        }
+        return Futures.transform(alarms, new Function<List<AlarmInfo>, TimePageData<AlarmInfo>>() {
             @Nullable
             @Override
-            public TimePageData<Alarm> apply(@Nullable List<Alarm> alarms) {
+            public TimePageData<AlarmInfo> apply(@Nullable List<AlarmInfo> alarms) {
                 return new TimePageData<>(alarms, query.getPageLink());
             }
         });
@@ -245,17 +260,45 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
             query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
             List<EntityId> parentEntities = relationService.findByQuery(query).get().stream().map(r -> r.getFrom()).collect(Collectors.toList());
             for (EntityId parentId : parentEntities) {
-                deleteRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM));
-                createRelation(new EntityRelation(parentId, alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM));
-            }
-            deleteRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + oldStatus.name(), RelationTypeGroup.ALARM));
-            createRelation(new EntityRelation(alarm.getOriginator(), alarm.getId(), ALARM_RELATION_PREFIX + newStatus.name(), RelationTypeGroup.ALARM));
+                updateAlarmRelation(parentId, alarm.getId(), oldStatus, newStatus);
+           }
+            updateAlarmRelation(alarm.getOriginator(), alarm.getId(), oldStatus, newStatus);
         } catch (ExecutionException | InterruptedException e) {
             log.warn("[{}] Failed to update relations. Old status: [{}], New status: [{}]", alarm.getId(), oldStatus, newStatus);
             throw new RuntimeException(e);
         }
     }
 
+    private void createAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status, boolean createAnyRelation) {
+        try {
+            if (createAnyRelation) {
+                createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + AlarmSearchStatus.ANY.name(), RelationTypeGroup.ALARM));
+            }
+            createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
+            createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
+            createRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
+        } catch (ExecutionException | InterruptedException e) {
+            log.warn("[{}] Failed to create relation. Status: [{}]", alarmId, status);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void deleteAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus status) {
+        try {
+            deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.name(), RelationTypeGroup.ALARM));
+            deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getClearSearchStatus().name(), RelationTypeGroup.ALARM));
+            deleteRelation(new EntityRelation(entityId, alarmId, ALARM_RELATION_PREFIX + status.getAckSearchStatus().name(), RelationTypeGroup.ALARM));
+        } catch (ExecutionException | InterruptedException e) {
+            log.warn("[{}] Failed to delete relation. Status: [{}]", alarmId, status);
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void updateAlarmRelation(EntityId entityId, EntityId alarmId, AlarmStatus oldStatus, AlarmStatus newStatus) {
+        deleteAlarmRelation(entityId, alarmId, oldStatus);
+        createAlarmRelation(entityId, alarmId, newStatus, false);
+    }
+
     private <T> ListenableFuture<T> getAndUpdate(AlarmId alarmId, Function<Alarm, T> function) {
         validateId(alarmId, "Alarm id should be specified!");
         ListenableFuture<Alarm> entity = alarmDao.findAlarmByIdAsync(alarmId.getId());
diff --git a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
index 9fc2382..77f79e1 100644
--- a/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
+++ b/dao/src/main/java/org/thingsboard/server/dao/relation/BaseRelationDao.java
@@ -178,9 +178,14 @@ public class BaseRelationDao extends AbstractAsyncDao implements RelationDao {
                         eq(ModelConstants.RELATION_TYPE_GROUP_PROPERTY, typeGroup.name()),
                         eq(ModelConstants.RELATION_TYPE_PROPERTY, relationType),
                         eq(ModelConstants.RELATION_TO_TYPE_PROPERTY, childType.name())),
-                Arrays.asList(QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY),
-                        QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY),
-                        QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)),
+                Arrays.asList(
+                        pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY) :
+                                QueryBuilder.asc(ModelConstants.RELATION_TYPE_GROUP_PROPERTY),
+                        pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TYPE_PROPERTY) :
+                                QueryBuilder.asc(ModelConstants.RELATION_TYPE_PROPERTY),
+                        pageLink.isAscOrder() ? QueryBuilder.desc(ModelConstants.RELATION_TO_TYPE_PROPERTY) :
+                                QueryBuilder.asc(ModelConstants.RELATION_TO_TYPE_PROPERTY)
+                ),
                 pageLink, ModelConstants.RELATION_TO_ID_PROPERTY);
         return getFuture(executeAsyncRead(query), rs -> getEntityRelations(rs));
     }
diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
index 3b72574..b22df63 100644
--- a/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
+++ b/dao/src/test/java/org/thingsboard/server/dao/service/AlarmServiceTest.java
@@ -22,10 +22,7 @@ import org.junit.Before;
 import org.junit.Test;
 import org.thingsboard.server.common.data.EntityType;
 import org.thingsboard.server.common.data.Tenant;
-import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmQuery;
-import org.thingsboard.server.common.data.alarm.AlarmSeverity;
-import org.thingsboard.server.common.data.alarm.AlarmStatus;
+import org.thingsboard.server.common.data.alarm.*;
 import org.thingsboard.server.common.data.id.AssetId;
 import org.thingsboard.server.common.data.id.DeviceId;
 import org.thingsboard.server.common.data.id.TenantId;
@@ -117,7 +114,7 @@ public class AlarmServiceTest extends AbstractServiceTest {
         Alarm created = alarmService.createOrUpdateAlarm(alarm);
 
         // Check child relation
-        TimePageData<Alarm> alarms = alarmService.findAlarms(AlarmQuery.builder()
+        TimePageData<AlarmInfo> alarms = alarmService.findAlarms(AlarmQuery.builder()
                 .affectedEntityId(childId)
                 .status(AlarmStatus.ACTIVE_UNACK).pageLink(
                         new TimePageLink(1, 0L, System.currentTimeMillis(), false)
diff --git a/ui/src/app/api/alarm.service.js b/ui/src/app/api/alarm.service.js
index d9ab4ff..ca892f2 100644
--- a/ui/src/app/api/alarm.service.js
+++ b/ui/src/app/api/alarm.service.js
@@ -91,7 +91,7 @@ function AlarmService($http, $q, $interval, $filter) {
         return deferred.promise;
     }
 
-    function getAlarms(entityType, entityId, pageLink, alarmStatus, ascOrder, config) {
+    function getAlarms(entityType, entityId, pageLink, alarmSearchStatus, alarmStatus, fetchOriginator, ascOrder, config) {
         var deferred = $q.defer();
         var url = '/api/alarm/' + entityType + '/' + entityId + '?limit=' + pageLink.limit;
 
@@ -104,9 +104,15 @@ function AlarmService($http, $q, $interval, $filter) {
         if (angular.isDefined(pageLink.idOffset)) {
             url += '&offset=' + pageLink.idOffset;
         }
+        if (alarmSearchStatus) {
+            url += '&searchStatus=' + alarmSearchStatus;
+        }
         if (alarmStatus) {
             url += '&status=' + alarmStatus;
         }
+        if (fetchOriginator) {
+            url += '&fetchOriginator=' + ((fetchOriginator===true) ? 'true' : 'false');
+        }
         if (angular.isDefined(ascOrder) && ascOrder != null) {
             url += '&ascOrder=' + (ascOrder ? 'true' : 'false');
         }
@@ -121,7 +127,8 @@ function AlarmService($http, $q, $interval, $filter) {
 
     function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) {
         getAlarms(alarmsQuery.entityType, alarmsQuery.entityId,
-            pageLink, alarmsQuery.alarmStatus, false, {ignoreLoading: true}).then(
+            pageLink, alarmsQuery.alarmSearchStatus, alarmsQuery.alarmStatus,
+            alarmsQuery.fetchOriginator, false, {ignoreLoading: true}).then(
             function success(alarms) {
                 if (!alarmsList) {
                     alarmsList = [];
@@ -171,7 +178,9 @@ function AlarmService($http, $q, $interval, $filter) {
         var alarmsQuery = {
             entityType: entityType,
             entityId: entityId,
+            alarmSearchStatus: null,
             alarmStatus: alarmStatus,
+            fetchOriginator: false,
             interval: interval,
             limit: limit,
             onAlarms: onAlarms
diff --git a/ui/src/app/common/types.constant.js b/ui/src/app/common/types.constant.js
index fe3f73c..2076093 100644
--- a/ui/src/app/common/types.constant.js
+++ b/ui/src/app/common/types.constant.js
@@ -65,6 +65,13 @@ export default angular.module('thingsboard.types', [])
                 clearedUnack: "CLEARED_UNACK",
                 clearedAck: "CLEARED_ACK"
             },
+            alarmSearchStatus: {
+                any: "ANY",
+                active: "ACTIVE",
+                cleared: "CLEARED",
+                ack: "ACK",
+                unack: "UNACK"
+            },
             aliasFilterType: {
                 entityList: {
                     value: 'entityList',