DATAMONGO-1542 - Refactor CondOperator and IfNullOperator to children of AggregationExpressions.
Renamed CondOperator to Cond and IfNullOperator to IfNull. Both conditional operations are now available from ConditionalOperators.when and ConditionalOperators.ifNull and accept AggregationExpressions for conditions and values. Original Pull Request: #421
This commit is contained in:
committed by
Christoph Strobl
parent
ea4782c421
commit
1a11877ae9
@@ -23,11 +23,11 @@ import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.*;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
import org.springframework.data.mongodb.core.query.NearQuery;
|
||||
@@ -396,68 +396,6 @@ public class Aggregation {
|
||||
return new LookupOperation(from, localField, foreignField, as);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link IfNullOperator} for the given {@code field} and {@code replacement} value.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param replacement must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public static IfNullOperator ifNull(String field, Object replacement) {
|
||||
return IfNullOperator.newBuilder().ifNull(field).thenReplaceWith(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link IfNullOperator} for the given {@link Field} and {@link Field} to obtain a value from.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param replacement must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public static IfNullOperator ifNull(Field field, Field replacement) {
|
||||
return IfNullOperator.newBuilder().ifNull(field).thenReplaceWith(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link IfNullOperator} for the given {@link Field} and {@code replacement} value.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param replacement must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public static IfNullOperator ifNull(Field field, Object replacement) {
|
||||
return IfNullOperator.newBuilder().ifNull(field).thenReplaceWith(replacement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConditionalOperator} for the given {@link Field} that holds a {@literal boolean} value.
|
||||
*
|
||||
* @param booleanField must not be {@literal null}.
|
||||
* @param then must not be {@literal null}.
|
||||
* @param otherwise must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public static ConditionalOperator conditional(Field booleanField, Object then, Object otherwise) {
|
||||
return ConditionalOperator.newBuilder().when(booleanField).then(then).otherwise(otherwise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConditionalOperator} for the given {@link Criteria}.
|
||||
*
|
||||
* @param criteria must not be {@literal null}.
|
||||
* @param then must not be {@literal null}.
|
||||
* @param otherwise must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public static ConditionalOperator conditional(Criteria criteria, Object then, Object otherwise) {
|
||||
return ConditionalOperator.newBuilder().when(criteria).then(then).otherwise(otherwise);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Fields} instance for the given field names.
|
||||
*
|
||||
|
||||
@@ -21,10 +21,15 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.AsBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -93,7 +98,7 @@ public interface AggregationExpressions {
|
||||
private final AggregationExpression expression;
|
||||
|
||||
/**
|
||||
* Creates new {@link ComparisonOperatorFactory} for given {@literal fieldReference}.
|
||||
* Creates new {@link BooleanOperatorFactory} for given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
*/
|
||||
@@ -105,7 +110,7 @@ public interface AggregationExpressions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creats new {@link ComparisonOperatorFactory} for given {@link AggregationExpression}.
|
||||
* Creates new {@link BooleanOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
@@ -191,6 +196,178 @@ public interface AggregationExpressions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gateway to {@literal conditional expressions} that evaluate their argument expressions as booleans to a value.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class ConditionalOperators {
|
||||
|
||||
/**
|
||||
* Take the field referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ConditionalOperatorFactory when(String fieldReference) {
|
||||
return new ConditionalOperatorFactory(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the value resulting from the given {@literal expression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ConditionalOperatorFactory when(AggregationExpression expression) {
|
||||
return new ConditionalOperatorFactory(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the value resulting from the given {@literal criteriaDefinition}.
|
||||
*
|
||||
* @param criteriaDefinition must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ConditionalOperatorFactory when(CriteriaDefinition criteriaDefinition) {
|
||||
return new ConditionalOperatorFactory(criteriaDefinition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpressions} that evaluates an expression and returns the value of the expression
|
||||
* if the expression evaluates to a non-null value. If the expression evaluates to a {@literal null} value,
|
||||
* including instances of undefined values or missing fields, returns the value of the replacement expression.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IfNull.ThenBuilder ifNull(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return IfNull.ifNull(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpressions} that evaluates an expression and returns the value of the expression
|
||||
* if the expression evaluates to a non-null value. If the expression evaluates to a {@literal null} value,
|
||||
* including instances of undefined values or missing fields, returns the value of the replacement expression.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static IfNull.ThenBuilder ifNull(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return IfNull.ifNull(expression);
|
||||
}
|
||||
|
||||
public static class ConditionalOperatorFactory {
|
||||
|
||||
private final String fieldReference;
|
||||
private final AggregationExpression expression;
|
||||
private final CriteriaDefinition criteriaDefinition;
|
||||
|
||||
/**
|
||||
* Creates new {@link ConditionalOperatorFactory} for given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperatorFactory(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
|
||||
this.fieldReference = fieldReference;
|
||||
this.expression = null;
|
||||
this.criteriaDefinition = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link ConditionalOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperatorFactory(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
|
||||
this.fieldReference = null;
|
||||
this.expression = expression;
|
||||
this.criteriaDefinition = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link ConditionalOperatorFactory} for given {@link CriteriaDefinition}.
|
||||
*
|
||||
* @param criteriaDefinition must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperatorFactory(CriteriaDefinition criteriaDefinition) {
|
||||
|
||||
Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null!");
|
||||
|
||||
this.fieldReference = null;
|
||||
this.expression = null;
|
||||
this.criteriaDefinition = criteriaDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpression} that evaluates a boolean expression to return one of the two
|
||||
* specified return expressions.
|
||||
*
|
||||
* @param value must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public OtherwiseBuilder then(Object value) {
|
||||
|
||||
Assert.notNull(value, "Value must not be null!");
|
||||
return createThenBuilder().then(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpressions} that evaluates a boolean expression to return one of the two
|
||||
* specified return expressions.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public OtherwiseBuilder thenValueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return createThenBuilder().then(expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link AggregationExpressions} that evaluates a boolean expression to return one of the two
|
||||
* specified return expressions.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public OtherwiseBuilder thenValueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return createThenBuilder().then(fieldReference);
|
||||
}
|
||||
|
||||
private ThenBuilder createThenBuilder() {
|
||||
|
||||
if (usesFieldRef()) {
|
||||
return Cond.newBuilder().when(fieldReference);
|
||||
}
|
||||
|
||||
return usesCriteriaDefinition() ? Cond.newBuilder().when(criteriaDefinition)
|
||||
: Cond.newBuilder().when(expression);
|
||||
}
|
||||
|
||||
private boolean usesFieldRef() {
|
||||
return this.fieldReference != null;
|
||||
}
|
||||
|
||||
private boolean usesCriteriaDefinition() {
|
||||
return this.criteriaDefinition != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gateway to {@literal Set expressions} which perform {@literal set} operation on arrays, treating arrays as sets.
|
||||
*
|
||||
@@ -411,7 +588,7 @@ public interface AggregationExpressions {
|
||||
class ComparisonOperators {
|
||||
|
||||
/**
|
||||
* Take the array referenced by given {@literal fieldReference}.
|
||||
* Take the field referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
@@ -421,13 +598,13 @@ public interface AggregationExpressions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the array referenced by given {@literal fieldReference}.
|
||||
* Take the value resulting from the given {@literal expression}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ComparisonOperatorFactory valueOf(AggregationExpression fieldReference) {
|
||||
return new ComparisonOperatorFactory(fieldReference);
|
||||
public static ComparisonOperatorFactory valueOf(AggregationExpression expression) {
|
||||
return new ComparisonOperatorFactory(expression);
|
||||
}
|
||||
|
||||
public static class ComparisonOperatorFactory {
|
||||
@@ -448,7 +625,7 @@ public interface AggregationExpressions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creats new {@link ComparisonOperatorFactory} for given {@link AggregationExpression}.
|
||||
* Creates new {@link ComparisonOperatorFactory} for given {@link AggregationExpression}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
*/
|
||||
@@ -730,7 +907,7 @@ public interface AggregationExpressions {
|
||||
class ArithmeticOperators {
|
||||
|
||||
/**
|
||||
* Take the array referenced by given {@literal fieldReference}.
|
||||
* Take the field referenced by given {@literal fieldReference}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return
|
||||
@@ -740,13 +917,13 @@ public interface AggregationExpressions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the array referenced by given {@literal fieldReference}.
|
||||
* Take the value resulting from the given {@literal expression}.
|
||||
*
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ArithmeticOperatorFactory valueOf(AggregationExpression fieldReference) {
|
||||
return new ArithmeticOperatorFactory(fieldReference);
|
||||
public static ArithmeticOperatorFactory valueOf(AggregationExpression expression) {
|
||||
return new ArithmeticOperatorFactory(expression);
|
||||
}
|
||||
|
||||
public static class ArithmeticOperatorFactory {
|
||||
@@ -2274,7 +2451,7 @@ public interface AggregationExpressions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creats new {@link AnyElementTrue}.
|
||||
* Creates new {@link AnyElementTrue}.
|
||||
*
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return
|
||||
@@ -5878,4 +6055,605 @@ public interface AggregationExpressions {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $ifNull} operator. Replacement values can be either {@link Field
|
||||
* field references}, {@link AggregationExpression expressions}, values of simple MongoDB types or values that can be
|
||||
* converted to a simple MongoDB type.
|
||||
*
|
||||
* @see http://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
class IfNull implements AggregationExpression {
|
||||
|
||||
private final Object condition;
|
||||
private final Object value;
|
||||
|
||||
private IfNull(Object condition, Object value) {
|
||||
|
||||
this.condition = condition;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IfNull}.
|
||||
*
|
||||
* @param fieldReference the field to check for a {@literal null} value, field reference must not be
|
||||
* {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ThenBuilder ifNull(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new IfNullOperatorBuilder().ifNull(fieldReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link IfNull}.
|
||||
*
|
||||
* @param expression the expression to check for a {@literal null} value, field reference must not be
|
||||
* {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static ThenBuilder ifNull(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new IfNullOperatorBuilder().ifNull(expression);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
|
||||
if (condition instanceof Field) {
|
||||
list.add(context.getReference((Field) condition).toString());
|
||||
} else if (condition instanceof AggregationExpression) {
|
||||
list.add(((AggregationExpression) condition).toDbObject(context));
|
||||
} else {
|
||||
list.add(condition);
|
||||
}
|
||||
|
||||
list.add(resolve(value, context));
|
||||
|
||||
return new BasicDBObject("$ifNull", list);
|
||||
}
|
||||
|
||||
private Object resolve(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof Field) {
|
||||
return context.getReference((Field) value).toString();
|
||||
} else if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
} else if (value instanceof DBObject) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static interface IfNullBuilder {
|
||||
|
||||
/**
|
||||
* @param fieldReference the field to check for a {@literal null} value, field reference must not be
|
||||
* {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder ifNull(String fieldReference);
|
||||
|
||||
/**
|
||||
* @param expression the expression to check for a {@literal null} value, field name must not be {@literal null}
|
||||
* or empty.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder ifNull(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static interface ThenBuilder {
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the {@code $ifNull} condition evaluates {@literal true}. Can be a
|
||||
* {@link DBObject}, a value that is supported by MongoDB or a value that can be converted to a MongoDB
|
||||
* representation but must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
IfNull then(Object value);
|
||||
|
||||
/**
|
||||
* @param fieldReference the field holding the replacement value, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
IfNull thenValueOf(String fieldReference);
|
||||
|
||||
/**
|
||||
* @param expression the expression yielding to the replacement value, must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public IfNull thenValueOf(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link IfNullOperator} creation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static final class IfNullOperatorBuilder implements IfNullBuilder, ThenBuilder {
|
||||
|
||||
private Object condition;
|
||||
|
||||
private IfNullOperatorBuilder() {}
|
||||
|
||||
/**
|
||||
* Creates a new builder for {@link IfNullOperator}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static IfNullOperatorBuilder newBuilder() {
|
||||
return new IfNullOperatorBuilder();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.IfNullBuilder#ifNull(java.lang.String)
|
||||
*/
|
||||
public ThenBuilder ifNull(String fieldReference) {
|
||||
|
||||
Assert.hasText(fieldReference, "FieldReference name must not be null or empty!");
|
||||
this.condition = Fields.field(fieldReference);
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.IfNullBuilder#ifNull(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder ifNull(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression name must not be null or empty!");
|
||||
this.condition = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#then(java.lang.Object)
|
||||
*/
|
||||
public IfNull then(Object value) {
|
||||
return new IfNull(condition, value);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#thenValueOf(java.lang.String)
|
||||
*/
|
||||
public IfNull thenValueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new IfNull(condition, Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#thenValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
public IfNull thenValueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "Expression must not be null!");
|
||||
return new IfNull(condition, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $cond} operator. A {@link Cond} allows nested conditions
|
||||
* {@code if-then[if-then-else]-else} using {@link Field}, {@link CriteriaDefinition}, {@link AggregationExpression}
|
||||
* or a {@link DBObject custom} condition. Replacement values can be either {@link Field field references},
|
||||
* {@link AggregationExpression expressions}, values of simple MongoDB types or values that can be converted to a
|
||||
* simple MongoDB type.
|
||||
*
|
||||
* @see http://docs.mongodb.com/manual/reference/operator/aggregation/cond/
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
class Cond implements AggregationExpression {
|
||||
|
||||
private final Object condition;
|
||||
private final Object thenValue;
|
||||
private final Object otherwiseValue;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Cond} for a given {@link Field} and {@code then}/{@code otherwise} values.
|
||||
*
|
||||
* @param condition must not be {@literal null}.
|
||||
* @param thenValue must not be {@literal null}.
|
||||
* @param otherwiseValue must not be {@literal null}.
|
||||
*/
|
||||
private Cond(Field condition, Object thenValue, Object otherwiseValue) {
|
||||
this((Object) condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Cond} for a given {@link CriteriaDefinition} and {@code then}/{@code otherwise} values.
|
||||
*
|
||||
* @param condition must not be {@literal null}.
|
||||
* @param thenValue must not be {@literal null}.
|
||||
* @param otherwiseValue must not be {@literal null}.
|
||||
*/
|
||||
private Cond(CriteriaDefinition condition, Object thenValue, Object otherwiseValue) {
|
||||
this((Object) condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
private Cond(Object condition, Object thenValue, Object otherwiseValue) {
|
||||
|
||||
Assert.notNull(condition, "Condition must not be null!");
|
||||
Assert.notNull(thenValue, "Then value must not be null!");
|
||||
Assert.notNull(otherwiseValue, "Otherwise value must not be null!");
|
||||
|
||||
assertNotBuilder(condition, "Condition");
|
||||
assertNotBuilder(thenValue, "Then value");
|
||||
assertNotBuilder(otherwiseValue, "Otherwise value");
|
||||
|
||||
this.condition = condition;
|
||||
this.thenValue = thenValue;
|
||||
this.otherwiseValue = otherwiseValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
BasicDBObject condObject = new BasicDBObject();
|
||||
|
||||
condObject.append("if", resolveCriteria(context, condition));
|
||||
condObject.append("then", resolveValue(context, thenValue));
|
||||
condObject.append("else", resolveValue(context, otherwiseValue));
|
||||
|
||||
return new BasicDBObject("$cond", condObject);
|
||||
}
|
||||
|
||||
private Object resolveValue(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject || value instanceof Field) {
|
||||
return resolve(context, value);
|
||||
}
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
}
|
||||
|
||||
return context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
|
||||
}
|
||||
|
||||
private Object resolveCriteria(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject || value instanceof Field) {
|
||||
return resolve(context, value);
|
||||
}
|
||||
|
||||
if (value instanceof AggregationExpression) {
|
||||
return ((AggregationExpression) value).toDbObject(context);
|
||||
}
|
||||
|
||||
if (value instanceof CriteriaDefinition) {
|
||||
|
||||
DBObject mappedObject = context.getMappedObject(((CriteriaDefinition) value).getCriteriaObject());
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
clauses.addAll(getClauses(context, mappedObject));
|
||||
|
||||
return clauses.size() == 1 ? clauses.get(0) : clauses;
|
||||
}
|
||||
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String.format("Invalid value in condition. Supported: DBObject, Field references, Criteria, got: %s", value));
|
||||
}
|
||||
|
||||
private List<Object> getClauses(AggregationOperationContext context, DBObject mappedObject) {
|
||||
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
for (String key : mappedObject.keySet()) {
|
||||
|
||||
Object predicate = mappedObject.get(key);
|
||||
clauses.addAll(getClauses(context, key, predicate));
|
||||
}
|
||||
|
||||
return clauses;
|
||||
}
|
||||
|
||||
private List<Object> getClauses(AggregationOperationContext context, String key, Object predicate) {
|
||||
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
if (predicate instanceof List) {
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
for (Object clause : (List<?>) predicate) {
|
||||
if (clause instanceof DBObject) {
|
||||
args.addAll(getClauses(context, (DBObject) clause));
|
||||
}
|
||||
}
|
||||
|
||||
clauses.add(new BasicDBObject(key, args));
|
||||
|
||||
} else if (predicate instanceof DBObject) {
|
||||
|
||||
DBObject nested = (DBObject) predicate;
|
||||
|
||||
for (String s : nested.keySet()) {
|
||||
|
||||
if (!isKeyword(s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add("$" + key);
|
||||
args.add(nested.get(s));
|
||||
clauses.add(new BasicDBObject(s, args));
|
||||
}
|
||||
|
||||
} else if (!isKeyword(key)) {
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add("$" + key);
|
||||
args.add(predicate);
|
||||
clauses.add(new BasicDBObject("$eq", args));
|
||||
}
|
||||
|
||||
return clauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link String} is a MongoDB keyword.
|
||||
*
|
||||
* @param candidate
|
||||
* @return
|
||||
*/
|
||||
private boolean isKeyword(String candidate) {
|
||||
return candidate.startsWith("$");
|
||||
}
|
||||
|
||||
private Object resolve(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject) {
|
||||
return context.getMappedObject((DBObject) value);
|
||||
}
|
||||
|
||||
return context.getReference((Field) value).toString();
|
||||
}
|
||||
|
||||
private void assertNotBuilder(Object toCheck, String name) {
|
||||
Assert.isTrue(!ClassUtils.isAssignableValue(ConditionalExpressionBuilder.class, toCheck),
|
||||
String.format("%s must not be of type %s", name, ConditionalExpressionBuilder.class.getSimpleName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a builder that allows fluent creation of {@link Cond}.
|
||||
*
|
||||
* @return a new {@link ConditionalExpressionBuilder}.
|
||||
*/
|
||||
public static ConditionalExpressionBuilder newBuilder() {
|
||||
return ConditionalExpressionBuilder.newBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static interface WhenBuilder {
|
||||
|
||||
/**
|
||||
* @param booleanExpression expression that yields in a boolean result, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(DBObject booleanExpression);
|
||||
|
||||
/**
|
||||
* @param expression expression that yields in a boolean result, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(AggregationExpression expression);
|
||||
|
||||
/**
|
||||
* @param booleanField name of a field holding a boolean value, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(String booleanField);
|
||||
|
||||
/**
|
||||
* @param criteria criteria to evaluate, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(CriteriaDefinition criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static interface ThenBuilder {
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the condition evaluates {@literal true}. Can be a {@link DBObject}, a
|
||||
* value that is supported by MongoDB or a value that can be converted to a MongoDB representation but
|
||||
* must not be {@literal null}.
|
||||
* @return the {@link OtherwiseBuilder}
|
||||
*/
|
||||
OtherwiseBuilder then(Object value);
|
||||
|
||||
/**
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return the {@link OtherwiseBuilder}
|
||||
*/
|
||||
OtherwiseBuilder thenValueOf(String fieldReference);
|
||||
|
||||
/**
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return the {@link OtherwiseBuilder}
|
||||
*/
|
||||
OtherwiseBuilder thenValueOf(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public static interface OtherwiseBuilder {
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the condition evaluates {@literal false}. Can be a {@link DBObject}, a
|
||||
* value that is supported by MongoDB or a value that can be converted to a MongoDB representation but
|
||||
* must not be {@literal null}.
|
||||
* @return the {@link Cond}
|
||||
*/
|
||||
Cond otherwise(Object value);
|
||||
|
||||
/**
|
||||
* @param fieldReference must not be {@literal null}.
|
||||
* @return the {@link Cond}
|
||||
*/
|
||||
Cond otherwiseValueOf(String fieldReference);
|
||||
|
||||
/**
|
||||
* @param expression must not be {@literal null}.
|
||||
* @return the {@link Cond}
|
||||
*/
|
||||
Cond otherwiseValueOf(AggregationExpression expression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link Cond} creation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
static class ConditionalExpressionBuilder implements WhenBuilder, ThenBuilder, OtherwiseBuilder {
|
||||
|
||||
private Object condition;
|
||||
private Object thenValue;
|
||||
|
||||
private ConditionalExpressionBuilder() {}
|
||||
|
||||
/**
|
||||
* Creates a new builder for {@link Cond}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static ConditionalExpressionBuilder newBuilder() {
|
||||
return new ConditionalExpressionBuilder();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(com.mongodb.DBObject)
|
||||
*/
|
||||
@Override
|
||||
public ConditionalExpressionBuilder when(DBObject booleanExpression) {
|
||||
|
||||
Assert.notNull(booleanExpression, "'Boolean expression' must not be null!");
|
||||
|
||||
this.condition = booleanExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(org.springframework.data.mongodb.core.query.CriteriaDefinition)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(CriteriaDefinition criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
this.condition = criteria;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression field must not be null!");
|
||||
this.condition = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(String booleanField) {
|
||||
|
||||
Assert.hasText(booleanField, "Boolean field name must not be null or empty!");
|
||||
this.condition = Fields.field(booleanField);
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#then(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public OtherwiseBuilder then(Object thenValue) {
|
||||
|
||||
Assert.notNull(thenValue, "Then-value must not be null!");
|
||||
this.thenValue = thenValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#thenValueOf(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public OtherwiseBuilder thenValueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
this.thenValue = Fields.field(fieldReference);
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#thenValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public OtherwiseBuilder thenValueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression must not be null!");
|
||||
this.thenValue = expression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwise(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public Cond otherwise(Object otherwiseValue) {
|
||||
|
||||
Assert.notNull(otherwiseValue, "Value must not be null!");
|
||||
return new Cond(condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwiseValueOf(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public Cond otherwiseValueOf(String fieldReference) {
|
||||
|
||||
Assert.notNull(fieldReference, "FieldReference must not be null!");
|
||||
return new Cond(condition, thenValue, Fields.field(fieldReference));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwiseValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
|
||||
*/
|
||||
@Override
|
||||
public Cond otherwiseValueOf(AggregationExpression expression) {
|
||||
|
||||
Assert.notNull(expression, "AggregationExpression must not be null!");
|
||||
return new Cond(condition, thenValue, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,394 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.core.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $cond} operator. A {@link ConditionalOperator} allows nested conditions
|
||||
* {@code if-then[if-then-else]-else} using {@link Field}, {@link CriteriaDefinition} or a {@link DBObject custom}
|
||||
* condition. Replacement values can be either {@link Field field references}, values of simple MongoDB types or values
|
||||
* that can be converted to a simple MongoDB type.
|
||||
*
|
||||
* @see http://docs.mongodb.com/manual/reference/operator/aggregation/cond/
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @since 1.10
|
||||
*/
|
||||
public class ConditionalOperator implements AggregationExpression {
|
||||
|
||||
private final Object condition;
|
||||
private final Object thenValue;
|
||||
private final Object otherwiseValue;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConditionalOperator} for a given {@link Field} and {@code then}/{@code otherwise} values.
|
||||
*
|
||||
* @param condition must not be {@literal null}.
|
||||
* @param thenValue must not be {@literal null}.
|
||||
* @param otherwiseValue must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperator(Field condition, Object thenValue, Object otherwiseValue) {
|
||||
this((Object) condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConditionalOperator} for a given {@link CriteriaDefinition} and {@code then}/{@code otherwise}
|
||||
* values.
|
||||
*
|
||||
* @param condition must not be {@literal null}.
|
||||
* @param thenValue must not be {@literal null}.
|
||||
* @param otherwiseValue must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperator(CriteriaDefinition condition, Object thenValue, Object otherwiseValue) {
|
||||
this((Object) condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ConditionalOperator} for a given {@link DBObject criteria} and {@code then}/{@code otherwise}
|
||||
* values.
|
||||
*
|
||||
* @param condition must not be {@literal null}.
|
||||
* @param thenValue must not be {@literal null}.
|
||||
* @param otherwiseValue must not be {@literal null}.
|
||||
*/
|
||||
public ConditionalOperator(DBObject condition, Object thenValue, Object otherwiseValue) {
|
||||
this((Object) condition, thenValue, otherwiseValue);
|
||||
}
|
||||
|
||||
private ConditionalOperator(Object condition, Object thenValue, Object otherwiseValue) {
|
||||
|
||||
Assert.notNull(condition, "Condition must not be null!");
|
||||
Assert.notNull(thenValue, "'Then value' must not be null!");
|
||||
Assert.notNull(otherwiseValue, "'Otherwise value' must not be null!");
|
||||
|
||||
assertNotBuilder(condition, "Condition");
|
||||
assertNotBuilder(thenValue, "'Then value'");
|
||||
assertNotBuilder(otherwiseValue, "'Otherwise value'");
|
||||
|
||||
this.condition = condition;
|
||||
this.thenValue = thenValue;
|
||||
this.otherwiseValue = otherwiseValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
BasicDBObject condObject = new BasicDBObject();
|
||||
|
||||
condObject.append("if", resolveCriteria(context, condition));
|
||||
condObject.append("then", resolveValue(context, thenValue));
|
||||
condObject.append("else", resolveValue(context, otherwiseValue));
|
||||
|
||||
return new BasicDBObject("$cond", condObject);
|
||||
}
|
||||
|
||||
private Object resolveValue(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject || value instanceof Field) {
|
||||
return resolve(context, value);
|
||||
}
|
||||
|
||||
if (value instanceof ConditionalOperator) {
|
||||
return ((ConditionalOperator) value).toDbObject(context);
|
||||
}
|
||||
|
||||
return context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
|
||||
}
|
||||
|
||||
private Object resolveCriteria(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject || value instanceof Field) {
|
||||
return resolve(context, value);
|
||||
}
|
||||
|
||||
if (value instanceof CriteriaDefinition) {
|
||||
|
||||
DBObject mappedObject = context.getMappedObject(((CriteriaDefinition) value).getCriteriaObject());
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
clauses.addAll(getClauses(context, mappedObject));
|
||||
|
||||
if (clauses.size() == 1) {
|
||||
return clauses.get(0);
|
||||
}
|
||||
|
||||
return clauses;
|
||||
}
|
||||
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
String.format("Invalid value in condition. Supported: DBObject, Field references, Criteria, got: %s", value));
|
||||
}
|
||||
|
||||
private List<Object> getClauses(AggregationOperationContext context, DBObject mappedObject) {
|
||||
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
for (String key : mappedObject.keySet()) {
|
||||
|
||||
Object predicate = mappedObject.get(key);
|
||||
clauses.addAll(getClauses(context, key, predicate));
|
||||
}
|
||||
|
||||
return clauses;
|
||||
}
|
||||
|
||||
private List<Object> getClauses(AggregationOperationContext context, String key, Object predicate) {
|
||||
|
||||
List<Object> clauses = new ArrayList<Object>();
|
||||
|
||||
if (predicate instanceof List) {
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
for (Object clause : (List<?>) predicate) {
|
||||
if (clause instanceof DBObject) {
|
||||
args.addAll(getClauses(context, (DBObject) clause));
|
||||
}
|
||||
}
|
||||
|
||||
clauses.add(new BasicDBObject(key, args));
|
||||
|
||||
} else if (predicate instanceof DBObject) {
|
||||
|
||||
DBObject nested = (DBObject) predicate;
|
||||
|
||||
for (String s : nested.keySet()) {
|
||||
|
||||
if (!isKeyword(s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add("$" + key);
|
||||
args.add(nested.get(s));
|
||||
clauses.add(new BasicDBObject(s, args));
|
||||
}
|
||||
|
||||
} else if (!isKeyword(key)) {
|
||||
|
||||
List<Object> args = new ArrayList<Object>();
|
||||
args.add("$" + key);
|
||||
args.add(predicate);
|
||||
clauses.add(new BasicDBObject("$eq", args));
|
||||
}
|
||||
|
||||
return clauses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given {@link String} is a MongoDB keyword.
|
||||
*
|
||||
* @param candidate
|
||||
* @return
|
||||
*/
|
||||
private boolean isKeyword(String candidate) {
|
||||
return candidate.startsWith("$");
|
||||
}
|
||||
|
||||
private Object resolve(AggregationOperationContext context, Object value) {
|
||||
|
||||
if (value instanceof DBObject) {
|
||||
return context.getMappedObject((DBObject) value);
|
||||
}
|
||||
|
||||
return context.getReference((Field) value).toString();
|
||||
}
|
||||
|
||||
private void assertNotBuilder(Object toCheck, String name) {
|
||||
Assert.isTrue(!ClassUtils.isAssignableValue(ConditionalExpressionBuilder.class, toCheck),
|
||||
String.format("%s must not be of type %s", name, ConditionalExpressionBuilder.class.getSimpleName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a builder that allows fluent creation of {@link ConditionalOperator}.
|
||||
*
|
||||
* @return a new {@link ConditionalExpressionBuilder}.
|
||||
*/
|
||||
public static ConditionalExpressionBuilder newBuilder() {
|
||||
return ConditionalExpressionBuilder.newBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.10
|
||||
*/
|
||||
public static interface WhenBuilder {
|
||||
|
||||
/**
|
||||
* @param booleanExpression expression that yields in a boolean result, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(DBObject booleanExpression);
|
||||
|
||||
/**
|
||||
* @param booleanField reference to a field holding a boolean value, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(Field booleanField);
|
||||
|
||||
/**
|
||||
* @param booleanField name of a field holding a boolean value, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(String booleanField);
|
||||
|
||||
/**
|
||||
* @param criteria criteria to evaluate, must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder when(CriteriaDefinition criteria);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.10
|
||||
*/
|
||||
public static interface ThenBuilder {
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the condition evaluates {@literal true}. Can be a {@link DBObject}, a value
|
||||
* that is supported by MongoDB or a value that can be converted to a MongoDB representation but must not
|
||||
* be {@literal null}.
|
||||
* @return the {@link OtherwiseBuilder}
|
||||
*/
|
||||
OtherwiseBuilder then(Object value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.10
|
||||
*/
|
||||
public static interface OtherwiseBuilder {
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the condition evaluates {@literal false}. Can be a {@link DBObject}, a value
|
||||
* that is supported by MongoDB or a value that can be converted to a MongoDB representation but must not
|
||||
* be {@literal null}.
|
||||
* @return the {@link ConditionalOperator}
|
||||
*/
|
||||
ConditionalOperator otherwise(Object value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link ConditionalOperator} creation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public static final class ConditionalExpressionBuilder implements WhenBuilder, ThenBuilder, OtherwiseBuilder {
|
||||
|
||||
private Object condition;
|
||||
private Object thenValue;
|
||||
|
||||
private ConditionalExpressionBuilder() {}
|
||||
|
||||
/**
|
||||
* Creates a new builder for {@link ConditionalOperator}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static ConditionalExpressionBuilder newBuilder() {
|
||||
return new ConditionalExpressionBuilder();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.WhenBuilder#when(com.mongodb.DBObject)
|
||||
*/
|
||||
@Override
|
||||
public ConditionalExpressionBuilder when(DBObject booleanExpression) {
|
||||
|
||||
Assert.notNull(booleanExpression, "'Boolean expression' must not be null!");
|
||||
|
||||
this.condition = booleanExpression;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.WhenBuilder#when(org.springframework.data.mongodb.core.query.CriteriaDefinition)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(CriteriaDefinition criteria) {
|
||||
|
||||
Assert.notNull(criteria, "Criteria must not be null!");
|
||||
|
||||
this.condition = criteria;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.WhenBuilder#when(org.springframework.data.mongodb.core.aggregation.Field)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(Field booleanField) {
|
||||
|
||||
Assert.notNull(booleanField, "Boolean field must not be null!");
|
||||
|
||||
this.condition = booleanField;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.WhenBuilder#when(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ThenBuilder when(String booleanField) {
|
||||
|
||||
Assert.hasText(booleanField, "Boolean field name must not be null or empty!");
|
||||
|
||||
this.condition = Fields.field(booleanField);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.ThenBuilder#then(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public OtherwiseBuilder then(Object thenValue) {
|
||||
|
||||
Assert.notNull(thenValue, "'Then-value' must not be null!");
|
||||
|
||||
this.thenValue = thenValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.OtherwiseBuilder#otherwise(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public ConditionalOperator otherwise(Object otherwiseValue) {
|
||||
|
||||
Assert.notNull(otherwiseValue, "'Otherwise-value' must not be null!");
|
||||
|
||||
return new ConditionalOperator(condition, thenValue, otherwiseValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.core.aggregation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $ifNull} operator. Replacement values can be either {@link Field field
|
||||
* references}, values of simple MongoDB types or values that can be converted to a simple MongoDB type.
|
||||
*
|
||||
* @see http://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public class IfNullOperator implements AggregationExpression {
|
||||
|
||||
private final Field field;
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
* Creates a new {@link IfNullOperator} for the given {@link Field} and replacement {@code value}.
|
||||
*
|
||||
* @param field must not be {@literal null}.
|
||||
* @param value must not be {@literal null}.
|
||||
*/
|
||||
public IfNullOperator(Field field, Object value) {
|
||||
|
||||
Assert.notNull(field, "Field must not be null!");
|
||||
Assert.notNull(value, "'Replacement-value' must not be null!");
|
||||
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDbObject(AggregationOperationContext context) {
|
||||
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
|
||||
list.add(context.getReference(field).toString());
|
||||
list.add(resolve(value, context));
|
||||
|
||||
return new BasicDBObject("$ifNull", list);
|
||||
}
|
||||
|
||||
private Object resolve(Object value, AggregationOperationContext context) {
|
||||
|
||||
if (value instanceof Field) {
|
||||
return context.getReference((Field) value).toString();
|
||||
} else if (value instanceof DBObject) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a builder that allows fluent creation of {@link IfNullOperator}.
|
||||
*
|
||||
* @return a new {@link IfNullBuilder}.
|
||||
*/
|
||||
public static IfNullBuilder newBuilder() {
|
||||
return IfNullOperatorBuilder.newBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.10
|
||||
*/
|
||||
public static interface IfNullBuilder {
|
||||
|
||||
/**
|
||||
* @param field the field to check for a {@literal null} value, field reference must not be {@literal null}.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder ifNull(Field field);
|
||||
|
||||
/**
|
||||
* @param field the field to check for a {@literal null} value, field name must not be {@literal null} or empty.
|
||||
* @return the {@link ThenBuilder}
|
||||
*/
|
||||
ThenBuilder ifNull(String field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 1.10
|
||||
*/
|
||||
public static interface ThenBuilder {
|
||||
|
||||
/**
|
||||
* @param field the field holding the replacement value, must not be {@literal null}.
|
||||
* @return the {@link IfNullOperator}
|
||||
*/
|
||||
IfNullOperator thenReplaceWith(Field field);
|
||||
|
||||
/**
|
||||
* @param value the value to be used if the {@code $ifNull }condition evaluates {@literal true}. Can be a
|
||||
* {@link DBObject}, a value that is supported by MongoDB or a value that can be converted to a MongoDB
|
||||
* representation but must not be {@literal null}.
|
||||
* @return the {@link IfNullOperator}
|
||||
*/
|
||||
IfNullOperator thenReplaceWith(Object value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link IfNullOperator} creation.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 1.10
|
||||
*/
|
||||
public static final class IfNullOperatorBuilder implements IfNullBuilder, ThenBuilder {
|
||||
|
||||
private Field field;
|
||||
|
||||
private IfNullOperatorBuilder() {}
|
||||
|
||||
/**
|
||||
* Creates a new builder for {@link IfNullOperator}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static IfNullOperatorBuilder newBuilder() {
|
||||
return new IfNullOperatorBuilder();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.IfNullOperator.IfNullBuilder#ifNull(org.springframework.data.mongodb.core.aggregation.Field)
|
||||
*/
|
||||
public ThenBuilder ifNull(Field field) {
|
||||
|
||||
Assert.notNull(field, "Field must not be null!");
|
||||
|
||||
this.field = field;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.IfNullOperator.IfNullBuilder#ifNull(java.lang.String)
|
||||
*/
|
||||
public ThenBuilder ifNull(String name) {
|
||||
|
||||
Assert.hasText(name, "Field name must not be null or empty!");
|
||||
|
||||
this.field = Fields.field(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.IfNullOperator.ThenReplaceBuilder#thenReplaceWith(org.springframework.data.mongodb.core.aggregation.Field)
|
||||
*/
|
||||
@Override
|
||||
public IfNullOperator thenReplaceWith(Field replacementField) {
|
||||
|
||||
Assert.notNull(replacementField, "Replacement field must not be null!");
|
||||
|
||||
return new IfNullOperator(this.field, replacementField);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.IfNullOperator.ThenReplaceBuilder#thenReplaceWith(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public IfNullOperator thenReplaceWith(Object value) {
|
||||
|
||||
Assert.notNull(value, "'Replacement-value' must not be null!");
|
||||
|
||||
return new IfNullOperator(this.field, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection;
|
||||
@@ -243,22 +245,22 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
public abstract ProjectionOperation as(String alias);
|
||||
|
||||
/**
|
||||
* Apply a conditional projection using {@link ConditionalOperator}.
|
||||
* Apply a conditional projection using {@link Cond}.
|
||||
*
|
||||
* @param conditionalOperator must not be {@literal null}.
|
||||
* @param cond must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public abstract ProjectionOperation applyCondition(ConditionalOperator conditionalOperator);
|
||||
public abstract ProjectionOperation applyCondition(Cond cond);
|
||||
|
||||
/**
|
||||
* Apply a conditional value replacement for {@literal null} values using {@link IfNullOperator}.
|
||||
* Apply a conditional value replacement for {@literal null} values using {@link IfNull}.
|
||||
*
|
||||
* @param ifNullOperator must not be {@literal null}.
|
||||
* @param ifNull must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public abstract ProjectionOperation applyCondition(IfNullOperator ifNullOperator);
|
||||
public abstract ProjectionOperation applyCondition(IfNull ifNull);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,10 +464,10 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.AbstractProjectionOperationBuilder#transform(org.springframework.data.mongodb.core.aggregation.ConditionalOperator)
|
||||
*/
|
||||
@Override
|
||||
public ProjectionOperation applyCondition(ConditionalOperator conditionalOperator) {
|
||||
public ProjectionOperation applyCondition(Cond cond) {
|
||||
|
||||
Assert.notNull(conditionalOperator, "ConditionalOperator must not be null!");
|
||||
return this.operation.and(new ExpressionProjection(Fields.field(name), conditionalOperator));
|
||||
Assert.notNull(cond, "ConditionalOperator must not be null!");
|
||||
return this.operation.and(new ExpressionProjection(Fields.field(name), cond));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -473,10 +475,10 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.AbstractProjectionOperationBuilder#transform(org.springframework.data.mongodb.core.aggregation.IfNullOperator)
|
||||
*/
|
||||
@Override
|
||||
public ProjectionOperation applyCondition(IfNullOperator ifNullOperator) {
|
||||
public ProjectionOperation applyCondition(IfNull ifNull) {
|
||||
|
||||
Assert.notNull(ifNullOperator, "IfNullOperator must not be null!");
|
||||
return this.operation.and(new ExpressionProjection(Fields.field(name), ifNullOperator));
|
||||
Assert.notNull(ifNull, "IfNullOperator must not be null!");
|
||||
return this.operation.and(new ExpressionProjection(Fields.field(name), ifNull));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,8 @@ import static org.springframework.data.mongodb.core.aggregation.Fields.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import lombok.Builder;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -55,6 +57,8 @@ import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.core.CollectionCallback;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.Venue;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry;
|
||||
import org.springframework.data.mongodb.core.index.GeospatialIndex;
|
||||
import org.springframework.data.mongodb.core.mapping.Document;
|
||||
@@ -73,8 +77,6 @@ import com.mongodb.DBObject;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
import lombok.Builder;
|
||||
|
||||
/**
|
||||
* Tests for {@link MongoTemplate#aggregate(String, AggregationPipeline, Class)}.
|
||||
*
|
||||
@@ -522,7 +524,7 @@ public class AggregationTests {
|
||||
TypedAggregation<InventoryItem> aggregation = newAggregation(InventoryItem.class, //
|
||||
project("item") //
|
||||
.and("discount")//
|
||||
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250)) //
|
||||
.applyCondition(Cond.newBuilder().when(Criteria.where("qty").gte(250)) //
|
||||
.then(30) //
|
||||
.otherwise(20)));
|
||||
|
||||
@@ -570,7 +572,7 @@ public class AggregationTests {
|
||||
|
||||
TypedAggregation<InventoryItem> aggregation = newAggregation(InventoryItem.class, //
|
||||
project("item") //
|
||||
.and(ifNull("description", "Unspecified")) //
|
||||
.and(ConditionalOperators.ifNull("description").then("Unspecified")) //
|
||||
.as("description")//
|
||||
);
|
||||
|
||||
@@ -597,7 +599,7 @@ public class AggregationTests {
|
||||
TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class, //
|
||||
project() //
|
||||
.and("largePopulation")//
|
||||
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("population").gte(20000)) //
|
||||
.applyCondition(ConditionalOperators.when(Criteria.where("population").gte(20000)) //
|
||||
.then(true) //
|
||||
.otherwise(false)) //
|
||||
.and("population").as("population"));
|
||||
@@ -622,9 +624,9 @@ public class AggregationTests {
|
||||
TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class, //
|
||||
project() //
|
||||
.and("size")//
|
||||
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("population").gte(20000)) //
|
||||
.then(ConditionalOperator.newBuilder().when(Criteria.where("population").gte(200000)).then("huge")
|
||||
.otherwise("small")) //
|
||||
.applyCondition(ConditionalOperators.when(Criteria.where("population").gte(20000)) //
|
||||
.then(
|
||||
ConditionalOperators.when(Criteria.where("population").gte(200000)).then("huge").otherwise("small")) //
|
||||
.otherwise("small")) //
|
||||
.and("population").as("population"));
|
||||
|
||||
@@ -649,9 +651,9 @@ public class AggregationTests {
|
||||
mongoTemplate.insert(new LineItem("idonly", null, 0));
|
||||
|
||||
TypedAggregation<LineItem> aggregation = newAggregation(LineItem.class, //
|
||||
project("id") //
|
||||
.and("caption")//
|
||||
.applyCondition(ifNull(field("caption"), "unknown")),
|
||||
project("id") //
|
||||
.and("caption")//
|
||||
.applyCondition(ConditionalOperators.ifNull("caption").then("unknown")),
|
||||
sort(ASC, "id"));
|
||||
|
||||
assertThat(aggregation.toString(), is(notNullValue()));
|
||||
@@ -678,7 +680,7 @@ public class AggregationTests {
|
||||
TypedAggregation<LineItem> aggregation = newAggregation(LineItem.class, //
|
||||
project("id") //
|
||||
.and("caption")//
|
||||
.applyCondition(ifNull(field("caption"), field("id"))),
|
||||
.applyCondition(ConditionalOperators.ifNull("caption").thenValueOf("id")),
|
||||
sort(ASC, "id"));
|
||||
|
||||
assertThat(aggregation.toString(), is(notNullValue()));
|
||||
@@ -714,12 +716,16 @@ public class AggregationTests {
|
||||
TypedAggregation<CarPerson> agg = Aggregation.newAggregation(CarPerson.class,
|
||||
unwind("descriptors.carDescriptor.entries"), //
|
||||
project() //
|
||||
.and(new ConditionalOperator(Criteria.where("descriptors.carDescriptor.entries.make").is("MAKE1"), "good",
|
||||
"meh"))
|
||||
.and(ConditionalOperators //
|
||||
.when(Criteria.where("descriptors.carDescriptor.entries.make").is("MAKE1")).then("good")
|
||||
.otherwise("meh"))
|
||||
.as("make") //
|
||||
.and("descriptors.carDescriptor.entries.model").as("model") //
|
||||
.and("descriptors.carDescriptor.entries.year").as("year"), //
|
||||
group("make").avg(new ConditionalOperator(Criteria.where("year").gte(2012), 1, 9000)).as("score"),
|
||||
group("make").avg(ConditionalOperators //
|
||||
.when(Criteria.where("year").gte(2012)) //
|
||||
.then(1) //
|
||||
.otherwise(9000)).as("score"),
|
||||
sort(ASC, "make"));
|
||||
|
||||
AggregationResults<DBObject> result = mongoTemplate.aggregate(agg, DBObject.class);
|
||||
|
||||
@@ -19,7 +19,6 @@ import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.DBObjectTestUtils.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.Fields.*;
|
||||
import static org.springframework.data.mongodb.core.query.Criteria.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
@@ -31,6 +30,8 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
@@ -391,7 +392,9 @@ public class AggregationUnitTests {
|
||||
|
||||
DBObject agg = newAggregation( //
|
||||
project("a"), //
|
||||
group("a").first(conditional(Criteria.where("a").gte(42), "answer", "no-answer")).as("foosum") //
|
||||
group("a")
|
||||
.first(ConditionalOperators.when(Criteria.where("a").gte(42)).thenValueOf("answer").otherwise("no-answer"))
|
||||
.as("foosum") //
|
||||
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -409,7 +412,7 @@ public class AggregationUnitTests {
|
||||
public void shouldRenderProjectionConditionalExpressionCorrectly() {
|
||||
|
||||
DBObject agg = Aggregation.newAggregation(//
|
||||
project().and(ConditionalOperator.newBuilder() //
|
||||
project().and(Cond.newBuilder() //
|
||||
.when("isYellow") //
|
||||
.then("bright") //
|
||||
.otherwise("dark")).as("color"))
|
||||
@@ -432,7 +435,7 @@ public class AggregationUnitTests {
|
||||
|
||||
DBObject agg = Aggregation.newAggregation(//
|
||||
project().and("color")
|
||||
.applyCondition(ConditionalOperator.newBuilder() //
|
||||
.applyCondition(Cond.newBuilder() //
|
||||
.when("isYellow") //
|
||||
.then("bright") //
|
||||
.otherwise("dark")))
|
||||
@@ -456,7 +459,8 @@ public class AggregationUnitTests {
|
||||
DBObject agg = Aggregation
|
||||
.newAggregation(project()//
|
||||
.and("color")//
|
||||
.applyCondition(conditional(Criteria.where("key").gt(5), "bright", "dark"))) //
|
||||
.applyCondition(Cond.newBuilder().when(Criteria.where("key").gt(5)) //
|
||||
.then("bright").otherwise("dark"))) //
|
||||
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject project = extractPipelineElement(agg, 0, "$project");
|
||||
@@ -478,7 +482,10 @@ public class AggregationUnitTests {
|
||||
.newAggregation(//
|
||||
project().and("color").as("chroma"),
|
||||
project().and("luminosity") //
|
||||
.applyCondition(conditional(field("chroma"), "bright", "dark"))) //
|
||||
.applyCondition(ConditionalOperators //
|
||||
.when("chroma") //
|
||||
.thenValueOf("bright") //
|
||||
.otherwise("dark"))) //
|
||||
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject project = extractPipelineElement(agg, 1, "$project");
|
||||
@@ -500,7 +507,10 @@ public class AggregationUnitTests {
|
||||
.newAggregation(//
|
||||
project().and("color").as("chroma"),
|
||||
project().and("luminosity") //
|
||||
.applyCondition(conditional(Criteria.where("chroma").is(100), "bright", "dark"))) //
|
||||
.applyCondition(Cond.newBuilder()
|
||||
.when(Criteria.where("chroma") //
|
||||
.is(100)) //
|
||||
.then("bright").otherwise("dark"))) //
|
||||
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject project = extractPipelineElement(agg, 1, "$project");
|
||||
@@ -522,7 +532,9 @@ public class AggregationUnitTests {
|
||||
.newAggregation(//
|
||||
project().and("color"), //
|
||||
project().and("luminosity") //
|
||||
.applyCondition(ifNull(field("chroma"), "unknown"))) //
|
||||
.applyCondition(ConditionalOperators //
|
||||
.ifNull("chroma") //
|
||||
.then("unknown"))) //
|
||||
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject project = extractPipelineElement(agg, 1, "$project");
|
||||
@@ -541,7 +553,8 @@ public class AggregationUnitTests {
|
||||
.newAggregation(//
|
||||
project("fallback").and("color").as("chroma"),
|
||||
project().and("luminosity") //
|
||||
.applyCondition(ifNull(field("chroma"), field("fallback")))) //
|
||||
.applyCondition(ConditionalOperators.ifNull("chroma") //
|
||||
.thenValueOf("fallback"))) //
|
||||
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject project = extractPipelineElement(agg, 1, "$project");
|
||||
|
||||
@@ -16,48 +16,26 @@
|
||||
package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.ConditionalOperator.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators;
|
||||
import org.springframework.data.mongodb.core.query.Criteria;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ConditionalOperator}.
|
||||
* Unit tests for {@link Cond}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class ConditionalOperatorUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldRejectNullCondition() {
|
||||
new ConditionalOperator((Field) null, "", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldRejectThenValue() {
|
||||
new ConditionalOperator(Fields.field("field"), null, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldRejectOtherwiseValue() {
|
||||
new ConditionalOperator(Fields.field("field"), "", null);
|
||||
}
|
||||
public class CondExpressionUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
@@ -92,12 +70,12 @@ public class ConditionalOperatorUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
* @see DATAMONGO-861, DATAMONGO-1542
|
||||
*/
|
||||
@Test
|
||||
public void simpleBuilderShouldRenderCorrectly() {
|
||||
|
||||
ConditionalOperator operator = newBuilder().when("isYellow").then("bright").otherwise("dark");
|
||||
Cond operator = ConditionalOperators.when("isYellow").thenValueOf("bright").otherwise("dark");
|
||||
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
DBObject expectedCondition = new BasicDBObject() //
|
||||
@@ -109,12 +87,12 @@ public class ConditionalOperatorUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
* @see DATAMONGO-861, DATAMONGO-1542
|
||||
*/
|
||||
@Test
|
||||
public void simpleCriteriaShouldRenderCorrectly() {
|
||||
|
||||
ConditionalOperator operator = newBuilder().when(Criteria.where("luminosity").gte(100)).then("bright")
|
||||
Cond operator = ConditionalOperators.when(Criteria.where("luminosity").gte(100)).thenValueOf("bright")
|
||||
.otherwise("dark");
|
||||
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
@@ -132,11 +110,10 @@ public class ConditionalOperatorUnitTests {
|
||||
@Test
|
||||
public void andCriteriaShouldRenderCorrectly() {
|
||||
|
||||
ConditionalOperator operator = newBuilder() //
|
||||
.when(Criteria.where("luminosity").gte(100) //
|
||||
.andOperator(Criteria.where("hue").is(50), //
|
||||
Criteria.where("saturation").lt(11)))
|
||||
.then("bright").otherwise("dark");
|
||||
Cond operator = ConditionalOperators.when(Criteria.where("luminosity").gte(100) //
|
||||
.andOperator(Criteria.where("hue").is(50), //
|
||||
Criteria.where("saturation").lt(11)))
|
||||
.thenValueOf("bright").otherwiseValueOf("dark-field");
|
||||
|
||||
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
@@ -147,20 +124,20 @@ public class ConditionalOperatorUnitTests {
|
||||
DBObject expectedCondition = new BasicDBObject() //
|
||||
.append("if", Arrays.<Object> asList(luminosity, new BasicDBObject("$and", Arrays.asList(hue, saturation)))) //
|
||||
.append("then", "bright") //
|
||||
.append("else", "dark");
|
||||
.append("else", "$dark-field");
|
||||
|
||||
assertThat(dbObject, isBsonObject().containing("$cond", expectedCondition));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
* @see DATAMONGO-861, DATAMONGO-1542
|
||||
*/
|
||||
@Test
|
||||
public void twoArgsCriteriaShouldRenderCorrectly() {
|
||||
|
||||
Criteria criteria = Criteria.where("luminosity").gte(100) //
|
||||
.and("saturation").and("chroma").is(200);
|
||||
ConditionalOperator operator = newBuilder().when(criteria).then("bright").otherwise("dark");
|
||||
Cond operator = ConditionalOperators.when(criteria).thenValueOf("bright").otherwise("dark");
|
||||
|
||||
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
@@ -176,14 +153,13 @@ public class ConditionalOperatorUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
* @see DATAMONGO-861, DATAMONGO-1542
|
||||
*/
|
||||
@Test
|
||||
public void nestedCriteriaShouldRenderCorrectly() {
|
||||
|
||||
ConditionalOperator operator = newBuilder() //
|
||||
.when(Criteria.where("luminosity").gte(100)) //
|
||||
.then(newBuilder() //
|
||||
Cond operator = ConditionalOperators.when(Criteria.where("luminosity").gte(100)) //
|
||||
.thenValueOf(newBuilder() //
|
||||
.when(Criteria.where("luminosity").gte(200)) //
|
||||
.then("verybright") //
|
||||
.otherwise("not-so-bright")) //
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 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
|
||||
*
|
||||
* http://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.core.aggregation;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link IfNullOperator}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class IfNullOperatorUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldRejectNullCondition() {
|
||||
new IfNullOperator(null, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldRejectThenValue() {
|
||||
new IfNullOperator(Fields.field("aa"), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
*/
|
||||
@Test
|
||||
public void simpleIfNullShouldRenderCorrectly() {
|
||||
|
||||
IfNullOperator operator = IfNullOperator.newBuilder() //
|
||||
.ifNull("optional") //
|
||||
.thenReplaceWith("a more sophisticated value");
|
||||
|
||||
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(dbObject,
|
||||
isBsonObject().containing("$ifNull", Arrays.<Object> asList("$optional", "a more sophisticated value")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
*/
|
||||
@Test
|
||||
public void fieldReplacementIfNullShouldRenderCorrectly() {
|
||||
|
||||
IfNullOperator operator = IfNullOperator.newBuilder() //
|
||||
.ifNull(Fields.field("optional")) //
|
||||
.thenReplaceWith(Fields.field("never-null"));
|
||||
|
||||
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(dbObject, isBsonObject().containing("$ifNull", Arrays.<Object> asList("$optional", "$never-null")));
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ArrayOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.BooleanOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ComparisonOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.DateOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.LiteralOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.SetOperators;
|
||||
@@ -49,6 +50,7 @@ import com.mongodb.util.JSON;
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
* @author Christoph Strobl
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class ProjectionOperationUnitTests {
|
||||
|
||||
@@ -1707,4 +1709,43 @@ public class ProjectionOperationUnitTests {
|
||||
private static DBObject exctractOperation(String field, DBObject fromProjectClause) {
|
||||
return (DBObject) fromProjectClause.get(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861, DATAMONGO-1542
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIfNullConditionAggregationExpression() {
|
||||
|
||||
DBObject agg = project().and(ConditionalOperators.ifNull(ArrayOperators.arrayOf("array").elementAt(1)).then("a more sophisticated value"))
|
||||
.as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg,
|
||||
is(JSON.parse("{ $project: { result: { $ifNull: [ { $arrayElemAt: [\"$array\", 1] }, \"a more sophisticated value\" ] } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1542
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIfNullValueAggregationExpression() {
|
||||
|
||||
DBObject agg = project()
|
||||
.and(ConditionalOperators.ifNull("field").then(ArrayOperators.arrayOf("array").elementAt(1))).as("result")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg,
|
||||
is(JSON.parse("{ $project: { result: { $ifNull: [ \"$field\", { $arrayElemAt: [\"$array\", 1] } ] } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861, DATAMONGO-1542
|
||||
*/
|
||||
@Test
|
||||
public void fieldReplacementIfNullShouldRenderCorrectly() {
|
||||
|
||||
DBObject agg = project().and(ConditionalOperators.ifNull("optional").thenValueOf("$never-null")).as("result")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, is(JSON.parse("{ $project: { result: { $ifNull: [ \"$optional\", \"$never-null\" ] } } }")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,10 @@ import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.PersistenceConstructor;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.convert.CustomConversions;
|
||||
import org.springframework.data.mongodb.core.convert.DbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
@@ -49,6 +50,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link TypeBasedAggregationOperationContext}.
|
||||
@@ -292,7 +294,8 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
TypedAggregation<FooPerson> agg = newAggregation(FooPerson.class,
|
||||
project("name") //
|
||||
.and("age") //
|
||||
.applyCondition(conditional(Criteria.where("age.value").lt(10), new Age(0), field("age"))) //
|
||||
.applyCondition(
|
||||
ConditionalOperators.when(Criteria.where("age.value").lt(10)).then(new Age(0)).otherwiseValueOf("age")) //
|
||||
);
|
||||
|
||||
DBObject dbo = agg.toDbObject("person", context);
|
||||
@@ -309,7 +312,7 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-861
|
||||
* @see DATAMONGO-861, DATAMONGO-1542
|
||||
*/
|
||||
@Test
|
||||
public void rendersAggregationIfNullInTypedAggregationContextCorrectly() {
|
||||
@@ -318,7 +321,7 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
TypedAggregation<FooPerson> agg = newAggregation(FooPerson.class,
|
||||
project("name") //
|
||||
.and("age") //
|
||||
.applyCondition(ifNull("age", new Age(0))) //
|
||||
.applyCondition(ConditionalOperators.ifNull("age").then(new Age(0))) //
|
||||
);
|
||||
|
||||
DBObject dbo = agg.toDbObject("person", context);
|
||||
@@ -329,6 +332,9 @@ public class TypeBasedAggregationOperationContextUnitTests {
|
||||
DBObject project = getValue(projection, "$project");
|
||||
DBObject age = getValue(project, "age");
|
||||
|
||||
assertThat(age, is(JSON.parse(
|
||||
"{ $ifNull: [ \"$age\", { \"_class\":\"org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContextUnitTests$Age\", \"value\": 0} ] }")));
|
||||
|
||||
assertThat(age, isBsonObject().containing("$ifNull.[0]", "$age"));
|
||||
assertThat(age, isBsonObject().containing("$ifNull.[1].value", 0));
|
||||
assertThat(age, isBsonObject().containing("$ifNull.[1]._class", Age.class.getName()));
|
||||
|
||||
Reference in New Issue
Block a user