Skip to content

Add support for Value Expressions for Repository Query methods #4683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 9 commits into from
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,13 @@
import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistry;

import org.springframework.core.env.StandardEnvironment;
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.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 +49,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 +80,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 +92,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 +111,35 @@ 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}
*/
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,31 @@ 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.3
*/
protected ValueExpressionEvaluator getExpressionEvaluatorFor(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);

}
};
}

/**
Expand Down
Loading