Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.4.0-SNAPSHOT</version>
<version>4.4.0-GH-4677-SNAPSHOT</version>
<packaging>pom</packaging>

<name>Spring Data MongoDB</name>
Expand All @@ -26,7 +26,7 @@
<properties>
<project.type>multi</project.type>
<dist.id>spring-data-mongodb</dist.id>
<springdata.commons>3.4.0-SNAPSHOT</springdata.commons>
<springdata.commons>3.4.0-GH-3049-SNAPSHOT</springdata.commons>
<mongo>5.1.4</mongo>
<mongo.reactivestreams>${mongo}</mongo.reactivestreams>
<jmh.version>1.19</jmh.version>
Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-benchmarks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.4.0-SNAPSHOT</version>
<version>4.4.0-GH-4677-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.4.0-SNAPSHOT</version>
<version>4.4.0-GH-4677-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion spring-data-mongodb/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
<parent>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb-parent</artifactId>
<version>4.4.0-SNAPSHOT</version>
<version>4.4.0-GH-4677-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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<ParameterBindingDocumentCodec> 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}.
Expand All @@ -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) {

Expand All @@ -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;
}
Expand Down Expand Up @@ -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));
}

/**
Expand Down Expand Up @@ -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);
}

Expand All @@ -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()));
}

/**
Expand Down Expand Up @@ -424,4 +468,5 @@ protected CodecRegistry getCodecRegistry() {
* @since 2.0.4
*/
protected abstract boolean isLimiting();

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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) {

Expand All @@ -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<Object> execute(Object[] parameters) {

return method.hasReactiveWrapperParameter() ? executeDeferred(parameters)
Expand Down Expand Up @@ -269,7 +319,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, getValueExpressionEvaluator(accessor));
}

/**
Expand Down Expand Up @@ -381,19 +431,19 @@ private Mono<AggregationOperation> computePipelineStage(String source, MongoPara
bsonString -> AbstractReactiveMongoQuery.this.decode(evaluator, bsonString, accessor, codec)));
}

private Mono<SpELExpressionEvaluator> expressionEvaluator(String source, MongoParameterAccessor accessor,
ParameterBindingDocumentCodec codec) {
private Mono<Tuple2<ValueExpressionEvaluator, ParameterBindingDocumentCodec>> expressionEvaluator(String source,
MongoParameterAccessor accessor, ParameterBindingDocumentCodec codec) {

ExpressionDependencies dependencies = codec.captureExpressionDependencies(source, accessor::getBindableValue,
expressionParser);
return getSpelEvaluatorFor(dependencies, accessor);
valueExpressionDelegate.getValueExpressionParser());
return getValueExpressionEvaluatorLater(dependencies, accessor).zipWith(Mono.just(codec));
}

private Document decode(SpELExpressionEvaluator expressionEvaluator, String source, MongoParameterAccessor accessor,
private Document decode(Tuple2<ValueExpressionEvaluator, ParameterBindingDocumentCodec> expressionEvaluator, String source, MongoParameterAccessor accessor,
ParameterBindingDocumentCodec codec) {

ParameterBindingContext bindingContext = new ParameterBindingContext(accessor::getBindableValue,
expressionEvaluator);
expressionEvaluator.getT1());
return codec.decode(source, bindingContext);
}

Expand All @@ -415,17 +465,54 @@ protected Mono<ParameterBindingDocumentCodec> getParameterBindingCodec() {
* @param accessor must not be {@literal null}.
* @return a {@link Mono} emitting the {@link SpELExpressionEvaluator} when ready.
* @since 3.4
* @deprecated since 4.4.0, use
* {@link #getValueExpressionEvaluatorLater(ExpressionDependencies, MongoParameterAccessor)} instead
*/
@Deprecated(since = "4.4.0")
protected Mono<SpELExpressionEvaluator> getSpelEvaluatorFor(ExpressionDependencies dependencies,
MongoParameterAccessor accessor) {

return evaluationContextProvider
.getEvaluationContextLater(getQueryMethod().getParameters(), accessor.getValues(), dependencies)
.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser,
evaluationContext))
return valueEvaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies)
.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(
new SpelExpressionParser(), evaluationContext.getEvaluationContext()))
.defaultIfEmpty(DefaultSpELExpressionEvaluator.unsupported());
}

/**
* Obtain a {@link ValueExpressionEvaluator} suitable to evaluate expressions.
*
* @param accessor must not be {@literal null}.
* @since 4.3
*/
ValueExpressionEvaluator getValueExpressionEvaluator(MongoParameterAccessor accessor) {

return new ValueExpressionEvaluator() {

@Override
public <T> T evaluate(String expressionString) {
ValueExpression expression = valueExpressionDelegate.parse(expressionString);
ValueEvaluationContext evaluationContext = valueEvaluationContextProvider.getEvaluationContext(accessor.getValues(),
expression.getExpressionDependencies());
return (T) expression.evaluate(evaluationContext);
}
};
}

/**
* Obtain a {@link Mono publisher} emitting the {@link ValueExpressionEvaluator} suitable to evaluate expressions
* backed by the given dependencies.
*
* @param dependencies must not be {@literal null}.
* @param accessor must not be {@literal null}.
* @return a {@link Mono} emitting the {@link ValueExpressionEvaluator} when ready.
* @since 4.3
*/
protected Mono<ValueExpressionEvaluator> getValueExpressionEvaluatorLater(ExpressionDependencies dependencies,
MongoParameterAccessor accessor) {

return valueEvaluationContextProvider.getEvaluationContextLater(accessor.getValues(), dependencies)
.map(evaluationContext -> new ValueExpressionDelegateValueExpressionEvaluator(valueExpressionDelegate, valueExpression -> evaluationContext));
}

/**
* @return a {@link Mono} emitting the {@link CodecRegistry} when ready.
* @since 2.4
Expand Down
Loading