2626import org .reactivestreams .Publisher ;
2727
2828import org .springframework .core .convert .converter .Converter ;
29+ import org .springframework .core .env .StandardEnvironment ;
30+ import org .springframework .data .expression .ReactiveValueEvaluationContextProvider ;
31+ import org .springframework .data .expression .ValueEvaluationContext ;
32+ import org .springframework .data .expression .ValueEvaluationContextProvider ;
33+ import org .springframework .data .expression .ValueExpression ;
34+ import org .springframework .data .expression .ValueExpressionParser ;
2935import org .springframework .data .mapping .model .EntityInstantiators ;
3036import org .springframework .data .mapping .model .SpELExpressionEvaluator ;
37+ import org .springframework .data .mapping .model .ValueExpressionEvaluator ;
3138import org .springframework .data .mongodb .core .MongoOperations ;
3239import org .springframework .data .mongodb .core .ReactiveFindOperation .FindWithProjection ;
3340import org .springframework .data .mongodb .core .ReactiveFindOperation .FindWithQuery ;
4855import org .springframework .data .mongodb .util .json .ParameterBindingContext ;
4956import org .springframework .data .mongodb .util .json .ParameterBindingDocumentCodec ;
5057import org .springframework .data .repository .query .ParameterAccessor ;
58+ import org .springframework .data .repository .query .QueryMethodValueEvaluationContextAccessor ;
5159import org .springframework .data .repository .query .ReactiveQueryMethodEvaluationContextProvider ;
5260import org .springframework .data .repository .query .RepositoryQuery ;
5361import org .springframework .data .repository .query .ResultProcessor ;
62+ import org .springframework .data .repository .query .ValueExpressionDelegate ;
5463import org .springframework .data .spel .ExpressionDependencies ;
5564import org .springframework .data .util .TypeInformation ;
5665import org .springframework .expression .ExpressionParser ;
66+ import org .springframework .expression .spel .standard .SpelExpressionParser ;
5767import org .springframework .lang .Nullable ;
5868import org .springframework .util .Assert ;
5969import org .springframework .util .ObjectUtils ;
6070import org .springframework .util .StringUtils ;
6171
6272import com .mongodb .MongoClientSettings ;
73+ import reactor .util .function .Tuple2 ;
6374
6475/**
6576 * Base class for reactive {@link RepositoryQuery} implementations for MongoDB.
@@ -76,8 +87,8 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
7687 private final EntityInstantiators instantiators ;
7788 private final FindWithProjection <?> findOperationWithProjection ;
7889 private final ReactiveUpdate <?> updateOps ;
79- private final ExpressionParser expressionParser ;
80- private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider ;
90+ private final ValueExpressionDelegate valueExpressionDelegate ;
91+ private final ReactiveValueEvaluationContextProvider valueEvaluationContextProvider ;
8192
8293 /**
8394 * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
@@ -87,7 +98,9 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
8798 * @param operations must not be {@literal null}.
8899 * @param expressionParser must not be {@literal null}.
89100 * @param evaluationContextProvider must not be {@literal null}.
101+ * @deprecated use the constructor version with {@link ValueExpressionDelegate}
90102 */
103+ @ Deprecated (since = "4.4.0" )
91104 public AbstractReactiveMongoQuery (ReactiveMongoQueryMethod method , ReactiveMongoOperations operations ,
92105 ExpressionParser expressionParser , ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider ) {
93106
@@ -99,20 +112,57 @@ public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongo
99112 this .method = method ;
100113 this .operations = operations ;
101114 this .instantiators = new EntityInstantiators ();
102- this .expressionParser = expressionParser ;
103- this .evaluationContextProvider = evaluationContextProvider ;
115+ this .valueExpressionDelegate = new ValueExpressionDelegate (new QueryMethodValueEvaluationContextAccessor (new StandardEnvironment (), evaluationContextProvider .getEvaluationContextProvider ()), ValueExpressionParser .create (() -> expressionParser ));
104116
105117 MongoEntityMetadata <?> metadata = method .getEntityInformation ();
106118 Class <?> type = metadata .getCollectionEntity ().getType ();
107119
108120 this .findOperationWithProjection = operations .query (type );
109121 this .updateOps = operations .update (type );
122+ ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate .createValueContextProvider (
123+ method .getParameters ());
124+ Assert .isInstanceOf (ReactiveValueEvaluationContextProvider .class , valueContextProvider , "ValueEvaluationContextProvider must be reactive" );
125+ this .valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider ) valueContextProvider ;
110126 }
111127
128+ /**
129+ * Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
130+ * {@link MongoOperations}.
131+ *
132+ * @param method must not be {@literal null}.
133+ * @param operations must not be {@literal null}.
134+ * @param delegate must not be {@literal null}.
135+ * @since 4.4.0
136+ */
137+ public AbstractReactiveMongoQuery (ReactiveMongoQueryMethod method , ReactiveMongoOperations operations ,
138+ ValueExpressionDelegate delegate ) {
139+
140+ Assert .notNull (method , "MongoQueryMethod must not be null" );
141+ Assert .notNull (operations , "ReactiveMongoOperations must not be null" );
142+ Assert .notNull (delegate , "ValueExpressionDelegate must not be null" );
143+
144+ this .method = method ;
145+ this .operations = operations ;
146+ this .instantiators = new EntityInstantiators ();
147+ this .valueExpressionDelegate = delegate ;
148+
149+ MongoEntityMetadata <?> metadata = method .getEntityInformation ();
150+ Class <?> type = metadata .getCollectionEntity ().getType ();
151+
152+ this .findOperationWithProjection = operations .query (type );
153+ this .updateOps = operations .update (type );
154+ ValueEvaluationContextProvider valueContextProvider = valueExpressionDelegate .createValueContextProvider (
155+ method .getParameters ());
156+ Assert .isInstanceOf (ReactiveValueEvaluationContextProvider .class , valueContextProvider , "ValueEvaluationContextProvider must be reactive" );
157+ this .valueEvaluationContextProvider = (ReactiveValueEvaluationContextProvider ) valueContextProvider ;
158+ }
159+
160+ @ Override
112161 public MongoQueryMethod getQueryMethod () {
113162 return method ;
114163 }
115164
165+ @ Override
116166 public Publisher <Object > execute (Object [] parameters ) {
117167
118168 return method .hasReactiveWrapperParameter () ? executeDeferred (parameters )
@@ -269,7 +319,7 @@ Query applyAnnotatedDefaultSortIfPresent(Query query) {
269319 Query applyAnnotatedCollationIfPresent (Query query , ConvertingParameterAccessor accessor ) {
270320
271321 return QueryUtils .applyCollation (query , method .hasAnnotatedCollation () ? method .getAnnotatedCollation () : null ,
272- accessor , getQueryMethod (). getParameters (), expressionParser , evaluationContextProvider );
322+ accessor , getValueExpressionEvaluator ( accessor ) );
273323 }
274324
275325 /**
@@ -381,19 +431,19 @@ private Mono<AggregationOperation> computePipelineStage(String source, MongoPara
381431 bsonString -> AbstractReactiveMongoQuery .this .decode (evaluator , bsonString , accessor , codec )));
382432 }
383433
384- private Mono <SpELExpressionEvaluator > expressionEvaluator (String source , MongoParameterAccessor accessor ,
385- ParameterBindingDocumentCodec codec ) {
434+ private Mono <Tuple2 < ValueExpressionEvaluator , ParameterBindingDocumentCodec >> expressionEvaluator (String source ,
435+ MongoParameterAccessor accessor , ParameterBindingDocumentCodec codec ) {
386436
387437 ExpressionDependencies dependencies = codec .captureExpressionDependencies (source , accessor ::getBindableValue ,
388- expressionParser );
389- return getSpelEvaluatorFor (dependencies , accessor );
438+ valueExpressionDelegate . getValueExpressionParser () );
439+ return getValueExpressionEvaluatorLater (dependencies , accessor ). zipWith ( Mono . just ( codec ) );
390440 }
391441
392- private Document decode (SpELExpressionEvaluator expressionEvaluator , String source , MongoParameterAccessor accessor ,
442+ private Document decode (Tuple2 < ValueExpressionEvaluator , ParameterBindingDocumentCodec > expressionEvaluator , String source , MongoParameterAccessor accessor ,
393443 ParameterBindingDocumentCodec codec ) {
394444
395445 ParameterBindingContext bindingContext = new ParameterBindingContext (accessor ::getBindableValue ,
396- expressionEvaluator );
446+ expressionEvaluator . getT1 () );
397447 return codec .decode (source , bindingContext );
398448 }
399449
@@ -415,17 +465,54 @@ protected Mono<ParameterBindingDocumentCodec> getParameterBindingCodec() {
415465 * @param accessor must not be {@literal null}.
416466 * @return a {@link Mono} emitting the {@link SpELExpressionEvaluator} when ready.
417467 * @since 3.4
468+ * @deprecated since 4.4.0, use
469+ * {@link #getValueExpressionEvaluatorLater(ExpressionDependencies, MongoParameterAccessor)} instead
418470 */
471+ @ Deprecated (since = "4.4.0" )
419472 protected Mono <SpELExpressionEvaluator > getSpelEvaluatorFor (ExpressionDependencies dependencies ,
420473 MongoParameterAccessor accessor ) {
421-
422- return evaluationContextProvider
423- .getEvaluationContextLater (getQueryMethod ().getParameters (), accessor .getValues (), dependencies )
424- .map (evaluationContext -> (SpELExpressionEvaluator ) new DefaultSpELExpressionEvaluator (expressionParser ,
425- evaluationContext ))
474+ return valueEvaluationContextProvider .getEvaluationContextLater (accessor .getValues (), dependencies )
475+ .map (evaluationContext -> (SpELExpressionEvaluator ) new DefaultSpELExpressionEvaluator (
476+ new SpelExpressionParser (), evaluationContext .getEvaluationContext ()))
426477 .defaultIfEmpty (DefaultSpELExpressionEvaluator .unsupported ());
427478 }
428479
480+ /**
481+ * Obtain a {@link ValueExpressionEvaluator} suitable to evaluate expressions.
482+ *
483+ * @param accessor must not be {@literal null}.
484+ * @since 4.3
485+ */
486+ ValueExpressionEvaluator getValueExpressionEvaluator (MongoParameterAccessor accessor ) {
487+
488+ return new ValueExpressionEvaluator () {
489+
490+ @ Override
491+ public <T > T evaluate (String expressionString ) {
492+ ValueExpression expression = valueExpressionDelegate .parse (expressionString );
493+ ValueEvaluationContext evaluationContext = valueEvaluationContextProvider .getEvaluationContext (accessor .getValues (),
494+ expression .getExpressionDependencies ());
495+ return (T ) expression .evaluate (evaluationContext );
496+ }
497+ };
498+ }
499+
500+ /**
501+ * Obtain a {@link Mono publisher} emitting the {@link ValueExpressionEvaluator} suitable to evaluate expressions
502+ * backed by the given dependencies.
503+ *
504+ * @param dependencies must not be {@literal null}.
505+ * @param accessor must not be {@literal null}.
506+ * @return a {@link Mono} emitting the {@link ValueExpressionEvaluator} when ready.
507+ * @since 4.3
508+ */
509+ protected Mono <ValueExpressionEvaluator > getValueExpressionEvaluatorLater (ExpressionDependencies dependencies ,
510+ MongoParameterAccessor accessor ) {
511+
512+ return valueEvaluationContextProvider .getEvaluationContextLater (accessor .getValues (), dependencies )
513+ .map (evaluationContext -> new ValueExpressionDelegateValueExpressionEvaluator (valueExpressionDelegate , valueExpression -> evaluationContext ));
514+ }
515+
429516 /**
430517 * @return a {@link Mono} emitting the {@link CodecRegistry} when ready.
431518 * @since 2.4
0 commit comments