2 * Copyright (c) 2010-2020 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.persistence.dynamodb.internal;
15 import java.time.ZonedDateTime;
16 import java.util.Collections;
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;
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;
32 * @author Sami Salonen - Initial contribution
35 public class DynamoDBQueryUtils {
37 * Construct dynamodb query from filter
40 * @return DynamoDBQueryExpression corresponding to the given FilterCriteria
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;
53 private static DynamoDBItem<?> getDynamoDBHashKey(Class<? extends DynamoDBItem<?>> dtoClass, String itemName) {
56 item = dtoClass.newInstance();
57 } catch (InstantiationException e) {
58 throw new RuntimeException(e);
59 } catch (IllegalAccessException e) {
60 throw new RuntimeException(e);
62 item.setName(itemName);
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(),
72 queryExpression.setFilterExpression(String.format("%s %s :opstate", DynamoDBItem.ATTRIBUTE_NAME_ITEMSTATE,
73 operatorAsString(filter.getOperator())));
75 filterState.accept(new DynamoDBItemVisitor() {
78 public void visit(DynamoDBStringItem dynamoStringItem) {
79 queryExpression.setExpressionAttributeValues(Collections.singletonMap(":opstate",
80 new AttributeValue().withS(dynamoStringItem.getState())));
84 public void visit(DynamoDBBigDecimalItem dynamoBigDecimalItem) {
85 queryExpression.setExpressionAttributeValues(Collections.singletonMap(":opstate",
86 new AttributeValue().withN(dynamoBigDecimalItem.getState().toPlainString())));
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));
102 private static @Nullable Condition constructTimeCondition(FilterCriteria filter) {
103 boolean hasBegin = filter.getBeginDate() != null;
104 boolean hasEnd = filter.getEndDate() != null;
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)));
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)));
120 return timeCondition;
124 * Convert op to string suitable for dynamodb filter expression
127 * @return string representation corresponding to the given the Operator
129 private static String operatorAsString(Operator op) {
145 throw new IllegalStateException("Unknown operator " + op);