Add support for $expr operator.

Also, allow construction of $match with an AggregationExpression.

Closes #3790
This commit is contained in:
divyajnu08
2021-08-29 16:41:52 +05:30
committed by Mark Paluch
parent f24e8e5361
commit e71ec874ab
8 changed files with 187 additions and 3 deletions

View File

@@ -201,4 +201,5 @@ public class AddFieldsOperation extends DocumentEnhancingOperation {
AddFieldsOperationBuilder withValueOfExpression(String operation, Object... values);
}
}
}

View File

@@ -498,7 +498,17 @@ public class Aggregation {
public static MatchOperation match(CriteriaDefinition criteria) {
return new MatchOperation(criteria);
}
/**
* Creates a new {@link MatchOperation}
*
* @return new instance of {@link MatchOperation}.
* @since 1.10
*/
public static MatchOperation match() {
return new MatchOperation();
}
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
* {@code distanceField} defines output field that contains the calculated distance.

View File

@@ -0,0 +1,109 @@
package org.springframework.data.mongodb.core.aggregation;
import org.springframework.util.Assert;
public class EvaluationOperators {
/**
* Take the value resulting from the given fieldReference.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link EvaluationOperatorFactory}.
*/
public static EvaluationOperatorFactory valueOf(String fieldReference) {
return new EvaluationOperatorFactory(fieldReference);
}
/**
* Take the value resulting from the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link EvaluationOperatorFactory}.
*/
public static EvaluationOperatorFactory valueOf(AggregationExpression expression) {
return new EvaluationOperatorFactory(expression);
}
public static class EvaluationOperatorFactory {
private final String fieldReference;
private final AggregationExpression expression;
/**
* Creates new {@link EvaluationOperatorFactory} for given {@literal fieldReference}.
*
* @param fieldReference must not be {@literal null}.
*/
public EvaluationOperatorFactory(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
this.fieldReference = fieldReference;
this.expression = null;
}
/**
* Creates new {@link EvaluationOperatorFactory} for given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
*/
public EvaluationOperatorFactory(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
this.fieldReference = null;
this.expression = expression;
}
/**
* Creates new {@link AggregationExpression} that is a valid aggregation expression.
*
* @return new instance of {@link Expr}.
*/
public Expr expr() {
return usesFieldRef() ? Expr.valueOf(fieldReference) : Expr.valueOf(expression);
}
public static class Expr extends AbstractAggregationExpression {
private Expr(Object value) {
super(value);
}
@Override
protected String getMongoMethod() {
return "$expr";
}
/**
* Creates new {@link Expr}.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Expr}.
*/
public static Expr valueOf(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new Expr(Fields.field(fieldReference));
}
/**
* Creates new {@link Expr}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Expr}.
*/
public static Expr valueOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new Expr(expression);
}
}
private boolean usesFieldRef() {
return fieldReference != null;
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.core.aggregation;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.EvaluationOperators.EvaluationOperatorFactory.Expr;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.util.Assert;
@@ -36,7 +37,16 @@ import org.springframework.util.Assert;
public class MatchOperation implements AggregationOperation {
private final CriteriaDefinition criteriaDefinition;
private final AggregationExpression expression;
/**
* Creates a new {@link MatchOperation}
*/
public MatchOperation() {
this.criteriaDefinition = null;
this.expression = null;
}
/**
* Creates a new {@link MatchOperation} for the given {@link CriteriaDefinition}.
*
@@ -46,14 +56,39 @@ public class MatchOperation implements AggregationOperation {
Assert.notNull(criteriaDefinition, "Criteria must not be null!");
this.criteriaDefinition = criteriaDefinition;
this.expression = null;
}
/**
* Creates a new {@link MatchOperation} for the given {@link Expression}.
*
* @param criteriaDefinition must not be {@literal null}.
*/
private MatchOperation(Expr expression) {
Assert.notNull(expression, "Expression must not be null!");
this.criteriaDefinition = null;
this.expression = expression;
}
/**
* Creates a new {@link MatchOperation} for the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
*/
public MatchOperation withValueOf(AggregationExpression expression) {
Assert.notNull(expression, "Expression must not be null!");
return new MatchOperation(EvaluationOperators.valueOf(expression).expr());
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
if(expression != null) {
return new Document(getOperator(), expression.toDocument());
}
return new Document(getOperator(), context.getMappedObject(criteriaDefinition.getCriteriaObject()));
}

View File

@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.List;
import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.expression.spel.ast.Projection;
import org.springframework.util.Assert;

View File

@@ -193,5 +193,6 @@ public class SetOperation extends DocumentEnhancingOperation {
*/
SetOperation withValueOfExpression(String operation, Object... values);
}
}
}

View File

@@ -0,0 +1,26 @@
package org.springframework.data.mongodb.core.aggregation;
import static org.assertj.core.api.Assertions.*;
import org.bson.Document;
import org.junit.jupiter.api.Test;
class MatchOperationUnitTests {
@Test // DATAMONGO - 3729
public void shouldRenderStdDevPopCorrectly() {
MatchOperation operation = Aggregation.match().withValueOf(ArithmeticOperators.valueOf("quiz").stdDevPop());
assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).
isEqualTo(Document.parse("{ $match: { \"$expr\" : { \"$stdDevPop\" : \"$quiz\" } } } "));
}
@Test // DATAMONGO - 3729
public void shouldRenderStdDevSampCorrectly() {
MatchOperation operation = Aggregation.match().withValueOf(ArithmeticOperators.valueOf("quiz").stdDevSamp());
assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).
isEqualTo(Document.parse("{ $match: { \"$expr\" : { \"$stdDevSamp\" : \"$quiz\" } } } "));
}
}

View File

@@ -21,6 +21,7 @@ import java.util.List;
import org.bson.Document;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.convert.QueryMapper;