DATAMONGO-1894 - Use reactive SpEL extensions for SpEL evaluation in query execution.
Original Pull Request: #874
This commit is contained in:
committed by
Christoph Strobl
parent
00aaf2145b
commit
66fae82798
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.mapping.model.EntityInstantiators;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
@@ -33,7 +33,7 @@ import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecu
|
||||
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.ResultProcessingConverter;
|
||||
import org.springframework.data.mongodb.repository.query.ReactiveMongoQueryExecution.ResultProcessingExecution;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
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.util.TypeInformation;
|
||||
@@ -55,7 +55,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
|
||||
private final EntityInstantiators instantiators;
|
||||
private final FindWithProjection<?> findOperationWithProjection;
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractReactiveMongoQuery} from the given {@link MongoQueryMethod} and
|
||||
@@ -67,12 +67,12 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public AbstractReactiveMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
Assert.notNull(method, "MongoQueryMethod must not be null!");
|
||||
Assert.notNull(operations, "ReactiveMongoOperations must not be null!");
|
||||
Assert.notNull(expressionParser, "SpelExpressionParser must not be null!");
|
||||
Assert.notNull(evaluationContextProvider, "QueryMethodEvaluationContextProvider must not be null!");
|
||||
Assert.notNull(evaluationContextProvider, "ReactiveEvaluationContextExtension must not be null!");
|
||||
|
||||
this.method = method;
|
||||
this.operations = operations;
|
||||
@@ -98,25 +98,21 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[])
|
||||
*/
|
||||
public Object execute(Object[] parameters) {
|
||||
public Publisher<Object> execute(Object[] parameters) {
|
||||
|
||||
return method.hasReactiveWrapperParameter() ? executeDeferred(parameters)
|
||||
: execute(new MongoParametersParameterAccessor(method, parameters));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object executeDeferred(Object[] parameters) {
|
||||
private Publisher<Object> executeDeferred(Object[] parameters) {
|
||||
|
||||
ReactiveMongoParameterAccessor parameterAccessor = new ReactiveMongoParameterAccessor(method, parameters);
|
||||
|
||||
if (getQueryMethod().isCollectionQuery()) {
|
||||
return Flux.defer(() -> (Publisher<Object>) execute(parameterAccessor));
|
||||
}
|
||||
|
||||
return Mono.defer(() -> (Mono<Object>) execute(parameterAccessor));
|
||||
return execute(parameterAccessor);
|
||||
}
|
||||
|
||||
private Object execute(MongoParameterAccessor parameterAccessor) {
|
||||
private Publisher<Object> execute(MongoParameterAccessor parameterAccessor) {
|
||||
|
||||
ConvertingParameterAccessor accessor = new ConvertingParameterAccessor(operations.getConverter(),
|
||||
parameterAccessor);
|
||||
@@ -141,24 +137,27 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
|
||||
* @param accessor for providing invocation arguments. Never {@literal null}.
|
||||
* @param typeToRead the desired component target type. Can be {@literal null}.
|
||||
*/
|
||||
protected Object doExecute(ReactiveMongoQueryMethod method, ResultProcessor processor,
|
||||
protected Publisher<Object> doExecute(ReactiveMongoQueryMethod method, ResultProcessor processor,
|
||||
ConvertingParameterAccessor accessor, @Nullable Class<?> typeToRead) {
|
||||
|
||||
Query query = createQuery(accessor);
|
||||
|
||||
applyQueryMetaAttributesWhenPresent(query);
|
||||
query = applyAnnotatedDefaultSortIfPresent(query);
|
||||
query = applyAnnotatedCollationIfPresent(query, accessor);
|
||||
return createQuery(accessor).flatMapMany(it -> {
|
||||
|
||||
FindWithQuery<?> find = typeToRead == null //
|
||||
? findOperationWithProjection //
|
||||
: findOperationWithProjection.as(typeToRead);
|
||||
Query query = it;
|
||||
applyQueryMetaAttributesWhenPresent(query);
|
||||
query = applyAnnotatedDefaultSortIfPresent(query);
|
||||
query = applyAnnotatedCollationIfPresent(query, accessor);
|
||||
|
||||
String collection = method.getEntityInformation().getCollectionName();
|
||||
FindWithQuery<?> find = typeToRead == null //
|
||||
? findOperationWithProjection //
|
||||
: findOperationWithProjection.as(typeToRead);
|
||||
|
||||
ReactiveMongoQueryExecution execution = getExecution(accessor,
|
||||
new ResultProcessingConverter(processor, operations, instantiators), find);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
|
||||
String collection = method.getEntityInformation().getCollectionName();
|
||||
|
||||
ReactiveMongoQueryExecution execution = getExecution(accessor,
|
||||
new ResultProcessingConverter(processor, operations, instantiators), find);
|
||||
return execution.execute(query, processor.getReturnedType().getDomainType(), collection);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,8 +253,8 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
|
||||
return applyQueryMetaAttributesWhenPresent(createQuery(accessor));
|
||||
protected Mono<Query> createCountQuery(ConvertingParameterAccessor accessor) {
|
||||
return createQuery(accessor).map(this::applyQueryMetaAttributesWhenPresent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,7 +263,7 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
|
||||
* @param accessor must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
protected abstract Query createQuery(ConvertingParameterAccessor accessor);
|
||||
protected abstract Mono<Query> createQuery(ConvertingParameterAccessor accessor);
|
||||
|
||||
/**
|
||||
* Returns whether the query should get a count projection applied.
|
||||
|
||||
@@ -72,8 +72,9 @@ abstract class CollationUtils {
|
||||
|
||||
if (StringUtils.trimLeadingWhitespace(collationExpression).startsWith("{")) {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue),
|
||||
expressionParser, () -> evaluationContextProvider.getEvaluationContext(parameters, accessor.getValues()));
|
||||
ParameterBindingContext bindingContext = ParameterBindingContext.forExpressions(accessor::getBindableValue,
|
||||
expressionParser, dependencies -> evaluationContextProvider.getEvaluationContext(parameters,
|
||||
accessor.getValues(), dependencies));
|
||||
|
||||
return Collation.from(CODEC.decode(collationExpression, bindingContext));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
|
||||
/**
|
||||
* Simple {@link SpELExpressionEvaluator} implementation using {@link ExpressionParser} and {@link EvaluationContext}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 3.1
|
||||
*/
|
||||
class DefaultSpELExpressionEvaluator implements SpELExpressionEvaluator {
|
||||
|
||||
private final ExpressionParser parser;
|
||||
private final EvaluationContext context;
|
||||
|
||||
DefaultSpELExpressionEvaluator(ExpressionParser parser, EvaluationContext context) {
|
||||
this.parser = parser;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.SpELExpressionEvaluator#evaluate(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T evaluate(String expression) {
|
||||
return (T) parser.parseExpression(expression).getValue(context, Object.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
interface ReactiveMongoQueryExecution {
|
||||
|
||||
Object execute(Query query, Class<?> type, String collection);
|
||||
Publisher<? extends Object> execute(Query query, Class<?> type, String collection);
|
||||
|
||||
/**
|
||||
* {@link MongoQueryExecution} to execute geo-near queries.
|
||||
@@ -74,7 +74,7 @@ interface ReactiveMongoQueryExecution {
|
||||
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
public Publisher<? extends Object> execute(Query query, Class<?> type, String collection) {
|
||||
|
||||
Flux<GeoResult<Object>> results = doExecuteQuery(query, type, collection);
|
||||
return isStreamOfGeoResult() ? results : results.map(GeoResult::getContent);
|
||||
@@ -132,7 +132,7 @@ interface ReactiveMongoQueryExecution {
|
||||
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery.Execution#execute(org.springframework.data.mongodb.core.query.Query, java.lang.Class, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
public Publisher<? extends Object> execute(Query query, Class<?> type, String collection) {
|
||||
|
||||
if (method.isCollectionQuery()) {
|
||||
return operations.findAllAndRemove(query, type, collection);
|
||||
@@ -166,8 +166,8 @@ interface ReactiveMongoQueryExecution {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(Query query, Class<?> type, String collection) {
|
||||
return converter.convert(delegate.execute(query, type, collection));
|
||||
public Publisher<? extends Object> execute(Query query, Class<?> type, String collection) {
|
||||
return (Publisher) converter.convert(delegate.execute(query, type, collection));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.json.JsonParseException;
|
||||
|
||||
@@ -26,7 +28,7 @@ import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.core.query.TextCriteria;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
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.ReturnedType;
|
||||
@@ -57,7 +59,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public ReactivePartTreeMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
|
||||
@@ -81,7 +83,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
|
||||
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#createQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor, boolean)
|
||||
*/
|
||||
@Override
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
protected Mono<Query> createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
MongoQueryCreator creator = new MongoQueryCreator(tree, accessor, context, isGeoNearQuery);
|
||||
Query query = creator.createQuery();
|
||||
@@ -105,7 +107,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
|
||||
returnedType.getInputProperties().forEach(query.fields()::include);
|
||||
}
|
||||
|
||||
return query;
|
||||
return Mono.just(query);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -113,7 +115,7 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
|
||||
BasicQuery result = new BasicQuery(query.getQueryObject(), Document.parse(fieldSpec));
|
||||
result.setSortObject(query.getSortObject());
|
||||
|
||||
return result;
|
||||
return Mono.just(result);
|
||||
|
||||
} catch (JsonParseException o_O) {
|
||||
throw new IllegalStateException(String.format("Invalid query or field specification in %s!", getQueryMethod()),
|
||||
@@ -126,8 +128,8 @@ public class ReactivePartTreeMongoQuery extends AbstractReactiveMongoQuery {
|
||||
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#createCountQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
|
||||
*/
|
||||
@Override
|
||||
protected Query createCountQuery(ConvertingParameterAccessor accessor) {
|
||||
return new MongoQueryCreator(tree, accessor, context, false).createQuery();
|
||||
protected Mono<Query> createCountQuery(ConvertingParameterAccessor accessor) {
|
||||
return Mono.fromSupplier(() -> new MongoQueryCreator(tree, accessor, context, false).createQuery());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
|
||||
@@ -29,8 +34,11 @@ import org.springframework.data.mongodb.core.aggregation.TypedAggregation;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.mongodb.util.json.ParameterBindingContext;
|
||||
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -44,8 +52,10 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
|
||||
|
||||
private static final ParameterBindingDocumentCodec CODEC = new ParameterBindingDocumentCodec();
|
||||
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ReactiveMongoOperations reactiveMongoOperations;
|
||||
private final MongoConverter mongoConverter;
|
||||
|
||||
@@ -57,7 +67,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
|
||||
*/
|
||||
public ReactiveStringBasedAggregation(ReactiveMongoQueryMethod method,
|
||||
ReactiveMongoOperations reactiveMongoOperations, SpelExpressionParser expressionParser,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, reactiveMongoOperations, expressionParser, evaluationContextProvider);
|
||||
|
||||
@@ -72,52 +82,81 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
|
||||
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#doExecute(org.springframework.data.mongodb.repository.query.ReactiveMongoQueryMethod, org.springframework.data.repository.query.ResultProcessor, org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
protected Object doExecute(ReactiveMongoQueryMethod method, ResultProcessor processor,
|
||||
protected Publisher<Object> doExecute(ReactiveMongoQueryMethod method, ResultProcessor processor,
|
||||
ConvertingParameterAccessor accessor, Class<?> typeToRead) {
|
||||
|
||||
Class<?> sourceType = method.getDomainClass();
|
||||
Class<?> targetType = typeToRead;
|
||||
return computePipeline(accessor).flatMapMany(it -> {
|
||||
|
||||
List<AggregationOperation> pipeline = computePipeline(accessor);
|
||||
AggregationUtils.appendSortIfPresent(pipeline, accessor, typeToRead);
|
||||
AggregationUtils.appendLimitAndOffsetIfPresent(pipeline, accessor);
|
||||
Class<?> sourceType = method.getDomainClass();
|
||||
Class<?> targetType = typeToRead;
|
||||
|
||||
boolean isSimpleReturnType = isSimpleReturnType(typeToRead);
|
||||
boolean isRawReturnType = ClassUtils.isAssignable(org.bson.Document.class, typeToRead);
|
||||
List<AggregationOperation> pipeline = it;
|
||||
|
||||
if (isSimpleReturnType || isRawReturnType) {
|
||||
targetType = Document.class;
|
||||
}
|
||||
AggregationUtils.appendSortIfPresent(pipeline, accessor, typeToRead);
|
||||
AggregationUtils.appendLimitAndOffsetIfPresent(pipeline, accessor);
|
||||
|
||||
AggregationOptions options = computeOptions(method, accessor);
|
||||
TypedAggregation<?> aggregation = new TypedAggregation<>(sourceType, pipeline, options);
|
||||
boolean isSimpleReturnType = isSimpleReturnType(typeToRead);
|
||||
boolean isRawReturnType = ClassUtils.isAssignable(org.bson.Document.class, typeToRead);
|
||||
|
||||
Flux<?> flux = reactiveMongoOperations.aggregate(aggregation, targetType);
|
||||
if (isSimpleReturnType || isRawReturnType) {
|
||||
targetType = Document.class;
|
||||
}
|
||||
|
||||
if (isSimpleReturnType && !isRawReturnType) {
|
||||
flux = flux.handle((it, sink) -> {
|
||||
AggregationOptions options = computeOptions(method, accessor);
|
||||
TypedAggregation<?> aggregation = new TypedAggregation<>(sourceType, pipeline, options);
|
||||
|
||||
Object result = AggregationUtils.extractSimpleTypeResult((Document) it, typeToRead, mongoConverter);
|
||||
Flux<?> flux = reactiveMongoOperations.aggregate(aggregation, targetType);
|
||||
|
||||
if (result != null) {
|
||||
sink.next(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (isSimpleReturnType && !isRawReturnType) {
|
||||
flux = flux.handle((item, sink) -> {
|
||||
|
||||
if (method.isCollectionQuery()) {
|
||||
return flux;
|
||||
} else {
|
||||
return flux.next();
|
||||
}
|
||||
Object result = AggregationUtils.extractSimpleTypeResult((Document) item, typeToRead, mongoConverter);
|
||||
|
||||
if (result != null) {
|
||||
sink.next(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (method.isCollectionQuery()) {
|
||||
return flux;
|
||||
} else {
|
||||
return flux.next();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isSimpleReturnType(Class<?> targetType) {
|
||||
return MongoSimpleTypes.HOLDER.isSimpleType(targetType);
|
||||
}
|
||||
|
||||
List<AggregationOperation> computePipeline(ConvertingParameterAccessor accessor) {
|
||||
return AggregationUtils.computePipeline(getQueryMethod(), accessor, expressionParser, evaluationContextProvider);
|
||||
private Mono<List<AggregationOperation>> computePipeline(ConvertingParameterAccessor accessor) {
|
||||
|
||||
MongoQueryMethod method = getQueryMethod();
|
||||
|
||||
List<Mono<AggregationOperation>> stages = new ArrayList<>(method.getAnnotatedAggregation().length);
|
||||
|
||||
for (String source : method.getAnnotatedAggregation()) {
|
||||
|
||||
Optional<ExpressionDependencies> dependencies = CODEC.getExpressionDependencies(source,
|
||||
accessor::getBindableValue, expressionParser);
|
||||
|
||||
Mono<SpELExpressionEvaluator> evaluator = dependencies.map(
|
||||
it -> evaluationContextProvider.getEvaluationContextLater(method.getParameters(), accessor.getValues(), it))
|
||||
.map(evaluationContext -> evaluationContext
|
||||
.map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it)))
|
||||
.orElseGet(() -> Mono.just(NoOpExpressionEvaluator.INSTANCE));
|
||||
|
||||
Mono<AggregationOperation> stage = evaluator.map(it -> {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext(accessor::getBindableValue, it);
|
||||
|
||||
return ctx -> ctx.getMappedObject(CODEC.decode(source, bindingContext), method.getDomainClass());
|
||||
});
|
||||
stages.add(stage);
|
||||
}
|
||||
|
||||
return Flux.concat(stages).collectList();
|
||||
}
|
||||
|
||||
private AggregationOptions computeOptions(MongoQueryMethod method, ConvertingParameterAccessor accessor) {
|
||||
@@ -136,7 +175,7 @@ public class ReactiveStringBasedAggregation extends AbstractReactiveMongoQuery {
|
||||
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#createQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
|
||||
*/
|
||||
@Override
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
protected Mono<Query> createQuery(ConvertingParameterAccessor accessor) {
|
||||
throw new UnsupportedOperationException("No query support for aggregation");
|
||||
}
|
||||
|
||||
|
||||
@@ -15,17 +15,25 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.util.json.ParameterBindingContext;
|
||||
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -46,7 +54,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
private final String fieldSpec;
|
||||
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
private final boolean isCountQuery;
|
||||
private final boolean isExistsQuery;
|
||||
@@ -62,13 +70,14 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
* @param evaluationContextProvider must not be {@literal null}.
|
||||
*/
|
||||
public ReactiveStringBasedMongoQuery(ReactiveMongoQueryMethod method, ReactiveMongoOperations mongoOperations,
|
||||
SpelExpressionParser expressionParser, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
SpelExpressionParser expressionParser, ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
this(method.getAnnotatedQuery(), method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ReactiveStringBasedMongoQuery} for the given {@link String}, {@link MongoQueryMethod},
|
||||
* {@link MongoOperations}, {@link SpelExpressionParser} and {@link QueryMethodEvaluationContextProvider}.
|
||||
* {@link MongoOperations}, {@link SpelExpressionParser} and
|
||||
* {@link ReactiveExtensionAwareQueryMethodEvaluationContextProvider}.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param method must not be {@literal null}.
|
||||
@@ -77,7 +86,7 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
*/
|
||||
public ReactiveStringBasedMongoQuery(String query, ReactiveMongoQueryMethod method,
|
||||
ReactiveMongoOperations mongoOperations, SpelExpressionParser expressionParser,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
super(method, mongoOperations, expressionParser, evaluationContextProvider);
|
||||
|
||||
@@ -114,21 +123,39 @@ public class ReactiveStringBasedMongoQuery extends AbstractReactiveMongoQuery {
|
||||
* @see org.springframework.data.mongodb.repository.query.AbstractReactiveMongoQuery#createQuery(org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor)
|
||||
*/
|
||||
@Override
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
protected Mono<Query> createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser,
|
||||
() -> evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues()));
|
||||
Mono<Document> queryObject = getBindingContext(accessor, expressionParser, this.query)
|
||||
.map(it -> CODEC.decode(this.query, it));
|
||||
Mono<Document> fieldsObject = getBindingContext(accessor, expressionParser, this.fieldSpec)
|
||||
.map(it -> CODEC.decode(this.fieldSpec, it));
|
||||
|
||||
Document queryObject = CODEC.decode(this.query, bindingContext);
|
||||
Document fieldsObject = CODEC.decode(this.fieldSpec, bindingContext);
|
||||
return queryObject.zipWith(fieldsObject).map(tuple -> {
|
||||
|
||||
Query query = new BasicQuery(queryObject, fieldsObject).with(accessor.getSort());
|
||||
Query query = new BasicQuery(tuple.getT1(), tuple.getT2()).with(accessor.getSort());
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Created query %s for %s fields.", query.getQueryObject(), query.getFieldsObject()));
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(String.format("Created query %s for %s fields.", query.getQueryObject(), query.getFieldsObject()));
|
||||
}
|
||||
|
||||
return query;
|
||||
return query;
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<ParameterBindingContext> getBindingContext(ConvertingParameterAccessor accessor,
|
||||
ExpressionParser expressionParser, String json) {
|
||||
|
||||
Optional<ExpressionDependencies> dependencies = CODEC.getExpressionDependencies(json, accessor::getBindableValue,
|
||||
expressionParser);
|
||||
|
||||
Mono<SpELExpressionEvaluator> evaluator = dependencies
|
||||
.map(it -> evaluationContextProvider.getEvaluationContextLater(getQueryMethod().getParameters(),
|
||||
accessor.getValues(), it))
|
||||
.map(evaluationContext -> evaluationContext
|
||||
.map(it -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser, it)))
|
||||
.orElseGet(() -> Mono.just(NoOpExpressionEvaluator.INSTANCE));
|
||||
|
||||
return evaluator.map(it -> new ParameterBindingContext(accessor::getBindableValue, it));
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -15,16 +15,22 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.repository.query;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.codecs.configuration.CodecRegistry;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.data.mongodb.core.query.BasicQuery;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.data.mongodb.util.json.ParameterBindingContext;
|
||||
import org.springframework.data.mongodb.util.json.ParameterBindingDocumentCodec;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -122,11 +128,8 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
@Override
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
|
||||
ParameterBindingContext bindingContext = new ParameterBindingContext((accessor::getBindableValue), expressionParser,
|
||||
() -> evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(), accessor.getValues()));
|
||||
|
||||
Document queryObject = codec.decode(this.query, bindingContext);
|
||||
Document fieldsObject = codec.decode(this.fieldSpec, bindingContext);
|
||||
Document queryObject = codec.decode(this.query, getBindingContext(accessor, expressionParser, this.query));
|
||||
Document fieldsObject = codec.decode(this.fieldSpec, getBindingContext(accessor, expressionParser, this.fieldSpec));
|
||||
|
||||
Query query = new BasicQuery(queryObject, fieldsObject).with(accessor.getSort());
|
||||
|
||||
@@ -137,6 +140,22 @@ public class StringBasedMongoQuery extends AbstractMongoQuery {
|
||||
return query;
|
||||
}
|
||||
|
||||
private ParameterBindingContext getBindingContext(ConvertingParameterAccessor accessor,
|
||||
ExpressionParser expressionParser, String json) {
|
||||
|
||||
Optional<ExpressionDependencies> dependencies = codec.getExpressionDependencies(json, accessor::getBindableValue,
|
||||
expressionParser);
|
||||
|
||||
SpELExpressionEvaluator evaluator = dependencies
|
||||
.map(it -> evaluationContextProvider.getEvaluationContext(getQueryMethod().getParameters(),
|
||||
accessor.getValues(), it))
|
||||
.map(evaluationContext -> (SpELExpressionEvaluator) new DefaultSpELExpressionEvaluator(expressionParser,
|
||||
evaluationContext))
|
||||
.orElse(NoOpExpressionEvaluator.INSTANCE);
|
||||
|
||||
return new ParameterBindingContext(accessor::getBindableValue, evaluator);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.repository.query.AbstractMongoQuery#isCountQuery()
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -127,7 +128,8 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
return Optional.of(new MongoQueryLookupStrategy(operations, evaluationContextProvider, mappingContext));
|
||||
return Optional.of(new MongoQueryLookupStrategy(operations,
|
||||
(ReactiveQueryMethodEvaluationContextProvider) evaluationContextProvider, mappingContext));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -157,11 +159,11 @@ public class ReactiveMongoRepositoryFactory extends ReactiveRepositoryFactorySup
|
||||
private static class MongoQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final ReactiveMongoOperations operations;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext;
|
||||
|
||||
MongoQueryLookupStrategy(ReactiveMongoOperations operations,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
ReactiveQueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext) {
|
||||
|
||||
this.operations = operations;
|
||||
|
||||
@@ -16,13 +16,17 @@
|
||||
package org.springframework.data.mongodb.repository.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.ReactiveMongoOperations;
|
||||
import org.springframework.data.mongodb.core.index.IndexOperationsAdapter;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -84,10 +88,7 @@ public class ReactiveMongoRepositoryFactoryBean<T extends Repository<S, ID>, S,
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryFactoryBeanSupport
|
||||
* #createRepositoryFactory()
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createRepositoryFactory()
|
||||
*/
|
||||
@Override
|
||||
protected RepositoryFactorySupport createRepositoryFactory() {
|
||||
@@ -102,6 +103,16 @@ public class ReactiveMongoRepositoryFactoryBean<T extends Repository<S, ID>, S,
|
||||
return factory;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#createDefaultQueryMethodEvaluationContextProvider(ListableBeanFactory)
|
||||
*/
|
||||
@Override
|
||||
protected Optional<QueryMethodEvaluationContextProvider> createDefaultQueryMethodEvaluationContextProvider(
|
||||
ListableBeanFactory beanFactory) {
|
||||
return Optional.of(new ReactiveExtensionAwareQueryMethodEvaluationContextProvider(beanFactory));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes a {@link RepositoryFactorySupport} instance.
|
||||
*
|
||||
@@ -114,10 +125,7 @@ public class ReactiveMongoRepositoryFactoryBean<T extends Repository<S, ID>, S,
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.support.RepositoryFactoryBeanSupport
|
||||
* #afterPropertiesSet()
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#afterPropertiesSet()
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.util.json;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
@@ -28,25 +31,21 @@ import org.springframework.lang.Nullable;
|
||||
* To be used along with {@link ParameterBindingDocumentCodec#decode(String, ParameterBindingContext)}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
* @since 2.2
|
||||
*/
|
||||
public class ParameterBindingContext {
|
||||
|
||||
private final ValueProvider valueProvider;
|
||||
private final SpelExpressionParser expressionParser;
|
||||
private final Lazy<EvaluationContext> evaluationContext;
|
||||
private final SpELExpressionEvaluator expressionEvaluator;
|
||||
|
||||
/**
|
||||
* @param valueProvider
|
||||
* @param expressionParser
|
||||
* @param evaluationContext
|
||||
* @deprecated since 2.2.3 - Please use
|
||||
* {@link #ParameterBindingContext(ValueProvider, SpelExpressionParser, Supplier)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser,
|
||||
EvaluationContext evaluationContext) {
|
||||
|
||||
this(valueProvider, expressionParser, () -> evaluationContext);
|
||||
}
|
||||
|
||||
@@ -59,9 +58,48 @@ public class ParameterBindingContext {
|
||||
public ParameterBindingContext(ValueProvider valueProvider, SpelExpressionParser expressionParser,
|
||||
Supplier<EvaluationContext> evaluationContext) {
|
||||
|
||||
this(valueProvider, new SpELExpressionEvaluator() {
|
||||
@Override
|
||||
public <T> T evaluate(String expressionString) {
|
||||
return (T) expressionParser.parseExpression(expressionString).getValue(evaluationContext.get(), Object.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param valueProvider
|
||||
* @param expressionEvaluator
|
||||
* @since 3.1
|
||||
*/
|
||||
public ParameterBindingContext(ValueProvider valueProvider, SpELExpressionEvaluator expressionEvaluator) {
|
||||
this.valueProvider = valueProvider;
|
||||
this.expressionParser = expressionParser;
|
||||
this.evaluationContext = evaluationContext instanceof Lazy ? (Lazy) evaluationContext : Lazy.of(evaluationContext);
|
||||
this.expressionEvaluator = expressionEvaluator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ParameterBindingContext} that is capable of expression parsing and can provide a
|
||||
* {@link EvaluationContext} based on {@link ExpressionDependencies}.
|
||||
*
|
||||
* @param valueProvider
|
||||
* @param expressionParser
|
||||
* @param contextFunction
|
||||
* @return
|
||||
* @since 3.1
|
||||
*/
|
||||
public static ParameterBindingContext forExpressions(ValueProvider valueProvider,
|
||||
SpelExpressionParser expressionParser, Function<ExpressionDependencies, EvaluationContext> contextFunction) {
|
||||
|
||||
return new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() {
|
||||
@Override
|
||||
public <T> T evaluate(String expressionString) {
|
||||
|
||||
Expression expression = expressionParser.parseExpression(expressionString);
|
||||
ExpressionDependencies dependencies = ExpressionDependencies.discover(expression);
|
||||
EvaluationContext evaluationContext = contextFunction.apply(dependencies);
|
||||
|
||||
return (T) expression.getValue(evaluationContext, Object.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -71,17 +109,7 @@ public class ParameterBindingContext {
|
||||
|
||||
@Nullable
|
||||
public Object evaluateExpression(String expressionString) {
|
||||
|
||||
Expression expression = expressionParser.parseExpression(expressionString);
|
||||
return expression.getValue(getEvaluationContext(), Object.class);
|
||||
}
|
||||
|
||||
public EvaluationContext getEvaluationContext() {
|
||||
return this.evaluationContext.get();
|
||||
}
|
||||
|
||||
public SpelExpressionParser getExpressionParser() {
|
||||
return expressionParser;
|
||||
return expressionEvaluator.evaluate(expressionString);
|
||||
}
|
||||
|
||||
public ValueProvider getValueProvider() {
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bson.AbstractBsonReader.State;
|
||||
@@ -39,7 +40,11 @@ import org.bson.Transformer;
|
||||
import org.bson.codecs.*;
|
||||
import org.bson.codecs.configuration.CodecRegistry;
|
||||
import org.bson.json.JsonParseException;
|
||||
|
||||
import org.springframework.data.mapping.model.SpELExpressionEvaluator;
|
||||
import org.springframework.data.spel.EvaluationContextProvider;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.NumberUtils;
|
||||
@@ -163,7 +168,7 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec<Document>
|
||||
public Document decode(@Nullable String json, Object[] values) {
|
||||
|
||||
return decode(json, new ParameterBindingContext((index) -> values[index], new SpelExpressionParser(),
|
||||
() -> EvaluationContextProvider.DEFAULT.getEvaluationContext(values)));
|
||||
EvaluationContextProvider.DEFAULT.getEvaluationContext(values)));
|
||||
}
|
||||
|
||||
public Document decode(@Nullable String json, ParameterBindingContext bindingContext) {
|
||||
@@ -176,6 +181,51 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec<Document>
|
||||
return this.decode(reader, DecoderContext.builder().build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine {@link ExpressionDependencies} from Expressions that are nested in the {@code json} content. Returns
|
||||
* {@link Optional#empty()} if {@code json} is empty or of it does not contain any SpEL expressions.
|
||||
*
|
||||
* @param json
|
||||
* @param expressionParser
|
||||
* @return a {@link Optional} containing merged {@link ExpressionDependencies} object if expressions were found,
|
||||
* otherwise {@link Optional#empty()}.
|
||||
* @since 3.1
|
||||
*/
|
||||
public Optional<ExpressionDependencies> getExpressionDependencies(@Nullable String json, ValueProvider valueProvider,
|
||||
ExpressionParser expressionParser) {
|
||||
|
||||
if (StringUtils.isEmpty(json)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
List<ExpressionDependencies> dependencies = new ArrayList<>();
|
||||
|
||||
ParameterBindingContext context = new ParameterBindingContext(valueProvider, new SpELExpressionEvaluator() {
|
||||
@Override
|
||||
public <T> T evaluate(String expression) {
|
||||
|
||||
dependencies.add(ExpressionDependencies.discover(expressionParser.parseExpression(expression)));
|
||||
|
||||
return (T) new Object();
|
||||
}
|
||||
});
|
||||
|
||||
ParameterBindingJsonReader reader = new ParameterBindingJsonReader(json, context);
|
||||
this.decode(reader, DecoderContext.builder().build());
|
||||
|
||||
if (dependencies.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
ExpressionDependencies result = ExpressionDependencies.empty();
|
||||
|
||||
for (ExpressionDependencies dependency : dependencies) {
|
||||
result = result.mergeWith(dependency);
|
||||
}
|
||||
|
||||
return Optional.of(result);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Override
|
||||
public Document decode(final BsonReader reader, final DecoderContext decoderContext) {
|
||||
@@ -349,4 +399,5 @@ public class ParameterBindingDocumentCodec implements CollectibleCodec<Document>
|
||||
reader.readEndArray();
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.bson.types.Decimal128;
|
||||
import org.bson.types.MaxKey;
|
||||
import org.bson.types.MinKey;
|
||||
import org.bson.types.ObjectId;
|
||||
|
||||
import org.springframework.data.spel.EvaluationContextProvider;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
@@ -67,6 +67,7 @@ import org.springframework.data.mongodb.test.util.MongoTestUtils;
|
||||
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import com.mongodb.reactivestreams.client.MongoClient;
|
||||
@@ -112,7 +113,7 @@ public class ReactiveMongoRepositoryTests {
|
||||
factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class);
|
||||
factory.setBeanClassLoader(beanFactory.getClass().getClassLoader());
|
||||
factory.setBeanFactory(beanFactory);
|
||||
factory.setEvaluationContextProvider(QueryMethodEvaluationContextProvider.DEFAULT);
|
||||
factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
|
||||
import org.springframework.data.mongodb.repository.support.ReactiveMongoRepositoryFactory;
|
||||
import org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -86,7 +87,7 @@ public class SimpleReactiveMongoRepositoryTests implements BeanClassLoaderAware,
|
||||
factory.setRepositoryBaseClass(SimpleReactiveMongoRepository.class);
|
||||
factory.setBeanClassLoader(classLoader);
|
||||
factory.setBeanFactory(beanFactory);
|
||||
factory.setEvaluationContextProvider(QueryMethodEvaluationContextProvider.DEFAULT);
|
||||
factory.setEvaluationContextProvider(ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
|
||||
|
||||
repository = factory.getRepository(ReactivePersonRepostitory.class);
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ package org.springframework.data.mongodb.repository.query;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -48,7 +51,7 @@ import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
/**
|
||||
@@ -85,13 +88,16 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
doReturn(executableFind).when(mongoOperationsMock).query(any());
|
||||
doReturn(withQueryMock).when(executableFind).as(any());
|
||||
doReturn(withQueryMock).when(withQueryMock).matching(any(Query.class));
|
||||
doReturn(Flux.empty()).when(withQueryMock).all();
|
||||
doReturn(Mono.empty()).when(withQueryMock).first();
|
||||
doReturn(Mono.empty()).when(withQueryMock).one();
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1854
|
||||
void shouldApplyStaticAnnotatedCollation() {
|
||||
|
||||
createQueryForMethod("findWithCollationUsingSpimpleStringValueByFirstName", String.class) //
|
||||
.execute(new Object[] { "dalinar" });
|
||||
.executeBlocking(new Object[] { "dalinar" });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -103,7 +109,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
void shouldApplyStaticAnnotatedCollationAsDocument() {
|
||||
|
||||
createQueryForMethod("findWithCollationUsingDocumentByFirstName", String.class) //
|
||||
.execute(new Object[] { "dalinar" });
|
||||
.executeBlocking(new Object[] { "dalinar" });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -115,7 +121,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
void shouldApplyDynamicAnnotatedCollationAsString() {
|
||||
|
||||
createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) //
|
||||
.execute(new Object[] { "dalinar", "en_US" });
|
||||
.executeBlocking(new Object[] { "dalinar", "en_US" });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -127,7 +133,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
void shouldApplyDynamicAnnotatedCollationAsDocument() {
|
||||
|
||||
createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) //
|
||||
.execute(new Object[] { "dalinar", new Document("locale", "en_US") });
|
||||
.executeBlocking(new Object[] { "dalinar", new Document("locale", "en_US") });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -139,7 +145,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
void shouldApplyDynamicAnnotatedCollationAsLocale() {
|
||||
|
||||
createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) //
|
||||
.execute(new Object[] { "dalinar", Locale.US });
|
||||
.executeBlocking(new Object[] { "dalinar", Locale.US });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -152,7 +158,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> {
|
||||
createQueryForMethod("findWithCollationUsingPlaceholderByFirstName", String.class, Object.class) //
|
||||
.execute(new Object[] { "dalinar", 100 });
|
||||
.executeBlocking(new Object[] { "dalinar", 100 });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -160,7 +166,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
void shouldApplyDynamicAnnotatedCollationIn() {
|
||||
|
||||
createQueryForMethod("findWithCollationUsingPlaceholderInDocumentByFirstName", String.class, String.class) //
|
||||
.execute(new Object[] { "dalinar", "en_US" });
|
||||
.executeBlocking(new Object[] { "dalinar", "en_US" });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -173,7 +179,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
|
||||
createQueryForMethod("findWithCollationUsingPlaceholdersInDocumentByFirstName", String.class, String.class,
|
||||
int.class) //
|
||||
.execute(new Object[] { "dalinar", "en_US", 2 });
|
||||
.executeBlocking(new Object[] { "dalinar", "en_US", 2 });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -186,7 +192,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
|
||||
Collation collation = Collation.of("en_US");
|
||||
createQueryForMethod("findWithCollationParameterByFirstName", String.class, Collation.class) //
|
||||
.execute(new Object[] { "dalinar", collation });
|
||||
.executeBlocking(new Object[] { "dalinar", collation });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -198,7 +204,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
|
||||
Collation collation = Collation.of("de_AT");
|
||||
createQueryForMethod("findWithWithCollationParameterAndAnnotationByFirstName", String.class, Collation.class) //
|
||||
.execute(new Object[] { "dalinar", collation });
|
||||
.executeBlocking(new Object[] { "dalinar", collation });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -209,7 +215,7 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
void collationParameterShouldNotBeAppliedWhenNullOverrideAnnotation() {
|
||||
|
||||
createQueryForMethod("findWithWithCollationParameterAndAnnotationByFirstName", String.class, Collation.class) //
|
||||
.execute(new Object[] { "dalinar", null });
|
||||
.executeBlocking(new Object[] { "dalinar", null });
|
||||
|
||||
ArgumentCaptor<Query> captor = ArgumentCaptor.forClass(Query.class);
|
||||
verify(withQueryMock).matching(captor.capture());
|
||||
@@ -242,12 +248,17 @@ class AbstractReactiveMongoQueryUnitTests {
|
||||
private boolean isLimitingQuery;
|
||||
|
||||
ReactiveMongoQueryFake(ReactiveMongoQueryMethod method, ReactiveMongoOperations operations) {
|
||||
super(method, operations, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT);
|
||||
super(method, operations, new SpelExpressionParser(),
|
||||
ReactiveExtensionAwareQueryMethodEvaluationContextProvider.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Query createQuery(ConvertingParameterAccessor accessor) {
|
||||
return new BasicQuery("{'foo':'bar'}");
|
||||
protected Mono<Query> createQuery(ConvertingParameterAccessor accessor) {
|
||||
return Mono.just(new BasicQuery("{'foo':'bar'}"));
|
||||
}
|
||||
|
||||
Object executeBlocking(Object[] parameters) {
|
||||
return Flux.from(super.execute(parameters)).collectList().block();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import lombok.Value;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -55,6 +56,7 @@ import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -172,7 +174,7 @@ public class ReactiveStringBasedAggregationUnitTests {
|
||||
ArgumentCaptor<TypedAggregation> aggregationCaptor = ArgumentCaptor.forClass(TypedAggregation.class);
|
||||
ArgumentCaptor<Class> targetTypeCaptor = ArgumentCaptor.forClass(Class.class);
|
||||
|
||||
Object result = aggregation.execute(args);
|
||||
Object result = Flux.from((Publisher) aggregation.execute(args)).blockLast();
|
||||
|
||||
verify(operations).aggregate(aggregationCaptor.capture(), targetTypeCaptor.capture());
|
||||
|
||||
@@ -186,7 +188,7 @@ public class ReactiveStringBasedAggregationUnitTests {
|
||||
ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method,
|
||||
new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext());
|
||||
return new ReactiveStringBasedAggregation(queryMethod, operations, PARSER,
|
||||
QueryMethodEvaluationContextProvider.DEFAULT);
|
||||
ReactiveQueryMethodEvaluationContextProvider.DEFAULT);
|
||||
}
|
||||
|
||||
private List<Document> pipelineOf(AggregationInvocation invocation) {
|
||||
|
||||
@@ -48,7 +48,10 @@ import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.spel.spi.EvaluationContextExtension;
|
||||
import org.springframework.data.spel.spi.ReactiveEvaluationContextExtension;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.Base64Utils;
|
||||
|
||||
@@ -83,7 +86,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastname", String.class);
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block();
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
|
||||
|
||||
assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject());
|
||||
@@ -101,7 +104,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
converter.write(address, dbObject);
|
||||
dbObject.remove(DefaultMongoTypeMapper.DEFAULT_TYPE_KEY);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block();
|
||||
Document queryObject = new Document("address", dbObject);
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(queryObject);
|
||||
|
||||
@@ -136,7 +139,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByParameterizedCriteriaAndFields",
|
||||
Document.class, Map.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor).block();
|
||||
|
||||
assertThat(query.getQueryObject())
|
||||
.isEqualTo(new BasicQuery("{ \"firstname\": \"first\", \"lastname\": \"last\"}").getQueryObject());
|
||||
@@ -150,7 +153,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithParametersInExpression", int.class,
|
||||
int.class, int.class, int.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor).block();
|
||||
|
||||
assertThat(query.getQueryObject())
|
||||
.isEqualTo(new BasicQuery("{$where: 'return this.date.getUTCMonth() == 3 && this.date.getUTCDay() == 4;'}")
|
||||
@@ -164,7 +167,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
String.class, String.class);
|
||||
ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "key", "value");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor).block();
|
||||
|
||||
assertThat(query.getQueryObject()).isEqualTo(new Document().append("key", "value"));
|
||||
}
|
||||
@@ -175,7 +178,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, "Matthews");
|
||||
ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpression", String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block();
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{'lastname' : 'Matthews'}");
|
||||
|
||||
assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject());
|
||||
@@ -188,7 +191,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndNestedObject",
|
||||
boolean.class, String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block();
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{ \"id\" : { \"$exists\" : true}}");
|
||||
|
||||
assertThat(query.getQueryObject()).isEqualTo(reference.getQueryObject());
|
||||
@@ -201,7 +204,7 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByQueryWithExpressionAndMultipleNestedObjects",
|
||||
boolean.class, String.class, String.class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block();
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(
|
||||
"{ \"id\" : { \"$exists\" : true} , \"foo\" : 42 , \"bar\" : { \"$exists\" : false}}");
|
||||
|
||||
@@ -215,21 +218,43 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter, binaryData);
|
||||
ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod("findByLastnameAsBinary", byte[].class);
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor);
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block();
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery(
|
||||
"{'lastname' : { '$binary' : '" + Base64Utils.encodeToString(binaryData) + "', '$type' : '" + 0 + "'}}");
|
||||
|
||||
assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson());
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1894
|
||||
void shouldConsiderReactiveSpelExtension() throws Exception {
|
||||
|
||||
ReactiveExtensionAwareQueryMethodEvaluationContextProvider contextProvider = new ReactiveExtensionAwareQueryMethodEvaluationContextProvider(
|
||||
Collections.singletonList(ReactiveSpelExtension.INSTANCE));
|
||||
|
||||
ConvertingParameterAccessor accesor = StubParameterAccessor.getAccessor(converter);
|
||||
ReactiveStringBasedMongoQuery mongoQuery = createQueryForMethod(contextProvider, "withReactiveSpelExtensions");
|
||||
|
||||
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accesor).block();
|
||||
org.springframework.data.mongodb.core.query.Query reference = new BasicQuery("{lastname: true}", "{project: true}");
|
||||
|
||||
assertThat(query.getQueryObject().toJson()).isEqualTo(reference.getQueryObject().toJson());
|
||||
}
|
||||
|
||||
private ReactiveStringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
|
||||
return createQueryForMethod(ReactiveQueryMethodEvaluationContextProvider.DEFAULT, name, parameters);
|
||||
}
|
||||
|
||||
private ReactiveStringBasedMongoQuery createQueryForMethod(
|
||||
ReactiveQueryMethodEvaluationContextProvider contextProvider, String name, Class<?>... parameters)
|
||||
throws Exception {
|
||||
|
||||
Method method = SampleRepository.class.getMethod(name, parameters);
|
||||
ProjectionFactory factory = new SpelAwareProxyProjectionFactory();
|
||||
ReactiveMongoQueryMethod queryMethod = new ReactiveMongoQueryMethod(method,
|
||||
new DefaultRepositoryMetadata(SampleRepository.class), factory, converter.getMappingContext());
|
||||
|
||||
return new ReactiveStringBasedMongoQuery(queryMethod, operations, PARSER,
|
||||
QueryMethodEvaluationContextProvider.DEFAULT);
|
||||
contextProvider);
|
||||
}
|
||||
|
||||
private interface SampleRepository extends Repository<Person, Long> {
|
||||
@@ -269,5 +294,42 @@ public class ReactiveStringBasedMongoQueryUnitTests {
|
||||
|
||||
@Query(value = "{ 'lastname' : ?0 }", exists = true)
|
||||
Mono<Boolean> existsByLastname(String lastname);
|
||||
|
||||
@Query(value = "{ 'lastname' : ?#{hasRole()} }", fields = "{project: ?#{hasRole()}}")
|
||||
Mono<Document> withReactiveSpelExtensions();
|
||||
}
|
||||
|
||||
public enum ReactiveSpelExtension implements ReactiveEvaluationContextExtension {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Mono<? extends EvaluationContextExtension> getExtension() {
|
||||
return Mono.just(SpelExtension.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "sample";
|
||||
}
|
||||
}
|
||||
|
||||
public enum SpelExtension implements EvaluationContextExtension {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public Object getRootObject() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExtensionId() {
|
||||
return "sample";
|
||||
}
|
||||
|
||||
public boolean hasRole() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,13 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.bson.codecs.DecoderContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.spel.EvaluationContextProvider;
|
||||
import org.springframework.data.spel.ExpressionDependencies;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
@@ -39,6 +41,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
* Unit tests for {@link ParameterBindingJsonReader}.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class ParameterBindingJsonReaderUnitTests {
|
||||
|
||||
@@ -260,6 +263,29 @@ class ParameterBindingJsonReaderUnitTests {
|
||||
.isEqualTo(new Document("name", new Document("$in", Collections.singletonList("dalinar,kohlin"))));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1894
|
||||
void discoversNoDependenciesInExpression() {
|
||||
|
||||
String json = "{ $and : [?#{ [0] == null ? { '$where' : 'true' } : { 'v1' : { '$in' : {[0]} } } }]}";
|
||||
|
||||
Optional<ExpressionDependencies> expressionDependencies = new ParameterBindingDocumentCodec()
|
||||
.getExpressionDependencies(json, it -> new Object(), new SpelExpressionParser());
|
||||
|
||||
assertThat(expressionDependencies).contains(ExpressionDependencies.empty());
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1894
|
||||
void discoversCorrectlyDependenciesInExpression() {
|
||||
|
||||
String json = "{ hello: ?#{hasRole('foo')} }";
|
||||
|
||||
Optional<ExpressionDependencies> expressionDependencies = new ParameterBindingDocumentCodec()
|
||||
.getExpressionDependencies(json, it -> new Object(), new SpelExpressionParser());
|
||||
|
||||
assertThat(expressionDependencies).isNotEmpty();
|
||||
assertThat(expressionDependencies.get()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2523
|
||||
void bindSpelExpressionInArrayCorrectly/* closing bracket must not have leading whitespace! */() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user