/* * Copyright (c) 2011-2014 Jeppetto and Jonathan Thompson * * 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.iternine.jeppetto.dao.dynamodb.expression; import org.iternine.jeppetto.dao.Condition; import org.iternine.jeppetto.dao.QueryModel; import org.iternine.jeppetto.dao.dynamodb.ConversionUtil; import org.iternine.jeppetto.dao.dynamodb.DynamoDBConstraint; import org.iternine.jeppetto.dao.dynamodb.DynamoDBOperator; import org.iternine.jeppetto.dao.dynamodb.DynamoDBQueryModelDAO.IndexData; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ComparisonOperator; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** */ public class ConditionExpressionBuilder extends ExpressionBuilder { //------------------------------------------------------------- // Constants //------------------------------------------------------------- private static final Map<DynamoDBOperator, String> OPERATOR_EXPRESSIONS = new HashMap<DynamoDBOperator, String>(11) {{ put(DynamoDBOperator.NotEqual, "%s <> %s"); put(DynamoDBOperator.GreaterThanEqual, "%s >= %s"); put(DynamoDBOperator.LessThanEqual, "%s <= %s"); put(DynamoDBOperator.Equal, "%s = %s"); put(DynamoDBOperator.GreaterThan, "%s > %s"); put(DynamoDBOperator.LessThan, "%s < %s"); put(DynamoDBOperator.NotWithin, "NOT %s IN %s"); put(DynamoDBOperator.Within, "%s IN %s"); put(DynamoDBOperator.Between, "%s BETWEEN %s AND %s"); put(DynamoDBOperator.IsNull, "attribute_not_exists(%s)"); put(DynamoDBOperator.IsNotNull, "attribute_exists(%s)"); put(DynamoDBOperator.BeginsWith, "begins_with(%s, %s)"); }}; private static final Set<ComparisonOperator> RANGE_KEY_COMPARISON_OPERATORS = new HashSet<ComparisonOperator>(7) {{ add(ComparisonOperator.EQ); add(ComparisonOperator.LE); add(ComparisonOperator.LT); add(ComparisonOperator.GE); add(ComparisonOperator.GT); add(ComparisonOperator.BEGINS_WITH); add(ComparisonOperator.BETWEEN); }}; private static final String EXPRESSION_ATTRIBUTE_VALUE_PREFIX = ":c"; private static final String EXPRESSION_ATTRIBUTE_NAME_PREFIX = "#c"; //------------------------------------------------------------- // Variables - Private //------------------------------------------------------------- private Condition hashKeyCondition; private Condition rangeKeyCondition; private final StringBuilder expression = new StringBuilder(); //------------------------------------------------------------- // Constructors //------------------------------------------------------------- public ConditionExpressionBuilder() { super(true); } public ConditionExpressionBuilder(QueryModel queryModel, Map<String, Map<String, IndexData>> indexes) { super(true); if (queryModel.getConditions() != null) { for (Condition condition : queryModel.getConditions()) { DynamoDBConstraint dynamoDBConstraint = (DynamoDBConstraint) condition.getConstraint(); ComparisonOperator comparisonOperator = dynamoDBConstraint.getOperator().getComparisonOperator(); if (indexes.containsKey(condition.getField()) && comparisonOperator == ComparisonOperator.EQ) { this.hashKeyCondition = condition; } else if (hashKeyCondition != null && rangeKeyCondition == null // First one wins... && indexes.get(hashKeyCondition.getField()).containsKey(condition.getField()) && RANGE_KEY_COMPARISON_OPERATORS.contains(comparisonOperator)) { this.rangeKeyCondition = condition; } else { add(getExpressionAttributeName(condition.getField()), dynamoDBConstraint); } } } if (queryModel.getAssociationConditions() != null) { for (Map.Entry<String, List<Condition>> associationConditions : queryModel.getAssociationConditions().entrySet()) { for (Condition condition : associationConditions.getValue()) { add(getExpressionAttributeName(associationConditions.getKey(), condition.getField()), (DynamoDBConstraint) condition.getConstraint()); } } } } //------------------------------------------------------------- // Implementation - ExpressionBuilder //------------------------------------------------------------- @Override public boolean hasExpression() { return expression.length() > 0; } @Override public String getExpression() { return expression.toString(); } @Override public String getExpressionAttributeValuePrefix() { return EXPRESSION_ATTRIBUTE_VALUE_PREFIX; } @Override public String getExpressionAttributeNamePrefix() { return EXPRESSION_ATTRIBUTE_NAME_PREFIX; } //------------------------------------------------------------- // Methods - Public //------------------------------------------------------------- public boolean hasHashKeyCondition() { return hashKeyCondition != null; } public Map<String, com.amazonaws.services.dynamodbv2.model.Condition> getKeyConditions() { if (rangeKeyCondition == null) { return Collections.singletonMap(hashKeyCondition.getField(), ((DynamoDBConstraint) hashKeyCondition.getConstraint()).asCondition()); } else { Map<String, com.amazonaws.services.dynamodbv2.model.Condition> keyConditions = new HashMap<>(); keyConditions.put(hashKeyCondition.getField(), ((DynamoDBConstraint) hashKeyCondition.getConstraint()).asCondition()); keyConditions.put(rangeKeyCondition.getField(), ((DynamoDBConstraint) rangeKeyCondition.getConstraint()).asCondition()); return keyConditions; } } public String getHashKey() { if (hashKeyCondition == null) { return null; } return hashKeyCondition.getField(); } public String getRangeKey() { if (rangeKeyCondition == null) { return null; } return rangeKeyCondition.getField(); } public void convertRangeKeyConditionToExpression() { if (rangeKeyCondition == null) { return; } add(getExpressionAttributeName(rangeKeyCondition.getField()), (DynamoDBConstraint) rangeKeyCondition.getConstraint()); } public Map<String, AttributeValue> getKey() { Map<String, AttributeValue> key; if (rangeKeyCondition == null) { key = Collections.singletonMap(hashKeyCondition.getField(), ConversionUtil.toAttributeValue(((DynamoDBConstraint) hashKeyCondition.getConstraint()).getValues()[0])); } else { key = new HashMap<>(2); key.put(hashKeyCondition.getField(), ConversionUtil.toAttributeValue(((DynamoDBConstraint) hashKeyCondition.getConstraint()).getValues()[0])); key.put(rangeKeyCondition.getField(), ConversionUtil.toAttributeValue(((DynamoDBConstraint) rangeKeyCondition.getConstraint()).getValues()[0])); } return key; } public ConditionExpressionBuilder with(String field, DynamoDBConstraint constraint) { add(getExpressionAttributeName(field), constraint); return this; } //------------------------------------------------------------- // Methods - Private //------------------------------------------------------------- private String getExpressionAttributeName(String association, String attributeName) { StringBuilder sb = new StringBuilder(); if (association.contains(".")) { String[] associationParts = association.split("."); for (String associationPart : associationParts) { sb.append(getExpressionAttributeName(associationPart)); sb.append('.'); } } else { sb.append(getExpressionAttributeName(association)); sb.append('.'); } return sb.append(getExpressionAttributeName(attributeName)).toString(); } private void add(String expressionAttributeName, DynamoDBConstraint constraint) { if (expression.length() > 0) { expression.append(" and "); } String operatorExpression = OPERATOR_EXPRESSIONS.get(constraint.getOperator()); int argumentCount = constraint.getOperator().getArgumentCount(); Object[] values = constraint.getValues(); if (argumentCount == 0) { expression.append(String.format(operatorExpression, expressionAttributeName)); } else if (argumentCount == 1) { String expressionAttributeKey = putExpressionAttributeValue(ConversionUtil.toAttributeValue(values[0])); expression.append(String.format(operatorExpression, expressionAttributeName, expressionAttributeKey)); } else if (argumentCount == 2) { String expressionAttributeKey0 = putExpressionAttributeValue(ConversionUtil.toAttributeValue(values[0])); String expressionAttributeKey1 = putExpressionAttributeValue(ConversionUtil.toAttributeValue(values[1])); expression.append(String.format(operatorExpression, expressionAttributeName, expressionAttributeKey0, expressionAttributeKey1)); } else { // N arguments StringBuilder placeholders = new StringBuilder("("); Collection<AttributeValue> attributeValues = ConversionUtil.toAttributeValueList(values[0]); for (AttributeValue attributeValue : attributeValues) { if (placeholders.length() > 1) { placeholders.append(", "); } String expressionAttributeKey = putExpressionAttributeValue(attributeValue); placeholders.append(expressionAttributeKey); } placeholders.append(')'); expression.append(String.format(operatorExpression, expressionAttributeName, placeholders.toString())); } } }