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 f3c7167b7..6c713d444 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 @@ -30,6 +30,7 @@ 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.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -47,7 +48,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { private final MongoQueryMethod method; private final MongoOperations operations; private final ExecutableFind executableFind; - private final SpelExpressionParser expressionParser; + private final ExpressionParser expressionParser; private final QueryMethodEvaluationContextProvider evaluationContextProvider; /** @@ -58,7 +59,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery { * @param expressionParser must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}. */ - public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, SpelExpressionParser expressionParser, + public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { Assert.notNull(operations, "MongoOperations must not be null!"); 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 0cd2dd4b5..00294e298 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 @@ -37,6 +37,7 @@ import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationCo import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.util.TypeInformation; +import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -54,7 +55,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { private final ReactiveMongoOperations operations; private final EntityInstantiators instantiators; private final FindWithProjection findOperationWithProjection; - private final SpelExpressionParser expressionParser; + private final ExpressionParser expressionParser; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; /** @@ -67,7 +68,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery { * @param evaluationContextProvider must not be {@literal null}. */ public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations, - SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { Assert.notNull(method, "MongoQueryMethod must not be null!"); Assert.notNull(operations, "ReactiveMongoOperations must not be null!"); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java index e71306c35..6e77b9558 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java @@ -34,6 +34,7 @@ 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.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; @@ -64,12 +65,12 @@ abstract class AggregationUtils { * @param accessor must not be {@literal null}. * @return the {@link Query} having proper {@link Collation}. * @see AggregationOptions#getCollation() - * @see CollationUtils#computeCollation(String, ConvertingParameterAccessor, MongoParameters, SpelExpressionParser, + * @see CollationUtils#computeCollation(String, ConvertingParameterAccessor, MongoParameters, ExpressionParser, * QueryMethodEvaluationContextProvider) */ static AggregationOptions.Builder applyCollation(AggregationOptions.Builder builder, @Nullable String collationExpression, ConvertingParameterAccessor accessor, MongoParameters parameters, - SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser, evaluationContextProvider); @@ -119,7 +120,7 @@ abstract class AggregationUtils { * @return */ static List computePipeline(MongoQueryMethod method, ConvertingParameterAccessor accessor, - SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser, () -> evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues())); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java index 74d9c2153..724cf2689 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java @@ -24,6 +24,7 @@ import org.springframework.data.mongodb.core.query.Collation; 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.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.NumberUtils; @@ -59,7 +60,7 @@ abstract class CollationUtils { */ @Nullable static Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor, - MongoParameters parameters, SpelExpressionParser expressionParser, + MongoParameters parameters, ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { if (accessor.getCollation() != null) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java index 6dc63be9d..ab4dcf8c8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java @@ -35,6 +35,15 @@ class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator { this.context = context; } + /** + * Return a {@link SpELExpressionEvaluator} that does not support expression evaluation. + * + * @return a {@link SpELExpressionEvaluator} that does not support expression evaluation. + */ + public static SpELExpressionEvaluator unsupported() { + return NoOpExpressionEvaluator.INSTANCE; + } + /* * (non-Javadoc) * @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String) @@ -44,4 +53,20 @@ class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator { public T evaluate(String expression) { return (T) parser.parseExpression(expression).getValue(context, Object.class); } + + /** + * {@link SpELExpressionEvaluator} that does not support SpEL evaluation. + * + * @author Mark Paluch + * @since 3.1 + */ + enum NoOpExpressionEvaluator implements SpELExpressionEvaluator { + + INSTANCE; + + @Override + public T evaluate(String expression) { + throw new UnsupportedOperationException("Expression evaluation not supported"); + } + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/NoOpExpressionEvaluator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/NoOpExpressionEvaluator.java deleted file mode 100644 index 416c8fbbf..000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/NoOpExpressionEvaluator.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2020 the original author or authors. - * - * 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 - * - * https://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.springframework.data.mongodb.repository.query; - -import org.springframework.data.mapping.model.SpELExpressionEvaluator; - -/** - * {@link SpELExpressionEvaluator} that does not support SpEL evaluation. - * - * @author Mark Paluch - * @since 3.1 - */ -enum NoOpExpressionEvaluator implements SpELExpressionEvaluator { - - INSTANCE; - - @Override - public T evaluate(String expression) { - throw new UnsupportedOperationException("Expression evaluation not supported"); - } -} 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 8063c17b2..902a36464 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 @@ -32,7 +32,7 @@ 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.expression.ExpressionParser; import org.springframework.util.StringUtils; /** @@ -59,7 +59,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery { * @param evaluationContextProvider must not be {@literal null}. */ public PartTreeMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, - SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { super(method, mongoOperations, expressionParser, evaluationContextProvider); 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 b62891e3b..5b68f79bf 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,6 +21,7 @@ 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.repository.query.QueryMethodEvaluationContextProvider; +import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; @@ -76,7 +77,7 @@ class QueryUtils { * @since 2.2 */ static Query applyCollation(Query query, @Nullable String collationExpression, ConvertingParameterAccessor accessor, - MongoParameters parameters, SpelExpressionParser expressionParser, + MongoParameters parameters, ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser, 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 f2226f6b0..cb0f4cb61 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 @@ -33,6 +33,7 @@ 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.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.StringUtils; @@ -59,7 +60,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery { * @param evaluationContextProvider must not be {@literal null}. */ public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, - SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { super(method, mongoOperations, expressionParser, evaluationContextProvider); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java index 8df312ec3..6ebdc61a2 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java @@ -39,6 +39,7 @@ import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.spel.ExpressionDependencies; +import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.util.ClassUtils; @@ -54,7 +55,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery { private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec(); - private final SpelExpressionParser expressionParser; + private final ExpressionParser expressionParser; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveMongoOperations reactiveMongoOperations; private final MongoConverter mongoConverter; @@ -66,7 +67,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery { * @param evaluationContextProvider must not be {@literal null}. */ public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method, - ReactiveMongoOperations reactiveMongoOperations, SpelExpressionParser expressionParser, + ReactiveMongoOperations reactiveMongoOperations, ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider); @@ -118,11 +119,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery { }); } - if (method.isCollectionQuery()) { - return flux; - } else { - return flux.next(); - } + return method.isCollectionQuery() ? flux : flux.next(); }); } @@ -145,7 +142,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery { it -> evaluationContextProvider.getEvaluationContextLater(method.getParameters(), accessor.getValues(), it)) .map(evaluationContext -> evaluationContext .map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it))) - .orElseGet(() -> Mono.just(NoOpExpressionEvaluator.INSTANCE)); + .orElseGet(() -> Mono.just(DefaultSpELExpressionEvaluator.unsupported())); Mono stage = evaluator.map(it -> { 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 d8a57f0e8..360a09575 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 @@ -53,7 +53,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { private final String query; private final String fieldSpec; - private final SpelExpressionParser expressionParser; + private final ExpressionParser expressionParser; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final boolean isCountQuery; @@ -70,7 +70,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { * @param evaluationContextProvider must not be {@literal null}. */ public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, - SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider); } @@ -85,7 +85,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { * @param expressionParser must not be {@literal null}. */ public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method, - ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser, + ReactiveMongoOperations mongoOperations, ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { super(method, mongoOperations, expressionParser, evaluationContextProvider); @@ -153,7 +153,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery { accessor.getValues(), it)) .map(evaluationContext -> evaluationContext .map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it))) - .orElseGet(() -> Mono.just(NoOpExpressionEvaluator.INSTANCE)); + .orElseGet(() -> Mono.just(DefaultSpELExpressionEvaluator.unsupported())); return evaluator.map(it -> new ParameterBindingContext(accessor::getBindableValue, it)); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java index 912f0e3c9..2dea1ff89 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java @@ -32,7 +32,7 @@ import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ResultProcessor; -import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.ExpressionParser; import org.springframework.util.ClassUtils; /** @@ -43,7 +43,7 @@ public class StringBasedAggregation extends AbstractMongoQuery { private final MongoOperations mongoOperations; private final MongoConverter mongoConverter; - private final SpelExpressionParser expressionParser; + private final ExpressionParser expressionParser; private final QueryMethodEvaluationContextProvider evaluationContextProvider; /** @@ -55,7 +55,7 @@ public class StringBasedAggregation extends AbstractMongoQuery { * @param evaluationContextProvider */ public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOperations, - SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { super(method, mongoOperations, expressionParser, evaluationContextProvider); this.mongoOperations = mongoOperations; 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 c1a0c7343..e09a7a1cd 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 @@ -54,7 +54,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { private final String fieldSpec; private final ParameterBindingDocumentCodec codec; - private final SpelExpressionParser expressionParser; + private final ExpressionParser expressionParser; private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final boolean isCountQuery; @@ -71,7 +71,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { * @param evaluationContextProvider must not be {@literal null}. */ public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, - SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider); } @@ -85,7 +85,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { * @param expressionParser must not be {@literal null}. */ public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations, - SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { + ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { super(method, mongoOperations, expressionParser, evaluationContextProvider); @@ -151,7 +151,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery { accessor.getValues(), it)) .map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, evaluationContext)) - .orElse(NoOpExpressionEvaluator.INSTANCE); + .orElse(DefaultSpELExpressionEvaluator.unsupported()); return new ParameterBindingContext(accessor::getBindableValue, evaluator); } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CachingExpressionParser.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CachingExpressionParser.java new file mode 100644 index 000000000..318bb6874 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CachingExpressionParser.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 the original author or authors. + * + * 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 + * + * https://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.springframework.data.mongodb.repository.support; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParseException; +import org.springframework.expression.ParserContext; + +/** + * Caching variant of {@link ExpressionParser}. This implementation does not support + * {@link #parseExpression(String, ParserContext) parsing with ParseContext}. + * + * @author Mark Paluch + * @since 3.1 + */ +class CachingExpressionParser implements ExpressionParser { + + private final ExpressionParser delegate; + private final Map cache = new ConcurrentHashMap<>(); + + public CachingExpressionParser(ExpressionParser delegate) { + this.delegate = delegate; + } + + /* + * (non-Javadoc) + * @see org.springframework.expression.ExpressionParser#parseExpression(java.lang.String) + */ + @Override + public Expression parseExpression(String expressionString) throws ParseException { + return cache.computeIfAbsent(expressionString, delegate::parseExpression); + } + + /* + * (non-Javadoc) + * @see org.springframework.expression.ExpressionParser#parseExpression(java.lang.String, org.springframework.expression.ParserContext) + */ + @Override + public Expression parseExpression(String expressionString, ParserContext context) throws ParseException { + throw new UnsupportedOperationException("Parsing using ParserContext is not supported"); + } +} 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 1cbccff76..ea0ba55db 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 @@ -44,6 +44,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -162,7 +163,8 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { private final MongoOperations operations; private final QueryMethodEvaluationContextProvider evaluationContextProvider; - MappingContext, MongoPersistentProperty> mappingContext; + private final MappingContext, MongoPersistentProperty> mappingContext; + private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER); public MongoQueryLookupStrategy(MongoOperations operations, QueryMethodEvaluationContextProvider evaluationContextProvider, @@ -186,14 +188,14 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport { if (namedQueries.hasQuery(namedQueryName)) { String namedQuery = namedQueries.getQuery(namedQueryName); - return new StringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER, + return new StringBasedMongoQuery(namedQuery, queryMethod, operations, expressionParser, evaluationContextProvider); } else if (queryMethod.hasAnnotatedAggregation()) { - return new StringBasedAggregation(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); + return new StringBasedAggregation(queryMethod, operations, expressionParser, evaluationContextProvider); } else if (queryMethod.hasAnnotatedQuery()) { - return new StringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); + return new StringBasedMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider); } else { - return new PartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); + return new PartTreeMongoQuery(queryMethod, operations, expressionParser, 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 6ab3ae0bb..f55f6ed8f 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 @@ -44,6 +44,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -161,6 +162,7 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup private final ReactiveMongoOperations operations; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final MappingContext, MongoPersistentProperty> mappingContext; + private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER); MongoQueryLookupStrategy(ReactiveMongoOperations operations, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider, @@ -184,15 +186,15 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup if (namedQueries.hasQuery(namedQueryName)) { String namedQuery = namedQueries.getQuery(namedQueryName); - return new ReactiveStringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER, + return new ReactiveStringBasedMongoQuery(namedQuery, queryMethod, operations, expressionParser, evaluationContextProvider); } else if (queryMethod.hasAnnotatedAggregation()) { - return new ReactiveStringBasedAggregation(queryMethod, operations, EXPRESSION_PARSER, + return new ReactiveStringBasedAggregation(queryMethod, operations, expressionParser, evaluationContextProvider); } else if (queryMethod.hasAnnotatedQuery()) { - return new ReactiveStringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); + return new ReactiveStringBasedMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider); } else { - return new ReactivePartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); + return new ReactivePartTreeMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider); } } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java index 519ff1dbd..73900d456 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java @@ -23,6 +23,7 @@ import org.springframework.data.spel.ExpressionDependencies; import org.springframework.data.util.Lazy; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.lang.Nullable; @@ -55,7 +56,7 @@ public class ParameterBindingContext { * @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval. * @since 2.2.3 */ - public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser, + public ParameterBindingContext(ValueProvider valueProvider, ExpressionParser expressionParser, Supplier evaluationContext) { this(valueProvider, new SpELExpressionEvaluator() { @@ -87,7 +88,7 @@ public class ParameterBindingContext { * @since 3.1 */ public static ParameterBindingContext forExpressions(ValueProvider valueProvider, - SpelExpressionParser expressionParser, Function contextFunction) { + ExpressionParser expressionParser, Function contextFunction) { return new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() { @Override diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index d0c9317f8..c773caa89 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -5,6 +5,7 @@ == What's New in Spring Data MongoDB 3.1 * <> enabled through `@EnableReactiveMongoAuditing`. `@EnableMongoAuditing` no longer registers `ReactiveAuditingEntityCallback`. +* Reactive SpEL support in `@Query` and `@Aggregation` query methods. [[new-features.3.0]] == What's New in Spring Data MongoDB 3.0 diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc index 48724e2b8..328a547b5 100644 --- a/src/main/asciidoc/reference/mongo-repositories.adoc +++ b/src/main/asciidoc/reference/mongo-repositories.adoc @@ -131,7 +131,7 @@ The preceding example creates an application context with Spring's unit test sup [[mongodb.repositories.queries]] == Query Methods -Most of the data access operations you usually trigger on a repository result in a query being executed against the MongoDB databases. Defining such a query is a matter of declaring a method on the repository interface, as the following example shows: +Most of the data access operations you usually trigger on a repository result in a query being executed against the MongoDB databases.Defining such a query is a matter of declaring a method on the repository interface, as the following example shows: .PersonRepository with query methods ==== @@ -445,7 +445,7 @@ results in `{ age : -1 }`. Using `Sort.by(ASC, "age")` overrides the defaults an Query strings and field definitions can be used together with SpEL expressions to create dynamic queries at runtime. SpEL expressions can provide predicate values and can be used to extend predicates with subdocuments. -Expressions expose method arguments through an array that contains all the arguments. The following query uses `[0]` +Expressions expose method arguments through an array that contains all the arguments.The following query uses `[0]` to declare the predicate value for `lastname` (which is equivalent to the `?0` parameter binding): [source,java] @@ -457,7 +457,7 @@ public interface PersonRepository extends MongoRepository { } ---- -Expressions can be used to invoke functions, evaluate conditionals, and construct values. SpEL expressions +Expressions can be used to invoke functions, evaluate conditionals, and construct values.SpEL expressions used in conjunction with JSON reveal a side-effect, because Map-like declarations inside of SpEL read like JSON, as the following example shows: [source,java] @@ -469,12 +469,12 @@ public interface PersonRepository extends MongoRepository { } ---- -SpEL in query strings can be a powerful way to enhance queries. However, they can also accept a broad range of unwanted arguments. +SpEL in query strings can be a powerful way to enhance queries.However, they can also accept a broad range of unwanted arguments. You should make sure to sanitize strings before passing them to the query to avoid unwanted changes to your query. Expression support is extensible through the Query SPI: `org.springframework.data.repository.query.spi.EvaluationContextExtension`. -The Query SPI can contribute properties and functions and can customize the root object. Extensions are retrieved from the application context -at the time of SpEL evaluation when the query is built. The following example shows how to use `EvaluationContextExtension`: +The Query SPI can contribute properties and functions and can customize the root object.Extensions are retrieved from the application context +at the time of SpEL evaluation when the query is built.The following example shows how to use `EvaluationContextExtension`: [source,java] ---- @@ -492,13 +492,15 @@ public class SampleEvaluationContextExtension extends EvaluationContextExtension } ---- -NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration -to pick up Query SPI extensions. +NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration to pick up Query SPI extensions. + +NOTE: Reactive query methods can make use of `org.springframework.data.spel.spi.ReactiveEvaluationContextExtension`. [[mongodb.repositories.queries.type-safe]] === Type-safe Query Methods -MongoDB repository support integrates with the http://www.querydsl.com/[Querydsl] project, which provides a way to perform type-safe queries. To quote from the project description, "Instead of writing queries as inline strings or externalizing them into XML files they are constructed via a fluent API." It provides the following features: +MongoDB repository support integrates with the http://www.querydsl.com/[Querydsl] project, which provides a way to perform type-safe queries. +To quote from the project description, "Instead of writing queries as inline strings or externalizing them into XML files they are constructed via a fluent API." It provides the following features: * Code completion in the IDE (all properties, methods, and operations can be expanded in your favorite Java IDE). * Almost no syntactically invalid queries allowed (type-safe on all levels). @@ -519,7 +521,7 @@ Page page = repository.findAll(person.lastname.contains("a"), PageRequest.of(0, 2, Direction.ASC, "lastname")); ---- -`QPerson` is a class that is generated by the Java annotation post-processing tool. It is a `Predicate` that lets you write type-safe queries. Notice that there are no strings in the query other than the `C0123` value. +`QPerson` is a class that is generated by the Java annotation post-processing tool.It is a `Predicate` that lets you write type-safe queries.Notice that there are no strings in the query other than the `C0123` value. You can use the generated `Predicate` class by using the `QuerydslPredicateExecutor` interface, which the following listing shows: