Skip to content

Commit f14f0f7

Browse files
authored
Added Filter Expressions for Queries (#29)
* Added Filter Expressions for Queries * More test coverage. * Remove duplicate code for Scan * Fixing bad checks, updating test coverage. * Fixing some warnings, added deprecation warning * Supported for named parameters * Supported for named parameters * Empty check on annotations. * Bumping test coverage * Bumping test coverage * Bumping test coverage
1 parent 3a45e57 commit f14f0f7

20 files changed

+398
-82
lines changed

pom.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@
3232
<licenses>
3333
<license>
3434
<name>The Apache License, Version 2.0</name>
35-
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
35+
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
3636
</license>
3737
</licenses>
3838

3939
<properties>
40-
<spring.version>5.2.0.RELEASE</spring.version>
41-
<spring-data.version>2.2.2.RELEASE</spring-data.version>
40+
<spring.version>5.2.3.RELEASE</spring.version>
41+
<spring-data.version>2.3.0.RELEASE</spring-data.version>
4242

4343
<hibernate-validator.version>6.1.0.Final</hibernate-validator.version>
4444
<aws-java-sdk.version>1.11.664</aws-java-sdk.version>
@@ -501,7 +501,7 @@
501501
<repositories>
502502
<repository>
503503
<id>spring-libs-snapshot</id>
504-
<url>http://repo.springsource.org/libs-snapshot</url>
504+
<url>https://repo.springsource.org/libs-snapshot</url>
505505
</repository>
506506
<repository>
507507
<id>dynamodb-local-oregon</id>
@@ -513,7 +513,7 @@
513513
<pluginRepositories>
514514
<pluginRepository>
515515
<id>com.springsource.repository.bundles.release</id>
516-
<url>http://repository.springsource.com/maven/bundles/release</url>
516+
<url>https://repository.springsource.com/maven/bundles/release</url>
517517
<snapshots>
518518
<enabled>false</enabled>
519519
</snapshots>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Copyright © 2018 spring-data-dynamodb (https://github.com/boostchicken/spring-data-dynamodb)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.socialsignin.spring.data.dynamodb.repository;
17+
18+
public @interface ExpressionAttribute {
19+
String key() default "";
20+
String value() default "";
21+
String parameterName() default "";
22+
}

src/main/java/org/socialsignin/spring/data/dynamodb/repository/Query.java

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,58 @@
5757
* "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html">Read Consistency</a>
5858
* @see <a href=
5959
* "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.OptionalConfig.html">DynamoDBMapper Configuration</a>
60-
* @return ConistentReadMode to enforce on query
60+
* @return ConsistentReadMode to enforce on query
6161
*/
6262
ConsistentReadMode consistentReads() default ConsistentReadMode.DEFAULT;
63+
64+
/**
65+
* Set filter expressions for a query
66+
*
67+
* @see <a href=
68+
* "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression">Filter Expressions</a>
69+
* @see <a href=
70+
* "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html"> Expression Syntax</a>
71+
*
72+
*
73+
* Example: {@code @Query(filterExpression = "contains(#field, :value)",
74+
* expressionMappingNames = {@ExpressionAttribute(key = "#field", value = "name")},
75+
* expressionMappingValues = {@ExpressionAttribute(key=":value", value = "John Doe")})}
76+
*
77+
* @return filter expression for query
78+
*/
79+
String filterExpression() default "";
80+
81+
/**
82+
* Set filter expressions for a query
83+
*
84+
* @see <a href=
85+
* "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression">Filter Expressions</a>
86+
* @see <a href=
87+
* "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html"> Expression Syntax</a>
88+
*
89+
*
90+
* Example: {@code @Query(filterExpression = "contains(#field, :value)",
91+
* expressionMappingNames = {@ExpressionAttribute(key = "#field", value = "name")},
92+
* expressionMappingValues = {@ExpressionAttribute(key=":value", value = "John Doe")})}
93+
*
94+
* @return expression name mappings for query
95+
*/
96+
ExpressionAttribute[] expressionMappingNames() default @ExpressionAttribute;
97+
98+
/**
99+
* Set filter expressions for a query
100+
*
101+
* @see <a href=
102+
* "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression">Filter Expressions</a>
103+
* @see <a href=
104+
* "https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html">Expression Syntax</a>
105+
*
106+
*
107+
* Example: {@code @Query(filterExpression = "contains(#field, :value)",
108+
* expressionMappingNames = {@ExpressionAttribute(key = "#field", value = "name")},
109+
* expressionMappingValues = {@ExpressionAttribute(key=":value", value = "John Doe")})}
110+
*
111+
* @return expression value mappings for query
112+
*/
113+
ExpressionAttribute[] expressionMappingValues() default @ExpressionAttribute;
63114
}

src/main/java/org/socialsignin/spring/data/dynamodb/repository/QueryConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ public enum ConsistentReadMode {
4747
*/
4848
EVENTUAL
4949
}
50+
5051
}

src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQuery.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,13 @@ protected QueryExecution<T, ID> getExecution() {
7777
protected abstract Integer getResultsRestrictionIfApplicable();
7878
protected abstract boolean isSingleEntityResultsRestriction();
7979

80-
protected Query<T> doCreateQueryWithPermissions(Object values[]) {
80+
protected Query<T> doCreateQueryWithPermissions(Object[] values) {
8181
Query<T> query = doCreateQuery(values);
8282
query.setScanEnabled(method.isScanEnabled());
8383
return query;
8484
}
8585

86-
protected Query<Long> doCreateCountQueryWithPermissions(Object values[], boolean pageQuery) {
86+
protected Query<Long> doCreateCountQueryWithPermissions(Object[] values, boolean pageQuery) {
8787
Query<Long> query = doCreateCountQuery(values, pageQuery);
8888
query.setScanCountEnabled(method.isScanCountEnabled());
8989
return query;
@@ -244,13 +244,13 @@ private Slice<T> createSlice(List<T> allResults, Pageable pageable) {
244244
if (pageable.getOffset() > 0) {
245245
long processedCount = scanThroughResults(iterator, pageable.getOffset());
246246
if (processedCount < pageable.getOffset())
247-
return new SliceImpl<>(new ArrayList<T>());
247+
return new SliceImpl<>(new ArrayList<>());
248248
}
249249
List<T> results = readPageOfResultsRestrictMaxResultsIfNecessary(iterator, pageable.getPageSize());
250250
// Scan ahead to retrieve the next page count
251251
boolean hasMoreResults = scanThroughResults(iterator, 1) > 0;
252252
if (getResultsRestrictionIfApplicable() != null
253-
&& getResultsRestrictionIfApplicable().intValue() <= results.size())
253+
&& getResultsRestrictionIfApplicable() <= results.size())
254254
hasMoreResults = false;
255255
return new SliceImpl<>(results, pageable, hasMoreResults);
256256
}

src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCreator.java

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,27 @@
1919
import com.amazonaws.services.dynamodbv2.model.ComparisonOperator;
2020
import org.socialsignin.spring.data.dynamodb.core.DynamoDBOperations;
2121
import org.socialsignin.spring.data.dynamodb.query.Query;
22+
import org.socialsignin.spring.data.dynamodb.repository.ExpressionAttribute;
2223
import org.socialsignin.spring.data.dynamodb.repository.QueryConstants;
2324
import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityInformation;
2425
import org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBIdIsHashAndRangeKeyEntityInformation;
2526
import org.springframework.data.mapping.PropertyPath;
27+
import org.springframework.data.repository.query.Parameter;
2628
import org.springframework.data.repository.query.ParameterAccessor;
29+
import org.springframework.data.repository.query.ParametersParameterAccessor;
2730
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
2831
import org.springframework.data.repository.query.parser.Part;
2932
import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
3033
import org.springframework.data.repository.query.parser.PartTree;
3134
import org.springframework.util.Assert;
3235
import org.springframework.util.ClassUtils;
3336
import org.springframework.util.ObjectUtils;
37+
import org.springframework.util.StringUtils;
3438

3539
import java.util.Arrays;
40+
import java.util.HashMap;
3641
import java.util.Iterator;
42+
import java.util.Map;
3743
import java.util.Optional;
3844

3945
/**
@@ -48,35 +54,71 @@ public abstract class AbstractDynamoDBQueryCreator<T, ID, R>
4854
protected final DynamoDBOperations dynamoDBOperations;
4955
protected final Optional<String> projection;
5056
protected final Optional<Integer> limit;
57+
protected final Optional<String> filterExpression;
58+
protected final ExpressionAttribute[] expressionAttributeNames;
59+
protected final ExpressionAttribute[] expressionAttributeValues;
60+
protected final Map<String, String> mappedExpressionValues = new HashMap<>();
5161
protected final QueryConstants.ConsistentReadMode consistentReads;
5262

5363
public AbstractDynamoDBQueryCreator(PartTree tree, DynamoDBEntityInformation<T, ID> entityMetadata,
54-
Optional<String> projection, Optional<Integer> limitResults, QueryConstants.ConsistentReadMode consistentReads, DynamoDBOperations dynamoDBOperations) {
64+
Optional<String> projection, Optional<Integer> limitResults, QueryConstants.ConsistentReadMode consistentReads,
65+
Optional<String> filterExpression, ExpressionAttribute[] names, ExpressionAttribute[] values, DynamoDBOperations dynamoDBOperations) {
5566
super(tree);
5667
this.entityMetadata = entityMetadata;
5768
this.projection = projection;
5869
this.limit = limitResults;
5970
this.consistentReads = consistentReads;
71+
this.filterExpression = filterExpression;
72+
if(names != null) {
73+
this.expressionAttributeNames = names.clone();
74+
} else{
75+
this.expressionAttributeNames = null;
76+
}
77+
if(values != null) {
78+
this.expressionAttributeValues = values.clone();
79+
} else{
80+
this.expressionAttributeValues = null;
81+
}
6082
this.dynamoDBOperations = dynamoDBOperations;
6183
}
6284

6385
public AbstractDynamoDBQueryCreator(PartTree tree, ParameterAccessor parameterAccessor,
6486
DynamoDBEntityInformation<T, ID> entityMetadata, Optional<String> projection,
65-
Optional<Integer> limitResults, QueryConstants.ConsistentReadMode consistentReads, DynamoDBOperations dynamoDBOperations) {
87+
Optional<Integer> limitResults, QueryConstants.ConsistentReadMode consistentReads, Optional<String> filterExpression, ExpressionAttribute[] names, ExpressionAttribute[] values, DynamoDBOperations dynamoDBOperations) {
6688
super(tree, parameterAccessor);
6789
this.entityMetadata = entityMetadata;
6890
this.projection = projection;
6991
this.limit = limitResults;
92+
this.filterExpression = filterExpression;
7093
this.consistentReads = consistentReads;
94+
if(names != null) {
95+
this.expressionAttributeNames = names.clone();
96+
} else{
97+
this.expressionAttributeNames = null;
98+
}
99+
if(values != null) {
100+
this.expressionAttributeValues = values.clone();
101+
for(ExpressionAttribute value: expressionAttributeValues) {
102+
if(!StringUtils.isEmpty(value.parameterName())) {
103+
for(Parameter p : ((ParametersParameterAccessor)parameterAccessor).getParameters()) {
104+
if(p.getName().isPresent() && p.getName().get().equals(value.parameterName())) {
105+
mappedExpressionValues.put(value.parameterName(), (String) parameterAccessor.getBindableValue(p.getIndex()));
106+
}
107+
}
108+
}
109+
}
110+
} else {
111+
this.expressionAttributeValues = null;
112+
}
71113
this.dynamoDBOperations = dynamoDBOperations;
72114
}
73115

74116
@Override
75117
protected DynamoDBQueryCriteria<T, ID> create(Part part, Iterator<Object> iterator) {
76118
final DynamoDBMapperTableModel<T> tableModel = dynamoDBOperations.getTableModel(entityMetadata.getJavaType());
77119
DynamoDBQueryCriteria<T, ID> criteria = entityMetadata.isRangeKeyAware()
78-
? new DynamoDBEntityWithHashAndRangeKeyCriteria<T, ID>(
79-
(DynamoDBIdIsHashAndRangeKeyEntityInformation<T, ID>) entityMetadata, tableModel)
120+
? new DynamoDBEntityWithHashAndRangeKeyCriteria<>(
121+
(DynamoDBIdIsHashAndRangeKeyEntityInformation<T, ID>) entityMetadata, tableModel)
80122
: new DynamoDBEntityWithHashKeyOnlyCriteria<>(entityMetadata, tableModel);
81123
return addCriteria(criteria, part, iterator);
82124
}
@@ -90,7 +132,7 @@ protected DynamoDBQueryCriteria<T, ID> addCriteria(DynamoDBQueryCriteria<T, ID>
90132

91133
PropertyPath leafNodePropertyPath = part.getProperty().getLeafProperty();
92134
String leafNodePropertyName = leafNodePropertyPath.toDotPath();
93-
if (leafNodePropertyName.indexOf(".") != -1) {
135+
if (leafNodePropertyName.contains(".")) {
94136
int index = leafNodePropertyName.lastIndexOf(".");
95137
leafNodePropertyName = leafNodePropertyName.substring(index);
96138
}

0 commit comments

Comments
 (0)