diff --git a/pom.xml b/pom.xml
index 3fa54424af..78fb3035d0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 4.4.0-SNAPSHOT
+ 4.4.0-GH-4677-SNAPSHOT
pom
Spring Data MongoDB
@@ -26,7 +26,7 @@
multi
spring-data-mongodb
- 3.4.0-SNAPSHOT
+ 3.4.0-GH-3049-SNAPSHOT
5.1.4
${mongo}
1.19
diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml
index a3dc49f892..ae7137cdd5 100644
--- a/spring-data-mongodb-benchmarks/pom.xml
+++ b/spring-data-mongodb-benchmarks/pom.xml
@@ -7,7 +7,7 @@
org.springframework.data
spring-data-mongodb-parent
- 4.4.0-SNAPSHOT
+ 4.4.0-GH-4677-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index acdc13437d..227a13850d 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -15,7 +15,7 @@
org.springframework.data
spring-data-mongodb-parent
- 4.4.0-SNAPSHOT
+ 4.4.0-GH-4677-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index fafe9c8793..801c987059 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -13,7 +13,7 @@
org.springframework.data
spring-data-mongodb-parent
- 4.4.0-SNAPSHOT
+ 4.4.0-GH-4677-SNAPSHOT
../pom.xml
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 61bfa0f7b3..1c817b17f2 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
@@ -21,7 +21,12 @@
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistry;
+import org.springframework.core.env.StandardEnvironment;
+import org.springframework.data.expression.ValueEvaluationContextProvider;
+import org.springframework.data.expression.ValueExpression;
+import org.springframework.data.expression.ValueExpressionParser;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
+import org.springframework.data.mapping.model.ValueExpressionEvaluator;
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.ExecutableFindOperation.FindWithQuery;
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind;
@@ -43,12 +48,15 @@
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
+import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
+import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.spel.ExpressionDependencies;
import org.springframework.data.util.Lazy;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -71,10 +79,10 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
private final MongoOperations operations;
private final ExecutableFind> executableFind;
private final ExecutableUpdate> executableUpdate;
- private final ExpressionParser expressionParser;
- private final QueryMethodEvaluationContextProvider evaluationContextProvider;
private final Lazy codec = Lazy
.of(() -> new ParameterBindingDocumentCodec(getCodecRegistry()));
+ private final ValueExpressionDelegate valueExpressionDelegate;
+ private final ValueEvaluationContextProvider valueEvaluationContextProvider;
/**
* Creates a new {@link AbstractMongoQuery} from the given {@link MongoQueryMethod} and {@link MongoOperations}.
@@ -83,7 +91,9 @@ public abstract class AbstractMongoQuery implements RepositoryQuery {
* @param operations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
+ * @deprecated use the constructor version with {@link ValueExpressionDelegate}
*/
+ @Deprecated(since = "4.4.0")
public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, ExpressionParser expressionParser,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
@@ -100,10 +110,36 @@ public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, E
this.executableFind = operations.query(type);
this.executableUpdate = operations.update(type);
- this.expressionParser = expressionParser;
- this.evaluationContextProvider = evaluationContextProvider;
+ this.valueExpressionDelegate = new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), ValueExpressionParser.create(() -> expressionParser));
+ this.valueEvaluationContextProvider = valueExpressionDelegate.createValueContextProvider(method.getParameters());
}
+ /**
+ * 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 delegate must not be {@literal null}
+ * @since 4.4.0
+ */
+ public AbstractMongoQuery(MongoQueryMethod method, MongoOperations operations, ValueExpressionDelegate delegate) {
+
+ Assert.notNull(operations, "MongoOperations must not be null");
+ Assert.notNull(method, "MongoQueryMethod must not be null");
+
+ this.method = method;
+ this.operations = operations;
+
+ MongoEntityMetadata> metadata = method.getEntityInformation();
+ Class> type = metadata.getCollectionEntity().getType();
+
+ this.executableFind = operations.query(type);
+ this.executableUpdate = operations.update(type);
+ this.valueExpressionDelegate = delegate;
+ this.valueEvaluationContextProvider = delegate.createValueContextProvider(method.getParameters());
+ }
+
+ @Override
public MongoQueryMethod getQueryMethod() {
return method;
}
@@ -243,7 +279,7 @@ Query applyAnnotatedDefaultSortIfPresent(Query query) {
Query applyAnnotatedCollationIfPresent(Query query, ConvertingParameterAccessor accessor) {
return QueryUtils.applyCollation(query, method.hasAnnotatedCollation() ? method.getAnnotatedCollation() : null,
- accessor, getQueryMethod().getParameters(), expressionParser, evaluationContextProvider);
+ accessor, getExpressionEvaluatorFor(accessor));
}
/**
@@ -346,10 +382,7 @@ private Document bindParameters(String source, ConvertingParameterAccessor acces
*/
protected ParameterBindingContext prepareBindingContext(String source, ConvertingParameterAccessor accessor) {
- ExpressionDependencies dependencies = getParameterBindingCodec().captureExpressionDependencies(source,
- accessor::getBindableValue, expressionParser);
-
- SpELExpressionEvaluator evaluator = getSpELExpressionEvaluatorFor(dependencies, accessor);
+ ValueExpressionEvaluator evaluator = getExpressionEvaluatorFor(accessor);
return new ParameterBindingContext(accessor::getBindableValue, evaluator);
}
@@ -374,8 +407,19 @@ protected ParameterBindingDocumentCodec getParameterBindingCodec() {
protected SpELExpressionEvaluator getSpELExpressionEvaluatorFor(ExpressionDependencies dependencies,
ConvertingParameterAccessor accessor) {
- return new DefaultSpELExpressionEvaluator(expressionParser, evaluationContextProvider
- .getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues(), dependencies));
+ return new DefaultSpELExpressionEvaluator(new SpelExpressionParser(), valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), dependencies).getEvaluationContext());
+ }
+
+ /**
+ * Obtain a {@link ValueExpressionEvaluator} suitable to evaluate expressions.
+ *
+ * @param accessor must not be {@literal null}.
+ * @return the {@link ValueExpressionEvaluator}.
+ * @since 4.4.0
+ */
+ protected ValueExpressionEvaluator getExpressionEvaluatorFor(MongoParameterAccessor accessor) {
+ return new ValueExpressionDelegateValueExpressionEvaluator(valueExpressionDelegate, (ValueExpression expression) ->
+ valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(), expression.getExpressionDependencies()));
}
/**
@@ -424,4 +468,5 @@ protected CodecRegistry getCodecRegistry() {
* @since 2.0.4
*/
protected abstract boolean isLimiting();
+
}
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 e653f1b39a..15ff5e5e23 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
@@ -26,8 +26,15 @@
import org.reactivestreams.Publisher;
import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.env.StandardEnvironment;
+import org.springframework.data.expression.ReactiveValueEvaluationContextProvider;
+import org.springframework.data.expression.ValueEvaluationContext;
+import org.springframework.data.expression.ValueEvaluationContextProvider;
+import org.springframework.data.expression.ValueExpression;
+import org.springframework.data.expression.ValueExpressionParser;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
+import org.springframework.data.mapping.model.ValueExpressionEvaluator;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithProjection;
import org.springframework.data.mongodb.core.ReactiveFindOperation.FindWithQuery;
@@ -48,18 +55,22 @@
import org.springframework.data.mongodb.util.json.ParameterBindingContext;
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
import org.springframework.data.repository.query.ParameterAccessor;
+import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
+import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.spel.ExpressionDependencies;
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;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import com.mongodb.MongoClientSettings;
+import reactor.util.function.Tuple2;
/**
* Base class for reactive {@link RepositoryQuery} implementations for MongoDB.
@@ -76,8 +87,8 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
private final EntityInstantiators instantiators;
private final FindWithProjection> findOperationWithProjection;
private final ReactiveUpdate> updateOps;
- private final ExpressionParser expressionParser;
- private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
+ private final ValueExpressionDelegate valueExpressionDelegate;
+ private final ReactiveValueEvaluationContextProvider valueEvaluationContextProvider;
/**
* Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
@@ -87,7 +98,9 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
* @param operations must not be {@literal null}.
* @param expressionParser must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
+ * @deprecated use the constructor version with {@link ValueExpressionDelegate}
*/
+ @Deprecated(since = "4.4.0")
public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
ExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
@@ -99,20 +112,57 @@ public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongo
this.method = method;
this.operations = operations;
this.instantiators = new EntityInstantiators();
- this.expressionParser = expressionParser;
- this.evaluationContextProvider = evaluationContextProvider;
+ this.valueExpressionDelegate = new ValueExpressionDelegate(new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), evaluationContextProvider.getEvaluationContextProvider()), ValueExpressionParser.create(() -> expressionParser));
MongoEntityMetadata> metadata = method.getEntityInformation();
Class> type = metadata.getCollectionEntity().getType();
this.findOperationWithProjection = operations.query(type);
this.updateOps = operations.update(type);
+ ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate.createValueContextProvider(
+ method.getParameters());
+ Assert.isInstanceOf(ReactiveValueEvaluationContextProvider.class, valueContextProvider, "ValueEvaluationContextProvider must be reactive");
+ this.valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider) valueContextProvider;
}
+ /**
+ * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
+ * {@link MongoOperations}.
+ *
+ * @param method must not be {@literal null}.
+ * @param operations must not be {@literal null}.
+ * @param delegate must not be {@literal null}.
+ * @since 4.4.0
+ */
+ public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
+ ValueExpressionDelegate delegate) {
+
+ Assert.notNull(method, "MongoQueryMethod must not be null");
+ Assert.notNull(operations, "ReactiveMongoOperations must not be null");
+ Assert.notNull(delegate, "ValueExpressionDelegate must not be null");
+
+ this.method = method;
+ this.operations = operations;
+ this.instantiators = new EntityInstantiators();
+ this.valueExpressionDelegate = delegate;
+
+ MongoEntityMetadata> metadata = method.getEntityInformation();
+ Class> type = metadata.getCollectionEntity().getType();
+
+ this.findOperationWithProjection = operations.query(type);
+ this.updateOps = operations.update(type);
+ ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate.createValueContextProvider(
+ method.getParameters());
+ Assert.isInstanceOf(ReactiveValueEvaluationContextProvider.class, valueContextProvider, "ValueEvaluationContextProvider must be reactive");
+ this.valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider) valueContextProvider;
+ }
+
+ @Override
public MongoQueryMethod getQueryMethod() {
return method;
}
+ @Override
public Publisher