Skip to content

Commit 8b7071e

Browse files
committed
DATAMONGO-1854 - Polishing.
Use ParameterBindingDocumentCodec to parse collation expressions. Original pull request: #644.
1 parent b8368a6 commit 8b7071e

17 files changed

+116
-32
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public static Collation of(CollationLocale locale) {
119119
}
120120

121121
/**
122-
* Parse the given collation string into a {@link Collation}.
122+
* Parse the given {@code collation} string into a {@link Collation}.
123123
*
124124
* @param collation the collation to parse. Can be a simple string like {@code en_US} or a
125125
* {@link Document#parse(String) parsable} document like <code>&#123; 'locale' : '?0' &#125;</code> .

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,9 @@
9797
String sort() default "";
9898

9999
/**
100-
* Defines the collation to apply when executing the query. <br />
101-
*
102-
* <pre>
103-
* <code>
100+
* Defines the collation to apply when executing the query.
104101
*
102+
* <pre class="code">
105103
* // Fixed value
106104
* &#64;Query(collation = "en_US")
107105
* List<Entry> findAllByFixedCollation();
@@ -121,7 +119,6 @@
121119
* // SpEL expression
122120
* &#64;Query(collation = "?#{[0]}")
123121
* List<Entry> findAllByDynamicSpElCollation(String collation);
124-
* </code>
125122
* </pre>
126123
*
127124
* @since 2.2

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.springframework.data.mongodb.repository.query;
1717

1818
import org.bson.Document;
19+
1920
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
2021
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
2122
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind;
@@ -27,8 +28,10 @@
2728
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagingGeoNearExecution;
2829
import org.springframework.data.mongodb.repository.query.MongoQueryExecution.SlicedExecution;
2930
import org.springframework.data.repository.query.ParameterAccessor;
31+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
3032
import org.springframework.data.repository.query.RepositoryQuery;
3133
import org.springframework.data.repository.query.ResultProcessor;
34+
import org.springframework.expression.spel.standard.SpelExpressionParser;
3235
import org.springframework.util.Assert;
3336

3437
/**
@@ -44,17 +47,24 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
4447
private final MongoQueryMethod method;
4548
private final MongoOperations operations;
4649
private final ExecutableFind<?> executableFind;
50+
private final SpelExpressionParser expressionParser;
51+
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
4752

4853
/**
4954
* Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
5055
*
5156
* @param method must not be {@literal null}.
5257
* @param operations must not be {@literal null}.
58+
* @param expressionParser must not be {@literal null}.
59+
* @param evaluationContextProvider must not be {@literal null}.
5360
*/
54-
public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations) {
61+
public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, SpelExpressionParser expressionParser,
62+
QueryMethodEvaluationContextProvider evaluationContextProvider) {
5563

5664
Assert.notNull(operations, "MongoOperations must not be null!");
5765
Assert.notNull(method, "MongoQueryMethod must not be null!");
66+
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");
67+
Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!");
5868

5969
this.method = method;
6070
this.operations = operations;
@@ -63,6 +73,8 @@ public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations) {
6373
Class<?> type = metadata.getCollectionEntity().getType();
6474

6575
this.executableFind = operations.query(type);
76+
this.expressionParser = expressionParser;
77+
this.evaluationContextProvider = evaluationContextProvider;
6678
}
6779

6880
/*
@@ -167,7 +179,7 @@ Query applyAnnotatedDefaultSortIfPresent(Query query) {
167179
Query applyAnnotatedCollationIfPresent(Query query, ConvertingParameterAccessor accessor) {
168180

169181
return QueryUtils.applyCollation(query, method.hasAnnotatedCollation() ? method.getAnnotatedCollation() : null,
170-
accessor);
182+
accessor, getQueryMethod().getParameters(), expressionParser, evaluationContextProvider);
171183
}
172184

173185
/**

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.bson.Document;
2222
import org.reactivestreams.Publisher;
23+
2324
import org.springframework.core.convert.converter.Converter;
2425
import org.springframework.data.convert.EntityInstantiators;
2526
import org.springframework.data.mongodb.core.MongoOperations;
@@ -33,8 +34,10 @@
3334
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.ResultProcessingConverter;
3435
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.ResultProcessingExecution;
3536
import org.springframework.data.repository.query.ParameterAccessor;
37+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
3638
import org.springframework.data.repository.query.RepositoryQuery;
3739
import org.springframework.data.repository.query.ResultProcessor;
40+
import org.springframework.expression.spel.standard.SpelExpressionParser;
3841
import org.springframework.util.Assert;
3942

4043
/**
@@ -50,22 +53,31 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
5053
private final ReactiveMongoOperations operations;
5154
private final EntityInstantiators instantiators;
5255
private final FindWithProjection<?> findOperationWithProjection;
56+
private final SpelExpressionParser expressionParser;
57+
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
5358

5459
/**
5560
* Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
5661
* {@link MongoOperations}.
5762
*
5863
* @param method must not be {@literal null}.
5964
* @param operations must not be {@literal null}.
65+
* @param expressionParser must not be {@literal null}.
66+
* @param evaluationContextProvider must not be {@literal null}.
6067
*/
61-
public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations) {
68+
public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
69+
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
6270

6371
Assert.notNull(method, "MongoQueryMethod must not be null!");
6472
Assert.notNull(operations, "ReactiveMongoOperations must not be null!");
73+
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");
74+
Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!");
6575

6676
this.method = method;
6777
this.operations = operations;
6878
this.instantiators = new EntityInstantiators();
79+
this.expressionParser = expressionParser;
80+
this.evaluationContextProvider = evaluationContextProvider;
6981

7082
MongoEntityMetadata<?> metadata = method.getEntityInformation();
7183
Class<?> type = metadata.getCollectionEntity().getType();
@@ -105,7 +117,8 @@ private Object executeDeferred(Object[] parameters) {
105117

106118
private Object execute(MongoParameterAccessor parameterAccessor) {
107119

108-
ConvertingParameterAccessor convertingParamterAccessor = new ConvertingParameterAccessor(operations.getConverter(), parameterAccessor);
120+
ConvertingParameterAccessor convertingParamterAccessor = new ConvertingParameterAccessor(operations.getConverter(),
121+
parameterAccessor);
109122
Query query = createQuery(convertingParamterAccessor);
110123

111124
applyQueryMetaAttributesWhenPresent(query);
@@ -209,7 +222,7 @@ Query applyAnnotatedDefaultSortIfPresent(Query query) {
209222
Query applyAnnotatedCollationIfPresent(Query query, ConvertingParameterAccessor accessor) {
210223

211224
return QueryUtils.applyCollation(query, method.hasAnnotatedCollation() ? method.getAnnotatedCollation() : null,
212-
accessor);
225+
accessor, getQueryMethod().getParameters(), expressionParser, evaluationContextProvider);
213226
}
214227

215228
/**

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,7 @@ public boolean hasAnnotatedCollation() {
335335
/**
336336
* Get the collation value extracted from the {@link Query} annotation.
337337
*
338-
* @return the {@link Query#sort()} value.
338+
* @return the {@link Query#collation()} value.
339339
* @throws IllegalStateException if method not annotated with {@link Query}. Make sure to check
340340
* {@link #hasAnnotatedQuery()} first.
341341
* @since 2.2

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.bson.Document;
1919
import org.bson.json.JsonParseException;
20+
2021
import org.springframework.data.mapping.context.MappingContext;
2122
import org.springframework.data.mongodb.core.MongoOperations;
2223
import org.springframework.data.mongodb.core.MongoTemplate;
@@ -26,10 +27,12 @@
2627
import org.springframework.data.mongodb.core.query.Query;
2728
import org.springframework.data.mongodb.core.query.TextCriteria;
2829
import org.springframework.data.repository.query.QueryMethod;
30+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
2931
import org.springframework.data.repository.query.RepositoryQuery;
3032
import org.springframework.data.repository.query.ResultProcessor;
3133
import org.springframework.data.repository.query.ReturnedType;
3234
import org.springframework.data.repository.query.parser.PartTree;
35+
import org.springframework.expression.spel.standard.SpelExpressionParser;
3336
import org.springframework.util.StringUtils;
3437

3538
/**
@@ -52,10 +55,13 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
5255
*
5356
* @param method must not be {@literal null}.
5457
* @param mongoOperations must not be {@literal null}.
58+
* @param expressionParser must not be {@literal null}.
59+
* @param evaluationContextProvider must not be {@literal null}.
5560
*/
56-
public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations) {
61+
public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
62+
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
5763

58-
super(method, mongoOperations);
64+
super(method, mongoOperations, expressionParser, evaluationContextProvider);
5965

6066
this.processor = method.getResultProcessor();
6167
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,32 @@
2121

2222
import org.aopalliance.intercept.MethodInterceptor;
2323
import org.bson.Document;
24+
2425
import org.springframework.aop.framework.ProxyFactory;
2526
import org.springframework.data.mongodb.core.query.Collation;
2627
import org.springframework.data.mongodb.core.query.Query;
28+
import org.springframework.data.mongodb.util.json.ParameterBindingContext;
29+
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
30+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
31+
import org.springframework.expression.spel.standard.SpelExpressionParser;
2732
import org.springframework.lang.Nullable;
2833
import org.springframework.util.NumberUtils;
2934
import org.springframework.util.ObjectUtils;
35+
import org.springframework.util.StringUtils;
3036

3137
/**
3238
* Internal utility class to help avoid duplicate code required in both the reactive and the sync {@link Query} support
3339
* offered by repositories.
3440
*
3541
* @author Christoph Strobl
42+
* @author Mark Paluch
3643
* @since 2.1
3744
* @currentRead Assassin's Apprentice - Robin Hobb
3845
*/
3946
class QueryUtils {
4047

48+
private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec();
49+
4150
private static final Pattern PARAMETER_BINDING_PATTERN = Pattern.compile("\\?(\\d+)");
4251

4352
/**
@@ -80,7 +89,9 @@ static Query decorateSort(Query query, Document defaultSort) {
8089
* @see Query#collation(Collation)
8190
* @since 2.2
8291
*/
83-
static Query applyCollation(Query query, @Nullable String collationExpression, ConvertingParameterAccessor accessor) {
92+
static Query applyCollation(Query query, @Nullable String collationExpression, ConvertingParameterAccessor accessor,
93+
MongoParameters parameters, SpelExpressionParser expressionParser,
94+
QueryMethodEvaluationContextProvider evaluationContextProvider) {
8495

8596
if (accessor.getCollation() != null) {
8697
return query.collation(accessor.getCollation());
@@ -90,10 +101,15 @@ static Query applyCollation(Query query, @Nullable String collationExpression, C
90101
return query;
91102
}
92103

93-
Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(collationExpression);
104+
if (StringUtils.trimLeadingWhitespace(collationExpression).startsWith("{")) {
94105

95-
// TODO: use parameter binding Parser instead of Document.parse once DATAMONGO-2199 is merged.
106+
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue),
107+
expressionParser, evaluationContextProvider.getEvaluationContext(parameters, accessor.getValues()));
96108

109+
return query.collation(Collation.from(CODEC.decode(collationExpression, bindingContext)));
110+
}
111+
112+
Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(collationExpression);
97113
if (!matcher.find()) {
98114
return query.collation(Collation.parse(collationExpression));
99115
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import org.bson.Document;
1919
import org.bson.json.JsonParseException;
20+
2021
import org.springframework.data.mapping.context.MappingContext;
2122
import org.springframework.data.mongodb.core.MongoTemplate;
2223
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
@@ -25,10 +26,12 @@
2526
import org.springframework.data.mongodb.core.query.Query;
2627
import org.springframework.data.mongodb.core.query.TextCriteria;
2728
import org.springframework.data.repository.query.QueryMethod;
29+
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
2830
import org.springframework.data.repository.query.RepositoryQuery;
2931
import org.springframework.data.repository.query.ResultProcessor;
3032
import org.springframework.data.repository.query.ReturnedType;
3133
import org.springframework.data.repository.query.parser.PartTree;
34+
import org.springframework.expression.spel.standard.SpelExpressionParser;
3235
import org.springframework.util.StringUtils;
3336

3437
/**
@@ -50,10 +53,13 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
5053
*
5154
* @param method must not be {@literal null}.
5255
* @param mongoOperations must not be {@literal null}.
56+
* @param expressionParser must not be {@literal null}.
57+
* @param evaluationContextProvider must not be {@literal null}.
5358
*/
54-
public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations) {
59+
public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
60+
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
5561

56-
super(method, mongoOperations);
62+
super(method, mongoOperations, expressionParser, evaluationContextProvider);
5763

5864
this.processor = method.getResultProcessor();
5965
this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType());

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.bson.Document;
1919
import org.slf4j.Logger;
2020
import org.slf4j.LoggerFactory;
21+
2122
import org.springframework.data.mongodb.core.MongoOperations;
2223
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
2324
import org.springframework.data.mongodb.core.query.BasicQuery;
@@ -78,7 +79,7 @@ public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod meth
7879
ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser,
7980
QueryMethodEvaluationContextProvider evaluationContextProvider) {
8081

81-
super(method, mongoOperations);
82+
super(method, mongoOperations, expressionParser, evaluationContextProvider);
8283

8384
Assert.notNull(query, "Query must not be null!");
8485
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.bson.Document;
1919
import org.slf4j.Logger;
2020
import org.slf4j.LoggerFactory;
21+
2122
import org.springframework.data.mongodb.core.MongoOperations;
2223
import org.springframework.data.mongodb.core.query.BasicQuery;
2324
import org.springframework.data.mongodb.core.query.Query;
@@ -77,7 +78,7 @@ public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOpera
7778
public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations,
7879
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
7980

80-
super(method, mongoOperations);
81+
super(method, mongoOperations, expressionParser, evaluationContextProvider);
8182

8283
Assert.notNull(query, "Query must not be null!");
8384
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.slf4j.Logger;
2323
import org.slf4j.LoggerFactory;
24+
2425
import org.springframework.data.domain.Sort;
2526
import org.springframework.data.domain.Sort.Direction;
2627
import org.springframework.data.domain.Sort.Order;

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata,
190190
} else if (queryMethod.hasAnnotatedQuery()) {
191191
return new StringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
192192
} else {
193-
return new PartTreeMongoQuery(queryMethod, operations);
193+
return new PartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
194194
}
195195
}
196196
}

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata,
180180
} else if (queryMethod.hasAnnotatedQuery()) {
181181
return new ReactiveStringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
182182
} else {
183-
return new ReactivePartTreeMongoQuery(queryMethod, operations);
183+
return new ReactivePartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider);
184184
}
185185
}
186186
}

0 commit comments

Comments
 (0)