From 8b7071e6aebbbe93c2c96ffd9042b3b88e30a847 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 20 Feb 2019 14:27:10 +0100 Subject: [PATCH] DATAMONGO-1854 - Polishing. Use ParameterBindingDocumentCodec to parse collation expressions. Original pull request: #644. --- .../data/mongodb/core/query/Collation.java | 2 +- .../data/mongodb/repository/Query.java | 7 ++---- .../repository/query/AbstractMongoQuery.java | 16 +++++++++++-- .../query/AbstractReactiveMongoQuery.java | 19 ++++++++++++--- .../repository/query/MongoQueryMethod.java | 2 +- .../repository/query/PartTreeMongoQuery.java | 10 ++++++-- .../mongodb/repository/query/QueryUtils.java | 24 +++++++++++++++---- .../query/ReactivePartTreeMongoQuery.java | 10 ++++++-- .../query/ReactiveStringBasedMongoQuery.java | 3 ++- .../query/StringBasedMongoQuery.java | 3 ++- .../IndexEnsuringQueryCreationListener.java | 1 + .../support/MongoRepositoryFactory.java | 2 +- .../ReactiveMongoRepositoryFactory.java | 2 +- .../query/AbstractMongoQueryUnitTests.java | 9 +++---- .../AbstractReactiveMongoQueryUnitTests.java | 22 ++++++++++++++++- .../query/MongoQueryExecutionUnitTests.java | 12 +++++++--- .../query/PartTreeMongoQueryUnitTests.java | 6 ++++- 17 files changed, 117 insertions(+), 33 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java index bdaa8385a..2dd71ef68 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Collation.java @@ -119,7 +119,7 @@ public class Collation { } /** - * Parse the given collation string into a {@link Collation}. + * Parse the given {@code collation} string into a {@link Collation}. * * @param collation the collation to parse. Can be a simple string like {@code en_US} or a * {@link Document#parse(String) parsable} document like { 'locale' : '?0' } . diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java index 7e2d4fbe4..1de05c341 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/Query.java @@ -97,11 +97,9 @@ public @interface Query { String sort() default ""; /** - * Defines the collation to apply when executing the query.
- * - *
-	 *     
+	 * Defines the collation to apply when executing the query.
 	 *
+	 * 
 	 * // Fixed value
 	 * @Query(collation = "en_US")
 	 * List findAllByFixedCollation();
@@ -121,7 +119,6 @@ public @interface Query {
 	 * // SpEL expression
 	 * @Query(collation = "?#{[0]}")
 	 * List findAllByDynamicSpElCollation(String collation);
-	 *     
 	 * 
* * @since 2.2 diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java index 0a4258e72..03dbc78ff 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java @@ -16,6 +16,7 @@ package org.springframework.data.mongodb.repository.query; import org.bson.Document; + import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind; import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery; import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind; @@ -27,8 +28,10 @@ import org.springframework.data.mongodb.repository.query.MongoQueryExecution.Pag import org.springframework.data.mongodb.repository.query.MongoQueryExecution.PagingGeoNearExecution; import org.springframework.data.mongodb.repository.query.MongoQueryExecution.SlicedExecution; import org.springframework.data.repository.query.ParameterAccessor; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.Assert; /** @@ -44,17 +47,24 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { private final MongoQueryMethod method; private final MongoOperations operations; private final ExecutableFind executableFind; + private final SpelExpressionParser expressionParser; + private final QueryMethodEvaluationContextProvider evaluationContextProvider; /** * Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}. * * @param method must not be {@literal null}. * @param operations must not be {@literal null}. + * @param expressionParser must not be {@literal null}. + * @param evaluationContextProvider must not be {@literal null}. */ - public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations) { + public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, SpelExpressionParser expressionParser, + QueryMethodEvaluationContextProvider evaluationContextProvider) { Assert.notNull(operations, "MongoOperations must not be null!"); Assert.notNull(method, "MongoQueryMethod must not be null!"); + Assert.notNull(expressionParser, "SpelExpressionParser must not be null!"); + Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!"); this.method = method; this.operations = operations; @@ -63,6 +73,8 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { Class type = metadata.getCollectionEntity().getType(); this.executableFind = operations.query(type); + this.expressionParser = expressionParser; + this.evaluationContextProvider = evaluationContextProvider; } /* @@ -167,7 +179,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { Query applyAnnotatedCollationIfPresent(Query query, ConvertingParameterAccessor accessor) { return QueryUtils.applyCollation(query, method.hasAnnotatedCollation() ? method.getAnnotatedCollation() : null, - accessor); + accessor, getQueryMethod().getParameters(), expressionParser, evaluationContextProvider); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java index 706a566f9..343965d70 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java @@ -20,6 +20,7 @@ import reactor.core.publisher.Mono; import org.bson.Document; import org.reactivestreams.Publisher; + import org.springframework.core.convert.converter.Converter; import org.springframework.data.convert.EntityInstantiators; import org.springframework.data.mongodb.core.MongoOperations; @@ -33,8 +34,10 @@ import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecu import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.ResultProcessingConverter; import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.ResultProcessingExecution; import org.springframework.data.repository.query.ParameterAccessor; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.Assert; /** @@ -50,6 +53,8 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { private final ReactiveMongoOperations operations; private final EntityInstantiators instantiators; private final FindWithProjection findOperationWithProjection; + private final SpelExpressionParser expressionParser; + private final QueryMethodEvaluationContextProvider evaluationContextProvider; /** * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and @@ -57,15 +62,22 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { * * @param method must not be {@literal null}. * @param operations must not be {@literal null}. + * @param expressionParser must not be {@literal null}. + * @param evaluationContextProvider must not be {@literal null}. */ - public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations) { + public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations, + SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { Assert.notNull(method, "MongoQueryMethod must not be null!"); Assert.notNull(operations, "ReactiveMongoOperations must not be null!"); + Assert.notNull(expressionParser, "SpelExpressionParser must not be null!"); + Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!"); this.method = method; this.operations = operations; this.instantiators = new EntityInstantiators(); + this.expressionParser = expressionParser; + this.evaluationContextProvider = evaluationContextProvider; MongoEntityMetadata metadata = method.getEntityInformation(); Class type = metadata.getCollectionEntity().getType(); @@ -105,7 +117,8 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { private Object execute(MongoParameterAccessor parameterAccessor) { - ConvertingParameterAccessor convertingParamterAccessor = new ConvertingParameterAccessor(operations.getConverter(), parameterAccessor); + ConvertingParameterAccessor convertingParamterAccessor = new ConvertingParameterAccessor(operations.getConverter(), + parameterAccessor); Query query = createQuery(convertingParamterAccessor); applyQueryMetaAttributesWhenPresent(query); @@ -209,7 +222,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { Query applyAnnotatedCollationIfPresent(Query query, ConvertingParameterAccessor accessor) { return QueryUtils.applyCollation(query, method.hasAnnotatedCollation() ? method.getAnnotatedCollation() : null, - accessor); + accessor, getQueryMethod().getParameters(), expressionParser, evaluationContextProvider); } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java index 84b120492..79caf54ec 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryMethod.java @@ -335,7 +335,7 @@ public class MongoQueryMethod extends QueryMethod { /** * Get the collation value extracted from the {@link Query} annotation. * - * @return the {@link Query#sort()} value. + * @return the {@link Query#collation()} value. * @throws IllegalStateException if method not annotated with {@link Query}. Make sure to check * {@link #hasAnnotatedQuery()} first. * @since 2.2 diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java index 16d2aeb6d..7a3f3e4b8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query; import org.bson.Document; import org.bson.json.JsonParseException; + import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.MongoTemplate; @@ -26,10 +27,12 @@ import org.springframework.data.mongodb.core.query.Field; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.StringUtils; /** @@ -52,10 +55,13 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { * * @param method must not be {@literal null}. * @param mongoOperations must not be {@literal null}. + * @param expressionParser must not be {@literal null}. + * @param evaluationContextProvider must not be {@literal null}. */ - public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations) { + public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, + SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations); + super(method, mongoOperations, expressionParser, evaluationContextProvider); this.processor = method.getResultProcessor(); this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java index 5bbafeec0..d73f0b415 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java @@ -21,23 +21,32 @@ import java.util.regex.Pattern; import org.aopalliance.intercept.MethodInterceptor; import org.bson.Document; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.data.mongodb.core.query.Collation; import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.util.json.ParameterBindingContext; +import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.NumberUtils; import org.springframework.util.ObjectUtils; +import org.springframework.util.StringUtils; /** * Internal utility class to help avoid duplicate code required in both the reactive and the sync {@link Query} support * offered by repositories. * * @author Christoph Strobl + * @author Mark Paluch * @since 2.1 * @currentRead Assassin's Apprentice - Robin Hobb */ class QueryUtils { + private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec(); + private static final Pattern PARAMETER_BINDING_PATTERN = Pattern.compile("\\?(\\d+)"); /** @@ -80,7 +89,9 @@ class QueryUtils { * @see Query#collation(Collation) * @since 2.2 */ - static Query applyCollation(Query query, @Nullable String collationExpression, ConvertingParameterAccessor accessor) { + static Query applyCollation(Query query, @Nullable String collationExpression, ConvertingParameterAccessor accessor, + MongoParameters parameters, SpelExpressionParser expressionParser, + QueryMethodEvaluationContextProvider evaluationContextProvider) { if (accessor.getCollation() != null) { return query.collation(accessor.getCollation()); @@ -90,10 +101,15 @@ class QueryUtils { return query; } + if (StringUtils.trimLeadingWhitespace(collationExpression).startsWith("{")) { + + ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), + expressionParser, evaluationContextProvider.getEvaluationContext(parameters, accessor.getValues())); + + return query.collation(Collation.from(CODEC.decode(collationExpression, bindingContext))); + } + Matcher matcher = PARAMETER_BINDING_PATTERN.matcher(collationExpression); - - // TODO: use parameter binding Parser instead of Document.parse once DATAMONGO-2199 is merged. - if (!matcher.find()) { return query.collation(Collation.parse(collationExpression)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java index 3fd1fd253..f5af831a7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java @@ -17,6 +17,7 @@ package org.springframework.data.mongodb.repository.query; import org.bson.Document; import org.bson.json.JsonParseException; + import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.ReactiveMongoOperations; @@ -25,10 +26,12 @@ import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.TextCriteria; import org.springframework.data.repository.query.QueryMethod; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.StringUtils; /** @@ -50,10 +53,13 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { * * @param method must not be {@literal null}. * @param mongoOperations must not be {@literal null}. + * @param expressionParser must not be {@literal null}. + * @param evaluationContextProvider must not be {@literal null}. */ - public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations) { + public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, + SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations); + super(method, mongoOperations, expressionParser, evaluationContextProvider); this.processor = method.getResultProcessor(); this.tree = new PartTree(method.getName(), processor.getReturnedType().getDomainType()); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java index 1850576e0..5e1e73040 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query; import org.bson.Document; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.ReactiveMongoOperations; import org.springframework.data.mongodb.core.query.BasicQuery; @@ -78,7 +79,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations); + super(method, mongoOperations, expressionParser, evaluationContextProvider); Assert.notNull(query, "Query must not be null!"); Assert.notNull(expressionParser, "SpelExpressionParser must not be null!"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java index 42a1c9fff..7b045e8f9 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java @@ -18,6 +18,7 @@ package org.springframework.data.mongodb.repository.query; import org.bson.Document; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Query; @@ -77,7 +78,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations, SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { - super(method, mongoOperations); + super(method, mongoOperations, expressionParser, evaluationContextProvider); Assert.notNull(query, "Query must not be null!"); Assert.notNull(expressionParser, "SpelExpressionParser must not be null!"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java index 1706b419c..62753898d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/IndexEnsuringQueryCreationListener.java @@ -21,6 +21,7 @@ import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java index 2920285d3..eeff22eb4 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java @@ -190,7 +190,7 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { } else if (queryMethod.hasAnnotatedQuery()) { return new StringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); } else { - return new PartTreeMongoQuery(queryMethod, operations); + return new PartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java index 0e24b1ea7..c5001dcf2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java @@ -180,7 +180,7 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup } else if (queryMethod.hasAnnotatedQuery()) { return new ReactiveStringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); } else { - return new ReactivePartTreeMongoQuery(queryMethod, operations); + return new ReactivePartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); } } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java index 99a917319..3a9835007 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractMongoQueryUnitTests.java @@ -16,9 +16,7 @@ package org.springframework.data.mongodb.repository.query; import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.lang.reflect.Method; @@ -35,6 +33,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; + import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -60,6 +59,8 @@ import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.expression.spel.standard.SpelExpressionParser; import com.mongodb.client.result.DeleteResult; @@ -455,7 +456,7 @@ public class AbstractMongoQueryUnitTests { private boolean isLimitingQuery; public MongoQueryFake(MongoQueryMethod method, MongoOperations operations) { - super(method, operations); + super(method, operations, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT); } @Override diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java index 22eab4989..82141b7a2 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQueryUnitTests.java @@ -30,6 +30,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; + import org.springframework.data.mongodb.core.Person; import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithQuery; import org.springframework.data.mongodb.core.ReactiveFindOperation.ReactiveFind; @@ -45,6 +46,8 @@ import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.expression.spel.standard.SpelExpressionParser; /** * @author Christoph Strobl @@ -162,6 +165,19 @@ public class AbstractReactiveMongoQueryUnitTests { .contains(Collation.of("en_US").toDocument()); } + @Test // DATAMONGO-1854 + public void shouldApplyDynamicAnnotatedCollationWithMultiplePlaceholders() { + + createQueryForMethod("findWithCollationUsingPlaceholdersInDocumentByFirstName", String.class, String.class, + int.class) // + .execute(new Object[] { "dalinar", "en_US", 2 }); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Query.class); + verify(withQueryMock).matching(captor.capture()); + assertThat(captor.getValue().getCollation().map(Collation::toDocument)) + .contains(Collation.of("en_US").strength(2).toDocument()); + } + @Test // DATAMONGO-1854 public void shouldApplyCollationParameter() { @@ -223,7 +239,7 @@ public class AbstractReactiveMongoQueryUnitTests { private boolean isLimitingQuery; public ReactiveMongoQueryFake(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations) { - super(method, operations); + super(method, operations, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT); } @Override @@ -277,6 +293,10 @@ public class AbstractReactiveMongoQueryUnitTests { @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : '?1' }") List findWithCollationUsingPlaceholderInDocumentByFirstName(String firstname, String collation); + @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : '?1', 'strength' : ?#{[2]}}") + List findWithCollationUsingPlaceholdersInDocumentByFirstName(String firstname, String collation, + int strength); + List findWithCollationParameterByFirstName(String firstname, Collation collation); @org.springframework.data.mongodb.repository.Query(collation = "{ 'locale' : 'en_US' }") diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java index 25874033c..62111a3d9 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryExecutionUnitTests.java @@ -15,7 +15,7 @@ */ package org.springframework.data.mongodb.repository.query; -import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.lang.reflect.Method; @@ -27,6 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; + import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.geo.Distance; @@ -53,6 +54,8 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.ReflectionUtils; /** @@ -72,6 +75,7 @@ public class MongoQueryExecutionUnitTests { @Mock TerminatingFindNear terminatingGeoMock; @Mock DbRefResolver dbRefResolver; + SpelExpressionParser EXPRESSION_PARSER = new SpelExpressionParser(); Point POINT = new Point(10, 20); Distance DISTANCE = new Distance(2.5, Metrics.KILOMETERS); RepositoryMetadata metadata = new DefaultRepositoryMetadata(PersonRepository.class); @@ -140,7 +144,8 @@ public class MongoQueryExecutionUnitTests { ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, new MongoParametersParameterAccessor(queryMethod, new Object[] { POINT, DISTANCE, PageRequest.of(0, 10) })); - PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock); + PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, EXPRESSION_PARSER, + QueryMethodEvaluationContextProvider.DEFAULT); PagingGeoNearExecution execution = new PagingGeoNearExecution(findOperationMock, queryMethod, accessor, query); execution.execute(new Query()); @@ -158,7 +163,8 @@ public class MongoQueryExecutionUnitTests { ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(converter, new MongoParametersParameterAccessor(queryMethod, new Object[] { POINT, DISTANCE, PageRequest.of(2, 10) })); - PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock); + PartTreeMongoQuery query = new PartTreeMongoQuery(queryMethod, mongoOperationsMock, EXPRESSION_PARSER, + QueryMethodEvaluationContextProvider.DEFAULT); PagingGeoNearExecution execution = new PagingGeoNearExecution(findOperationMock, queryMethod, accessor, query); execution.execute(new Query()); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java index 6fad108a8..5759fcce4 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java @@ -33,6 +33,7 @@ import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; + import org.springframework.beans.factory.annotation.Value; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind; @@ -51,6 +52,8 @@ import org.springframework.data.mongodb.repository.Query; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.projection.SpelAwareProxyProjectionFactory; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; +import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.expression.spel.standard.SpelExpressionParser; /** * Unit tests for {@link PartTreeMongoQuery}. @@ -217,7 +220,8 @@ public class PartTreeMongoQueryUnitTests { MongoQueryMethod queryMethod = new MongoQueryMethod(method, new DefaultRepositoryMetadata(Repo.class), factory, mappingContext); - return new PartTreeMongoQuery(queryMethod, mongoOperationsMock); + return new PartTreeMongoQuery(queryMethod, mongoOperationsMock, new SpelExpressionParser(), + QueryMethodEvaluationContextProvider.DEFAULT); } catch (Exception e) { throw new IllegalArgumentException(e.getMessage(), e); }