Browse Source

DATAMONGO-1894 - Introduce cached ExpressionParser.

Original Pull Request: #874
pull/879/head
Mark Paluch 6 years ago committed by Christoph Strobl
parent
commit
41607b10d0
  1. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractMongoQuery.java
  2. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
  3. 7
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AggregationUtils.java
  4. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/CollationUtils.java
  5. 25
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/DefaultSpELExpressionEvaluator.java
  6. 34
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/NoOpExpressionEvaluator.java
  7. 4
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQuery.java
  8. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/QueryUtils.java
  9. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactivePartTreeMongoQuery.java
  10. 13
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedAggregation.java
  11. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ReactiveStringBasedMongoQuery.java
  12. 6
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedAggregation.java
  13. 8
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java
  14. 59
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/CachingExpressionParser.java
  15. 12
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/MongoRepositoryFactory.java
  16. 10
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/support/ReactiveMongoRepositoryFactory.java
  17. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/json/ParameterBindingContext.java
  18. 1
      src/main/asciidoc/new-features.adoc
  19. 8
      src/main/asciidoc/reference/mongo-repositories.adoc

5
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.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -47,7 +48,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
private final MongoQueryMethod method; private final MongoQueryMethod method;
private final MongoOperations operations; private final MongoOperations operations;
private final ExecutableFind<?> executableFind; private final ExecutableFind<?> executableFind;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final QueryMethodEvaluationContextProvider evaluationContextProvider;
/** /**
@ -58,7 +59,7 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
* @param expressionParser must not be {@literal null}. * @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider 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) { QueryMethodEvaluationContextProvider evaluationContextProvider) {
Assert.notNull(operations, "MongoOperations must not be null!"); Assert.notNull(operations, "MongoOperations must not be null!");

5
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.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.util.TypeInformation; import org.springframework.data.util.TypeInformation;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -54,7 +55,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
private final ReactiveMongoOperations operations; private final ReactiveMongoOperations operations;
private final EntityInstantiators instantiators; private final EntityInstantiators instantiators;
private final FindWithProjection<?> findOperationWithProjection; private final FindWithProjection<?> findOperationWithProjection;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
/** /**
@ -67,7 +68,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
* @param evaluationContextProvider 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, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
Assert.notNull(method, "MongoQueryMethod must not be null!"); Assert.notNull(method, "MongoQueryMethod must not be null!");
Assert.notNull(operations, "ReactiveMongoOperations must not be null!"); Assert.notNull(operations, "ReactiveMongoOperations must not be null!");

7
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.ParameterBindingContext;
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -64,12 +65,12 @@ abstract class AggregationUtils {
* @param accessor must not be {@literal null}. * @param accessor must not be {@literal null}.
* @return the {@link Query} having proper {@link Collation}. * @return the {@link Query} having proper {@link Collation}.
* @see AggregationOptions#getCollation() * @see AggregationOptions#getCollation()
* @see CollationUtils#computeCollation(String, ConvertingParameterAccessor, MongoParameters, SpelExpressionParser, * @see CollationUtils#computeCollation(String, ConvertingParameterAccessor, MongoParameters, ExpressionParser,
* QueryMethodEvaluationContextProvider) * QueryMethodEvaluationContextProvider)
*/ */
static AggregationOptions.Builder applyCollation(AggregationOptions.Builder builder, static AggregationOptions.Builder applyCollation(AggregationOptions.Builder builder,
@Nullable String collationExpression, ConvertingParameterAccessor accessor, MongoParameters parameters, @Nullable String collationExpression, ConvertingParameterAccessor accessor, MongoParameters parameters,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser, Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser,
evaluationContextProvider); evaluationContextProvider);
@ -119,7 +120,7 @@ abstract class AggregationUtils {
* @return * @return
*/ */
static List<AggregationOperation> computePipeline(MongoQueryMethod method, ConvertingParameterAccessor accessor, static List<AggregationOperation> computePipeline(MongoQueryMethod method, ConvertingParameterAccessor accessor,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser, ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser,
() -> evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues())); () -> evaluationContextProvider.getEvaluationContext(method.getParameters(), accessor.getValues()));

3
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.ParameterBindingContext;
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec; import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.NumberUtils; import org.springframework.util.NumberUtils;
@ -59,7 +60,7 @@ abstract class CollationUtils {
*/ */
@Nullable @Nullable
static Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor, static Collation computeCollation(@Nullable String collationExpression, ConvertingParameterAccessor accessor,
MongoParameters parameters, SpelExpressionParser expressionParser, MongoParameters parameters, ExpressionParser expressionParser,
QueryMethodEvaluationContextProvider evaluationContextProvider) { QueryMethodEvaluationContextProvider evaluationContextProvider) {
if (accessor.getCollation() != null) { if (accessor.getCollation() != null) {

25
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; 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) * (non-Javadoc)
* @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String) * @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String)
@ -44,4 +53,20 @@ class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
public <T> T evaluate(String expression) { public <T> T evaluate(String expression) {
return (T) parser.parseExpression(expression).getValue(context, Object.class); 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> T evaluate(String expression) {
throw new UnsupportedOperationException("Expression evaluation not supported");
}
}
} }

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

@ -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> T evaluate(String expression) {
throw new UnsupportedOperationException("Expression evaluation not supported");
}
}

4
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.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.PartTree; 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; import org.springframework.util.StringUtils;
/** /**
@ -59,7 +59,7 @@ public class PartTreeMongoQuery extends AbstractMongoQuery {
* @param evaluationContextProvider 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) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);

3
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.Collation;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -76,7 +77,7 @@ class QueryUtils {
* @since 2.2 * @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, MongoParameters parameters, ExpressionParser expressionParser,
QueryMethodEvaluationContextProvider evaluationContextProvider) { QueryMethodEvaluationContextProvider evaluationContextProvider) {
Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser, Collation collation = CollationUtils.computeCollation(collationExpression, accessor, parameters, expressionParser,

3
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.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.ReturnedType;
import org.springframework.data.repository.query.parser.PartTree; import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -59,7 +60,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
* @param evaluationContextProvider 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, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);

13
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.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.spel.ExpressionDependencies; import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -54,7 +55,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec(); private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec();
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
private final ReactiveMongoOperations reactiveMongoOperations; private final ReactiveMongoOperations reactiveMongoOperations;
private final MongoConverter mongoConverter; private final MongoConverter mongoConverter;
@ -66,7 +67,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method, public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method,
ReactiveMongoOperations reactiveMongoOperations, SpelExpressionParser expressionParser, ReactiveMongoOperations reactiveMongoOperations, ExpressionParser expressionParser,
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider); super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider);
@ -118,11 +119,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
}); });
} }
if (method.isCollectionQuery()) { return method.isCollectionQuery() ? flux : flux.next();
return flux;
} else {
return flux.next();
}
}); });
} }
@ -145,7 +142,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
it -> evaluationContextProvider.getEvaluationContextLater(method.getParameters(), accessor.getValues(), it)) it -> evaluationContextProvider.getEvaluationContextLater(method.getParameters(), accessor.getValues(), it))
.map(evaluationContext -> evaluationContext .map(evaluationContext -> evaluationContext
.map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it))) .map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it)))
.orElseGet(() -> Mono.just(NoOpExpressionEvaluator.INSTANCE)); .orElseGet(() -> Mono.just(DefaultSpELExpressionEvaluator.unsupported()));
Mono<AggregationOperation> stage = evaluator.map(it -> { Mono<AggregationOperation> stage = evaluator.map(it -> {

8
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 query;
private final String fieldSpec; private final String fieldSpec;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
private final boolean isCountQuery; private final boolean isCountQuery;
@ -70,7 +70,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations, public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider); this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider);
} }
@ -85,7 +85,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
* @param expressionParser must not be {@literal null}. * @param expressionParser must not be {@literal null}.
*/ */
public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method, public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method,
ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser, ReactiveMongoOperations mongoOperations, ExpressionParser expressionParser,
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) { ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);
@ -153,7 +153,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
accessor.getValues(), it)) accessor.getValues(), it))
.map(evaluationContext -> evaluationContext .map(evaluationContext -> evaluationContext
.map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it))) .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)); return evaluator.map(it -> new ParameterBindingContext(accessor::getBindableValue, it));
} }

6
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.mongodb.core.query.Query;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.ExpressionParser;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
/** /**
@ -43,7 +43,7 @@ public class StringBasedAggregation extends AbstractMongoQuery {
private final MongoOperations mongoOperations; private final MongoOperations mongoOperations;
private final MongoConverter mongoConverter; private final MongoConverter mongoConverter;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final QueryMethodEvaluationContextProvider evaluationContextProvider;
/** /**
@ -55,7 +55,7 @@ public class StringBasedAggregation extends AbstractMongoQuery {
* @param evaluationContextProvider * @param evaluationContextProvider
*/ */
public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOperations, public StringBasedAggregation(MongoQueryMethod method, MongoOperations mongoOperations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);
this.mongoOperations = mongoOperations; this.mongoOperations = mongoOperations;

8
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 String fieldSpec;
private final ParameterBindingDocumentCodec codec; private final ParameterBindingDocumentCodec codec;
private final SpelExpressionParser expressionParser; private final ExpressionParser expressionParser;
private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final QueryMethodEvaluationContextProvider evaluationContextProvider;
private final boolean isCountQuery; private final boolean isCountQuery;
@ -71,7 +71,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
* @param evaluationContextProvider must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}.
*/ */
public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations, public StringBasedMongoQuery(MongoQueryMethod method, MongoOperations mongoOperations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider); this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider);
} }
@ -85,7 +85,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
* @param expressionParser must not be {@literal null}. * @param expressionParser must not be {@literal null}.
*/ */
public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations, public StringBasedMongoQuery(String query, MongoQueryMethod method, MongoOperations mongoOperations,
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) { ExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
super(method, mongoOperations, expressionParser, evaluationContextProvider); super(method, mongoOperations, expressionParser, evaluationContextProvider);
@ -151,7 +151,7 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
accessor.getValues(), it)) accessor.getValues(), it))
.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, .map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser,
evaluationContext)) evaluationContext))
.orElse(NoOpExpressionEvaluator.INSTANCE); .orElse(DefaultSpELExpressionEvaluator.unsupported());
return new ParameterBindingContext(accessor::getBindableValue, evaluator); return new ParameterBindingContext(accessor::getBindableValue, evaluator);
} }

59
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<String, Expression> 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");
}
}

12
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.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -162,7 +163,8 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
private final MongoOperations operations; private final MongoOperations operations;
private final QueryMethodEvaluationContextProvider evaluationContextProvider; private final QueryMethodEvaluationContextProvider evaluationContextProvider;
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext; private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER);
public MongoQueryLookupStrategy(MongoOperations operations, public MongoQueryLookupStrategy(MongoOperations operations,
QueryMethodEvaluationContextProvider evaluationContextProvider, QueryMethodEvaluationContextProvider evaluationContextProvider,
@ -186,14 +188,14 @@ public class MongoRepositoryFactory extends RepositoryFactorySupport {
if (namedQueries.hasQuery(namedQueryName)) { if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName); String namedQuery = namedQueries.getQuery(namedQueryName);
return new StringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER, return new StringBasedMongoQuery(namedQuery, queryMethod, operations, expressionParser,
evaluationContextProvider); evaluationContextProvider);
} else if (queryMethod.hasAnnotatedAggregation()) { } else if (queryMethod.hasAnnotatedAggregation()) {
return new StringBasedAggregation(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new StringBasedAggregation(queryMethod, operations, expressionParser, evaluationContextProvider);
} else if (queryMethod.hasAnnotatedQuery()) { } else if (queryMethod.hasAnnotatedQuery()) {
return new StringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new StringBasedMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
} else { } else {
return new PartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new PartTreeMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
} }
} }
} }

10
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.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -161,6 +162,7 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
private final ReactiveMongoOperations operations; private final ReactiveMongoOperations operations;
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider; private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext; private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
private final ExpressionParser expressionParser = new CachingExpressionParser(EXPRESSION_PARSER);
MongoQueryLookupStrategy(ReactiveMongoOperations operations, MongoQueryLookupStrategy(ReactiveMongoOperations operations,
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider,
@ -184,15 +186,15 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
if (namedQueries.hasQuery(namedQueryName)) { if (namedQueries.hasQuery(namedQueryName)) {
String namedQuery = namedQueries.getQuery(namedQueryName); String namedQuery = namedQueries.getQuery(namedQueryName);
return new ReactiveStringBasedMongoQuery(namedQuery, queryMethod, operations, EXPRESSION_PARSER, return new ReactiveStringBasedMongoQuery(namedQuery, queryMethod, operations, expressionParser,
evaluationContextProvider); evaluationContextProvider);
} else if (queryMethod.hasAnnotatedAggregation()) { } else if (queryMethod.hasAnnotatedAggregation()) {
return new ReactiveStringBasedAggregation(queryMethod, operations, EXPRESSION_PARSER, return new ReactiveStringBasedAggregation(queryMethod, operations, expressionParser,
evaluationContextProvider); evaluationContextProvider);
} else if (queryMethod.hasAnnotatedQuery()) { } else if (queryMethod.hasAnnotatedQuery()) {
return new ReactiveStringBasedMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new ReactiveStringBasedMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
} else { } else {
return new ReactivePartTreeMongoQuery(queryMethod, operations, EXPRESSION_PARSER, evaluationContextProvider); return new ReactivePartTreeMongoQuery(queryMethod, operations, expressionParser, evaluationContextProvider);
} }
} }
} }

5
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.data.util.Lazy;
import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -55,7 +56,7 @@ public class ParameterBindingContext {
* @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval. * @param evaluationContext a {@link Supplier} for {@link Lazy} context retrieval.
* @since 2.2.3 * @since 2.2.3
*/ */
public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser, public ParameterBindingContext(ValueProvider valueProvider, ExpressionParser expressionParser,
Supplier<EvaluationContext> evaluationContext) { Supplier<EvaluationContext> evaluationContext) {
this(valueProvider, new SpELExpressionEvaluator() { this(valueProvider, new SpELExpressionEvaluator() {
@ -87,7 +88,7 @@ public class ParameterBindingContext {
* @since 3.1 * @since 3.1
*/ */
public static ParameterBindingContext forExpressions(ValueProvider valueProvider, public static ParameterBindingContext forExpressions(ValueProvider valueProvider,
SpelExpressionParser expressionParser, Function<ExpressionDependencies, EvaluationContext> contextFunction) { ExpressionParser expressionParser, Function<ExpressionDependencies, EvaluationContext> contextFunction) {
return new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() { return new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() {
@Override @Override

1
src/main/asciidoc/new-features.adoc

@ -5,6 +5,7 @@
== What's New in Spring Data MongoDB 3.1 == What's New in Spring Data MongoDB 3.1
* <<mongo.auditing,Reactive auditing>> enabled through `@EnableReactiveMongoAuditing`. `@EnableMongoAuditing` no longer registers `ReactiveAuditingEntityCallback`. * <<mongo.auditing,Reactive auditing>> enabled through `@EnableReactiveMongoAuditing`. `@EnableMongoAuditing` no longer registers `ReactiveAuditingEntityCallback`.
* Reactive SpEL support in `@Query` and `@Aggregation` query methods.
[[new-features.3.0]] [[new-features.3.0]]
== What's New in Spring Data MongoDB 3.0 == What's New in Spring Data MongoDB 3.0

8
src/main/asciidoc/reference/mongo-repositories.adoc

@ -492,13 +492,15 @@ public class SampleEvaluationContextExtension extends EvaluationContextExtension
} }
---- ----
NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration NOTE: Bootstrapping `MongoRepositoryFactory` yourself is not application context-aware and requires further configuration to pick up Query SPI extensions.
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]] [[mongodb.repositories.queries.type-safe]]
=== Type-safe Query Methods === 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). * 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). * Almost no syntactically invalid queries allowed (type-safe on all levels).

Loading…
Cancel
Save