diff --git a/pom.xml b/pom.xml
index ea80a3cb74..9b7e4a3af9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.10.0.BUILD-SNAPSHOT
+ 1.10.0.DATAMONGO-1536-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml
index ae0a5d6c8f..5a268e7017 100644
--- a/spring-data-mongodb-cross-store/pom.xml
+++ b/spring-data-mongodb-cross-store/pom.xml
@@ -6,7 +6,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.10.0.BUILD-SNAPSHOT
+ 1.10.0.DATAMONGO-1536-SNAPSHOT
../pom.xml
@@ -48,7 +48,7 @@
org.springframework.data
spring-data-mongodb
- 1.10.0.BUILD-SNAPSHOT
+ 1.10.0.DATAMONGO-1536-SNAPSHOT
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index 2d02722262..4122ee99e1 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -13,7 +13,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.10.0.BUILD-SNAPSHOT
+ 1.10.0.DATAMONGO-1536-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml
index ee5e3336db..258d769265 100644
--- a/spring-data-mongodb-log4j/pom.xml
+++ b/spring-data-mongodb-log4j/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.10.0.BUILD-SNAPSHOT
+ 1.10.0.DATAMONGO-1536-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 8072d3f665..6c1e8e62fa 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.10.0.BUILD-SNAPSHOT
+ 1.10.0.DATAMONGO-1536-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressions.java
index e06fa43fb5..be93d1e190 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressions.java
@@ -16,11 +16,17 @@
package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+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.util.Assert;
+import org.springframework.util.ObjectUtils;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -32,259 +38,5075 @@
public interface AggregationExpressions {
/**
- * {@code $filter} {@link AggregationExpression} allows to select a subset of the array to return based on the
- * specified condition.
+ * Gateway to {@literal boolean expressions} that evaluate their argument expressions as booleans and return a boolean
+ * as the result.
*
* @author Christoph Strobl
- * @since 1.10
*/
- class Filter implements AggregationExpression {
-
- private Object input;
- private ExposedField as;
- private Object condition;
-
- private Filter() {
- // used by builder
- }
+ class BooleanOperators {
/**
- * Set the {@literal field} to apply the {@code $filter} to.
+ * Take the array referenced by given {@literal fieldReference}.
*
- * @param field must not be {@literal null}.
- * @return never {@literal null}.
+ * @param fieldReference must not be {@literal null}.
+ * @return
*/
- public static AsBuilder filter(String field) {
-
- Assert.notNull(field, "Field must not be null!");
- return filter(Fields.field(field));
+ public static BooleanOperatorFactory valueOf(String fieldReference) {
+ return new BooleanOperatorFactory(fieldReference);
}
/**
- * Set the {@literal field} to apply the {@code $filter} to.
+ * Take the array referenced by given {@literal fieldReference}.
*
- * @param field must not be {@literal null}.
- * @return never {@literal null}.
+ * @param fieldReference must not be {@literal null}.
+ * @return
*/
- public static AsBuilder filter(Field field) {
-
- Assert.notNull(field, "Field must not be null!");
- return new FilterExpressionBuilder().filter(field);
+ public static BooleanOperatorFactory valueOf(AggregationExpression fieldReference) {
+ return new BooleanOperatorFactory(fieldReference);
}
/**
- * Set the {@literal values} to apply the {@code $filter} to.
+ * Creates new {@link AggregationExpressions} that evaluates the boolean value of the referenced field and returns
+ * the opposite boolean value.
*
- * @param values must not be {@literal null}.
+ * @param fieldReference must not be {@literal null}.
* @return
*/
- public static AsBuilder filter(List> values) {
-
- Assert.notNull(values, "Values must not be null!");
- return new FilterExpressionBuilder().filter(values);
+ public static Not not(String fieldReference) {
+ return Not.not(fieldReference);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
+ /**
+ * Creates new {@link AggregationExpressions} that evaluates the boolean value of {@link AggregationExpression}
+ * result and returns the opposite boolean value.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
*/
- @Override
- public DBObject toDbObject(final AggregationOperationContext context) {
+ public static Not not(AggregationExpression expression) {
+ return Not.not(expression);
+ }
- return toFilter(new ExposedFieldsAggregationOperationContext(ExposedFields.from(as), context) {
+ public static class BooleanOperatorFactory {
- @Override
- public FieldReference getReference(Field field) {
+ private final String fieldReference;
+ private final AggregationExpression expression;
- FieldReference ref = null;
- try {
- ref = context.getReference(field);
- } catch (Exception e) {
- // just ignore that one.
- }
- return ref != null ? ref : super.getReference(field);
- }
- });
- }
+ /**
+ * Creates new {@link ComparisonOperatorFactory} for given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ */
+ public BooleanOperatorFactory(String fieldReference) {
- private DBObject toFilter(AggregationOperationContext context) {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ this.fieldReference = fieldReference;
+ this.expression = null;
+ }
- DBObject filterExpression = new BasicDBObject();
+ /**
+ * Creats new {@link ComparisonOperatorFactory} for given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ */
+ public BooleanOperatorFactory(AggregationExpression expression) {
- filterExpression.putAll(context.getMappedObject(new BasicDBObject("input", getMappedInput(context))));
- filterExpression.put("as", as.getTarget());
+ Assert.notNull(expression, "Expression must not be null!");
+ this.fieldReference = null;
+ this.expression = expression;
+ }
- filterExpression.putAll(context.getMappedObject(new BasicDBObject("cond", getMappedCondition(context))));
+ /**
+ * Creates new {@link AggregationExpressions} that evaluates one or more expressions and returns {@literal true}
+ * if all of the expressions are {@literal true}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public And and(AggregationExpression expression) {
- return new BasicDBObject("$filter", filterExpression);
- }
+ Assert.notNull(expression, "Expression must not be null!");
+ return createAnd().andExpression(expression);
+ }
- private Object getMappedInput(AggregationOperationContext context) {
- return input instanceof Field ? context.getReference((Field) input).toString() : input;
- }
+ /**
+ * Creates new {@link AggregationExpressions} that evaluates one or more expressions and returns {@literal true}
+ * if all of the expressions are {@literal true}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public And and(String fieldReference) {
- private Object getMappedCondition(AggregationOperationContext context) {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createAnd().andField(fieldReference);
+ }
- if (!(condition instanceof AggregationExpression)) {
- return condition;
+ private And createAnd() {
+ return usesFieldRef() ? And.and(Fields.field(fieldReference)) : And.and(expression);
}
- NestedDelegatingExpressionAggregationOperationContext nea = new NestedDelegatingExpressionAggregationOperationContext(context);
- return ((AggregationExpression) condition).toDbObject(nea);
- }
+ /**
+ * Creates new {@link AggregationExpressions} that evaluates one or more expressions and returns {@literal true}
+ * if any of the expressions are {@literal true}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Or or(AggregationExpression expression) {
- /**
- * @author Christoph Strobl
- */
- public interface InputBuilder {
+ Assert.notNull(expression, "Expression must not be null!");
+ return createOr().orExpression(expression);
+ }
/**
- * Set the {@literal values} to apply the {@code $filter} to.
+ * Creates new {@link AggregationExpressions} that evaluates one or more expressions and returns {@literal true}
+ * if any of the expressions are {@literal true}.
*
- * @param array must not be {@literal null}.
+ * @param fieldReference must not be {@literal null}.
* @return
*/
- AsBuilder filter(List> array);
+ public Or or(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createOr().orField(fieldReference);
+ }
+
+ private Or createOr() {
+ return usesFieldRef() ? Or.or(Fields.field(fieldReference)) : Or.or(expression);
+ }
/**
- * Set the {@literal field} holding an array to apply the {@code $filter} to.
+ * Creates new {@link AggregationExpression} that evaluates a boolean and returns the opposite boolean value.
*
- * @param field must not be {@literal null}.
* @return
*/
- AsBuilder filter(Field field);
+ public Not not() {
+ return usesFieldRef() ? Not.not(fieldReference) : Not.not(expression);
+ }
+
+ private boolean usesFieldRef() {
+ return this.fieldReference != null;
+ }
}
+ }
+
+ /**
+ * Gateway to {@literal Set expressions} which perform {@literal set} operation on arrays, treating arrays as sets.
+ *
+ * @author Christoph Strobl
+ */
+ class SetOperators {
/**
- * @author Christoph Strobl
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
*/
- public interface AsBuilder {
+ public static SetOperatorFactory arrayAsSet(String fieldReference) {
+ return new SetOperatorFactory(fieldReference);
+ }
+
+ /**
+ * Take the array resulting from the given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static SetOperatorFactory arrayAsSet(AggregationExpression expression) {
+ return new SetOperatorFactory(expression);
+ }
+
+ public static class SetOperatorFactory {
+
+ private final String fieldReference;
+ private final AggregationExpression expression;
/**
- * Set the {@literal variableName} for the elements in the input array.
+ * Creates new {@link SetOperatorFactory} for given {@literal fieldReference}.
*
- * @param variableName must not be {@literal null}.
- * @return
+ * @param fieldReference must not be {@literal null}.
*/
- ConditionBuilder as(String variableName);
- }
+ public SetOperatorFactory(String fieldReference) {
- /**
- * @author Christoph Strobl
- */
- public interface ConditionBuilder {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ this.fieldReference = fieldReference;
+ this.expression = null;
+ }
/**
- * Set the {@link AggregationExpression} that determines whether to include the element in the resulting array.
+ * Creates new {@link SetOperatorFactory} for given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
- * @return
*/
- Filter by(AggregationExpression expression);
+ public SetOperatorFactory(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ this.fieldReference = null;
+ this.expression = expression;
+ }
/**
- * Set the {@literal expression} that determines whether to include the element in the resulting array.
+ * Creates new {@link AggregationExpressions} that compares the previously mentioned field to one or more arrays
+ * and returns {@literal true} if they have the same distinct elements and {@literal false} otherwise.
*
- * @param expression must not be {@literal null}.
+ * @param arrayReferences must not be {@literal null}.
* @return
*/
- Filter by(String expression);
+ public SetEquals isEqualTo(String... arrayReferences) {
+ return createSetEquals().isEqualTo(arrayReferences);
+ }
/**
- * Set the {@literal expression} that determines whether to include the element in the resulting array.
+ * Creates new {@link AggregationExpressions} that compares the previously mentioned field to one or more arrays
+ * and returns {@literal true} if they have the same distinct elements and {@literal false} otherwise.
*
- * @param expression must not be {@literal null}.
+ * @param expressions must not be {@literal null}.
* @return
*/
- Filter by(DBObject expression);
- }
+ public SetEquals isEqualTo(AggregationExpression... expressions) {
+ return createSetEquals().isEqualTo(expressions);
+ }
- /**
- * @author Christoph Strobl
- */
- static final class FilterExpressionBuilder implements InputBuilder, AsBuilder, ConditionBuilder {
+ private SetEquals createSetEquals() {
+ return usesFieldRef() ? SetEquals.arrayAsSet(fieldReference) : SetEquals.arrayAsSet(expression);
+ }
- private final Filter filter;
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and one or more
+ * arrays and returns an array that contains the elements that appear in every of those.
+ *
+ * @param arrayReferences must not be {@literal null}.
+ * @return
+ */
+ public SetIntersection intersects(String... arrayReferences) {
+ return createSetIntersection().intersects(arrayReferences);
+ }
- FilterExpressionBuilder() {
- this.filter = new Filter();
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and one or more
+ * arrays and returns an array that contains the elements that appear in every of those.
+ *
+ * @param expressions must not be {@literal null}.
+ * @return
+ */
+ public SetIntersection intersects(AggregationExpression... expressions) {
+ return createSetIntersection().intersects(expressions);
}
- public static InputBuilder newBuilder() {
- return new FilterExpressionBuilder();
+ private SetIntersection createSetIntersection() {
+ return usesFieldRef() ? SetIntersection.arrayAsSet(fieldReference) : SetIntersection.arrayAsSet(expression);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.InputBuilder#filter(java.util.List)
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and one or more
+ * arrays and returns an array that contains the elements that appear in any of those.
+ *
+ * @param arrayReferences must not be {@literal null}.
+ * @return
*/
- @Override
- public AsBuilder filter(List> array) {
-
- Assert.notNull(array, "Array must not be null!");
- filter.input = new ArrayList(array);
- return this;
+ public SetUnion union(String... arrayReferences) {
+ return createSetUnion().union(arrayReferences);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.InputBuilder#filter(org.springframework.data.mongodb.core.aggregation.Field)
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and one or more
+ * arrays and returns an array that contains the elements that appear in any of those.
+ *
+ * @param expressions must not be {@literal null}.
+ * @return
*/
- @Override
- public AsBuilder filter(Field field) {
+ public SetUnion union(AggregationExpression... expressions) {
+ return createSetUnion().union(expressions);
+ }
- Assert.notNull(field, "Field must not be null!");
- filter.input = field;
- return this;
+ private SetUnion createSetUnion() {
+ return usesFieldRef() ? SetUnion.arrayAsSet(fieldReference) : SetUnion.arrayAsSet(expression);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.AsBuilder#as(java.lang.String)
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and returns an
+ * array containing the elements that do not exist in the given {@literal arrayReference}.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
*/
- @Override
- public ConditionBuilder as(String variableName) {
-
- Assert.notNull(variableName, "Variable name must not be null!");
- filter.as = new ExposedField(variableName, true);
- return this;
+ public SetDifference differenceTo(String arrayReference) {
+ return createSetDifference().differenceTo(arrayReference);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and returns an
+ * array containing the elements that do not exist in the given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
*/
- @Override
- public Filter by(AggregationExpression condition) {
+ public SetDifference differenceTo(AggregationExpression expression) {
+ return createSetDifference().differenceTo(expression);
+ }
- Assert.notNull(condition, "Condition must not be null!");
- filter.condition = condition;
- return filter;
+ private SetDifference createSetDifference() {
+ return usesFieldRef() ? SetDifference.arrayAsSet(fieldReference) : SetDifference.arrayAsSet(expression);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(java.lang.String)
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and returns
+ * {@literal true} if it is a subset of the given {@literal arrayReference}.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
*/
- @Override
- public Filter by(String expression) {
-
- Assert.notNull(expression, "Expression must not be null!");
- filter.condition = expression;
- return filter;
+ public SetIsSubset isSubsetOf(String arrayReference) {
+ return createSetIsSubset().isSubsetOf(arrayReference);
}
- /*
- * (non-Javadoc)
- * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(com.mongodb.DBObject)
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and returns
+ * {@literal true} if it is a subset of the given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
*/
- @Override
- public Filter by(DBObject expression) {
+ public SetIsSubset isSubsetOf(AggregationExpression expression) {
+ return createSetIsSubset().isSubsetOf(expression);
+ }
- Assert.notNull(expression, "Expression must not be null!");
- filter.condition = expression;
- return filter;
+ private SetIsSubset createSetIsSubset() {
+ return usesFieldRef() ? SetIsSubset.arrayAsSet(fieldReference) : SetIsSubset.arrayAsSet(expression);
}
- }
- }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes array of the previously mentioned field and returns
+ * {@literal true} if any of the elements are {@literal true} and {@literal false} otherwise.
+ *
+ * @return
+ */
+ public AnyElementTrue anyElementTrue() {
+ return usesFieldRef() ? AnyElementTrue.arrayAsSet(fieldReference) : AnyElementTrue.arrayAsSet(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that tkes array of the previously mentioned field and returns
+ * {@literal true} if no elements is {@literal false}.
+ *
+ * @return
+ */
+ public AllElementsTrue allElementsTrue() {
+ return usesFieldRef() ? AllElementsTrue.arrayAsSet(fieldReference) : AllElementsTrue.arrayAsSet(expression);
+ }
+
+ private boolean usesFieldRef() {
+ return this.fieldReference != null;
+ }
+ }
+ }
+
+ /**
+ * Gateway to {@literal comparison expressions}.
+ *
+ * @author Christoph Strobl
+ */
+ class ComparisonOperators {
+
+ /**
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static ComparisonOperatorFactory valueOf(String fieldReference) {
+ return new ComparisonOperatorFactory(fieldReference);
+ }
+
+ /**
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static ComparisonOperatorFactory valueOf(AggregationExpression fieldReference) {
+ return new ComparisonOperatorFactory(fieldReference);
+ }
+
+ public static class ComparisonOperatorFactory {
+
+ private final String fieldReference;
+ private final AggregationExpression expression;
+
+ /**
+ * Creates new {@link ComparisonOperatorFactory} for given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ */
+ public ComparisonOperatorFactory(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ this.fieldReference = fieldReference;
+ this.expression = null;
+ }
+
+ /**
+ * Creats new {@link ComparisonOperatorFactory} for given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ */
+ public ComparisonOperatorFactory(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ this.fieldReference = null;
+ this.expression = expression;
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Cmp compareTo(String fieldReference) {
+ return createCmp().compareTo(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Cmp compareTo(AggregationExpression expression) {
+ return createCmp().compareTo(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Cmp compareToValue(Object value) {
+ return createCmp().compareToValue(value);
+ }
+
+ private Cmp createCmp() {
+ return usesFieldRef() ? Cmp.valueOf(fieldReference) : Cmp.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is equal to the value of the referenced field.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Eq equalTo(String fieldReference) {
+ return createEq().equalTo(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is equal to the expression result.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Eq equalTo(AggregationExpression expression) {
+ return createEq().equalTo(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is equal to the given value.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Eq equalToValue(Object value) {
+ return createEq().equalToValue(value);
+ }
+
+ private Eq createEq() {
+ return usesFieldRef() ? Eq.valueOf(fieldReference) : Eq.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is greater than the value of the referenced field.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Gt greaterThan(String fieldReference) {
+ return createGt().greaterThan(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is greater than the expression result.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Gt greaterThan(AggregationExpression expression) {
+ return createGt().greaterThan(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is greater than the given value.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Gt greaterThanValue(Object value) {
+ return createGt().greaterThanValue(value);
+ }
+
+ private Gt createGt() {
+ return usesFieldRef() ? Gt.valueOf(fieldReference) : Gt.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is greater than or equivalent to the value of the referenced field.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Gte greaterThanEqualTo(String fieldReference) {
+ return createGte().greaterThanEqualTo(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is greater than or equivalent to the expression result.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Gte greaterThanEqualTo(AggregationExpression expression) {
+ return createGte().greaterThanEqualTo(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is greater than or equivalent to the given value.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Gte greaterThanEqualToValue(Object value) {
+ return createGte().greaterThanEqualToValue(value);
+ }
+
+ private Gte createGte() {
+ return usesFieldRef() ? Gte.valueOf(fieldReference) : Gte.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is less than the value of the referenced field.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Lt lessThan(String fieldReference) {
+ return createLt().lessThan(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is less than the expression result.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Lt lessThan(AggregationExpression expression) {
+ return createLt().lessThan(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is less than to the given value.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Lt lessThanValue(Object value) {
+ return createLt().lessThanValue(value);
+ }
+
+ private Lt createLt() {
+ return usesFieldRef() ? Lt.valueOf(fieldReference) : Lt.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is less than or equivalent to the value of the referenced field.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Lte lessThanEqualTo(String fieldReference) {
+ return createLte().lessThanEqualTo(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is less than or equivalent to the expression result.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Lte lessThanEqualTo(AggregationExpression expression) {
+ return createLte().lessThanEqualTo(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the first
+ * value is less than or equivalent to the given value.
+ *
+ * @param value
+ * @return
+ */
+ public Lte lessThanEqualToValue(Object value) {
+ return createLte().lessThanEqualToValue(value);
+ }
+
+ private Lte createLte() {
+ return usesFieldRef() ? Lte.valueOf(fieldReference) : Lte.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the values
+ * are not equivalent.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Ne notEqualTo(String fieldReference) {
+ return createNe().notEqualTo(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the values
+ * are not equivalent.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Ne notEqualTo(AggregationExpression expression) {
+ return createNe().notEqualTo(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that compares two values and returns {@literal true} when the values
+ * are not equivalent.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Ne notEqualToValue(Object value) {
+ return createNe().notEqualToValue(value);
+ }
+
+ private Ne createNe() {
+ return usesFieldRef() ? Ne.valueOf(fieldReference) : Ne.valueOf(expression);
+ }
+
+ private boolean usesFieldRef() {
+ return fieldReference != null;
+ }
+ }
+
+ }
+
+ /**
+ * Gateway to {@literal Arithmetic} aggregation operations that perform mathematic operations on numbers.
+ *
+ * @author Christoph Strobl
+ */
+ class ArithmeticOperators {
+
+ /**
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static ArithmeticOperatorFactory valueOf(String fieldReference) {
+ return new ArithmeticOperatorFactory(fieldReference);
+ }
+
+ /**
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static ArithmeticOperatorFactory valueOf(AggregationExpression fieldReference) {
+ return new ArithmeticOperatorFactory(fieldReference);
+ }
+
+ public static class ArithmeticOperatorFactory {
+
+ private final String fieldReference;
+ private final AggregationExpression expression;
+
+ /**
+ * Creates new {@link ArithmeticOperatorFactory} for given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ */
+ public ArithmeticOperatorFactory(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ this.fieldReference = fieldReference;
+ this.expression = null;
+ }
+
+ /**
+ * Creates new {@link ArithmeticOperatorFactory} for given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ */
+ public ArithmeticOperatorFactory(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ this.fieldReference = null;
+ this.expression = expression;
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the absolute value of the associated number.
+ *
+ * @return
+ */
+ public Abs abs() {
+ return fieldReference != null ? Abs.absoluteValueOf(fieldReference) : Abs.absoluteValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that adds the value of {@literal fieldReference} to the associated
+ * number.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Add add(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createAdd().add(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that adds the resulting value of the given
+ * {@link AggregationExpression} to the associated number.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Add add(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createAdd().add(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that adds the given {@literal value} to the associated number.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Add add(Number value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return createAdd().add(value);
+ }
+
+ private Add createAdd() {
+ return fieldReference != null ? Add.valueOf(fieldReference) : Add.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the smallest integer greater than or equal to the
+ * assoicated number.
+ *
+ * @return
+ */
+ public Ceil ceil() {
+ return fieldReference != null ? Ceil.ceilValueOf(fieldReference) : Ceil.ceilValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that ivides the associated number by number referenced via
+ * {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Divide divideBy(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createDivide().divideBy(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that divides the associated number by number extracted via
+ * {@literal expression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Divide divideBy(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createDivide().divideBy(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that divides the associated number by given {@literal value}.
+ *
+ * @param value
+ * @return
+ */
+ public Divide divideBy(Number value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return createDivide().divideBy(value);
+ }
+
+ private Divide createDivide() {
+ return fieldReference != null ? Divide.valueOf(fieldReference) : Divide.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that raises Euler’s number (i.e. e ) on the associated number.
+ *
+ * @return
+ */
+ public Exp exp() {
+ return fieldReference != null ? Exp.expValueOf(fieldReference) : Exp.expValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the largest integer less than or equal to the
+ * associated number.
+ *
+ * @return
+ */
+ public Floor floor() {
+ return fieldReference != null ? Floor.floorValueOf(fieldReference) : Floor.floorValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates the natural logarithm ln (i.e loge) of the
+ * assoicated number.
+ *
+ * @return
+ */
+ public Ln ln() {
+ return fieldReference != null ? Ln.lnValueOf(fieldReference) : Ln.lnValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates the log of the associated number in the specified
+ * base referenced via {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Log log(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createLog().log(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates the log of the associated number in the specified
+ * base extracted by given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Log log(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createLog().log(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates the log of a the associated number in the specified
+ * {@literal base}.
+ *
+ * @param base must not be {@literal null}.
+ * @return
+ */
+ public Log log(Number base) {
+
+ Assert.notNull(base, "Base must not be null!");
+ return createLog().log(base);
+ }
+
+ private Log createLog() {
+ return fieldReference != null ? Log.valueOf(fieldReference) : Log.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates the log base 10 for the associated number.
+ *
+ * @return
+ */
+ public Log10 log10() {
+ return fieldReference != null ? Log10.log10ValueOf(fieldReference) : Log10.log10ValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that divides the associated number by another and returns the
+ * remainder.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Mod mod(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createMod().mod(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that divides the associated number by another and returns the
+ * remainder.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Mod mod(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createMod().mod(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that divides the associated number by another and returns the
+ * remainder.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Mod mod(Number value) {
+
+ Assert.notNull(value, "Base must not be null!");
+ return createMod().mod(value);
+ }
+
+ private Mod createMod() {
+ return fieldReference != null ? Mod.valueOf(fieldReference) : Mod.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that multiplies the associated number with another.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Multiply multiplyBy(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createMultiply().multiplyBy(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that multiplies the associated number with another.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Multiply multiplyBy(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createMultiply().multiplyBy(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that multiplies the associated number with another.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Multiply multiplyBy(Number value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return createMultiply().multiplyBy(value);
+ }
+
+ private Multiply createMultiply() {
+ return fieldReference != null ? Multiply.valueOf(fieldReference) : Multiply.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that raises the associated number to the specified exponent.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Pow pow(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createPow().pow(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that raises the associated number to the specified exponent.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Pow pow(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createPow().pow(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that raises the associated number to the specified exponent.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Pow pow(Number value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return createPow().pow(value);
+ }
+
+ private Pow createPow() {
+ return fieldReference != null ? Pow.valueOf(fieldReference) : Pow.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates the square root of the associated number.
+ *
+ * @return
+ */
+ public Sqrt sqrt() {
+ return fieldReference != null ? Sqrt.sqrtOf(fieldReference) : Sqrt.sqrtOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that subtracts value of given from the associated number.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Subtract subtract(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createSubtract().subtract(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that subtracts value of given from the associated number.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Subtract subtract(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createSubtract().subtract(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that subtracts value from the associated number.
+ *
+ * @param value
+ * @return
+ */
+ public Subtract subtract(Number value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return createSubtract().subtract(value);
+ }
+
+ private Subtract createSubtract() {
+ return fieldReference != null ? Subtract.valueOf(fieldReference) : Subtract.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that truncates a number to its integer.
+ *
+ * @return
+ */
+ public Trunc trunc() {
+ return fieldReference != null ? Trunc.truncValueOf(fieldReference) : Trunc.truncValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates and returns the sum of numeric values.
+ *
+ * @return
+ */
+ public Sum sum() {
+ return fieldReference != null ? Sum.sumOf(fieldReference) : Sum.sumOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the average value of the numeric values.
+ *
+ * @return
+ */
+ public Avg avg() {
+ return fieldReference != null ? Avg.avgOf(fieldReference) : Avg.avgOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the maximum value.
+ *
+ * @return
+ */
+ public Max max() {
+ return fieldReference != null ? Max.maxOf(fieldReference) : Max.maxOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the minimum value.
+ *
+ * @return
+ */
+ public Min min() {
+ return fieldReference != null ? Min.minOf(fieldReference) : Min.minOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates the population standard deviation of the input
+ * values.
+ *
+ * @return
+ */
+ public StdDevPop stdDevPop() {
+ return fieldReference != null ? StdDevPop.stdDevPopOf(fieldReference) : StdDevPop.stdDevPopOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that calculates the sample standard deviation of the input values.
+ *
+ * @return
+ */
+ public StdDevSamp stdDevSamp() {
+ return fieldReference != null ? StdDevSamp.stdDevSampOf(fieldReference) : StdDevSamp.stdDevSampOf(expression);
+ }
+ }
+ }
+
+ /**
+ * Gateway to {@literal String} aggregation operations.
+ *
+ * @author Christoph Strobl
+ */
+ class StringOperators {
+
+ /**
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static StringOperatorFactory valueOf(String fieldReference) {
+ return new StringOperatorFactory(fieldReference);
+ }
+
+ /**
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static StringOperatorFactory valueOf(AggregationExpression fieldReference) {
+ return new StringOperatorFactory(fieldReference);
+ }
+
+ public static class StringOperatorFactory {
+
+ private final String fieldReference;
+ private final AggregationExpression expression;
+
+ public StringOperatorFactory(String fieldReference) {
+ this.fieldReference = fieldReference;
+ this.expression = null;
+ }
+
+ public StringOperatorFactory(AggregationExpression expression) {
+ this.fieldReference = null;
+ this.expression = expression;
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and concats the
+ * value of the referenced field to it.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Concat concatValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createConcat().concatValueOf(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and concats the
+ * result of the given {@link AggregationExpression} to it.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Concat concatValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createConcat().concatValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and concats given
+ * {@literal value} to it.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Concat concat(String value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return createConcat().concat(value);
+ }
+
+ private Concat createConcat() {
+ return fieldReference != null ? Concat.valueOf(fieldReference) : Concat.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and returns a
+ * substring starting at a specified index position.
+ *
+ * @param start
+ * @return
+ */
+ public Substr substring(int start) {
+ return substring(start, -1);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and returns a
+ * substring starting at a specified index position including the specified number of characters.
+ *
+ * @param start
+ * @param nrOfChars
+ * @return
+ */
+ public Substr substring(int start, int nrOfChars) {
+ return createSubstr().substring(start, nrOfChars);
+ }
+
+ private Substr createSubstr() {
+ return fieldReference != null ? Substr.valueOf(fieldReference) : Substr.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and lowers it.
+ *
+ * @return
+ */
+ public ToLower toLower() {
+ return fieldReference != null ? ToLower.lowerValueOf(fieldReference) : ToLower.lowerValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and uppers it.
+ *
+ * @return
+ */
+ public ToUpper toUpper() {
+ return fieldReference != null ? ToUpper.upperValueOf(fieldReference) : ToUpper.upperValueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and performs
+ * case-insensitive comparison to the given {@literal value}.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public StrCaseCmp strCaseCmp(String value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return createStrCaseCmp().strcasecmp(value);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and performs
+ * case-insensitive comparison to the referenced {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public StrCaseCmp strCaseCmpValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createStrCaseCmp().strcasecmpValueOf(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated string representation and performs
+ * case-insensitive comparison to the result of the given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public StrCaseCmp strCaseCmpValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createStrCaseCmp().strcasecmpValueOf(expression);
+ }
+
+ private StrCaseCmp createStrCaseCmp() {
+ return fieldReference != null ? StrCaseCmp.valueOf(fieldReference) : StrCaseCmp.valueOf(expression);
+ }
+ }
+ }
+
+ /**
+ * Gateway to {@litearl array} aggregation operations.
+ *
+ * @author Christoph Strobl
+ */
+ class ArrayOperators {
+
+ /**
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static ArrayOperatorFactory arrayOf(String fieldReference) {
+ return new ArrayOperatorFactory(fieldReference);
+ }
+
+ /**
+ * Take the array referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static ArrayOperatorFactory arrayOf(AggregationExpression expression) {
+ return new ArrayOperatorFactory(expression);
+ }
+
+ public static class ArrayOperatorFactory {
+
+ private final String fieldReference;
+ private final AggregationExpression expression;
+
+ public ArrayOperatorFactory(String fieldReference) {
+ this.fieldReference = fieldReference;
+ this.expression = null;
+ }
+
+ public ArrayOperatorFactory(AggregationExpression expression) {
+ this.fieldReference = null;
+ this.expression = expression;
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and returns the element at the
+ * specified array {@literal position}.
+ *
+ * @param position
+ * @return
+ */
+ public ArrayElemtAt elementAt(int position) {
+ return createArrayElemAt().elementAt(position);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and returns the element at the
+ * position resulting form the given {@literal expression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public ArrayElemtAt elementAt(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createArrayElemAt().elementAt(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and returns the element at the
+ * position defined by the referenced {@literal field}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public ArrayElemtAt elementAt(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createArrayElemAt().elementAt(fieldReference);
+ }
+
+ private ArrayElemtAt createArrayElemAt() {
+ return usesFieldRef() ? ArrayElemtAt.arrayOf(fieldReference) : ArrayElemtAt.arrayOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and concats the given
+ * {@literal arrayFieldReference} to it.
+ *
+ * @param arrayFieldReference must not be {@literal null}.
+ * @return
+ */
+ public ConcatArrays concat(String arrayFieldReference) {
+
+ Assert.notNull(arrayFieldReference, "ArrayFieldReference must not be null!");
+ return createConcatArrays().concat(arrayFieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and concats the array resulting form
+ * the given {@literal expression} to it.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public ConcatArrays concat(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createConcatArrays().concat(expression);
+ }
+
+ private ConcatArrays createConcatArrays() {
+ return usesFieldRef() ? ConcatArrays.arrayOf(fieldReference) : ConcatArrays.arrayOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and selects a subset of the array to
+ * return based on the specified condition.
+ *
+ * @return
+ */
+ public AsBuilder filter() {
+ return Filter.filter(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and an check if its an array.
+ *
+ * @return
+ */
+ public IsArray isArray() {
+ return usesFieldRef() ? IsArray.isArray(fieldReference) : IsArray.isArray(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and retrieves its length.
+ *
+ * @return
+ */
+ public Size length() {
+ return usesFieldRef() ? Size.lengthOfArray(fieldReference) : Size.lengthOfArray(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that takes the associated array and selects a subset from it.
+ *
+ * @return
+ */
+ public Slice slice() {
+ return usesFieldRef() ? Slice.sliceArrayOf(fieldReference) : Slice.sliceArrayOf(expression);
+ }
+
+ private boolean usesFieldRef() {
+ return fieldReference != null;
+ }
+ }
+ }
+
+ /**
+ * @author Christoph Strobl
+ */
+ class LiteralOperators {
+
+ /**
+ * Take the value referenced by given {@literal value}.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public static LiteralOperatorFactory valueOf(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new LiteralOperatorFactory(value);
+ }
+
+ public static class LiteralOperatorFactory {
+
+ private final Object value;
+
+ public LiteralOperatorFactory(Object value) {
+ this.value = value;
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the associated value without parsing.
+ *
+ * @return
+ */
+ public Literal asLiteral() {
+ return Literal.asLiteral(value);
+ }
+ }
+ }
+
+ /**
+ * @author Christoph Strobl
+ */
+ class DateOperators {
+
+ /**
+ * Take the date referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static DateOperatorFactory dateOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new DateOperatorFactory(fieldReference);
+ }
+
+ /**
+ * Take the date resulting from the given {@literal expression}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static DateOperatorFactory dateOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new DateOperatorFactory(expression);
+ }
+
+ public static class DateOperatorFactory {
+
+ private final String fieldReference;
+ private final AggregationExpression expression;
+
+ public DateOperatorFactory(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ this.fieldReference = fieldReference;
+ this.expression = null;
+ }
+
+ public DateOperatorFactory(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ this.fieldReference = null;
+ this.expression = expression;
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the day of the year for a date as a number between 1
+ * and 366.
+ *
+ * @return
+ */
+ public DayOfYear dayOfYear() {
+ return usesFieldRef() ? DayOfYear.dayOfYear(fieldReference) : DayOfYear.dayOfYear(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the day of the month for a date as a number between 1
+ * and 31.
+ *
+ * @return
+ */
+ public DayOfMonth dayOfMonth() {
+ return usesFieldRef() ? DayOfMonth.dayOfMonth(fieldReference) : DayOfMonth.dayOfMonth(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the day of the week for a date as a number between 1
+ * (Sunday) and 7 (Saturday).
+ *
+ * @return
+ */
+ public DayOfWeek dayOfWeek() {
+ return usesFieldRef() ? DayOfWeek.dayOfWeek(fieldReference) : DayOfWeek.dayOfWeek(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the year portion of a date.
+ *
+ * @return
+ */
+ public Year year() {
+ return usesFieldRef() ? Year.yearOf(fieldReference) : Year.yearOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the month of a date as a number between 1 and 12.
+ *
+ * @return
+ */
+ public Month month() {
+ return usesFieldRef() ? Month.monthOf(fieldReference) : Month.monthOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the week of the year for a date as a number between 0
+ * and 53.
+ *
+ * @return
+ */
+ public Week week() {
+ return usesFieldRef() ? Week.weekOf(fieldReference) : Week.weekOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the hour portion of a date as a number between 0 and
+ * 23.
+ *
+ * @return
+ */
+ public Hour hour() {
+ return usesFieldRef() ? Hour.hourOf(fieldReference) : Hour.hourOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the minute portion of a date as a number between 0 and
+ * 59.
+ *
+ * @return
+ */
+ public Minute minute() {
+ return usesFieldRef() ? Minute.minuteOf(fieldReference) : Minute.minuteOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the second portion of a date as a number between 0 and
+ * 59, but can be 60 to account for leap seconds.
+ *
+ * @return
+ */
+ public Second second() {
+ return usesFieldRef() ? Second.secondOf(fieldReference) : Second.secondOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that returns the millisecond portion of a date as an integer between
+ * 0 and 999.
+ *
+ * @return
+ */
+ public Millisecond millisecond() {
+ return usesFieldRef() ? Millisecond.millisecondOf(fieldReference) : Millisecond.millisecondOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpressions} that converts a date object to a string according to a
+ * user-specified {@literal format}.
+ *
+ * @param format must not be {@literal null}.
+ * @return
+ */
+ public DateToString toString(String format) {
+ return (usesFieldRef() ? DateToString.dateOf(fieldReference) : DateToString.dateOf(expression))
+ .toString(format);
+ }
+
+ private boolean usesFieldRef() {
+ return fieldReference != null;
+ }
+ }
+ }
+
+ /**
+ * @author Christoph Strobl
+ */
+ abstract class AbstractAggregationExpression implements AggregationExpression {
+
+ private final Object value;
+
+ protected AbstractAggregationExpression(Object value) {
+ this.value = value;
+ }
+
+ @Override
+ public DBObject toDbObject(AggregationOperationContext context) {
+ return toDbObject(this.value, context);
+ }
+
+ public DBObject toDbObject(Object value, AggregationOperationContext context) {
+
+ Object valueToUse;
+ if (value instanceof List) {
+
+ List arguments = (List) value;
+ List args = new ArrayList(arguments.size());
+
+ for (Object val : arguments) {
+ args.add(unpack(val, context));
+ }
+ valueToUse = args;
+ } else if (value instanceof Map) {
+
+ DBObject dbo = new BasicDBObject();
+ for (Map.Entry entry : ((Map) value).entrySet()) {
+ dbo.put(entry.getKey(), unpack(entry.getValue(), context));
+ }
+ valueToUse = dbo;
+ } else {
+ valueToUse = unpack(value, context);
+ }
+
+ return new BasicDBObject(getMongoMethod(), valueToUse);
+ }
+
+ protected static List asFields(String... fieldRefs) {
+
+ if (ObjectUtils.isEmpty(fieldRefs)) {
+ return Collections.emptyList();
+ }
+
+ return Fields.fields(fieldRefs).asList();
+ }
+
+ private Object unpack(Object value, AggregationOperationContext context) {
+
+ if (value instanceof AggregationExpression) {
+ return ((AggregationExpression) value).toDbObject(context);
+ }
+
+ if (value instanceof Field) {
+ return context.getReference((Field) value).toString();
+ }
+
+ return value;
+ }
+
+ protected List append(Object value) {
+
+ if (this.value instanceof List) {
+
+ List clone = new ArrayList((List) this.value);
+
+ if (value instanceof List) {
+ for (Object val : (List) value) {
+ clone.add(val);
+ }
+ } else {
+ clone.add(value);
+ }
+ return clone;
+ }
+
+ return Arrays.asList(this.value, value);
+ }
+
+ protected Object append(String key, Object value) {
+
+ if (!(value instanceof Map)) {
+ throw new IllegalArgumentException("o_O");
+ }
+ Map clone = new LinkedHashMap((Map) value);
+ clone.put(key, value);
+ return clone;
+
+ }
+
+ public abstract String getMongoMethod();
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $setEquals}.
+ *
+ * @author Christoph Strobl
+ */
+ class SetEquals extends AbstractAggregationExpression {
+
+ private SetEquals(List> arrays) {
+ super(arrays);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$setEquals";
+ }
+
+ /**
+ * Create new {@link SetEquals}.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public static SetEquals arrayAsSet(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new SetEquals(asFields(arrayReference));
+ }
+
+ /**
+ * Create new {@link SetEquals}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static SetEquals arrayAsSet(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new SetEquals(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link java.util.Set} with all previously added arguments appending the given one.
+ *
+ * @param arrayReferences must not be {@literal null}.
+ * @return
+ */
+ public SetEquals isEqualTo(String... arrayReferences) {
+
+ Assert.notNull(arrayReferences, "ArrayReferences must not be null!");
+ return new SetEquals(append(Fields.fields(arrayReferences).asList()));
+ }
+
+ /**
+ * Creates new {@link Sum} with all previously added arguments appending the given one.
+ *
+ * @param expressions must not be {@literal null}.
+ * @return
+ */
+ public SetEquals isEqualTo(AggregationExpression... expressions) {
+
+ Assert.notNull(expressions, "Expressions must not be null!");
+ return new SetEquals(append(Arrays.asList(expressions)));
+ }
+
+ /**
+ * Creates new {@link Sum} with all previously added arguments appending the given one.
+ *
+ * @param array must not be {@literal null}.
+ * @return
+ */
+ public SetEquals isEqualTo(Object[] array) {
+
+ Assert.notNull(array, "Array must not be null!");
+ return new SetEquals(append(array));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $setIntersection}.
+ *
+ * @author Christoph Strobl
+ */
+ class SetIntersection extends AbstractAggregationExpression {
+
+ private SetIntersection(List> arrays) {
+ super(arrays);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$setIntersection";
+ }
+
+ /**
+ * Creates new {@link SetIntersection}
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public static SetIntersection arrayAsSet(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new SetIntersection(asFields(arrayReference));
+ }
+
+ /**
+ * Creates new {@link SetIntersection}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static SetIntersection arrayAsSet(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new SetIntersection(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link SetIntersection} with all previously added arguments appending the given one.
+ *
+ * @param arrayReferences must not be {@literal null}.
+ * @return
+ */
+ public SetIntersection intersects(String... arrayReferences) {
+
+ Assert.notNull(arrayReferences, "ArrayReferences must not be null!");
+ return new SetIntersection(append(asFields(arrayReferences)));
+ }
+
+ /**
+ * Creates new {@link SetIntersection} with all previously added arguments appending the given one.
+ *
+ * @param expressions must not be {@literal null}.
+ * @return
+ */
+ public SetIntersection intersects(AggregationExpression... expressions) {
+
+ Assert.notNull(expressions, "Expressions must not be null!");
+ return new SetIntersection(append(Arrays.asList(expressions)));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $setUnion}.
+ *
+ * @author Christoph Strobl
+ */
+ class SetUnion extends AbstractAggregationExpression {
+
+ private SetUnion(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$setUnion";
+ }
+
+ /**
+ * Creates new {@link SetUnion}.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public static SetUnion arrayAsSet(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new SetUnion(asFields(arrayReference));
+ }
+
+ /**
+ * Creates new {@link SetUnion}.
+ *
+ * @param expressions must not be {@literal null}.
+ * @return
+ */
+ public static SetUnion arrayAsSet(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new SetUnion(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link SetUnion} with all previously added arguments appending the given one.
+ *
+ * @param arrayReferences must not be {@literal null}.
+ * @return
+ */
+ public SetUnion union(String... arrayReferences) {
+
+ Assert.notNull(arrayReferences, "ArrayReferences must not be null!");
+ return new SetUnion(append(asFields(arrayReferences)));
+ }
+
+ /**
+ * Creates new {@link SetUnion} with all previously added arguments appending the given one.
+ *
+ * @param expressions must not be {@literal null}.
+ * @return
+ */
+ public SetUnion union(AggregationExpression... expressions) {
+
+ Assert.notNull(expressions, "Expressions must not be null!");
+ return new SetUnion(append(Arrays.asList(expressions)));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $setDifference}.
+ *
+ * @author Christoph Strobl
+ */
+ class SetDifference extends AbstractAggregationExpression {
+
+ private SetDifference(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$setDifference";
+ }
+
+ /**
+ * Creates new {@link SetDifference}.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public static SetDifference arrayAsSet(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new SetDifference(asFields(arrayReference));
+ }
+
+ /**
+ * Creates new {@link SetDifference}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static SetDifference arrayAsSet(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new SetDifference(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link SetDifference} with all previously added arguments appending the given one.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public SetDifference differenceTo(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new SetDifference(append(Fields.field(arrayReference)));
+ }
+
+ /**
+ * Creates new {@link SetDifference} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public SetDifference differenceTo(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new SetDifference(append(expression));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $setIsSubset}.
+ *
+ * @author Christoph Strobl
+ */
+ class SetIsSubset extends AbstractAggregationExpression {
+
+ private SetIsSubset(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$setIsSubset";
+ }
+
+ /**
+ * Creates new {@link SetIsSubset}.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public static SetIsSubset arrayAsSet(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new SetIsSubset(asFields(arrayReference));
+ }
+
+ /**
+ * Creates new {@link SetIsSubset}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static SetIsSubset arrayAsSet(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new SetIsSubset(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link SetIsSubset} with all previously added arguments appending the given one.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public SetIsSubset isSubsetOf(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new SetIsSubset(append(Fields.field(arrayReference)));
+ }
+
+ /**
+ * Creates new {@link SetIsSubset} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public SetIsSubset isSubsetOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new SetIsSubset(append(expression));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $anyElementTrue}.
+ *
+ * @author Christoph Strobl
+ */
+ class AnyElementTrue extends AbstractAggregationExpression {
+
+ private AnyElementTrue(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$anyElementTrue";
+ }
+
+ /**
+ * Creates new {@link AnyElementTrue}.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public static AnyElementTrue arrayAsSet(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new AnyElementTrue(asFields(arrayReference));
+ }
+
+ /**
+ * Creats new {@link AnyElementTrue}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static AnyElementTrue arrayAsSet(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new AnyElementTrue(Collections.singletonList(expression));
+ }
+
+ public AnyElementTrue anyElementTrue() {
+ return this;
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $allElementsTrue}.
+ *
+ * @author Christoph Strobl
+ */
+ class AllElementsTrue extends AbstractAggregationExpression {
+
+ private AllElementsTrue(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$allElementsTrue";
+ }
+
+ /**
+ * Creates new {@link AllElementsTrue}.
+ *
+ * @param arrayReference must not be {@literal null}.
+ * @return
+ */
+ public static AllElementsTrue arrayAsSet(String arrayReference) {
+
+ Assert.notNull(arrayReference, "ArrayReference must not be null!");
+ return new AllElementsTrue(asFields(arrayReference));
+ }
+
+ /**
+ * Creates new {@link AllElementsTrue}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static AllElementsTrue arrayAsSet(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new AllElementsTrue(Collections.singletonList(expression));
+ }
+
+ public AllElementsTrue allElementsTrue() {
+ return this;
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $abs}.
+ *
+ * @author Christoph Strobl
+ */
+ class Abs extends AbstractAggregationExpression {
+
+ private Abs(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$abs";
+ }
+
+ public static Abs absoluteValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Abs(Fields.field(fieldReference));
+ }
+
+ public static Abs absoluteValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Abs(expression);
+ }
+
+ public static Abs absoluteValueOf(Number value) {
+ return new Abs(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $add}.
+ *
+ * @author Christoph Strobl
+ */
+ class Add extends AbstractAggregationExpression {
+
+ protected Add(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$add";
+ }
+
+ public static Add valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Add(asFields(fieldReference));
+ }
+
+ public static Add valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Add(Collections.singletonList(expression));
+ }
+
+ public static Add valueOf(Number value) {
+ return new Add(Collections.singletonList(value));
+ }
+
+ public Add add(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Add(append(Fields.field(fieldReference)));
+ }
+
+ public Add add(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Add(append(expression));
+ }
+
+ public Add add(Number value) {
+ return new Add(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $ceil}.
+ *
+ * @author Christoph Strobl
+ */
+ class Ceil extends AbstractAggregationExpression {
+
+ private Ceil(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$ceil";
+ }
+
+ public static Ceil ceilValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Ceil(Fields.field(fieldReference));
+ }
+
+ public static Ceil ceilValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Ceil(expression);
+ }
+
+ public static Ceil ceilValueOf(Number value) {
+ return new Ceil(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $divide}.
+ *
+ * @author Christoph Strobl
+ */
+ class Divide extends AbstractAggregationExpression {
+
+ private Divide(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$divide";
+ }
+
+ public static Divide valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Divide(asFields(fieldReference));
+ }
+
+ public static Divide valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Divide(Collections.singletonList(expression));
+ }
+
+ public static Divide valueOf(Number value) {
+ return new Divide(Collections.singletonList(value));
+ }
+
+ public Divide divideBy(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Divide(append(Fields.field(fieldReference)));
+ }
+
+ public Divide divideBy(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Divide(append(expression));
+ }
+
+ public Divide divideBy(Number value) {
+ return new Divide(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $exp}.
+ *
+ * @author Christoph Strobl
+ */
+ class Exp extends AbstractAggregationExpression {
+
+ protected Exp(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$exp";
+ }
+
+ public static Exp expValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Exp(Fields.field(fieldReference));
+ }
+
+ public static Exp expValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Exp(expression);
+ }
+
+ public static Exp expValueOf(Number value) {
+ return new Exp(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $floor}.
+ *
+ * @author Christoph Strobl
+ */
+ class Floor extends AbstractAggregationExpression {
+
+ protected Floor(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$floor";
+ }
+
+ public static Floor floorValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Floor(Fields.field(fieldReference));
+ }
+
+ public static Floor floorValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Floor(expression);
+ }
+
+ public static Floor floorValueOf(Number value) {
+ return new Floor(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $ln}.
+ *
+ * @author Christoph Strobl
+ */
+ class Ln extends AbstractAggregationExpression {
+
+ private Ln(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$ln";
+ }
+
+ public static Ln lnValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Ln(Fields.field(fieldReference));
+ }
+
+ public static Ln lnValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Ln(expression);
+ }
+
+ public static Ln lnValueOf(Number value) {
+ return new Ln(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $log}.
+ *
+ * @author Christoph Strobl
+ */
+ class Log extends AbstractAggregationExpression {
+
+ private Log(List> values) {
+ super(values);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$log";
+ }
+
+ public static Log valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Log(asFields(fieldReference));
+ }
+
+ public static Log valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Log(Collections.singletonList(expression));
+ }
+
+ public static Log valueOf(Number value) {
+ return new Log(Collections.singletonList(value));
+ }
+
+ public Log log(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Log(append(Fields.field(fieldReference)));
+ }
+
+ public Log log(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Log(append(expression));
+ }
+
+ public Log log(Number base) {
+ return new Log(append(base));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $log10}.
+ *
+ * @author Christoph Strobl
+ */
+ class Log10 extends AbstractAggregationExpression {
+
+ private Log10(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$log10";
+ }
+
+ public static Log10 log10ValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Log10(Fields.field(fieldReference));
+ }
+
+ public static Log10 log10ValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Log10(expression);
+ }
+
+ public static Log10 log10ValueOf(Number value) {
+ return new Log10(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $mod}.
+ *
+ * @author Christoph Strobl
+ */
+ class Mod extends AbstractAggregationExpression {
+
+ private Mod(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$mod";
+ }
+
+ public static Mod valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Mod(asFields(fieldReference));
+ }
+
+ public static Mod valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Mod(Collections.singletonList(expression));
+ }
+
+ public static Mod valueOf(Number value) {
+ return new Mod(Collections.singletonList(value));
+ }
+
+ public Mod mod(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Mod(append(Fields.field(fieldReference)));
+ }
+
+ public Mod mod(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Mod(append(expression));
+ }
+
+ public Mod mod(Number base) {
+ return new Mod(append(base));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $multiply}.
+ *
+ * @author Christoph Strobl
+ */
+ class Multiply extends AbstractAggregationExpression {
+
+ private Multiply(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$multiply";
+ }
+
+ public static Multiply valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Multiply(asFields(fieldReference));
+ }
+
+ public static Multiply valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Multiply(Collections.singletonList(expression));
+ }
+
+ public static Multiply valueOf(Number value) {
+ return new Multiply(Collections.singletonList(value));
+ }
+
+ public Multiply multiplyBy(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Multiply(append(Fields.field(fieldReference)));
+ }
+
+ public Multiply multiplyBy(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Multiply(append(expression));
+ }
+
+ public Multiply multiplyBy(Number value) {
+ return new Multiply(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $pow}.
+ *
+ * @author Christoph Strobl
+ */
+ class Pow extends AbstractAggregationExpression {
+
+ private Pow(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$pow";
+ }
+
+ public static Pow valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Pow(asFields(fieldReference));
+ }
+
+ public static Pow valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Pow(Collections.singletonList(expression));
+ }
+
+ public static Pow valueOf(Number value) {
+ return new Pow(Collections.singletonList(value));
+ }
+
+ public Pow pow(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Pow(append(Fields.field(fieldReference)));
+ }
+
+ public Pow pow(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Pow(append(expression));
+ }
+
+ public Pow pow(Number value) {
+ return new Pow(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $sqrt}.
+ *
+ * @author Christoph Strobl
+ */
+ class Sqrt extends AbstractAggregationExpression {
+
+ private Sqrt(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$sqrt";
+ }
+
+ public static Sqrt sqrtOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Sqrt(Fields.field(fieldReference));
+ }
+
+ public static Sqrt sqrtOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Sqrt(expression);
+ }
+
+ public static Sqrt sqrtOf(Number value) {
+ return new Sqrt(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $subtract}.
+ *
+ * @author Christoph Strobl
+ */
+ class Subtract extends AbstractAggregationExpression {
+
+ protected Subtract(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$subtract";
+ }
+
+ public static Subtract valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Subtract(asFields(fieldReference));
+ }
+
+ public static Subtract valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Subtract(Collections.singletonList(expression));
+ }
+
+ public static Subtract valueOf(Number value) {
+ return new Subtract(Collections.singletonList(value));
+ }
+
+ public Subtract subtract(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Subtract(append(Fields.field(fieldReference)));
+ }
+
+ public Subtract subtract(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Subtract(append(expression));
+ }
+
+ public Subtract subtract(Number value) {
+ return new Subtract(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $trunc}.
+ *
+ * @author Christoph Strobl
+ */
+ class Trunc extends AbstractAggregationExpression {
+
+ private Trunc(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$trunc";
+ }
+
+ public static Trunc truncValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Trunc(Fields.field(fieldReference));
+ }
+
+ public static Trunc truncValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Trunc(expression);
+ }
+
+ public static Trunc truncValueOf(Number value) {
+ return new Trunc(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $concat}.
+ *
+ * @author Christoph Strobl
+ */
+ class Concat extends AbstractAggregationExpression {
+
+ private Concat(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$concat";
+ }
+
+ public static Concat valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Concat(asFields(fieldReference));
+ }
+
+ public static Concat valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Concat(Collections.singletonList(expression));
+ }
+
+ public static Concat stringValue(String value) {
+ return new Concat(Collections.singletonList(value));
+ }
+
+ public Concat concatValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Concat(append(Fields.field(fieldReference)));
+ }
+
+ public Concat concatValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Concat(append(expression));
+ }
+
+ public Concat concat(String value) {
+ return new Concat(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $substr}.
+ *
+ * @author Christoph Strobl
+ */
+ class Substr extends AbstractAggregationExpression {
+
+ private Substr(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$substr";
+ }
+
+ public static Substr valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Substr(asFields(fieldReference));
+ }
+
+ public static Substr valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Substr(Collections.singletonList(expression));
+ }
+
+ public Substr substring(int start) {
+ return substring(start, -1);
+ }
+
+ public Substr substring(int start, int nrOfChars) {
+ return new Substr(append(Arrays.asList(start, nrOfChars)));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $toLower}.
+ *
+ * @author Christoph Strobl
+ */
+ class ToLower extends AbstractAggregationExpression {
+
+ private ToLower(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$toLower";
+ }
+
+ public static ToLower lower(String value) {
+ return new ToLower(value);
+ }
+
+ public static ToLower lowerValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new ToLower(Fields.field(fieldReference));
+ }
+
+ public static ToLower lowerValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new ToLower(Collections.singletonList(expression));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $toUpper}.
+ *
+ * @author Christoph Strobl
+ */
+ class ToUpper extends AbstractAggregationExpression {
+
+ private ToUpper(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$toUpper";
+ }
+
+ public static ToUpper upper(String value) {
+ return new ToUpper(value);
+ }
+
+ public static ToUpper upperValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new ToUpper(Fields.field(fieldReference));
+ }
+
+ public static ToUpper upperValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new ToUpper(Collections.singletonList(expression));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $strcasecmp}.
+ *
+ * @author Christoph Strobl
+ */
+ class StrCaseCmp extends AbstractAggregationExpression {
+
+ private StrCaseCmp(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$strcasecmp";
+ }
+
+ public static StrCaseCmp valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new StrCaseCmp(asFields(fieldReference));
+ }
+
+ public static StrCaseCmp valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new StrCaseCmp(Collections.singletonList(expression));
+ }
+
+ public static StrCaseCmp stringValue(String value) {
+ return new StrCaseCmp(Collections.singletonList(value));
+ }
+
+ public StrCaseCmp strcasecmp(String value) {
+ return new StrCaseCmp(append(value));
+ }
+
+ public StrCaseCmp strcasecmpValueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new StrCaseCmp(append(Fields.field(fieldReference)));
+ }
+
+ public StrCaseCmp strcasecmpValueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new StrCaseCmp(append(expression));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $arrayElementAt}.
+ *
+ * @author Christoph Strobl
+ */
+ class ArrayElemtAt extends AbstractAggregationExpression {
+
+ private ArrayElemtAt(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$arrayElemAt";
+ }
+
+ public static ArrayElemtAt arrayOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new ArrayElemtAt(asFields(fieldReference));
+ }
+
+ public static ArrayElemtAt arrayOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new ArrayElemtAt(Collections.singletonList(expression));
+ }
+
+ public ArrayElemtAt elementAt(int index) {
+ return new ArrayElemtAt(append(index));
+ }
+
+ public ArrayElemtAt elementAt(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new ArrayElemtAt(append(expression));
+ }
+
+ public ArrayElemtAt elementAt(String arrayFieldReference) {
+
+ Assert.notNull(arrayFieldReference, "ArrayReference must not be null!");
+ return new ArrayElemtAt(append(Fields.field(arrayFieldReference)));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $concatArrays}.
+ *
+ * @author Christoph Strobl
+ */
+ class ConcatArrays extends AbstractAggregationExpression {
+
+ private ConcatArrays(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$concatArrays";
+ }
+
+ public static ConcatArrays arrayOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new ConcatArrays(asFields(fieldReference));
+ }
+
+ public static ConcatArrays arrayOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new ConcatArrays(Collections.singletonList(expression));
+ }
+
+ public ConcatArrays concat(String arrayFieldReference) {
+
+ Assert.notNull(arrayFieldReference, "ArrayFieldReference must not be null!");
+ return new ConcatArrays(append(Fields.field(arrayFieldReference)));
+ }
+
+ public ConcatArrays concat(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new ConcatArrays(append(expression));
+ }
+ }
+
+ /**
+ * {@code $filter} {@link AggregationExpression} allows to select a subset of the array to return based on the
+ * specified condition.
+ *
+ * @author Christoph Strobl
+ * @since 1.10
+ */
+ class Filter implements AggregationExpression {
+
+ private Object input;
+ private ExposedField as;
+ private Object condition;
+
+ private Filter() {
+ // used by builder
+ }
+
+ /**
+ * Set the {@literal field} to apply the {@code $filter} to.
+ *
+ * @param field must not be {@literal null}.
+ * @return never {@literal null}.
+ */
+ public static AsBuilder filter(String field) {
+
+ Assert.notNull(field, "Field must not be null!");
+ return filter(Fields.field(field));
+ }
+
+ /**
+ * Set the {@literal field} to apply the {@code $filter} to.
+ *
+ * @param field must not be {@literal null}.
+ * @return never {@literal null}.
+ */
+ public static AsBuilder filter(Field field) {
+
+ Assert.notNull(field, "Field must not be null!");
+ return new FilterExpressionBuilder().filter(field);
+ }
+
+ /**
+ * Set the {@literal values} to apply the {@code $filter} to.
+ *
+ * @param values must not be {@literal null}.
+ * @return
+ */
+ public static AsBuilder filter(List> values) {
+
+ Assert.notNull(values, "Values must not be null!");
+ return new FilterExpressionBuilder().filter(values);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
+ */
+ @Override
+ public DBObject toDbObject(final AggregationOperationContext context) {
+
+ return toFilter(new ExposedFieldsAggregationOperationContext(ExposedFields.from(as), context) {
+
+ @Override
+ public FieldReference getReference(Field field) {
+
+ FieldReference ref = null;
+ try {
+ ref = context.getReference(field);
+ } catch (Exception e) {
+ // just ignore that one.
+ }
+ return ref != null ? ref : super.getReference(field);
+ }
+ });
+ }
+
+ private DBObject toFilter(AggregationOperationContext context) {
+
+ DBObject filterExpression = new BasicDBObject();
+
+ filterExpression.putAll(context.getMappedObject(new BasicDBObject("input", getMappedInput(context))));
+ filterExpression.put("as", as.getTarget());
+
+ filterExpression.putAll(context.getMappedObject(new BasicDBObject("cond", getMappedCondition(context))));
+
+ return new BasicDBObject("$filter", filterExpression);
+ }
+
+ private Object getMappedInput(AggregationOperationContext context) {
+ return input instanceof Field ? context.getReference((Field) input).toString() : input;
+ }
+
+ private Object getMappedCondition(AggregationOperationContext context) {
+
+ if (!(condition instanceof AggregationExpression)) {
+ return condition;
+ }
+
+ NestedDelegatingExpressionAggregationOperationContext nea = new NestedDelegatingExpressionAggregationOperationContext(
+ context);
+ return ((AggregationExpression) condition).toDbObject(nea);
+ }
+
+ /**
+ * @author Christoph Strobl
+ */
+ public interface InputBuilder {
+
+ /**
+ * Set the {@literal values} to apply the {@code $filter} to.
+ *
+ * @param array must not be {@literal null}.
+ * @return
+ */
+ AsBuilder filter(List> array);
+
+ /**
+ * Set the {@literal field} holding an array to apply the {@code $filter} to.
+ *
+ * @param field must not be {@literal null}.
+ * @return
+ */
+ AsBuilder filter(Field field);
+ }
+
+ /**
+ * @author Christoph Strobl
+ */
+ public interface AsBuilder {
+
+ /**
+ * Set the {@literal variableName} for the elements in the input array.
+ *
+ * @param variableName must not be {@literal null}.
+ * @return
+ */
+ ConditionBuilder as(String variableName);
+ }
+
+ /**
+ * @author Christoph Strobl
+ */
+ public interface ConditionBuilder {
+
+ /**
+ * Set the {@link AggregationExpression} that determines whether to include the element in the resulting array.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ Filter by(AggregationExpression expression);
+
+ /**
+ * Set the {@literal expression} that determines whether to include the element in the resulting array.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ Filter by(String expression);
+
+ /**
+ * Set the {@literal expression} that determines whether to include the element in the resulting array.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ Filter by(DBObject expression);
+ }
+
+ /**
+ * @author Christoph Strobl
+ */
+ static final class FilterExpressionBuilder implements InputBuilder, AsBuilder, ConditionBuilder {
+
+ private final Filter filter;
+
+ FilterExpressionBuilder() {
+ this.filter = new Filter();
+ }
+
+ public static InputBuilder newBuilder() {
+ return new FilterExpressionBuilder();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.InputBuilder#filter(java.util.List)
+ */
+ @Override
+ public AsBuilder filter(List> array) {
+
+ Assert.notNull(array, "Array must not be null!");
+ filter.input = new ArrayList(array);
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.InputBuilder#filter(org.springframework.data.mongodb.core.aggregation.Field)
+ */
+ @Override
+ public AsBuilder filter(Field field) {
+
+ Assert.notNull(field, "Field must not be null!");
+ filter.input = field;
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.AsBuilder#as(java.lang.String)
+ */
+ @Override
+ public ConditionBuilder as(String variableName) {
+
+ Assert.notNull(variableName, "Variable name must not be null!");
+ filter.as = new ExposedField(variableName, true);
+ return this;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
+ */
+ @Override
+ public Filter by(AggregationExpression condition) {
+
+ Assert.notNull(condition, "Condition must not be null!");
+ filter.condition = condition;
+ return filter;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(java.lang.String)
+ */
+ @Override
+ public Filter by(String expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ filter.condition = expression;
+ return filter;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(com.mongodb.DBObject)
+ */
+ @Override
+ public Filter by(DBObject expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ filter.condition = expression;
+ return filter;
+ }
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $isArray}.
+ *
+ * @author Christoph Strobl
+ */
+ class IsArray extends AbstractAggregationExpression {
+
+ private IsArray(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$isArray";
+ }
+
+ public static IsArray isArray(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new IsArray(Fields.field(fieldReference));
+ }
+
+ public static IsArray isArray(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new IsArray(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $size}.
+ *
+ * @author Christoph Strobl
+ */
+ class Size extends AbstractAggregationExpression {
+
+ private Size(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$size";
+ }
+
+ public static Size lengthOfArray(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Size(Fields.field(fieldReference));
+ }
+
+ public static Size lengthOfArray(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Size(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $slice}.
+ *
+ * @author Christoph Strobl
+ */
+ class Slice extends AbstractAggregationExpression {
+
+ private Slice(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$slice";
+ }
+
+ public static Slice sliceArrayOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Slice(asFields(fieldReference));
+ }
+
+ public static Slice sliceArrayOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Slice(Collections.singletonList(expression));
+ }
+
+ public Slice itemCount(int nrElements) {
+ return new Slice(append(nrElements));
+ }
+
+ public SliceElementsBuilder offset(final int position) {
+
+ return new SliceElementsBuilder() {
+
+ @Override
+ public Slice itemCount(int nrElements) {
+ return new Slice(append(position)).itemCount(nrElements);
+ }
+ };
+ }
+
+ public interface SliceElementsBuilder {
+ Slice itemCount(int nrElements);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $literal}.
+ *
+ * @author Christoph Strobl
+ */
+ class Literal extends AbstractAggregationExpression {
+
+ private Literal(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$literal";
+ }
+
+ public static Literal asLiteral(Object value) {
+ return new Literal(value);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $dayOfYear}.
+ *
+ * @author Christoph Strobl
+ */
+ class DayOfYear extends AbstractAggregationExpression {
+
+ private DayOfYear(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$dayOfYear";
+ }
+
+ public static DayOfYear dayOfYear(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new DayOfYear(Fields.field(fieldReference));
+ }
+
+ public static DayOfYear dayOfYear(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new DayOfYear(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $dayOfMonth}.
+ *
+ * @author Christoph Strobl
+ */
+ class DayOfMonth extends AbstractAggregationExpression {
+
+ private DayOfMonth(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$dayOfMonth";
+ }
+
+ public static DayOfMonth dayOfMonth(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new DayOfMonth(Fields.field(fieldReference));
+ }
+
+ public static DayOfMonth dayOfMonth(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new DayOfMonth(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $dayOfWeek}.
+ *
+ * @author Christoph Strobl
+ */
+ class DayOfWeek extends AbstractAggregationExpression {
+
+ private DayOfWeek(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$dayOfWeek";
+ }
+
+ public static DayOfWeek dayOfWeek(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new DayOfWeek(Fields.field(fieldReference));
+ }
+
+ public static DayOfWeek dayOfWeek(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new DayOfWeek(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $year}.
+ *
+ * @author Christoph Strobl
+ */
+ class Year extends AbstractAggregationExpression {
+
+ private Year(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$year";
+ }
+
+ public static Year yearOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Year(Fields.field(fieldReference));
+ }
+
+ public static Year yearOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Year(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $month}.
+ *
+ * @author Christoph Strobl
+ */
+ class Month extends AbstractAggregationExpression {
+
+ private Month(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$month";
+ }
+
+ public static Month monthOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Month(Fields.field(fieldReference));
+ }
+
+ public static Month monthOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Month(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $week}.
+ *
+ * @author Christoph Strobl
+ */
+ class Week extends AbstractAggregationExpression {
+
+ private Week(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$week";
+ }
+
+ public static Week weekOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Week(Fields.field(fieldReference));
+ }
+
+ public static Week weekOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Week(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $hour}.
+ *
+ * @author Christoph Strobl
+ */
+ class Hour extends AbstractAggregationExpression {
+
+ private Hour(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$hour";
+ }
+
+ public static Hour hourOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Hour(Fields.field(fieldReference));
+ }
+
+ public static Hour hourOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Hour(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $minute}.
+ *
+ * @author Christoph Strobl
+ */
+ class Minute extends AbstractAggregationExpression {
+
+ private Minute(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$minute";
+ }
+
+ public static Minute minuteOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Minute(Fields.field(fieldReference));
+ }
+
+ public static Minute minuteOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Minute(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $second}.
+ *
+ * @author Christoph Strobl
+ */
+ class Second extends AbstractAggregationExpression {
+
+ private Second(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$second";
+ }
+
+ /**
+ * Creates new {@link Second}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Second secondOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Second(Fields.field(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Second}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Second secondOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Second(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $millisecond}.
+ *
+ * @author Christoph Strobl
+ */
+ class Millisecond extends AbstractAggregationExpression {
+
+ private Millisecond(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$millisecond";
+ }
+
+ /**
+ * Creates new {@link Millisecond}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Millisecond millisecondOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Millisecond(Fields.field(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Millisecond}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Millisecond millisecondOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Millisecond(expression);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $dateToString}.
+ *
+ * @author Christoph Strobl
+ */
+ class DateToString extends AbstractAggregationExpression {
+
+ private DateToString(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$dateToString";
+ }
+
+ /**
+ * Creates new {@link FormatBuilder} allowing to define the date format to apply.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static FormatBuilder dateOf(final String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new FormatBuilder() {
+ @Override
+ public DateToString toString(String format) {
+
+ Assert.notNull(format, "Format must not be null!");
+ return new DateToString(argumentMap(Fields.field(fieldReference), format));
+ }
+ };
+ }
+
+ /**
+ * Creates new {@link FormatBuilder} allowing to define the date format to apply.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static FormatBuilder dateOf(final AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new FormatBuilder() {
+ @Override
+ public DateToString toString(String format) {
+
+ Assert.notNull(format, "Format must not be null!");
+
+ return new DateToString(argumentMap(expression, format));
+ }
+ };
+ }
+
+ private static Map argumentMap(Object date, String format) {
+
+ Map args = new LinkedHashMap(2);
+ args.put("format", format);
+ args.put("date", date);
+ return args;
+ }
+
+ public interface FormatBuilder {
+
+ /**
+ * Creates new {@link DateToString} with all previously added arguments appending the given one.
+ *
+ * @param format must not be {@literal null}.
+ * @return
+ */
+ DateToString toString(String format);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $sum}.
+ *
+ * @author Christoph Strobl
+ */
+ class Sum extends AbstractAggregationExpression {
+
+ private Sum(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$sum";
+ }
+
+ /**
+ * Creates new {@link Sum}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Sum sumOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Sum(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Sum}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Sum sumOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Sum(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Sum} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Sum and(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Sum(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Sum} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Sum and(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Sum(append(expression));
+ }
+
+ @Override
+ public DBObject toDbObject(Object value, AggregationOperationContext context) {
+
+ if (value instanceof List) {
+ if (((List) value).size() == 1) {
+ return super.toDbObject(((List) value).iterator().next(), context);
+ }
+ }
+
+ return super.toDbObject(value, context);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $avg}.
+ *
+ * @author Christoph Strobl
+ */
+ class Avg extends AbstractAggregationExpression {
+
+ private Avg(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$avg";
+ }
+
+ /**
+ * Creates new {@link Avg}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Avg avgOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Avg(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Avg}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Avg avgOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Avg(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Avg} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Avg and(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Avg(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Avg} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Avg and(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Avg(append(expression));
+ }
+
+ @Override
+ public DBObject toDbObject(Object value, AggregationOperationContext context) {
+
+ if (value instanceof List) {
+ if (((List) value).size() == 1) {
+ return super.toDbObject(((List) value).iterator().next(), context);
+ }
+ }
+
+ return super.toDbObject(value, context);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $max}.
+ *
+ * @author Christoph Strobl
+ */
+ class Max extends AbstractAggregationExpression {
+
+ private Max(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$max";
+ }
+
+ /**
+ * Creates new {@link Max}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Max maxOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Max(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Max}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Max maxOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Max(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Max} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Max and(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Max(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Max} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Max and(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Max(append(expression));
+ }
+
+ @Override
+ public DBObject toDbObject(Object value, AggregationOperationContext context) {
+
+ if (value instanceof List) {
+ if (((List) value).size() == 1) {
+ return super.toDbObject(((List) value).iterator().next(), context);
+ }
+ }
+
+ return super.toDbObject(value, context);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $min}.
+ *
+ * @author Christoph Strobl
+ */
+ class Min extends AbstractAggregationExpression {
+
+ private Min(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$min";
+ }
+
+ /**
+ * Creates new {@link Min}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Min minOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Min(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Min}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Min minOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Min(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Min} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Min and(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Min(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Min} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Min and(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Min(append(expression));
+ }
+
+ @Override
+ public DBObject toDbObject(Object value, AggregationOperationContext context) {
+
+ if (value instanceof List) {
+ if (((List) value).size() == 1) {
+ return super.toDbObject(((List) value).iterator().next(), context);
+ }
+ }
+
+ return super.toDbObject(value, context);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $stdDevPop}.
+ *
+ * @author Christoph Strobl
+ */
+ class StdDevPop extends AbstractAggregationExpression {
+
+ private StdDevPop(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$stdDevPop";
+ }
+
+ /**
+ * Creates new {@link StdDevPop}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static StdDevPop stdDevPopOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new StdDevPop(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link StdDevPop} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static StdDevPop stdDevPopOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new StdDevPop(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link StdDevPop} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public StdDevPop and(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new StdDevPop(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link StdDevSamp} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public StdDevPop and(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new StdDevPop(append(expression));
+ }
+
+ @Override
+ public DBObject toDbObject(Object value, AggregationOperationContext context) {
+
+ if (value instanceof List) {
+ if (((List) value).size() == 1) {
+ return super.toDbObject(((List) value).iterator().next(), context);
+ }
+ }
+
+ return super.toDbObject(value, context);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $stdDevSamp}.
+ *
+ * @author Christoph Strobl
+ */
+ class StdDevSamp extends AbstractAggregationExpression {
+
+ private StdDevSamp(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$stdDevSamp";
+ }
+
+ /**
+ * Creates new {@link StdDevSamp}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static StdDevSamp stdDevSampOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new StdDevSamp(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link StdDevSamp}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static StdDevSamp stdDevSampOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new StdDevSamp(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link StdDevSamp} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public StdDevSamp and(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new StdDevSamp(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link StdDevSamp} with all previously added arguments appending the given one.
+ * NOTE: Only possible in {@code $project} stage.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public StdDevSamp and(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new StdDevSamp(append(expression));
+ }
+
+ @Override
+ public DBObject toDbObject(Object value, AggregationOperationContext context) {
+
+ if (value instanceof List) {
+ if (((List) value).size() == 1) {
+ return super.toDbObject(((List) value).iterator().next(), context);
+ }
+ }
+
+ return super.toDbObject(value, context);
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $cmp}.
+ *
+ * @author Christoph Strobl
+ */
+ class Cmp extends AbstractAggregationExpression {
+
+ private Cmp(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$cmp";
+ }
+
+ /**
+ * Creates new {@link Cmp}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Cmp valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Cmp(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Cmp}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Cmp valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Cmp(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Cmp} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Cmp compareTo(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Cmp(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Cmp} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Cmp compareTo(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Cmp(append(expression));
+ }
+
+ /**
+ * Creates new {@link Cmp} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Cmp compareToValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new Cmp(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $eq}.
+ *
+ * @author Christoph Strobl
+ */
+ class Eq extends AbstractAggregationExpression {
+
+ private Eq(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$eq";
+ }
+
+ /**
+ * Creates new {@link Eq}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Eq valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Eq(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Eq}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Eq valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Eq(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Eq} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Eq equalTo(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Eq(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Eq} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Eq equalTo(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Eq(append(expression));
+ }
+
+ /**
+ * Creates new {@link Eq} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Eq equalToValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new Eq(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $gt}.
+ *
+ * @author Christoph Strobl
+ */
+ class Gt extends AbstractAggregationExpression {
+
+ private Gt(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$gt";
+ }
+
+ /**
+ * Creates new {@link Gt}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Gt valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Gt(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Gt}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Gt valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Gt(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Gt} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Gt greaterThan(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Gt(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Gt} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Gt greaterThan(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Gt(append(expression));
+ }
+
+ /**
+ * Creates new {@link Gt} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Gt greaterThanValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new Gt(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $lt}.
+ *
+ * @author Christoph Strobl
+ */
+ class Lt extends AbstractAggregationExpression {
+
+ private Lt(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$lt";
+ }
+
+ /**
+ * Creates new {@link Lt}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Lt valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Lt(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Lt}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Lt valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Lt(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Lt} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Lt lessThan(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Lt(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Lt} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Lt lessThan(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Lt(append(expression));
+ }
+
+ /**
+ * Creates new {@link Lt} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Lt lessThanValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new Lt(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $gte}.
+ *
+ * @author Christoph Strobl
+ */
+ class Gte extends AbstractAggregationExpression {
+
+ private Gte(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$gte";
+ }
+
+ /**
+ * Creates new {@link Gte}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Gte valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Gte(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Gte}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Gte valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Gte(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Gte} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Gte greaterThanEqualTo(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Gte(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Gte} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Gte greaterThanEqualTo(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Gte(append(expression));
+ }
+
+ /**
+ * Creates new {@link Gte} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Gte greaterThanEqualToValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new Gte(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $lte}.
+ *
+ * @author Christoph Strobl
+ */
+ class Lte extends AbstractAggregationExpression {
+
+ private Lte(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$lte";
+ }
+
+ /**
+ * Creates new {@link Lte}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Lte valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Lte(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Lte}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Lte valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Lte(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Lte} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Lte lessThanEqualTo(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Lte(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Lte} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Lte lessThanEqualTo(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Lte(append(expression));
+ }
+
+ /**
+ * Creates new {@link Lte} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Lte lessThanEqualToValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new Lte(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $ne}.
+ *
+ * @author Christoph Strobl
+ */
+ class Ne extends AbstractAggregationExpression {
+
+ private Ne(List> value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$ne";
+ }
+
+ /**
+ * Creates new {@link Ne}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Ne valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Ne(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Ne}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Ne valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Ne(Collections.singletonList(expression));
+ }
+
+ /**
+ * Creates new {@link Ne} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Ne notEqualTo(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Ne(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Ne} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Ne notEqualTo(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Ne(append(expression));
+ }
+
+ /**
+ * Creates new {@link Eq} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Ne notEqualToValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new Ne(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $and}.
+ *
+ * @author Christoph Strobl
+ */
+ class And extends AbstractAggregationExpression {
+
+ private And(List> values) {
+ super(values);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$and";
+ }
+
+ /**
+ * Creates new {@link And} that evaluates one or more expressions and returns {@literal true} if all of the
+ * expressions are {@literal true}.
+ *
+ * @param expressions
+ * @return
+ */
+ public static And and(Object... expressions) {
+ return new And(Arrays.asList(expressions));
+ }
+
+ /**
+ * Creates new {@link And} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public And andExpression(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new And(append(expression));
+ }
+
+ /**
+ * Creates new {@link And} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public And andField(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new And(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link And} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public And andValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new And(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $or}.
+ *
+ * @author Christoph Strobl
+ */
+ class Or extends AbstractAggregationExpression {
+
+ private Or(List> values) {
+ super(values);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$or";
+ }
+
+ /**
+ * Creates new {@link Or} that evaluates one or more expressions and returns {@literal true} if any of the
+ * expressions are {@literal true}.
+ *
+ * @param expressions must not be {@literal null}.
+ * @return
+ */
+ public static Or or(Object... expressions) {
+
+ Assert.notNull(expressions, "Expressions must not be null!");
+ return new Or(Arrays.asList(expressions));
+ }
+
+ /**
+ * Creates new {@link Or} with all previously added arguments appending the given one.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public Or orExpression(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Or(append(expression));
+ }
+
+ /**
+ * Creates new {@link Or} with all previously added arguments appending the given one.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public Or orField(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Or(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link Or} with all previously added arguments appending the given one.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ */
+ public Or orValue(Object value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return new Or(append(value));
+ }
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $not}.
+ *
+ * @author Christoph Strobl
+ */
+ class Not extends AbstractAggregationExpression {
+
+ private Not(Object value) {
+ super(value);
+ }
+
+ @Override
+ public String getMongoMethod() {
+ return "$not";
+ }
+
+ /**
+ * Creates new {@link Not} that evaluates the boolean value of the referenced field and returns the opposite boolean
+ * value.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return
+ */
+ public static Not not(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Not(asFields(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Not} that evaluates the resulting boolean value of the given {@link AggregationExpression} and
+ * returns the opposite boolean value.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ */
+ public static Not not(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Not(Collections.singletonList(expression));
+ }
+ }
+
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationFunctionExpressions.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationFunctionExpressions.java
index 761f2c3164..1e862f33a2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationFunctionExpressions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationFunctionExpressions.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 the original author or authors.
+ * Copyright 2015-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.
@@ -29,11 +29,14 @@
*
* @author Thomas Darimont
* @author Oliver Gierke
- * @since 1.10
+ * @author Christoph Strobl
+ * @since 1.7
+ * @deprecated since 1.10. Please use {@link AggregationExpressions} instead.
*/
+@Deprecated
public enum AggregationFunctionExpressions {
- SIZE, CMP, EQ, GT, GTE, LT, LTE, NE;
+ SIZE, CMP, EQ, GT, GTE, LT, LTE, NE, SUBTRACT;
/**
* Returns an {@link AggregationExpression} build from the current {@link Enum} name and the given parameters.
@@ -52,7 +55,7 @@ public AggregationExpression of(Object... parameters) {
*
* @author Thomas Darimont
* @author Oliver Gierke
- * @since 1.10
+ * @since 1.7
*/
static class FunctionExpression implements AggregationExpression {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java
index 70cd2070a3..183d526520 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Fields.java
@@ -17,6 +17,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -185,6 +186,14 @@ public Iterator iterator() {
return fields.iterator();
}
+ /**
+ *
+ * @return
+ * @since 1.10
+ */
+ public List asList() {
+ return Collections.unmodifiableList(fields);
+ }
/**
* Value object to encapsulate a field in an aggregation operation.
*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java
index 2eaba433dd..cc80896223 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java
@@ -528,6 +528,20 @@ public ProjectionOperationBuilder minus(String fieldReference) {
return project("subtract", Fields.field(fieldReference));
}
+ /**
+ * Generates an {@code $subtract} expression that subtracts the result of the given {@link AggregationExpression}
+ * from the previously mentioned field.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder minus(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return project("subtract", expression);
+ }
+
/**
* Generates an {@code $multiply} expression that multiplies the given number with the previously mentioned field.
*
@@ -553,6 +567,20 @@ public ProjectionOperationBuilder multiply(String fieldReference) {
return project("multiply", Fields.field(fieldReference));
}
+ /**
+ * Generates an {@code $multiply} expression that multiplies the previously with the result of the
+ * {@link AggregationExpression}. mentioned field.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder multiply(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return project("multiply", expression);
+ }
+
/**
* Generates an {@code $divide} expression that divides the previously mentioned field by the given number.
*
@@ -579,6 +607,20 @@ public ProjectionOperationBuilder divide(String fieldReference) {
return project("divide", Fields.field(fieldReference));
}
+ /**
+ * Generates an {@code $divide} expression that divides the value of the previously mentioned by the result of the
+ * {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder divide(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return project("divide", expression);
+ }
+
/**
* Generates an {@code $mod} expression that divides the previously mentioned field by the given number and returns
* the remainder.
@@ -596,7 +638,7 @@ public ProjectionOperationBuilder mod(Number number) {
/**
* Generates an {@code $mod} expression that divides the value of the given field by the previously mentioned field
* and returns the remainder.
- *
+ *
* @param fieldReference
* @return
*/
@@ -606,6 +648,20 @@ public ProjectionOperationBuilder mod(String fieldReference) {
return project("mod", Fields.field(fieldReference));
}
+ /**
+ * Generates an {@code $mod} expression that divides the value of the previously mentioned field by the result of
+ * the {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder mod(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return project("mod", expression);
+ }
+
/**
* Generates a {@code $size} expression that returns the size of the array held by the given field.
*
@@ -733,6 +789,410 @@ public ProjectionOperationBuilder filter(String as, AggregationExpression condit
return this.operation.and(AggregationExpressions.Filter.filter(name).as(as).by(condition));
}
+ // SET OPERATORS
+
+ /**
+ * Generates a {@code $setEquals} expression that compares the previously mentioned field to one or more arrays and
+ * returns {@literal true} if they have the same distinct elements and {@literal false} otherwise.
+ *
+ * @param arrays must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder equalsArrays(String... arrays) {
+
+ Assert.notEmpty(arrays, "Arrays must not be null or empty!");
+ return project("setEquals", Fields.fields(arrays));
+ }
+
+ /**
+ * Generates a {@code $setIntersection} expression that takes array of the previously mentioned field and one or
+ * more arrays and returns an array that contains the elements that appear in every of those.
+ *
+ * @param arrays must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder intersectsArrays(String... arrays) {
+
+ Assert.notEmpty(arrays, "Arrays must not be null or empty!");
+ return project("setIntersection", Fields.fields(arrays));
+ }
+
+ /**
+ * Generates a {@code $setUnion} expression that takes array of the previously mentioned field and one or more
+ * arrays and returns an array that contains the elements that appear in any of those.
+ *
+ * @param arrays must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder unionArrays(String... arrays) {
+
+ Assert.notEmpty(arrays, "Arrays must not be null or empty!");
+ return project("setUnion", Fields.fields(arrays));
+ }
+
+ /**
+ * Generates a {@code $setDifference} expression that takes array of the previously mentioned field and returns an
+ * array containing the elements that do not exist in the given {@literal array}.
+ *
+ * @param array must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder differenceToArray(String array) {
+
+ Assert.hasText(array, "Array must not be null or empty!");
+ return project("setDifference", Fields.fields(array));
+ }
+
+ /**
+ * Generates a {@code $setIsSubset} expression that takes array of the previously mentioned field and returns
+ * {@literal true} if it is a subset of the given {@literal array}.
+ *
+ * @param array must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder subsetOfArray(String array) {
+
+ Assert.hasText(array, "Array must not be null or empty!");
+ return project("setIsSubset", Fields.fields(array));
+ }
+
+ /**
+ * Generates an {@code $anyElementTrue} expression that Takes array of the previously mentioned field and returns
+ * {@literal true} if any of the elements are {@literal true} and {@literal false} otherwise.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder anyElementInArrayTrue() {
+ return project("anyElementTrue");
+ }
+
+ /**
+ * Generates an {@code $allElementsTrue} expression that takes array of the previously mentioned field and returns
+ * {@literal true} if no elements is {@literal false}.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder allElementsInArrayTrue() {
+ return project("allElementsTrue");
+ }
+
+ /**
+ * Generates a {@code $abs} expression that takes the number of the previously mentioned field and returns the
+ * absolute value of it.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder absoluteValue() {
+ return this.operation.and(AggregationExpressions.Abs.absoluteValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $ceil} expression that takes the number of the previously mentioned field and returns the
+ * smallest integer greater than or equal to the specified number.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder ceil() {
+ return this.operation.and(AggregationExpressions.Ceil.ceilValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $exp} expression that takes the number of the previously mentioned field and raises Euler’s
+ * number (i.e. e ) on it.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder exp() {
+ return this.operation.and(AggregationExpressions.Exp.expValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $floor} expression that takes the number of the previously mentioned field and returns the
+ * largest integer less than or equal to it.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder floor() {
+ return this.operation.and(AggregationExpressions.Floor.floorValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $ln} expression that takes the number of the previously mentioned field and calculates the
+ * natural logarithm ln (i.e loge) of it.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder ln() {
+ return this.operation.and(AggregationExpressions.Ln.lnValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $log} expression that takes the number of the previously mentioned field and calculates the
+ * log of the associated number in the specified base.
+ *
+ * @param baseFieldRef must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder log(String baseFieldRef) {
+ return this.operation.and(AggregationExpressions.Log.valueOf(name).log(baseFieldRef));
+ }
+
+ /**
+ * Generates a {@code $log} expression that takes the number of the previously mentioned field and calculates the
+ * log of the associated number in the specified base.
+ *
+ * @param base must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder log(Number base) {
+ return this.operation.and(AggregationExpressions.Log.valueOf(name).log(base));
+ }
+
+ /**
+ * Generates a {@code $log} expression that takes the number of the previously mentioned field and calculates the
+ * log of the associated number in the specified base.
+ *
+ * @param base must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder log(AggregationExpression base) {
+ return this.operation.and(AggregationExpressions.Log.valueOf(name).log(base));
+ }
+
+ /**
+ * Generates a {@code $log10} expression that takes the number of the previously mentioned field and calculates the
+ * log base 10.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder log10() {
+ return this.operation.and(AggregationExpressions.Log10.log10ValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $pow} expression that takes the number of the previously mentioned field and raises it by the
+ * specified exponent.
+ *
+ * @param exponentFieldRef must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder pow(String exponentFieldRef) {
+ return this.operation.and(AggregationExpressions.Pow.valueOf(name).pow(exponentFieldRef));
+ }
+
+ /**
+ * Generates a {@code $pow} expression that takes the number of the previously mentioned field and raises it by the
+ * specified exponent.
+ *
+ * @param exponent must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder pow(Number exponent) {
+ return this.operation.and(AggregationExpressions.Pow.valueOf(name).pow(exponent));
+ }
+
+ /**
+ * Generates a {@code $pow} expression that Takes the number of the previously mentioned field and raises it by the
+ * specified exponent.
+ *
+ * @param exponentExpression must not be {@literal null}.
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder pow(AggregationExpression exponentExpression) {
+ return this.operation.and(AggregationExpressions.Pow.valueOf(name).pow(exponentExpression));
+ }
+
+ /**
+ * Generates a {@code $sqrt} expression that takes the number of the previously mentioned field and calculates the
+ * square root.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder sqrt() {
+ return this.operation.and(AggregationExpressions.Sqrt.sqrtOf(name));
+ }
+
+ /**
+ * Takes the number of the previously mentioned field and truncates it to its integer value.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder trunc() {
+ return this.operation.and(AggregationExpressions.Trunc.truncValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $concat} expression that takes the string representation of the previously mentioned field and
+ * concats given values to it.
+ *
+ * @return never {@literal null}.
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder concat(Object... values) {
+ return project("concat", values);
+ }
+
+ /**
+ * Generates a {@code $substr} expression that Takes the string representation of the previously mentioned field and
+ * returns a substring starting at a specified index position.
+ *
+ * @param start
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder substring(int start) {
+ return substring(start, -1);
+ }
+
+ /**
+ * Generates a {@code $substr} expression that takes the string representation of the previously mentioned field and
+ * returns a substring starting at a specified index position including the specified number of characters.
+ *
+ * @param start
+ * @param nrOfChars
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder substring(int start, int nrOfChars) {
+ return project("substr", start, nrOfChars);
+ }
+
+ /**
+ * Generates a {@code $toLower} expression that takes the string representation of the previously mentioned field
+ * and lowers it.
+ *
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder toLower() {
+ return this.operation.and(AggregationExpressions.ToLower.lowerValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $toUpper} expression that takes the string representation of the previously mentioned field
+ * and uppers it.
+ *
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder toUpper() {
+ return this.operation.and(AggregationExpressions.ToUpper.upperValueOf(name));
+ }
+
+ /**
+ * Generates a {@code $strcasecmp} expression that takes the string representation of the previously mentioned field
+ * and performs case-insensitive comparison to the given {@literal value}.
+ *
+ * @param value must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder strCaseCmp(String value) {
+ return project("strcasecmp", value);
+ }
+
+ /**
+ * Generates a {@code $strcasecmp} expression that takes the string representation of the previously mentioned field
+ * and performs case-insensitive comparison to the referenced {@literal fieldRef}.
+ *
+ * @param fieldRef must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder strCaseCmpValueOf(String fieldRef) {
+ return project("strcasecmp", fieldRef);
+ }
+
+ /**
+ * Generates a {@code $strcasecmp} expression that takes the string representation of the previously mentioned field
+ * and performs case-insensitive comparison to the result of the given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder strCaseCmp(AggregationExpression expression) {
+ return project("strcasecmp", expression);
+ }
+
+ /**
+ * Generates a {@code $arrayElemAt} expression that takes the string representation of the previously mentioned
+ * field and returns the element at the specified array {@literal position}.
+ *
+ * @param position
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder arrayElementAt(int position) {
+ return project("arrayElemAt", position);
+ }
+
+ /**
+ * Generates a {@code $concatArrays} expression that takes the string representation of the previously mentioned
+ * field and concats it with the arrays from the referenced {@literal fields}.
+ *
+ * @param fields must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder concatArrays(String... fields) {
+ return project("concatArrays", Fields.fields(fields));
+ }
+
+ /**
+ * Generates a {@code $isArray} expression that takes the string representation of the previously mentioned field
+ * and checks if its an array.
+ *
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder isArray() {
+ return this.operation.and(AggregationExpressions.IsArray.isArray(name));
+ }
+
+ /**
+ * Generates a {@code $literal} expression that Takes the value previously and uses it as literal.
+ *
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder asLiteral() {
+ return this.operation.and(AggregationExpressions.Literal.asLiteral(name));
+ }
+
+ /**
+ * Generates a {@code $dateToString} expression that takes the date representation of the previously mentioned field
+ * and applies given {@literal format} to it.
+ *
+ * @param format must not be {@literal null}.
+ * @return
+ * @since 1.10
+ */
+ public ProjectionOperationBuilder dateAsFormattedString(String format) {
+ return this.operation.and(AggregationExpressions.DateToString.dateOf(name).toString(format));
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
@@ -917,7 +1377,18 @@ protected List getOperationArguments(AggregationOperationContext context
result.add(context.getReference(getField().getName()).toString());
for (Object element : values) {
- result.add(element instanceof Field ? context.getReference((Field) element).toString() : element);
+
+ if (element instanceof Field) {
+ result.add(context.getReference((Field) element).toString());
+ } else if (element instanceof Fields) {
+ for (Field field : (Fields) element) {
+ result.add(context.getReference(field).toString());
+ }
+ } else if (element instanceof AggregationExpression) {
+ result.add(((AggregationExpression) element).toDbObject(context));
+ } else {
+ result.add(element);
+ }
}
return result;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
index 3807e30d73..59ba03bc3e 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
@@ -17,6 +17,7 @@
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
+import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
import static org.springframework.data.mongodb.core.aggregation.AggregationFunctionExpressions.*;
import static org.springframework.data.mongodb.core.aggregation.Fields.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
@@ -27,10 +28,19 @@
import org.junit.Test;
import org.springframework.data.mongodb.core.DBObjectTestUtils;
+import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ArithmeticOperators;
+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.DateOperators;
+import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.LiteralOperators;
+import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.SetOperators;
+import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.StringOperators;
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
+import com.mongodb.util.JSON;
/**
* Unit tests for {@link ProjectionOperation}.
@@ -275,9 +285,8 @@ public void projectionExpressions() {
.and("foo").as("bar"); //
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT);
- assertThat(
- dbObject.toString(),
- is("{ \"$project\" : { \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"bar\" : \"$foo\"}}"));
+ assertThat(dbObject.toString(), is(
+ "{ \"$project\" : { \"grossSalesPrice\" : { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\" , 2]} , \"bar\" : \"$foo\"}}"));
}
/**
@@ -332,10 +341,8 @@ public void shouldRenderDateTimeFragmentExtractionsForExpressionProjectionsCorre
assertThat(dbObject, is(notNullValue()));
DBObject projected = exctractOperation("$project", dbObject);
- assertThat(
- projected.get("dayOfYearPlus1Day"),
- is((Object) new BasicDBObject("$dayOfYear", Arrays.asList(new BasicDBObject("$add", Arrays. asList(
- "$date", 86400000))))));
+ assertThat(projected.get("dayOfYearPlus1Day"), is((Object) new BasicDBObject("$dayOfYear",
+ Arrays.asList(new BasicDBObject("$add", Arrays. asList("$date", 86400000))))));
}
/**
@@ -487,6 +494,1185 @@ public void shouldRenderNeCorrectly() {
isBsonObject().containing("$project.ne10.$ne.[0]", "$field").containing("$project.ne10.$ne.[1]", 10));
}
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetEquals() {
+
+ DBObject agg = project("A", "B").and("A").equalsArrays("B").as("sameElements")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { A: 1, B: 1, sameElements: { $setEquals: [ \"$A\", \"$B\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetEqualsAggregationExpresssion() {
+
+ DBObject agg = project("A", "B").and(SetOperators.arrayAsSet("A").isEqualTo("B")).as("sameElements")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { A: 1, B: 1, sameElements: { $setEquals: [ \"$A\", \"$B\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetIntersection() {
+
+ DBObject agg = project("A", "B").and("A").intersectsArrays("B").as("commonToBoth")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { A: 1, B: 1, commonToBoth: { $setIntersection: [ \"$A\", \"$B\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetIntersectionAggregationExpresssion() {
+
+ DBObject agg = project("A", "B").and(SetOperators.arrayAsSet("A").intersects("B")).as("commonToBoth")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { A: 1, B: 1, commonToBoth: { $setIntersection: [ \"$A\", \"$B\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetUnion() {
+
+ DBObject agg = project("A", "B").and("A").unionArrays("B").as("allValues").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { A: 1, B: 1, allValues: { $setUnion: [ \"$A\", \"$B\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetUnionAggregationExpresssion() {
+
+ DBObject agg = project("A", "B").and(SetOperators.arrayAsSet("A").union("B")).as("allValues")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { A: 1, B: 1, allValues: { $setUnion: [ \"$A\", \"$B\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetDifference() {
+
+ DBObject agg = project("A", "B").and("B").differenceToArray("A").as("inBOnly")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { A: 1, B: 1, inBOnly: { $setDifference: [ \"$B\", \"$A\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetDifferenceAggregationExpresssion() {
+
+ DBObject agg = project("A", "B").and(SetOperators.arrayAsSet("B").differenceTo("A")).as("inBOnly")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { A: 1, B: 1, inBOnly: { $setDifference: [ \"$B\", \"$A\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetIsSubset() {
+
+ DBObject agg = project("A", "B").and("A").subsetOfArray("B").as("aIsSubsetOfB")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { A: 1, B: 1, aIsSubsetOfB: { $setIsSubset: [ \"$A\", \"$B\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSetIsSubsetAggregationExpresssion() {
+
+ DBObject agg = project("A", "B").and(SetOperators.arrayAsSet("A").isSubsetOf("B")).as("aIsSubsetOfB")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { A: 1, B: 1, aIsSubsetOfB: { $setIsSubset: [ \"$A\", \"$B\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAnyElementTrue() {
+
+ DBObject agg = project("responses").and("responses").anyElementInArrayTrue().as("isAnyTrue")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { responses: 1, isAnyTrue: { $anyElementTrue: [ \"$responses\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAnyElementTrueAggregationExpresssion() {
+
+ DBObject agg = project("responses").and(SetOperators.arrayAsSet("responses").anyElementTrue()).as("isAnyTrue")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { responses: 1, isAnyTrue: { $anyElementTrue: [ \"$responses\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAllElementsTrue() {
+
+ DBObject agg = project("responses").and("responses").allElementsInArrayTrue().as("isAllTrue")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { responses: 1, isAllTrue: { $allElementsTrue: [ \"$responses\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAllElementsTrueAggregationExpresssion() {
+
+ DBObject agg = project("responses").and(SetOperators.arrayAsSet("responses").allElementsTrue()).as("isAllTrue")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { responses: 1, isAllTrue: { $allElementsTrue: [ \"$responses\" ] }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAbs() {
+
+ DBObject agg = project().and("anyNumber").absoluteValue().as("absoluteValue")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { absoluteValue : { $abs: \"$anyNumber\" }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAbsAggregationExpresssion() {
+
+ DBObject agg = project()
+ .and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).abs())
+ .as("delta").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { delta: { $abs: { $subtract: [ \"$start\", \"$end\" ] } } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAddAggregationExpresssion() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("price").add("fee")).as("total")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse(" { $project: { total: { $add: [ \"$price\", \"$fee\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderCeil() {
+
+ DBObject agg = project().and("anyNumber").ceil().as("ceilValue").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { ceilValue : { $ceil: \"$anyNumber\" }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderCeilAggregationExpresssion() {
+
+ DBObject agg = project().and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).ceil())
+ .as("delta").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { delta: { $ceil: { $subtract: [ \"$start\", \"$end\" ] } } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderDivide() {
+
+ DBObject agg = project().and("value")
+ .divide(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).as("result")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { result: { $divide: [ \"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderDivideAggregationExpresssion() {
+
+ DBObject agg = project()
+ .and(ArithmeticOperators.valueOf("anyNumber")
+ .divideBy(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON
+ .parse("{ $project: { result: { $divide: [ \"$anyNumber\", { $subtract: [ \"$start\", \"$end\" ] }] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderExp() {
+
+ DBObject agg = project().and("value").exp().as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $exp: \"$value\" } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderExpAggregationExpresssion() {
+
+ DBObject agg = project()
+ .and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).exp())
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $exp: { $subtract: [ \"$start\", \"$end\" ] } } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderFloor() {
+
+ DBObject agg = project().and("value").floor().as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $floor: \"$value\" } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderFloorAggregationExpresssion() {
+
+ DBObject agg = project().and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).floor())
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $floor: { $subtract: [ \"$start\", \"$end\" ] } } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLn() {
+
+ DBObject agg = project().and("value").ln().as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $ln: \"$value\"} }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLnAggregationExpresssion() {
+
+ DBObject agg = project()
+ .and(ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).ln())
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $ln: { $subtract: [ \"$start\", \"$end\" ] } } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLog() {
+
+ DBObject agg = project().and("value").log(2).as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $log: [ \"$value\", 2] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLogAggregationExpresssion() {
+
+ DBObject agg = project().and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).log(2))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $log: [ { $subtract: [ \"$start\", \"$end\" ] }, 2] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLog10() {
+
+ DBObject agg = project().and("value").log10().as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $log10: \"$value\" } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLog10AggregationExpresssion() {
+
+ DBObject agg = project().and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).log10())
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $log10: { $subtract: [ \"$start\", \"$end\" ] } } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMod() {
+
+ DBObject agg = project().and("value").mod(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end")))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { result: { $mod: [\"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderModAggregationExpresssion() {
+
+ DBObject agg = project().and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).mod(2))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $mod: [{ $subtract: [ \"$start\", \"$end\" ] }, 2] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMultiply() {
+
+ DBObject agg = project().and("value")
+ .multiply(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).as("result")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(
+ JSON.parse("{ $project: { result: { $multiply: [\"$value\", { $subtract: [ \"$start\", \"$end\" ] }] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMultiplyAggregationExpresssion() {
+
+ DBObject agg = project()
+ .and(ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end")))
+ .multiplyBy(2).multiplyBy("refToAnotherNumber"))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse(
+ "{ $project: { result: { $multiply: [{ $subtract: [ \"$start\", \"$end\" ] }, 2, \"$refToAnotherNumber\"] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderPow() {
+
+ DBObject agg = project().and("value").pow(2).as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $pow: [\"$value\", 2] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderPowAggregationExpresssion() {
+
+ DBObject agg = project().and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).pow(2))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $pow: [{ $subtract: [ \"$start\", \"$end\" ] }, 2] } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSqrt() {
+
+ DBObject agg = project().and("value").sqrt().as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $sqrt: \"$value\" } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSqrtAggregationExpresssion() {
+
+ DBObject agg = project().and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).sqrt())
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $sqrt: { $subtract: [ \"$start\", \"$end\" ] } } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSubtract() {
+
+ DBObject agg = project().and("numericField").minus(AggregationFunctionExpressions.SIZE.of(field("someArray")))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { result: { $subtract: [ \"$numericField\", { $size : [\"$someArray\"]}] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSubtractAggregationExpresssion() {
+
+ DBObject agg = project()
+ .and(ArithmeticOperators.valueOf("numericField")
+ .subtract(AggregationFunctionExpressions.SIZE.of(field("someArray"))))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { result: { $subtract: [ \"$numericField\", { $size : [\"$someArray\"]}] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderTrunc() {
+
+ DBObject agg = project().and("value").trunc().as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result : { $trunc: \"$value\" }}}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderTruncAggregationExpresssion() {
+
+ DBObject agg = project().and(
+ ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).trunc())
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $trunc: { $subtract: [ \"$start\", \"$end\" ] } } }}")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderConcat() {
+
+ DBObject agg = project().and("item").concat(" - ", field("description")).as("itemDescription")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { itemDescription: { $concat: [ \"$item\", \" - \", \"$description\" ] } } }")));
+
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderConcatAggregationExpression() {
+
+ DBObject agg = project().and(StringOperators.valueOf("item").concat(" - ").concatValueOf("description"))
+ .as("itemDescription").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { itemDescription: { $concat: [ \"$item\", \" - \", \"$description\" ] } } }")));
+
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSubstr() {
+
+ DBObject agg = project().and("quarter").substring(0, 2).as("yearSubstring").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { yearSubstring: { $substr: [ \"$quarter\", 0, 2 ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSubstrAggregationExpression() {
+
+ DBObject agg = project().and(StringOperators.valueOf("quarter").substring(0, 2)).as("yearSubstring")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { yearSubstring: { $substr: [ \"$quarter\", 0, 2 ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderToLower() {
+
+ DBObject agg = project().and("item").toLower().as("item").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { item: { $toLower: \"$item\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderToLowerAggregationExpression() {
+
+ DBObject agg = project().and(StringOperators.valueOf("item").toLower()).as("item")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { item: { $toLower: \"$item\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderToUpper() {
+
+ DBObject agg = project().and("item").toUpper().as("item").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { item: { $toUpper: \"$item\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderToUpperAggregationExpression() {
+
+ DBObject agg = project().and(StringOperators.valueOf("item").toUpper()).as("item")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { item: { $toUpper: \"$item\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderStrCaseCmp() {
+
+ DBObject agg = project().and("quarter").strCaseCmp("13q4").as("comparisonResult")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { comparisonResult: { $strcasecmp: [ \"$quarter\", \"13q4\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderStrCaseCmpAggregationExpression() {
+
+ DBObject agg = project().and(StringOperators.valueOf("quarter").strCaseCmp("13q4")).as("comparisonResult")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { comparisonResult: { $strcasecmp: [ \"$quarter\", \"13q4\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderArrayElementAt() {
+
+ DBObject agg = project().and("favorites").arrayElementAt(0).as("first").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { first: { $arrayElemAt: [ \"$favorites\", 0 ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderArrayElementAtAggregationExpression() {
+
+ DBObject agg = project().and(ArrayOperators.arrayOf("favorites").elementAt(0)).as("first")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { first: { $arrayElemAt: [ \"$favorites\", 0 ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderConcatArrays() {
+
+ DBObject agg = project().and("instock").concatArrays("ordered").as("items").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { items: { $concatArrays: [ \"$instock\", \"$ordered\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderConcatArraysAggregationExpression() {
+
+ DBObject agg = project().and(ArrayOperators.arrayOf("instock").concat("ordered")).as("items")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { items: { $concatArrays: [ \"$instock\", \"$ordered\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderIsArray() {
+
+ DBObject agg = project().and("instock").isArray().as("isAnArray").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { isAnArray: { $isArray: \"$instock\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderIsArrayAggregationExpression() {
+
+ DBObject agg = project().and(ArrayOperators.arrayOf("instock").isArray()).as("isAnArray")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { isAnArray: { $isArray: \"$instock\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSizeAggregationExpression() {
+
+ DBObject agg = project().and(ArrayOperators.arrayOf("instock").length()).as("arraySize")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { arraySize: { $size: \"$instock\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSliceAggregationExpression() {
+
+ DBObject agg = project().and(ArrayOperators.arrayOf("favorites").slice().itemCount(3)).as("threeFavorites")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { threeFavorites: { $slice: [ \"$favorites\", 3 ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSliceWithPositionAggregationExpression() {
+
+ DBObject agg = project().and(ArrayOperators.arrayOf("favorites").slice().offset(2).itemCount(3))
+ .as("threeFavorites").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { threeFavorites: { $slice: [ \"$favorites\", 2, 3 ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLiteral() {
+
+ DBObject agg = project().and("$1").asLiteral().as("literalOnly").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { literalOnly: { $literal: \"$1\"} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLiteralAggregationExpression() {
+
+ DBObject agg = project().and(LiteralOperators.valueOf("$1").asLiteral()).as("literalOnly")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { literalOnly: { $literal: \"$1\"} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderDayOfYearAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").dayOfYear()).as("dayOfYear")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { dayOfYear: { $dayOfYear: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderDayOfMonthAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").dayOfMonth()).as("day")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { day: { $dayOfMonth: \"$date\" }} }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderDayOfWeekAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").dayOfWeek()).as("dayOfWeek")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { dayOfWeek: { $dayOfWeek: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderYearAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").year()).as("year")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { year: { $year: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMonthAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").month()).as("month")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { month: { $month: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderWeekAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").week()).as("week")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { week: { $week: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderHourAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").hour()).as("hour")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { hour: { $hour: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMinuteAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").minute()).as("minute")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { minute: { $minute: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSecondAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").second()).as("second")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { second: { $second: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMillisecondAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").millisecond()).as("msec")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { msec: { $millisecond: \"$date\" } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderDateToString() {
+
+ DBObject agg = project().and("date").dateAsFormattedString("%H:%M:%S:%L").as("time")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\" } } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderDateToStringAggregationExpression() {
+
+ DBObject agg = project().and(DateOperators.dateOf("date").toString("%H:%M:%S:%L")).as("time")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg,
+ is(JSON.parse("{ $project: { time: { $dateToString: { format: \"%H:%M:%S:%L\", date: \"$date\" } } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSumAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("quizzes").sum()).as("quizTotal")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { quizTotal: { $sum: \"$quizzes\"} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderSumWithMultipleArgsAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("final").sum().and("midterm")).as("examTotal")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { examTotal: { $sum: [ \"$final\", \"$midterm\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAvgAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("quizzes").avg()).as("quizAvg")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { quizAvg: { $avg: \"$quizzes\"} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderAvgWithMultipleArgsAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("final").avg().and("midterm")).as("examAvg")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { examAvg: { $avg: [ \"$final\", \"$midterm\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMaxAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("quizzes").max()).as("quizMax")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { quizMax: { $max: \"$quizzes\"} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMaxWithMultipleArgsAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("final").max().and("midterm")).as("examMax")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { examMax: { $max: [ \"$final\", \"$midterm\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMinAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("quizzes").min()).as("quizMin")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { quizMin: { $min: \"$quizzes\"} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderMinWithMultipleArgsAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("final").min().and("midterm")).as("examMin")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { examMin: { $min: [ \"$final\", \"$midterm\" ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderStdDevPopAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("scores").stdDevPop()).as("stdDev")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { stdDev: { $stdDevPop: \"$scores\"} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderStdDevSampAggregationExpression() {
+
+ DBObject agg = project().and(ArithmeticOperators.valueOf("scores").stdDevSamp()).as("stdDev")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { stdDev: { $stdDevSamp: \"$scores\"} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderCmpAggregationExpression() {
+
+ DBObject agg = project().and(ComparisonOperators.valueOf("qty").compareToValue(250)).as("cmp250")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { cmp250: { $cmp: [\"$qty\", 250]} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderEqAggregationExpression() {
+
+ DBObject agg = project().and(ComparisonOperators.valueOf("qty").equalToValue(250)).as("eq250")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { eq250: { $eq: [\"$qty\", 250]} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderGtAggregationExpression() {
+
+ DBObject agg = project().and(ComparisonOperators.valueOf("qty").greaterThanValue(250)).as("gt250")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { gt250: { $gt: [\"$qty\", 250]} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderGteAggregationExpression() {
+
+ DBObject agg = project().and(ComparisonOperators.valueOf("qty").greaterThanEqualToValue(250)).as("gte250")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { gte250: { $gte: [\"$qty\", 250]} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLtAggregationExpression() {
+
+ DBObject agg = project().and(ComparisonOperators.valueOf("qty").lessThanValue(250)).as("lt250")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { lt250: { $lt: [\"$qty\", 250]} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLteAggregationExpression() {
+
+ DBObject agg = project().and(ComparisonOperators.valueOf("qty").lessThanEqualToValue(250)).as("lte250")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { lte250: { $lte: [\"$qty\", 250]} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderNeAggregationExpression() {
+
+ DBObject agg = project().and(ComparisonOperators.valueOf("qty").notEqualToValue(250)).as("ne250")
+ .toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { ne250: { $ne: [\"$qty\", 250]} } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLogicAndAggregationExpression() {
+
+ DBObject agg = project()
+ .and(BooleanOperators.valueOf(ComparisonOperators.valueOf("qty").greaterThanValue(100))
+ .and(ComparisonOperators.valueOf("qty").lessThanValue(250)))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(
+ JSON.parse("{ $project: { result: { $and: [ { $gt: [ \"$qty\", 100 ] }, { $lt: [ \"$qty\", 250 ] } ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderLogicOrAggregationExpression() {
+
+ DBObject agg = project()
+ .and(BooleanOperators.valueOf(ComparisonOperators.valueOf("qty").greaterThanValue(250))
+ .or(ComparisonOperators.valueOf("qty").lessThanValue(200)))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(
+ JSON.parse("{ $project: { result: { $or: [ { $gt: [ \"$qty\", 250 ] }, { $lt: [ \"$qty\", 200 ] } ] } } }")));
+ }
+
+ /**
+ * @see DATAMONGO-1536
+ */
+ @Test
+ public void shouldRenderNotAggregationExpression() {
+
+ DBObject agg = project().and(BooleanOperators.not(ComparisonOperators.valueOf("qty").greaterThanValue(250)))
+ .as("result").toDBObject(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg, is(JSON.parse("{ $project: { result: { $not: [ { $gt: [ \"$qty\", 250 ] } ] } } }")));
+ }
+
private static DBObject exctractOperation(String field, DBObject fromProjectClause) {
return (DBObject) fromProjectClause.get(field);
}
diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc
index c375facbba..a6e2870de9 100644
--- a/src/main/asciidoc/reference/mongodb.adoc
+++ b/src/main/asciidoc/reference/mongodb.adoc
@@ -1676,17 +1676,29 @@ At the time of this writing we provide support for the following Aggregation Ope
| Pipeline Aggregation Operators
| project, skip, limit, lookup, unwind, group, sort, geoNear
+| Set Aggregation Operators
+| setEquals, setIntersection, setUnion, setDifference, setIsSubset, anyElementTrue, allElementsTrue
+
| Group Aggregation Operators
-| addToSet, first, last, max, min, avg, push, sum, (*count)
+| addToSet, first, last, max, min, avg, push, sum, (*count), stdDevPop, stdDevSamp
| Arithmetic Aggregation Operators
-| add (*via plus), subtract (*via minus), multiply, divide, mod
+| abs, add (*via plus), ceil, divide, exp, floor, ln, log, log10, mod, multiply, pow, sqrt, subtract (*via minus), trunc
+
+| String Aggregation Operators
+| concat, substr, toLower, toUpper, stcasecmp
| Comparison Aggregation Operators
| eq (*via: is), gt, gte, lt, lte, ne
| Array Aggregation Operators
-| size, slice, filter
+| arrayElementAt, concatArrays, filter, isArray, size, slice
+
+| Literal Operators
+| literal
+
+| Date Aggregation Operators
+| dayOfYear, dayOfMonth, dayOfWeek, year, month, week, hour, minute, second, millisecond, dateToString
| Conditional Aggregation Operators
| cond, ifNull