]> git.basschouten.com Git - openhab-addons.git/blob
637d00a0b906188aec132fd0ceb8a0e1e85b7f13
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2020 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.persistence.dynamodb.internal;
14
15 import java.time.ZonedDateTime;
16 import java.util.Collections;
17
18 import org.eclipse.jdt.annotation.NonNullByDefault;
19 import org.eclipse.jdt.annotation.Nullable;
20 import org.openhab.core.persistence.FilterCriteria;
21 import org.openhab.core.persistence.FilterCriteria.Operator;
22 import org.openhab.core.persistence.FilterCriteria.Ordering;
23
24 import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
25 import com.amazonaws.services.dynamodbv2.model.AttributeValue;
26 import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
27 import com.amazonaws.services.dynamodbv2.model.Condition;
28
29 /**
30  * Utility class
31  *
32  * @author Sami Salonen - Initial contribution
33  */
34 @NonNullByDefault
35 public class DynamoDBQueryUtils {
36     /**
37      * Construct dynamodb query from filter
38      *
39      * @param filter
40      * @return DynamoDBQueryExpression corresponding to the given FilterCriteria
41      */
42     public static DynamoDBQueryExpression<DynamoDBItem<?>> createQueryExpression(
43             Class<? extends DynamoDBItem<?>> dtoClass, FilterCriteria filter) {
44         DynamoDBItem<?> item = getDynamoDBHashKey(dtoClass, filter.getItemName());
45         final DynamoDBQueryExpression<DynamoDBItem<?>> queryExpression = new DynamoDBQueryExpression<DynamoDBItem<?>>()
46                 .withHashKeyValues(item).withScanIndexForward(filter.getOrdering() == Ordering.ASCENDING)
47                 .withLimit(filter.getPageSize());
48         maybeAddTimeFilter(queryExpression, filter);
49         maybeAddStateFilter(filter, queryExpression);
50         return queryExpression;
51     }
52
53     private static DynamoDBItem<?> getDynamoDBHashKey(Class<? extends DynamoDBItem<?>> dtoClass, String itemName) {
54         DynamoDBItem<?> item;
55         try {
56             item = dtoClass.newInstance();
57         } catch (InstantiationException e) {
58             throw new RuntimeException(e);
59         } catch (IllegalAccessException e) {
60             throw new RuntimeException(e);
61         }
62         item.setName(itemName);
63         return item;
64     }
65
66     private static void maybeAddStateFilter(FilterCriteria filter,
67             final DynamoDBQueryExpression<DynamoDBItem<?>> queryExpression) {
68         if (filter.getOperator() != null && filter.getState() != null) {
69             // Convert filter's state to DynamoDBItem in order get suitable string representation for the state
70             final DynamoDBItem<?> filterState = AbstractDynamoDBItem.fromState(filter.getItemName(), filter.getState(),
71                     ZonedDateTime.now());
72             queryExpression.setFilterExpression(String.format("%s %s :opstate", DynamoDBItem.ATTRIBUTE_NAME_ITEMSTATE,
73                     operatorAsString(filter.getOperator())));
74
75             filterState.accept(new DynamoDBItemVisitor() {
76
77                 @Override
78                 public void visit(DynamoDBStringItem dynamoStringItem) {
79                     queryExpression.setExpressionAttributeValues(Collections.singletonMap(":opstate",
80                             new AttributeValue().withS(dynamoStringItem.getState())));
81                 }
82
83                 @Override
84                 public void visit(DynamoDBBigDecimalItem dynamoBigDecimalItem) {
85                     queryExpression.setExpressionAttributeValues(Collections.singletonMap(":opstate",
86                             new AttributeValue().withN(dynamoBigDecimalItem.getState().toPlainString())));
87                 }
88             });
89         }
90     }
91
92     private static @Nullable Condition maybeAddTimeFilter(
93             final DynamoDBQueryExpression<DynamoDBItem<?>> queryExpression, final FilterCriteria filter) {
94         final Condition timeCondition = constructTimeCondition(filter);
95         if (timeCondition != null) {
96             queryExpression.setRangeKeyConditions(
97                     Collections.singletonMap(DynamoDBItem.ATTRIBUTE_NAME_TIMEUTC, timeCondition));
98         }
99         return timeCondition;
100     }
101
102     private static @Nullable Condition constructTimeCondition(FilterCriteria filter) {
103         boolean hasBegin = filter.getBeginDate() != null;
104         boolean hasEnd = filter.getEndDate() != null;
105
106         final Condition timeCondition;
107         if (!hasBegin && !hasEnd) {
108             timeCondition = null;
109         } else if (hasBegin && !hasEnd) {
110             timeCondition = new Condition().withComparisonOperator(ComparisonOperator.GE).withAttributeValueList(
111                     new AttributeValue().withS(filter.getBeginDate().format(AbstractDynamoDBItem.DATEFORMATTER)));
112         } else if (!hasBegin && hasEnd) {
113             timeCondition = new Condition().withComparisonOperator(ComparisonOperator.LE).withAttributeValueList(
114                     new AttributeValue().withS(filter.getEndDate().format(AbstractDynamoDBItem.DATEFORMATTER)));
115         } else {
116             timeCondition = new Condition().withComparisonOperator(ComparisonOperator.BETWEEN).withAttributeValueList(
117                     new AttributeValue().withS(filter.getBeginDate().format(AbstractDynamoDBItem.DATEFORMATTER)),
118                     new AttributeValue().withS(filter.getEndDate().format(AbstractDynamoDBItem.DATEFORMATTER)));
119         }
120         return timeCondition;
121     }
122
123     /**
124      * Convert op to string suitable for dynamodb filter expression
125      *
126      * @param op
127      * @return string representation corresponding to the given the Operator
128      */
129     private static String operatorAsString(Operator op) {
130         switch (op) {
131             case EQ:
132                 return "=";
133             case NEQ:
134                 return "<>";
135             case LT:
136                 return "<";
137             case LTE:
138                 return "<=";
139             case GT:
140                 return ">";
141             case GTE:
142                 return ">=";
143
144             default:
145                 throw new IllegalStateException("Unknown operator " + op);
146         }
147     }
148 }