Browse Source

Polishing.

Simplify reactive composition. Switch to eager operator evaluation.

See #4712
Original pull request: #4717
pull/4759/head
Mark Paluch 2 years ago
parent
commit
5cb38218d4
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 21
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java
  2. 15
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java
  3. 31
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationOperationUnitTests.java

21
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/AbstractReactiveMongoQuery.java

@ -17,7 +17,6 @@ package org.springframework.data.mongodb.repository.query;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -25,6 +24,7 @@ import java.util.List;
import org.bson.Document; import org.bson.Document;
import org.bson.codecs.configuration.CodecRegistry; import org.bson.codecs.configuration.CodecRegistry;
import org.reactivestreams.Publisher; import org.reactivestreams.Publisher;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.data.mapping.model.EntityInstantiators; import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.mapping.model.SpELExpressionEvaluator; import org.springframework.data.mapping.model.SpELExpressionEvaluator;
@ -231,7 +231,6 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
return method.getTailableAnnotation() != null; return method.getTailableAnnotation() != null;
} }
Query applyQueryMetaAttributesWhenPresent(Query query) { Query applyQueryMetaAttributesWhenPresent(Query query) {
if (method.hasQueryMetaAttributes()) { if (method.hasQueryMetaAttributes()) {
@ -290,7 +289,8 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
} }
/** /**
* If present apply the {@link com.mongodb.ReadPreference} from the {@link org.springframework.data.mongodb.repository.ReadPreference} annotation. * If present apply the {@link com.mongodb.ReadPreference} from the
* {@link org.springframework.data.mongodb.repository.ReadPreference} annotation.
* *
* @param query must not be {@literal null}. * @param query must not be {@literal null}.
* @return never {@literal null}. * @return never {@literal null}.
@ -339,8 +339,8 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
String updateJson = updateSource.update(); String updateJson = updateSource.update();
return getParameterBindingCodec() // return getParameterBindingCodec() //
.flatMap(codec -> expressionEvaluator(updateJson, accessor, codec)) // .flatMap(codec -> expressionEvaluator(updateJson, accessor, codec) //
.map(it -> decode(it.getT1(), updateJson, accessor, it.getT2())) // .map(evaluator -> decode(evaluator, updateJson, accessor, codec))) //
.map(BasicUpdate::fromDocument); .map(BasicUpdate::fromDocument);
} }
if (!ObjectUtils.isEmpty(updateSource.pipeline())) { if (!ObjectUtils.isEmpty(updateSource.pipeline())) {
@ -376,16 +376,17 @@ public abstract class AbstractReactiveMongoQuery implements RepositoryQuery {
private Mono<AggregationOperation> computePipelineStage(String source, MongoParameterAccessor accessor, private Mono<AggregationOperation> computePipelineStage(String source, MongoParameterAccessor accessor,
ParameterBindingDocumentCodec codec) { ParameterBindingDocumentCodec codec) {
return expressionEvaluator(source, accessor, codec).map( return expressionEvaluator(source, accessor, codec).map(evaluator -> new StringAggregationOperation(source,
it -> new StringAggregationOperation(source, AbstractReactiveMongoQuery.this.getQueryMethod().getDomainClass(), bsonString -> AbstractReactiveMongoQuery.this.decode(it.getT1(), bsonString, accessor, it.getT2()))); AbstractReactiveMongoQuery.this.getQueryMethod().getDomainClass(),
bsonString -> AbstractReactiveMongoQuery.this.decode(evaluator, bsonString, accessor, codec)));
} }
private Mono<Tuple2<SpELExpressionEvaluator, ParameterBindingDocumentCodec>> expressionEvaluator(String source, private Mono<SpELExpressionEvaluator> expressionEvaluator(String source, MongoParameterAccessor accessor,
MongoParameterAccessor accessor, ParameterBindingDocumentCodec codec) { ParameterBindingDocumentCodec codec) {
ExpressionDependencies dependencies = codec.captureExpressionDependencies(source, accessor::getBindableValue, ExpressionDependencies dependencies = codec.captureExpressionDependencies(source, accessor::getBindableValue,
expressionParser); expressionParser);
return getSpelEvaluatorFor(dependencies, accessor).zipWith(Mono.just(codec)); return getSpelEvaluatorFor(dependencies, accessor);
} }
private Document decode(SpELExpressionEvaluator expressionEvaluator, String source, MongoParameterAccessor accessor, private Document decode(SpELExpressionEvaluator expressionEvaluator, String source, MongoParameterAccessor accessor,

15
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringAggregationOperation.java

@ -22,9 +22,13 @@ import java.util.regex.Pattern;
import org.bson.Document; import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation; import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext; import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.lang.Nullable;
/** /**
* String-based aggregation operation for a repository query method.
*
* @author Christoph Strobl * @author Christoph Strobl
* @since 4.3.1
*/ */
class StringAggregationOperation implements AggregationOperation { class StringAggregationOperation implements AggregationOperation {
@ -33,12 +37,16 @@ class StringAggregationOperation implements AggregationOperation {
private final String source; private final String source;
private final Class<?> domainType; private final Class<?> domainType;
private final Function<String, Document> bindFunction; private final Function<String, Document> bindFunction;
private final @Nullable String operator;
StringAggregationOperation(String source, Class<?> domainType, Function<String, Document> bindFunction) { StringAggregationOperation(String source, Class<?> domainType, Function<String, Document> bindFunction) {
this.source = source; this.source = source;
this.domainType = domainType; this.domainType = domainType;
this.bindFunction = bindFunction; this.bindFunction = bindFunction;
Matcher matcher = OPERATOR_PATTERN.matcher(source);
this.operator = matcher.find() ? matcher.group() : null;
} }
@Override @Override
@ -48,11 +56,6 @@ class StringAggregationOperation implements AggregationOperation {
@Override @Override
public String getOperator() { public String getOperator() {
return operator != null ? operator : AggregationOperation.super.getOperator();
Matcher matcher = OPERATOR_PATTERN.matcher(source);
if (matcher.find()) {
return matcher.group();
}
return AggregationOperation.super.getOperator();
} }
} }

31
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedAggregationOperationUnitTests.java

@ -15,7 +15,7 @@
*/ */
package org.springframework.data.mongodb.repository.query; package org.springframework.data.mongodb.repository.query;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.*;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.bson.Document; import org.bson.Document;
@ -24,27 +24,28 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
/** /**
* Unit tests for {@link StringBasedAggregation}.
*
* @author Christoph Strobl * @author Christoph Strobl
*/ */
public class StringBasedAggregationOperationUnitTests { public class StringBasedAggregationOperationUnitTests {
@ParameterizedTest // GH-4712 @ParameterizedTest // GH-4712
@ValueSource(strings = { "$project", "'$project'", "\"$project\"" }) @ValueSource(strings = { "$project", "'$project'", "\"$project\"" })
void extractsAggregationOperatorFromAggregationStringWithoutBindingParameters(String operator) { void extractsAggregationOperatorFromAggregationStringWithoutBindingParameters(String operator) {
StringAggregationOperation agg = new StringAggregationOperation("{ %s : { 'fn' : 1 } }".formatted(operator), StringAggregationOperation agg = new StringAggregationOperation("{ %s : { 'fn' : 1 } }".formatted(operator),
Object.class, (it) -> Assertions.fail("o_O Parameter binding")); Object.class, (it) -> Assertions.fail("o_O Parameter binding"));
assertThat(agg.getOperator()).isEqualTo("$project"); assertThat(agg.getOperator()).isEqualTo("$project");
} }
@Test @Test // GH-4712
// GH-4712 void fallbackToParameterBindingIfAggregationOperatorCannotBeExtractedFromAggregationStringWithoutBindingParameters() {
void fallbackToParameterBindingIfAggregationOperatorCannotBeExtractedFromAggregationStringWithoutBindingParameters() {
StringAggregationOperation agg = new StringAggregationOperation("{ happy-madison : { 'fn' : 1 } }", Object.class, StringAggregationOperation agg = new StringAggregationOperation("{ happy-madison : { 'fn' : 1 } }", Object.class,
(it) -> new Document("$project", "")); (it) -> new Document("$project", ""));
assertThat(agg.getOperator()).isEqualTo("$project"); assertThat(agg.getOperator()).isEqualTo("$project");
} }
} }

Loading…
Cancel
Save