From 512f5c227bb3bb5b9ddafc99aab5f8c6044432c8 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 14 Dec 2016 14:49:39 +0100 Subject: [PATCH 1/2] DATAMONGO-1564 - Split up AggregationExpressions. Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 4 ++-- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb-log4j/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index ed9720988d..da38eedcb2 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-1564-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..8bdce38016 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-1564-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1564-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 2d02722262..7c445614e2 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-1564-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml index ee5e3336db..1080261ab3 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-1564-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 8072d3f665..7a85b2aca5 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-1564-SNAPSHOT ../pom.xml From e74c97f0df121d288b671ffeaa41aeceff631d33 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Thu, 15 Dec 2016 09:18:39 +0100 Subject: [PATCH 2/2] DATAMONGO-1564 - Split up AggregationExpressions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactored to multiple smaller Aggregation Operator classes reflecting the grouping (array operators, string operators,…) predefined by MongoDB. --- .../AbstractAggregationExpression.java | 150 + .../aggregation/AccumulatorOperators.java | 642 ++ .../aggregation/AggregationExpressions.java | 8656 ----------------- .../core/aggregation/ArithmeticOperators.java | 1421 +++ .../core/aggregation/ArrayOperators.java | 1517 +++ .../core/aggregation/BooleanOperators.java | 353 + .../core/aggregation/ComparisonOperators.java | 879 ++ .../aggregation/ConditionalOperators.java | 975 ++ .../core/aggregation/DataTypeOperators.java | 67 + .../core/aggregation/DateOperators.java | 838 ++ .../core/aggregation/LiteralOperators.java | 96 + .../core/aggregation/ProjectionOperation.java | 50 +- .../core/aggregation/SetOperators.java | 666 ++ .../core/aggregation/StringOperators.java | 1089 +++ .../core/aggregation/VariableOperators.java | 391 + .../core/aggregation/AggregationTests.java | 16 +- .../aggregation/AggregationUnitTests.java | 10 +- .../aggregation/BucketOperationUnitTests.java | 1 - .../aggregation/CondExpressionUnitTests.java | 5 +- .../FilterExpressionUnitTests.java | 2 +- .../GraphLookupOperationUnitTests.java | 5 +- .../ProjectionOperationUnitTests.java | 43 +- .../ReplaceRootOperationUnitTests.java | 1 - ...dAggregationOperationContextUnitTests.java | 1 - 24 files changed, 9137 insertions(+), 8737 deletions(-) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java delete mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressions.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DataTypeOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LiteralOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java new file mode 100644 index 0000000000..45348ea188 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AbstractAggregationExpression.java @@ -0,0 +1,150 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; +import org.springframework.util.ObjectUtils; + +/** + * @author Christoph Strobl + * @since 1.10 + */ +abstract class AbstractAggregationExpression implements AggregationExpression { + + private final Object value; + + protected AbstractAggregationExpression(Object value) { + this.value = value; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @Override + public DBObject toDbObject(AggregationOperationContext context) { + 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 java.util.Map) { + + DBObject dbo = new BasicDBObject(); + for (java.util.Map.Entry entry : ((java.util.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(); + } + + if (value instanceof List) { + + List sourceList = (List) value; + List mappedList = new ArrayList(sourceList.size()); + + for (Object item : sourceList) { + mappedList.add(unpack(item, context)); + } + return mappedList; + } + + 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 java.util.Map append(String key, Object value) { + + if (!(this.value instanceof java.util.Map)) { + throw new IllegalArgumentException("o_O"); + } + java.util.Map clone = new LinkedHashMap( + (java.util.Map) this.value); + clone.put(key, value); + return clone; + + } + + protected List values() { + + if (value instanceof List) { + return new ArrayList((List) value); + } + if (value instanceof java.util.Map) { + return new ArrayList(((java.util.Map) value).values()); + } + return new ArrayList(Arrays.asList(value)); + } + + protected abstract String getMongoMethod(); +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java new file mode 100644 index 0000000000..43cab0347c --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperators.java @@ -0,0 +1,642 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.Collections; +import java.util.List; + +import org.springframework.util.Assert; + +import com.mongodb.DBObject; + +/** + * Gateway to {@literal accumulator} aggregation operations. + * + * @author Christoph Strobl + * @since 1.10 + * @soundtrack Rage Against The Machine - Killing In The Name + */ +public class AccumulatorOperators { + + /** + * Take the numeric value referenced by given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static AccumulatorOperatorFactory valueOf(String fieldReference) { + return new AccumulatorOperatorFactory(fieldReference); + } + + /** + * Take the numeric value referenced resulting from given {@link AggregationExpression}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static AccumulatorOperatorFactory valueOf(AggregationExpression expression) { + return new AccumulatorOperatorFactory(expression); + } + + /** + * @author Christoph Strobl + */ + public static class AccumulatorOperatorFactory { + + private final String fieldReference; + private final AggregationExpression expression; + + /** + * Creates new {@link AccumulatorOperatorFactory} for given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + */ + public AccumulatorOperatorFactory(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + this.fieldReference = fieldReference; + this.expression = null; + } + + /** + * Creates new {@link ArrayOperatorFactory} for given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + */ + public AccumulatorOperatorFactory(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + this.fieldReference = null; + this.expression = expression; + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated numeric value expression and calculates and + * returns the sum. + * + * @return + */ + public Sum sum() { + return usesFieldRef() ? Sum.sumOf(fieldReference) : Sum.sumOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated numeric value expression and returns the + * average value. + * + * @return + */ + public Avg avg() { + return usesFieldRef() ? Avg.avgOf(fieldReference) : Avg.avgOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated numeric value expression and returns the + * maximum value. + * + * @return + */ + public Max max() { + return usesFieldRef() ? Max.maxOf(fieldReference) : Max.maxOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated numeric value expression and returns the + * minimum value. + * + * @return + */ + public Min min() { + return usesFieldRef() ? Min.minOf(fieldReference) : Min.minOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated numeric value expression and calculates the + * population standard deviation of the input values. + * + * @return + */ + public StdDevPop stdDevPop() { + return usesFieldRef() ? StdDevPop.stdDevPopOf(fieldReference) : StdDevPop.stdDevPopOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated numeric value expression and calculates the + * sample standard deviation of the input values. + * + * @return + */ + public StdDevSamp stdDevSamp() { + return usesFieldRef() ? StdDevSamp.stdDevSampOf(fieldReference) : StdDevSamp.stdDevSampOf(expression); + } + + private boolean usesFieldRef() { + return fieldReference != null; + } + } + + /** + * {@link AggregationExpression} for {@code $sum}. + * + * @author Christoph Strobl + */ + public static class Sum extends AbstractAggregationExpression { + + private Sum(Object value) { + super(value); + } + + @Override + protected 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)); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @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 + */ + public static class Avg extends AbstractAggregationExpression { + + private Avg(Object value) { + super(value); + } + + @Override + protected 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)); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @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 + */ + public static class Max extends AbstractAggregationExpression { + + private Max(Object value) { + super(value); + } + + @Override + protected 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)); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @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 + */ + public static class Min extends AbstractAggregationExpression { + + private Min(Object value) { + super(value); + } + + @Override + protected 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)); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @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 + */ + public static class StdDevPop extends AbstractAggregationExpression { + + private StdDevPop(Object value) { + super(value); + } + + @Override + protected 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)); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @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 + */ + public static class StdDevSamp extends AbstractAggregationExpression { + + private StdDevSamp(Object value) { + super(value); + } + + @Override + protected 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)); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @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); + } + } +} 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 deleted file mode 100644 index 0c86caf1e7..0000000000 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationExpressions.java +++ /dev/null @@ -1,8656 +0,0 @@ -/* - * Copyright 2016. the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.data.mongodb.core.aggregation; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; - -import org.springframework.dao.InvalidDataAccessApiUsageException; -import org.springframework.data.domain.Range; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ArithmeticOperators.ArithmeticOperatorFactory; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.AsBuilder; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Let.ExpressionVariable; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Reduce.PropertyExpression; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Switch.CaseOperator; -import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; -import org.springframework.data.mongodb.core.query.CriteriaDefinition; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; - -import com.mongodb.BasicDBObject; -import com.mongodb.DBObject; - -/** - * @author Christoph Strobl - * @author Mark Paluch - * @since 1.10 - */ -public interface AggregationExpressions { - - /** - * Gateway to {@literal boolean expressions} that evaluate their argument expressions as booleans and return a boolean - * as the result. - * - * @author Christoph Strobl - */ - class BooleanOperators { - - /** - * Take the array referenced by given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static BooleanOperatorFactory valueOf(String fieldReference) { - return new BooleanOperatorFactory(fieldReference); - } - - /** - * Take the array referenced by given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static BooleanOperatorFactory valueOf(AggregationExpression fieldReference) { - return new BooleanOperatorFactory(fieldReference); - } - - /** - * Creates new {@link AggregationExpressions} 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) { - return Not.not(fieldReference); - } - - /** - * Creates new {@link AggregationExpressions} that evaluates the boolean value of {@link AggregationExpression} - * result and returns the opposite boolean value. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Not not(AggregationExpression expression) { - return Not.not(expression); - } - - public static class BooleanOperatorFactory { - - private final String fieldReference; - private final AggregationExpression expression; - - /** - * Creates new {@link BooleanOperatorFactory} for given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - */ - public BooleanOperatorFactory(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - this.fieldReference = fieldReference; - this.expression = null; - } - - /** - * Creates new {@link BooleanOperatorFactory} for given {@link AggregationExpression}. - * - * @param expression must not be {@literal null}. - */ - public BooleanOperatorFactory(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - this.fieldReference = null; - this.expression = expression; - } - - /** - * 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) { - - Assert.notNull(expression, "Expression must not be null!"); - return createAnd().andExpression(expression); - } - - /** - * 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) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return createAnd().andField(fieldReference); - } - - private And createAnd() { - return usesFieldRef() ? And.and(Fields.field(fieldReference)) : And.and(expression); - } - - /** - * 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) { - - Assert.notNull(expression, "Expression must not be null!"); - return createOr().orExpression(expression); - } - - /** - * Creates new {@link AggregationExpressions} that evaluates one or more expressions and returns {@literal true} - * if any of the expressions are {@literal true}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - 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); - } - - /** - * Creates new {@link AggregationExpression} that evaluates a boolean and returns the opposite boolean value. - * - * @return - */ - public Not not() { - return usesFieldRef() ? Not.not(fieldReference) : Not.not(expression); - } - - private boolean usesFieldRef() { - return this.fieldReference != null; - } - } - } - - /** - * Gateway to {@literal conditional expressions} that evaluate their argument expressions as booleans to a value. - * - * @author Mark Paluch - */ - class ConditionalOperators { - - /** - * Take the field referenced by given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static ConditionalOperatorFactory when(String fieldReference) { - return new ConditionalOperatorFactory(fieldReference); - } - - /** - * Take the value resulting from the given {@literal expression}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static ConditionalOperatorFactory when(AggregationExpression expression) { - return new ConditionalOperatorFactory(expression); - } - - /** - * Take the value resulting from the given {@literal criteriaDefinition}. - * - * @param criteriaDefinition must not be {@literal null}. - * @return - */ - public static ConditionalOperatorFactory when(CriteriaDefinition criteriaDefinition) { - return new ConditionalOperatorFactory(criteriaDefinition); - } - - /** - * Creates new {@link AggregationExpressions} that evaluates an expression and returns the value of the expression - * if the expression evaluates to a non-null value. If the expression evaluates to a {@literal null} value, - * including instances of undefined values or missing fields, returns the value of the replacement expression. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static IfNull.ThenBuilder ifNull(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return IfNull.ifNull(fieldReference); - } - - /** - * Creates new {@link AggregationExpressions} that evaluates an expression and returns the value of the expression - * if the expression evaluates to a non-null value. If the expression evaluates to a {@literal null} value, - * including instances of undefined values or missing fields, returns the value of the replacement expression. - * - * @param expression must not be {@literal null}. - * @return - */ - public static IfNull.ThenBuilder ifNull(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return IfNull.ifNull(expression); - } - - /** - * Creates new {@link AggregationExpression} that evaluates a series of {@link CaseOperator} expressions. When it - * finds an expression which evaluates to {@literal true}, {@code $switch} executes a specified expression and - * breaks out of the control flow. - * - * @param conditions must not be {@literal null}. - * @return - */ - public static Switch switchCases(CaseOperator... conditions) { - return Switch.switchCases(conditions); - } - - /** - * Creates new {@link AggregationExpression} that evaluates a series of {@link CaseOperator} expressions. When it - * finds an expression which evaluates to {@literal true}, {@code $switch} executes a specified expression and - * breaks out of the control flow. - * - * @param conditions must not be {@literal null}. - * @return - */ - public static Switch switchCases(List conditions) { - return Switch.switchCases(conditions); - } - - public static class ConditionalOperatorFactory { - - private final String fieldReference; - private final AggregationExpression expression; - private final CriteriaDefinition criteriaDefinition; - - /** - * Creates new {@link ConditionalOperatorFactory} for given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - */ - public ConditionalOperatorFactory(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - - this.fieldReference = fieldReference; - this.expression = null; - this.criteriaDefinition = null; - } - - /** - * Creates new {@link ConditionalOperatorFactory} for given {@link AggregationExpression}. - * - * @param expression must not be {@literal null}. - */ - public ConditionalOperatorFactory(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - - this.fieldReference = null; - this.expression = expression; - this.criteriaDefinition = null; - } - - /** - * Creates new {@link ConditionalOperatorFactory} for given {@link CriteriaDefinition}. - * - * @param criteriaDefinition must not be {@literal null}. - */ - public ConditionalOperatorFactory(CriteriaDefinition criteriaDefinition) { - - Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null!"); - - this.fieldReference = null; - this.expression = null; - this.criteriaDefinition = criteriaDefinition; - } - - /** - * Creates new {@link AggregationExpression} that evaluates a boolean expression to return one of the two - * specified return expressions. - * - * @param value must not be {@literal null}. - * @return - */ - public OtherwiseBuilder then(Object value) { - - Assert.notNull(value, "Value must not be null!"); - return createThenBuilder().then(value); - } - - /** - * Creates new {@link AggregationExpressions} that evaluates a boolean expression to return one of the two - * specified return expressions. - * - * @param expression must not be {@literal null}. - * @return - */ - public OtherwiseBuilder thenValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return createThenBuilder().then(expression); - } - - /** - * Creates new {@link AggregationExpressions} that evaluates a boolean expression to return one of the two - * specified return expressions. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public OtherwiseBuilder thenValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return createThenBuilder().then(fieldReference); - } - - private ThenBuilder createThenBuilder() { - - if (usesFieldRef()) { - return Cond.newBuilder().when(fieldReference); - } - - return usesCriteriaDefinition() ? Cond.newBuilder().when(criteriaDefinition) - : Cond.newBuilder().when(expression); - } - - private boolean usesFieldRef() { - return this.fieldReference != null; - } - - private boolean usesCriteriaDefinition() { - return this.criteriaDefinition != null; - } - } - } - - /** - * Gateway to {@literal Set expressions} which perform {@literal set} operation on arrays, treating arrays as sets. - * - * @author Christoph Strobl - */ - class SetOperators { - - /** - * Take the array referenced by given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - 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; - - /** - * Creates new {@link SetOperatorFactory} for given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - */ - public SetOperatorFactory(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - this.fieldReference = fieldReference; - this.expression = null; - } - - /** - * Creates new {@link SetOperatorFactory} for given {@link AggregationExpression}. - * - * @param expression must not be {@literal null}. - */ - public SetOperatorFactory(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - this.fieldReference = null; - this.expression = expression; - } - - /** - * 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 arrayReferences must not be {@literal null}. - * @return - */ - public SetEquals isEqualTo(String... arrayReferences) { - return createSetEquals().isEqualTo(arrayReferences); - } - - /** - * 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 expressions must not be {@literal null}. - * @return - */ - public SetEquals isEqualTo(AggregationExpression... expressions) { - return createSetEquals().isEqualTo(expressions); - } - - private SetEquals createSetEquals() { - return usesFieldRef() ? SetEquals.arrayAsSet(fieldReference) : SetEquals.arrayAsSet(expression); - } - - /** - * 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); - } - - /** - * 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); - } - - private SetIntersection createSetIntersection() { - return usesFieldRef() ? SetIntersection.arrayAsSet(fieldReference) : SetIntersection.arrayAsSet(expression); - } - - /** - * 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 - */ - public SetUnion union(String... arrayReferences) { - return createSetUnion().union(arrayReferences); - } - - /** - * 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 - */ - public SetUnion union(AggregationExpression... expressions) { - return createSetUnion().union(expressions); - } - - private SetUnion createSetUnion() { - return usesFieldRef() ? SetUnion.arrayAsSet(fieldReference) : SetUnion.arrayAsSet(expression); - } - - /** - * 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 - */ - public SetDifference differenceTo(String arrayReference) { - return createSetDifference().differenceTo(arrayReference); - } - - /** - * 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 - */ - public SetDifference differenceTo(AggregationExpression expression) { - return createSetDifference().differenceTo(expression); - } - - private SetDifference createSetDifference() { - return usesFieldRef() ? SetDifference.arrayAsSet(fieldReference) : SetDifference.arrayAsSet(expression); - } - - /** - * 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 - */ - public SetIsSubset isSubsetOf(String arrayReference) { - return createSetIsSubset().isSubsetOf(arrayReference); - } - - /** - * 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 - */ - public SetIsSubset isSubsetOf(AggregationExpression expression) { - return createSetIsSubset().isSubsetOf(expression); - } - - 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 field 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 value resulting from the given {@literal expression}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static ComparisonOperatorFactory valueOf(AggregationExpression expression) { - return new ComparisonOperatorFactory(expression); - } - - 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; - } - - /** - * Creates 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 field 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 value resulting from the given {@literal expression}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static ArithmeticOperatorFactory valueOf(AggregationExpression expression) { - return new ArithmeticOperatorFactory(expression); - } - - 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; - - /** - * Creates new {@link StringOperatorFactory} for given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - */ - public StringOperatorFactory(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - this.fieldReference = fieldReference; - this.expression = null; - } - - /** - * Creates new {@link StringOperatorFactory} for given {@link AggregationExpression}. - * - * @param expression must not be {@literal null}. - */ - public StringOperatorFactory(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - 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); - } - - /** - * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a - * string for an occurrence of a given {@literal substring} and returns the UTF-8 byte index (zero-based) of the - * first occurrence. - * - * @param substring must not be {@literal null}. - * @return - */ - public IndexOfBytes indexOf(String substring) { - - Assert.notNull(substring, "Substring must not be null!"); - return createIndexOfBytesSubstringBuilder().indexOf(substring); - } - - /** - * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a - * string for an occurrence of a substring contained in the given {@literal field reference} and returns the UTF-8 - * byte index (zero-based) of the first occurrence. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public IndexOfBytes indexOf(Field fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return createIndexOfBytesSubstringBuilder().indexOf(fieldReference); - } - - /** - * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a - * string for an occurrence of a substring resulting from the given {@link AggregationExpression} and returns the - * UTF-8 byte index (zero-based) of the first occurrence. - * - * @param expression must not be {@literal null}. - * @return - */ - public IndexOfBytes indexOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return createIndexOfBytesSubstringBuilder().indexOf(expression); - } - - private IndexOfBytes.SubstringBuilder createIndexOfBytesSubstringBuilder() { - return fieldReference != null ? IndexOfBytes.valueOf(fieldReference) : IndexOfBytes.valueOf(expression); - } - - /** - * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a - * string for an occurrence of a given {@literal substring} and returns the UTF-8 code point index (zero-based) of - * the first occurrence. - * - * @param substring must not be {@literal null}. - * @return - */ - public IndexOfCP indexOfCP(String substring) { - - Assert.notNull(substring, "Substring must not be null!"); - return createIndexOfCPSubstringBuilder().indexOf(substring); - } - - /** - * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a - * string for an occurrence of a substring contained in the given {@literal field reference} and returns the UTF-8 - * code point index (zero-based) of the first occurrence. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public IndexOfCP indexOfCP(Field fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return createIndexOfCPSubstringBuilder().indexOf(fieldReference); - } - - /** - * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a - * string for an occurrence of a substring resulting from the given {@link AggregationExpression} and returns the - * UTF-8 code point index (zero-based) of the first occurrence. - * - * @param expression must not be {@literal null}. - * @return - */ - public IndexOfCP indexOfCP(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return createIndexOfCPSubstringBuilder().indexOf(expression); - } - - private IndexOfCP.SubstringBuilder createIndexOfCPSubstringBuilder() { - return fieldReference != null ? IndexOfCP.valueOf(fieldReference) : IndexOfCP.valueOf(expression); - } - - /** - * Creates new {@link AggregationExpression} that divides the associated string representation into an array of - * substrings based on the given delimiter. - * - * @param delimiter must not be {@literal null}. - * @return - */ - public Split split(String delimiter) { - return createSplit().split(delimiter); - } - - /** - * Creates new {@link AggregationExpression} that divides the associated string representation into an array of - * substrings based on the delimiter resulting from the referenced field.. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public Split split(Field fieldReference) { - return createSplit().split(fieldReference); - } - - /** - * Creates new {@link AggregationExpression} that divides the associated string representation into an array of - * substrings based on a delimiter resulting from the given {@link AggregationExpression}. - * - * @param expression must not be {@literal null}. - * @return - */ - public Split split(AggregationExpression expression) { - return createSplit().split(expression); - } - - private Split createSplit() { - return fieldReference != null ? Split.valueOf(fieldReference) : Split.valueOf(expression); - } - - /** - * Creates new {@link AggregationExpression} that returns the number of UTF-8 bytes in the associated string - * representation. - * - * @return - */ - public StrLenBytes length() { - return fieldReference != null ? StrLenBytes.stringLengthOf(fieldReference) - : StrLenBytes.stringLengthOf(expression); - } - - /** - * Creates new {@link AggregationExpression} that returns the number of UTF-8 code points in the associated string - * representation. - * - * @return - */ - public StrLenCP lengthCP() { - return fieldReference != null ? StrLenCP.stringLengthOfCP(fieldReference) - : StrLenCP.stringLengthOfCP(expression); - } - - /** - * Creates new {@link AggregationExpressions} that takes the associated string representation and returns a - * substring starting at a specified code point index position. - * - * @param codePointStart - * @return - */ - public SubstrCP substringCP(int codePointStart) { - return substringCP(codePointStart, -1); - } - - /** - * Creates new {@link AggregationExpressions} that takes the associated string representation and returns a - * substring starting at a specified code point index position including the specified number of code points. - * - * @param codePointStart - * @param nrOfCodePoints - * @return - */ - public SubstrCP substringCP(int codePointStart, int nrOfCodePoints) { - return createSubstrCP().substringCP(codePointStart, nrOfCodePoints); - } - - private SubstrCP createSubstrCP() { - return fieldReference != null ? SubstrCP.valueOf(fieldReference) : SubstrCP.valueOf(expression); - } - } - } - - /** - * Gateway to {@literal 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; - - /** - * Creates new {@link ArrayOperatorFactory} for given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - */ - public ArrayOperatorFactory(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - this.fieldReference = fieldReference; - this.expression = null; - } - - /** - * Creates new {@link ArrayOperatorFactory} for given {@link AggregationExpression}. - * - * @param expression must not be {@literal null}. - */ - public ArrayOperatorFactory(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - 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 ArrayElemAt 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 ArrayElemAt 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 ArrayElemAt elementAt(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return createArrayElemAt().elementAt(fieldReference); - } - - private ArrayElemAt createArrayElemAt() { - return usesFieldRef() ? ArrayElemAt.arrayOf(fieldReference) : ArrayElemAt.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); - } - - /** - * Creates new {@link AggregationExpressions} that searches the associated array for an occurrence of a specified - * value and returns the array index (zero-based) of the first occurrence. - * - * @param value must not be {@literal null}. - * @return - */ - public IndexOfArray indexOf(Object value) { - return usesFieldRef() ? IndexOfArray.arrayOf(fieldReference).indexOf(value) - : IndexOfArray.arrayOf(expression).indexOf(value); - } - - /** - * Creates new {@link AggregationExpressions} that returns an array with the elements in reverse order. - * - * @return - */ - public ReverseArray reverse() { - return usesFieldRef() ? ReverseArray.reverseArrayOf(fieldReference) : ReverseArray.reverseArrayOf(expression); - } - - /** - * Start creating new {@link AggregationExpressions} that applies an {@link AggregationExpression} to each element - * in an array and combines them into a single value. - * - * @param expression must not be {@literal null}. - * @return - */ - public ReduceInitialValueBuilder reduce(final AggregationExpression expression) { - return new ReduceInitialValueBuilder() { - - @Override - public Reduce startingWith(Object initialValue) { - return (usesFieldRef() ? Reduce.arrayOf(fieldReference) : Reduce.arrayOf(expression)) - .withInitialValue(initialValue).reduce(expression); - } - }; - } - - /** - * Start creating new {@link AggregationExpressions} that applies an {@link AggregationExpression} to each element - * in an array and combines them into a single value. - * - * @param expressions - * @return - */ - public ReduceInitialValueBuilder reduce(final PropertyExpression... expressions) { - - return new ReduceInitialValueBuilder() { - - @Override - public Reduce startingWith(Object initialValue) { - return (usesFieldRef() ? Reduce.arrayOf(fieldReference) : Reduce.arrayOf(expression)) - .withInitialValue(initialValue).reduce(expressions); - } - }; - } - - /** - * Creates new {@link AggregationExpressions} that transposes an array of input arrays so that the first element - * of the output array would be an array containing, the first element of the first input array, the first element - * of the second input array, etc. - * - * @param arrays must not be {@literal null}. - * @return - */ - public Zip zipWith(Object... arrays) { - return (usesFieldRef() ? Zip.arrayOf(fieldReference) : Zip.arrayOf(expression)).zip(arrays); - } - - /** - * Creates new {@link AggregationExpressions} that returns a boolean indicating whether a specified value is in - * the associated array. - * - * @param value must not be {@literal null}. - * @return - */ - public In containsValue(Object value) { - return (usesFieldRef() ? In.arrayOf(fieldReference) : In.arrayOf(expression)).containsValue(value); - } - - /** - * @author Christoph Strobl - */ - public interface ReduceInitialValueBuilder { - - /** - * Define the initial cumulative value set before in is applied to the first element of the input array. - * - * @param initialValue must not be {@literal null}. - * @return - */ - Reduce startingWith(Object initialValue); - } - - private boolean usesFieldRef() { - return fieldReference != null; - } - } - } - - /** - * Gateway to {@literal literal} aggregation operations. - * - * @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; - - /** - * Creates new {@link LiteralOperatorFactory} for given {@literal value}. - * - * @param value must not be {@literal null}. - */ - public LiteralOperatorFactory(Object value) { - - Assert.notNull(value, "Value must not be null!"); - this.value = value; - } - - /** - * Creates new {@link AggregationExpressions} that returns the associated value without parsing. - * - * @return - */ - public Literal asLiteral() { - return Literal.asLiteral(value); - } - } - } - - /** - * Gateway to {@literal Date} aggregation operations. - * - * @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 expression 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; - - /** - * Creates new {@link ArithmeticOperatorFactory} for given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - */ - public DateOperatorFactory(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 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); - } - - /** - * Creates new {@link AggregationExpressions} that returns the weekday number in ISO 8601 format, ranging from 1 - * (for Monday) to 7 (for Sunday). - * - * @return - */ - public IsoDayOfWeek isoDayOfWeek() { - return usesFieldRef() ? IsoDayOfWeek.isoDayOfWeek(fieldReference) : IsoDayOfWeek.isoDayOfWeek(expression); - } - - /** - * Creates new {@link AggregationExpressions} that returns the week number in ISO 8601 format, ranging from 1 to - * 53. - * - * @return - */ - public IsoWeek isoWeek() { - return usesFieldRef() ? IsoWeek.isoWeekOf(fieldReference) : IsoWeek.isoWeekOf(expression); - } - - /** - * Creates new {@link AggregationExpressions} that returns the year number in ISO 8601 format. - * - * @return - */ - public IsoWeekYear isoWeekYear() { - return usesFieldRef() ? IsoWeekYear.isoWeekYearOf(fieldReference) : IsoWeekYear.isoWeekYearOf(expression); - } - - private boolean usesFieldRef() { - return fieldReference != null; - } - } - } - - /** - * Gateway to {@literal variable} aggregation operations. - * - * @author Christoph Strobl - * @author Mark Paluch - */ - class VariableOperators { - - /** - * Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array - * and returns an array with the applied results. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Map.AsBuilder mapItemsOf(String fieldReference) { - return Map.itemsOf(fieldReference); - } - - /** - * Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array - * and returns an array with the applied results. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Map.AsBuilder mapItemsOf(AggregationExpression expression) { - return Map.itemsOf(expression); - } - - /** - * Start creating new {@link Let} that allows definition of {@link ExpressionVariable} that can be used within a - * nested {@link AggregationExpression}. - * - * @param variables must not be {@literal null}. - * @return - */ - public static Let.LetBuilder define(ExpressionVariable... variables) { - return Let.define(variables); - } - - /** - * Start creating new {@link Let} that allows definition of {@link ExpressionVariable} that can be used within a - * nested {@link AggregationExpression}. - * - * @param variables must not be {@literal null}. - * @return - */ - public static Let.LetBuilder define(Collection variables) { - return Let.define(variables); - } - } - - /** - * @author Christoph Strobl - */ - abstract class AbstractAggregationExpression implements AggregationExpression { - - private final Object value; - - protected AbstractAggregationExpression(Object value) { - this.value = value; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @Override - public DBObject toDbObject(AggregationOperationContext context) { - 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 java.util.Map) { - - DBObject dbo = new BasicDBObject(); - for (java.util.Map.Entry entry : ((java.util.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(); - } - - if (value instanceof List) { - - List sourceList = (List) value; - List mappedList = new ArrayList(sourceList.size()); - - for (Object item : sourceList) { - mappedList.add(unpack(item, context)); - } - return mappedList; - } - - 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 java.util.Map append(String key, Object value) { - - if (!(this.value instanceof java.util.Map)) { - throw new IllegalArgumentException("o_O"); - } - java.util.Map clone = new LinkedHashMap( - (java.util.Map) this.value); - clone.put(key, value); - return clone; - - } - - protected List values() { - - if (value instanceof List) { - return new ArrayList((List) value); - } - if (value instanceof java.util.Map) { - return new ArrayList(((java.util.Map) value).values()); - } - return new ArrayList(Arrays.asList(value)); - } - - protected abstract String getMongoMethod(); - } - - /** - * {@link AggregationExpression} for {@code $setEquals}. - * - * @author Christoph Strobl - */ - class SetEquals extends AbstractAggregationExpression { - - private SetEquals(List arrays) { - super(arrays); - } - - @Override - protected 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 - protected 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 - protected 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 expression 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 - protected 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 - protected 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 - protected 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)); - } - - /** - * Creates 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 - protected 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 - protected String getMongoMethod() { - return "$abs"; - } - - /** - * Creates new {@link Abs}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Abs absoluteValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Abs(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Abs}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Abs absoluteValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Abs(expression); - } - - /** - * Creates new {@link Abs}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Abs absoluteValueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - return new Abs(value); - } - } - - /** - * {@link AggregationExpression} for {@code $add}. - * - * @author Christoph Strobl - */ - class Add extends AbstractAggregationExpression { - - protected Add(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$add"; - } - - /** - * Creates new {@link Add}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Add valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Add(asFields(fieldReference)); - } - - /** - * Creates new {@link Add}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Add valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Add(Collections.singletonList(expression)); - } - - /** - * Creates new {@link Add}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Add valueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - 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 - protected String getMongoMethod() { - return "$ceil"; - } - - /** - * Creates new {@link Ceil}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Ceil ceilValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Ceil(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Ceil}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Ceil ceilValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Ceil(expression); - } - - /** - * Creates new {@link Ceil}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Ceil ceilValueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - return new Ceil(value); - } - } - - /** - * {@link AggregationExpression} for {@code $divide}. - * - * @author Christoph Strobl - */ - class Divide extends AbstractAggregationExpression { - - private Divide(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$divide"; - } - - /** - * Creates new {@link Divide}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Divide valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Divide(asFields(fieldReference)); - } - - /** - * Creates new {@link Divide}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Divide valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Divide(Collections.singletonList(expression)); - } - - /** - * Creates new {@link Divide}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Divide valueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - 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 { - - private Exp(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$exp"; - } - - /** - * Creates new {@link Exp}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Exp expValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Exp(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Exp}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Exp expValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Exp(expression); - } - - /** - * Creates new {@link Exp}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Exp expValueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - return new Exp(value); - } - } - - /** - * {@link AggregationExpression} for {@code $floor}. - * - * @author Christoph Strobl - */ - class Floor extends AbstractAggregationExpression { - - private Floor(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$floor"; - } - - /** - * Creates new {@link Floor}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Floor floorValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Floor(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Floor}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Floor floorValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Floor(expression); - } - - /** - * Creates new {@link Floor}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Floor floorValueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - return new Floor(value); - } - } - - /** - * {@link AggregationExpression} for {@code $ln}. - * - * @author Christoph Strobl - */ - class Ln extends AbstractAggregationExpression { - - private Ln(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$ln"; - } - - /** - * Creates new {@link Ln}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Ln lnValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Ln(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Ln}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Ln lnValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Ln(expression); - } - - /** - * Creates new {@link Ln}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Ln lnValueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - return new Ln(value); - } - } - - /** - * {@link AggregationExpression} for {@code $log}. - * - * @author Christoph Strobl - */ - class Log extends AbstractAggregationExpression { - - private Log(List values) { - super(values); - } - - @Override - protected String getMongoMethod() { - return "$log"; - } - - /** - * Creates new {@link Min}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Log valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Log(asFields(fieldReference)); - } - - /** - * Creates new {@link Log}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Log valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Log(Collections.singletonList(expression)); - } - - /** - * Creates new {@link Log}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Log valueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - 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 - protected String getMongoMethod() { - return "$log10"; - } - - /** - * Creates new {@link Log10}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Log10 log10ValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Log10(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Log10}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Log10 log10ValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Log10(expression); - } - - /** - * Creates new {@link Log10}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Log10 log10ValueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - return new Log10(value); - } - } - - /** - * {@link AggregationExpression} for {@code $mod}. - * - * @author Christoph Strobl - */ - class Mod extends AbstractAggregationExpression { - - private Mod(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$mod"; - } - - /** - * Creates new {@link Mod}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Mod valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Mod(asFields(fieldReference)); - } - - /** - * Creates new {@link Mod}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Mod valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Mod(Collections.singletonList(expression)); - } - - /** - * Creates new {@link Mod}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Mod valueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - 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 - protected String getMongoMethod() { - return "$multiply"; - } - - /** - * Creates new {@link Multiply}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Multiply valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Multiply(asFields(fieldReference)); - } - - /** - * Creates new {@link Multiply}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Multiply valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Multiply(Collections.singletonList(expression)); - } - - /** - * Creates new {@link Multiply}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Multiply valueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - 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 - protected String getMongoMethod() { - return "$pow"; - } - - /** - * Creates new {@link Pow}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Pow valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Pow(asFields(fieldReference)); - } - - /** - * Creates new {@link Pow}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Pow valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Pow(Collections.singletonList(expression)); - } - - /** - * Creates new {@link Pow}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Pow valueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - 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 - protected String getMongoMethod() { - return "$sqrt"; - } - - /** - * Creates new {@link Sqrt}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Sqrt sqrtOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Sqrt(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Sqrt}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Sqrt sqrtOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Sqrt(expression); - } - - /** - * Creates new {@link Sqrt}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Sqrt sqrtOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - return new Sqrt(value); - } - } - - /** - * {@link AggregationExpression} for {@code $subtract}. - * - * @author Christoph Strobl - */ - class Subtract extends AbstractAggregationExpression { - - private Subtract(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$subtract"; - } - - /** - * Creates new {@link Subtract}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Subtract valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Subtract(asFields(fieldReference)); - } - - /** - * Creates new {@link Subtract}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Subtract valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Subtract(Collections.singletonList(expression)); - } - - /** - * Creates new {@link Subtract}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Subtract valueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - 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 - protected String getMongoMethod() { - return "$trunc"; - } - - /** - * Creates new {@link Trunc}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Trunc truncValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Trunc(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Trunc}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Trunc truncValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Trunc(expression); - } - - /** - * Creates new {@link Trunc}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Trunc truncValueOf(Number value) { - - Assert.notNull(value, "Value must not be null!"); - return new Trunc(value); - } - } - - // ######################################### - // STRING OPERATORS - // ######################################### - - /** - * {@link AggregationExpression} for {@code $concat}. - * - * @author Christoph Strobl - */ - class Concat extends AbstractAggregationExpression { - - private Concat(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$concat"; - } - - /** - * Creates new {@link Concat}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Concat valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Concat(asFields(fieldReference)); - } - - /** - * Creates new {@link Concat}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Concat valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Concat(Collections.singletonList(expression)); - } - - /** - * Creates new {@link Concat}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Concat stringValue(String value) { - - Assert.notNull(value, "Value must not be null!"); - 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 - protected String getMongoMethod() { - return "$substr"; - } - - /** - * Creates new {@link Substr}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Substr valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Substr(asFields(fieldReference)); - } - - /** - * Creates new {@link Substr}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$toLower"; - } - - /** - * Creates new {@link ToLower}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static ToLower lowerValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new ToLower(Fields.field(fieldReference)); - } - - /** - * Creates new {@link ToLower}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static ToLower lowerValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new ToLower(Collections.singletonList(expression)); - } - - /** - * Creates new {@link ToLower}. - * - * @param value must not be {@literal null}. - * @return - */ - public static ToLower lower(String value) { - - Assert.notNull(value, "Value must not be null!"); - return new ToLower(value); - } - } - - /** - * {@link AggregationExpression} for {@code $toUpper}. - * - * @author Christoph Strobl - */ - class ToUpper extends AbstractAggregationExpression { - - private ToUpper(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$toUpper"; - } - - /** - * Creates new {@link ToUpper}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static ToUpper upperValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new ToUpper(Fields.field(fieldReference)); - } - - /** - * Creates new {@link ToUpper}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static ToUpper upperValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new ToUpper(Collections.singletonList(expression)); - } - - /** - * Creates new {@link ToUpper}. - * - * @param value must not be {@literal null}. - * @return - */ - public static ToUpper upper(String value) { - - Assert.notNull(value, "Value must not be null!"); - return new ToUpper(value); - } - } - - /** - * {@link AggregationExpression} for {@code $strcasecmp}. - * - * @author Christoph Strobl - */ - class StrCaseCmp extends AbstractAggregationExpression { - - private StrCaseCmp(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$strcasecmp"; - } - - /** - * Creates new {@link StrCaseCmp}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static StrCaseCmp valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new StrCaseCmp(asFields(fieldReference)); - } - - /** - * Creates new {@link StrCaseCmp}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static StrCaseCmp valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new StrCaseCmp(Collections.singletonList(expression)); - } - - /** - * Creates new {@link StrCaseCmp}. - * - * @param value must not be {@literal null}. - * @return - */ - public static StrCaseCmp stringValue(String value) { - - Assert.notNull(value, "Value must not be null!"); - 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 $indexOfBytes}. - * - * @author Christoph Strobl - */ - class IndexOfBytes extends AbstractAggregationExpression { - - private IndexOfBytes(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$indexOfBytes"; - } - - /** - * Start creating a new {@link IndexOfBytes}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static SubstringBuilder valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new SubstringBuilder(Fields.field(fieldReference)); - } - - /** - * Start creating a new {@link IndexOfBytes}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static SubstringBuilder valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new SubstringBuilder(expression); - } - - /** - * Optionally define the substring search start and end position. - * - * @param range must not be {@literal null}. - * @return - */ - public IndexOfBytes within(Range range) { - - Assert.notNull(range, "Range must not be null!"); - - List rangeValues = new ArrayList(2); - rangeValues.add(range.getLowerBound()); - if (range.getUpperBound() != null) { - rangeValues.add(range.getUpperBound()); - } - - return new IndexOfBytes(append(rangeValues)); - } - - public static class SubstringBuilder { - - private final Object stringExpression; - - private SubstringBuilder(Object stringExpression) { - this.stringExpression = stringExpression; - } - - /** - * Creates a new {@link IndexOfBytes} given {@literal substring}. - * - * @param substring must not be {@literal null}. - * @return - */ - public IndexOfBytes indexOf(String substring) { - return new IndexOfBytes(Arrays.asList(stringExpression, substring)); - } - - /** - * Creates a new {@link IndexOfBytes} given {@link AggregationExpression} that resolves to the substring. - * - * @param expression must not be {@literal null}. - * @return - */ - public IndexOfBytes indexOf(AggregationExpression expression) { - return new IndexOfBytes(Arrays.asList(stringExpression, expression)); - } - - /** - * Creates a new {@link IndexOfBytes} given {@link Field} that resolves to the substring. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public IndexOfBytes indexOf(Field fieldReference) { - return new IndexOfBytes(Arrays.asList(stringExpression, fieldReference)); - } - } - } - - /** - * {@link AggregationExpression} for {@code $indexOfCP}. - * - * @author Christoph Strobl - */ - class IndexOfCP extends AbstractAggregationExpression { - - private IndexOfCP(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$indexOfCP"; - } - - /** - * Start creating a new {@link IndexOfCP}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static SubstringBuilder valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new SubstringBuilder(Fields.field(fieldReference)); - } - - /** - * Start creating a new {@link IndexOfCP}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static SubstringBuilder valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new SubstringBuilder(expression); - } - - /** - * Optionally define the substring search start and end position. - * - * @param range must not be {@literal null}. - * @return - */ - public IndexOfCP within(Range range) { - - Assert.notNull(range, "Range must not be null!"); - - List rangeValues = new ArrayList(2); - rangeValues.add(range.getLowerBound()); - if (range.getUpperBound() != null) { - rangeValues.add(range.getUpperBound()); - } - - return new IndexOfCP(append(rangeValues)); - } - - public static class SubstringBuilder { - - private final Object stringExpression; - - private SubstringBuilder(Object stringExpression) { - this.stringExpression = stringExpression; - } - - /** - * Creates a new {@link IndexOfCP} given {@literal substring}. - * - * @param substring must not be {@literal null}. - * @return - */ - public IndexOfCP indexOf(String substring) { - return new IndexOfCP(Arrays.asList(stringExpression, substring)); - } - - /** - * Creates a new {@link IndexOfCP} given {@link AggregationExpression} that resolves to the substring. - * - * @param expression must not be {@literal null}. - * @return - */ - public IndexOfCP indexOf(AggregationExpression expression) { - return new IndexOfCP(Arrays.asList(stringExpression, expression)); - } - - /** - * Creates a new {@link IndexOfCP} given {@link Field} that resolves to the substring. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public IndexOfCP indexOf(Field fieldReference) { - return new IndexOfCP(Arrays.asList(stringExpression, fieldReference)); - } - } - } - - /** - * {@link AggregationExpression} for {@code $split}. - * - * @author Christoph Strobl - */ - class Split extends AbstractAggregationExpression { - - private Split(List values) { - super(values); - } - - @Override - protected String getMongoMethod() { - return "$split"; - } - - /** - * Start creating a new {@link Split}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Split valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Split(asFields(fieldReference)); - } - - /** - * Start creating a new {@link Split}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static Split valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Split(Collections.singletonList(expression)); - } - - /** - * Use given {@link String} as delimiter. - * - * @param delimiter must not be {@literal null}. - * @return - */ - public Split split(String delimiter) { - - Assert.notNull(delimiter, "Delimiter must not be null!"); - return new Split(append(delimiter)); - } - - /** - * Use value of referenced field as delimiter. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public Split split(Field fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Split(append(fieldReference)); - } - - /** - * Use value resulting from {@link AggregationExpression} as delimiter. - * - * @param expression must not be {@literal null}. - * @return - */ - public Split split(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Split(append(expression)); - } - } - - /** - * {@link AggregationExpression} for {@code $strLenBytes}. - * - * @author Christoph Strobl - */ - class StrLenBytes extends AbstractAggregationExpression { - - private StrLenBytes(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$strLenBytes"; - } - - /** - * Creates new {@link StrLenBytes}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static StrLenBytes stringLengthOf(String fieldReference) { - return new StrLenBytes(Fields.field(fieldReference)); - } - - /** - * Creates new {@link StrLenBytes}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static StrLenBytes stringLengthOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new StrLenBytes(expression); - } - } - - /** - * {@link AggregationExpression} for {@code $strLenCP}. - * - * @author Christoph Strobl - */ - class StrLenCP extends AbstractAggregationExpression { - - private StrLenCP(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$strLenCP"; - } - - /** - * Creates new {@link StrLenCP}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static StrLenCP stringLengthOfCP(String fieldReference) { - return new StrLenCP(Fields.field(fieldReference)); - } - - /** - * Creates new {@link StrLenCP}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static StrLenCP stringLengthOfCP(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new StrLenCP(expression); - } - } - - /** - * {@link AggregationExpression} for {@code $substrCP}. - * - * @author Christoph Strobl - */ - class SubstrCP extends AbstractAggregationExpression { - - private SubstrCP(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$substrCP"; - } - - /** - * Creates new {@link SubstrCP}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static SubstrCP valueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new SubstrCP(asFields(fieldReference)); - } - - /** - * Creates new {@link SubstrCP}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static SubstrCP valueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new SubstrCP(Collections.singletonList(expression)); - } - - public SubstrCP substringCP(int start) { - return substringCP(start, -1); - } - - public SubstrCP substringCP(int start, int nrOfChars) { - return new SubstrCP(append(Arrays.asList(start, nrOfChars))); - } - } - - // ######################################### - // ARRAY OPERATORS - // ######################################### - - /** - * {@link AggregationExpression} for {@code $arrayElementAt}. - * - * @author Christoph Strobl - */ - class ArrayElemAt extends AbstractAggregationExpression { - - private ArrayElemAt(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$arrayElemAt"; - } - - /** - * Creates new {@link ArrayElemAt}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static ArrayElemAt arrayOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new ArrayElemAt(asFields(fieldReference)); - } - - /** - * Creates new {@link ArrayElemAt}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static ArrayElemAt arrayOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new ArrayElemAt(Collections.singletonList(expression)); - } - - public ArrayElemAt elementAt(int index) { - return new ArrayElemAt(append(index)); - } - - public ArrayElemAt elementAt(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new ArrayElemAt(append(expression)); - } - - public ArrayElemAt elementAt(String arrayFieldReference) { - - Assert.notNull(arrayFieldReference, "ArrayReference must not be null!"); - return new ArrayElemAt(append(Fields.field(arrayFieldReference))); - } - } - - /** - * {@link AggregationExpression} for {@code $concatArrays}. - * - * @author Christoph Strobl - */ - class ConcatArrays extends AbstractAggregationExpression { - - private ConcatArrays(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$concatArrays"; - } - - /** - * Creates new {@link ConcatArrays}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static ConcatArrays arrayOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new ConcatArrays(asFields(fieldReference)); - } - - /** - * Creates new {@link ConcatArrays}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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(ExposedFields.from(as), context); - } - - private DBObject toFilter(ExposedFields exposedFields, AggregationOperationContext context) { - - DBObject filterExpression = new BasicDBObject(); - InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext( - exposedFields, context); - - filterExpression.putAll(context.getMappedObject(new BasicDBObject("input", getMappedInput(context)))); - filterExpression.put("as", as.getTarget()); - - filterExpression.putAll(context.getMappedObject(new BasicDBObject("cond", getMappedCondition(operationContext)))); - - 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 - protected String getMongoMethod() { - return "$isArray"; - } - - /** - * Creates new {@link IsArray}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static IsArray isArray(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new IsArray(Fields.field(fieldReference)); - } - - /** - * Creates new {@link IsArray}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$size"; - } - - /** - * Creates new {@link Size}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Size lengthOfArray(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Size(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Size}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$slice"; - } - - /** - * Creates new {@link Slice}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Slice sliceArrayOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Slice(asFields(fieldReference)); - } - - /** - * Creates new {@link Slice}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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); - } - }; - } - - /** - * @author Christoph Strobl - */ - public interface SliceElementsBuilder { - - /** - * Set the number of elements given {@literal nrElements}. - * - * @param nrElements - * @return - */ - Slice itemCount(int nrElements); - } - } - - /** - * {@link AggregationExpression} for {@code $indexOfArray}. - * - * @author Christoph Strobl - */ - class IndexOfArray extends AbstractAggregationExpression { - - private IndexOfArray(List value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$indexOfArray"; - } - - /** - * Start creating new {@link IndexOfArray}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static IndexOfArrayBuilder arrayOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new IndexOfArrayBuilder(Fields.field(fieldReference)); - } - - /** - * Start creating new {@link IndexOfArray}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static IndexOfArrayBuilder arrayOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new IndexOfArrayBuilder(expression); - } - - public IndexOfArray within(Range range) { - - Assert.notNull(range, "Range must not be null!"); - - List rangeValues = new ArrayList(2); - rangeValues.add(range.getLowerBound()); - if (range.getUpperBound() != null) { - rangeValues.add(range.getUpperBound()); - } - - return new IndexOfArray(append(rangeValues)); - } - - /** - * @author Christoph Strobl - */ - public static class IndexOfArrayBuilder { - - private final Object targetArray; - - private IndexOfArrayBuilder(Object targetArray) { - this.targetArray = targetArray; - } - - /** - * Set the {@literal value} to check for its index in the array. - * - * @param value must not be {@literal null}. - * @return - */ - public IndexOfArray indexOf(Object value) { - - Assert.notNull(value, "Value must not be null!"); - return new IndexOfArray(Arrays.asList(targetArray, value)); - } - } - } - - /** - * {@link AggregationExpression} for {@code $range}. - * - * @author Christoph Strobl - */ - class RangeOperator extends AbstractAggregationExpression { - - private RangeOperator(List values) { - super(values); - } - - @Override - protected String getMongoMethod() { - return "$range"; - } - - /** - * Start creating new {@link RangeOperator}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static RangeOperatorBuilder rangeStartingAt(String fieldReference) { - return new RangeOperatorBuilder(Fields.field(fieldReference)); - } - - /** - * Start creating new {@link RangeOperator}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static RangeOperatorBuilder rangeStartingAt(AggregationExpression expression) { - return new RangeOperatorBuilder(expression); - } - - /** - * Start creating new {@link RangeOperator}. - * - * @param value - * @return - */ - public static RangeOperatorBuilder rangeStartingAt(long value) { - return new RangeOperatorBuilder(value); - } - - public RangeOperator withStepSize(long stepSize) { - return new RangeOperator(append(stepSize)); - } - - public static class RangeOperatorBuilder { - - private final Object startPoint; - - private RangeOperatorBuilder(Object startPoint) { - this.startPoint = startPoint; - } - - /** - * Creates new {@link RangeOperator}. - * - * @param index - * @return - */ - public RangeOperator to(long index) { - return new RangeOperator(Arrays.asList(startPoint, index)); - } - - /** - * Creates new {@link RangeOperator}. - * - * @param expression must not be {@literal null}. - * @return - */ - public RangeOperator to(AggregationExpression expression) { - return new RangeOperator(Arrays.asList(startPoint, expression)); - } - - /** - * Creates new {@link RangeOperator}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public RangeOperator to(String fieldReference) { - return new RangeOperator(Arrays.asList(startPoint, Fields.field(fieldReference))); - } - } - } - - /** - * {@link AggregationExpression} for {@code $reverseArray}. - * - * @author Christoph Strobl - */ - class ReverseArray extends AbstractAggregationExpression { - - private ReverseArray(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$reverseArray"; - } - - /** - * Creates new {@link ReverseArray} given {@literal fieldReference}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static ReverseArray reverseArrayOf(String fieldReference) { - return new ReverseArray(Fields.field(fieldReference)); - } - - /** - * Creates new {@link ReverseArray} given {@link AggregationExpression}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static ReverseArray reverseArrayOf(AggregationExpression expression) { - return new ReverseArray(expression); - } - } - - /** - * {@link AggregationExpression} for {@code $reduce}. - * - * @author Christoph Strobl - */ - class Reduce implements AggregationExpression { - - private final Object input; - private final Object initialValue; - private final List reduceExpressions; - - private Reduce(Object input, Object initialValue, List reduceExpressions) { - - this.input = input; - this.initialValue = initialValue; - this.reduceExpressions = reduceExpressions; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @Override - public DBObject toDbObject(AggregationOperationContext context) { - - DBObject dbo = new BasicDBObject(); - - dbo.put("input", getMappedValue(input, context)); - dbo.put("initialValue", getMappedValue(initialValue, context)); - - if (reduceExpressions.iterator().next() instanceof PropertyExpression) { - - DBObject properties = new BasicDBObject(); - for (AggregationExpression e : reduceExpressions) { - properties.putAll(e.toDbObject(context)); - } - dbo.put("in", properties); - } else { - dbo.put("in", (reduceExpressions.iterator().next()).toDbObject(context)); - } - - return new BasicDBObject("$reduce", dbo); - } - - private Object getMappedValue(Object value, AggregationOperationContext context) { - - if (value instanceof DBObject) { - return value; - } - if (value instanceof AggregationExpression) { - return ((AggregationExpression) value).toDbObject(context); - } else if (value instanceof Field) { - return context.getReference(((Field) value)).toString(); - } else { - return context.getMappedObject(new BasicDBObject("###val###", value)).get("###val###"); - } - } - - /** - * Start creating new {@link Reduce}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static InitialValueBuilder arrayOf(final String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null"); - - return new InitialValueBuilder() { - - @Override - public ReduceBuilder withInitialValue(final Object initialValue) { - - Assert.notNull(initialValue, "Initial value must not be null"); - - return new ReduceBuilder() { - - @Override - public Reduce reduce(AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression must not be null"); - return new Reduce(Fields.field(fieldReference), initialValue, Collections.singletonList(expression)); - } - - @Override - public Reduce reduce(PropertyExpression... expressions) { - - Assert.notNull(expressions, "PropertyExpressions must not be null"); - - return new Reduce(Fields.field(fieldReference), initialValue, - Arrays. asList(expressions)); - } - }; - } - }; - } - - /** - * Start creating new {@link Reduce}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static InitialValueBuilder arrayOf(final AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression must not be null"); - - return new InitialValueBuilder() { - - @Override - public ReduceBuilder withInitialValue(final Object initialValue) { - - Assert.notNull(initialValue, "Initial value must not be null"); - - return new ReduceBuilder() { - - @Override - public Reduce reduce(AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression must not be null"); - return new Reduce(expression, initialValue, Collections.singletonList(expression)); - } - - @Override - public Reduce reduce(PropertyExpression... expressions) { - - Assert.notNull(expressions, "PropertyExpressions must not be null"); - return new Reduce(expression, initialValue, Arrays. asList(expressions)); - } - }; - } - }; - } - - /** - * @author Christoph Strobl - */ - public interface InitialValueBuilder { - - /** - * Define the initial cumulative value set before in is applied to the first element of the input array. - * - * @param initialValue must not be {@literal null}. - * @return - */ - ReduceBuilder withInitialValue(Object initialValue); - } - - /** - * @author Christoph Strobl - */ - public interface ReduceBuilder { - - /** - * Define the {@link AggregationExpression} to apply to each element in the input array in left-to-right order. - *
- * NOTE: During evaluation of the in expression the variable references {@link Variable#THIS} and - * {@link Variable#VALUE} are available. - * - * @param expression must not be {@literal null}. - * @return - */ - Reduce reduce(AggregationExpression expression); - - /** - * Define the {@link PropertyExpression}s to apply to each element in the input array in left-to-right order. - *
- * NOTE: During evaluation of the in expression the variable references {@link Variable#THIS} and - * {@link Variable#VALUE} are available. - * - * @param expression must not be {@literal null}. - * @return - */ - Reduce reduce(PropertyExpression... expressions); - } - - /** - * @author Christoph Strobl - */ - public static class PropertyExpression implements AggregationExpression { - - private final String propertyName; - private final AggregationExpression aggregationExpression; - - protected PropertyExpression(String propertyName, AggregationExpression aggregationExpression) { - - Assert.notNull(propertyName, "Property name must not be null!"); - Assert.notNull(aggregationExpression, "AggregationExpression must not be null!"); - - this.propertyName = propertyName; - this.aggregationExpression = aggregationExpression; - } - - /** - * Define a result property for an {@link AggregationExpression} used in {@link Reduce}. - * - * @param name must not be {@literal null}. - * @return - */ - public static AsBuilder property(final String name) { - - return new AsBuilder() { - - @Override - public PropertyExpression definedAs(AggregationExpression expression) { - return new PropertyExpression(name, expression); - } - }; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @Override - public DBObject toDbObject(AggregationOperationContext context) { - return new BasicDBObject(propertyName, aggregationExpression.toDbObject(context)); - } - - /** - * @author Christoph Strobl - */ - interface AsBuilder { - - /** - * Set the {@link AggregationExpression} resulting in the properties value. - * - * @param expression must not be {@literal null}. - * @return - */ - PropertyExpression definedAs(AggregationExpression expression); - } - } - - public enum Variable implements Field { - - THIS { - @Override - public String getName() { - return "$$this"; - } - - @Override - public String getTarget() { - return "$$this"; - } - - @Override - public boolean isAliased() { - return false; - } - - @Override - public String toString() { - return getName(); - } - }, - - VALUE { - @Override - public String getName() { - return "$$value"; - } - - @Override - public String getTarget() { - return "$$value"; - } - - @Override - public boolean isAliased() { - return false; - } - - @Override - public String toString() { - return getName(); - } - }; - - /** - * Create a {@link Field} reference to a given {@literal property} prefixed with the {@link Variable} identifier. - * eg. {@code $$value.product} - * - * @param property must not be {@literal null}. - * @return - */ - public Field referringTo(final String property) { - - return new Field() { - @Override - public String getName() { - return Variable.this.getName() + "." + property; - } - - @Override - public String getTarget() { - return Variable.this.getTarget() + "." + property; - } - - @Override - public boolean isAliased() { - return false; - } - - @Override - public String toString() { - return getName(); - } - }; - } - } - } - - /** - * {@link AggregationExpression} for {@code $zip}. - * - * @author Christoph Strobl - */ - class Zip extends AbstractAggregationExpression { - - protected Zip(java.util.Map value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$zip"; - } - - /** - * Start creating new {@link Zip}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static ZipBuilder arrayOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new ZipBuilder(Fields.field(fieldReference)); - } - - /** - * Start creating new {@link Zip}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static ZipBuilder arrayOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new ZipBuilder(expression); - } - - /** - * Create new {@link Zip} and set the {@code useLongestLength} property to {@literal true}. - * - * @return - */ - public Zip useLongestLength() { - return new Zip(append("useLongestLength", true)); - } - - /** - * Optionally provide a default value. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public Zip defaultTo(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Zip(append("defaults", Fields.field(fieldReference))); - } - - /** - * Optionally provide a default value. - * - * @param expression must not be {@literal null}. - * @return - */ - public Zip defaultTo(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Zip(append("defaults", expression)); - } - - /** - * Optionally provide a default value. - * - * @param array must not be {@literal null}. - * @return - */ - public Zip defaultTo(Object[] array) { - - Assert.notNull(array, "Array must not be null!"); - return new Zip(append("defaults", array)); - } - - public static class ZipBuilder { - - private final List sourceArrays; - - private ZipBuilder(Object sourceArray) { - - this.sourceArrays = new ArrayList(); - this.sourceArrays.add(sourceArray); - } - - /** - * Creates new {@link Zip} that transposes an array of input arrays so that the first element of the output array - * would be an array containing, the first element of the first input array, the first element of the second input - * array, etc. - * - * @param arrays arrays to zip the referenced one with. must not be {@literal null}. - * @return - */ - public Zip zip(Object... arrays) { - - Assert.notNull(arrays, "Arrays must not be null!"); - for (Object value : arrays) { - - if (value instanceof String) { - sourceArrays.add(Fields.field((String) value)); - } else { - sourceArrays.add(value); - } - } - - return new Zip(Collections. singletonMap("inputs", sourceArrays)); - } - } - } - - /** - * {@link AggregationExpression} for {@code $in}. - * - * @author Christoph Strobl - */ - class In extends AbstractAggregationExpression { - - private In(List values) { - super(values); - } - - @Override - protected String getMongoMethod() { - return "$in"; - } - - /** - * Start creating {@link In}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static InBuilder arrayOf(final String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - - return new InBuilder() { - - @Override - public In containsValue(Object value) { - - Assert.notNull(value, "Value must not be null!"); - return new In(Arrays.asList(value, Fields.field(fieldReference))); - } - }; - } - - /** - * Start creating {@link In}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static InBuilder arrayOf(final AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - - return new InBuilder() { - - @Override - public In containsValue(Object value) { - - Assert.notNull(value, "Value must not be null!"); - return new In(Arrays.asList(value, expression)); - } - }; - } - - /** - * @author Christoph Strobl - */ - public interface InBuilder { - - /** - * Set the {@literal value} to check for existence in the array. - * - * @param value must not be {@literal value}. - * @return - */ - In containsValue(Object value); - } - } - - // ############ - // LITERAL OPERATORS - // ############ - - /** - * {@link AggregationExpression} for {@code $literal}. - * - * @author Christoph Strobl - */ - class Literal extends AbstractAggregationExpression { - - private Literal(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$literal"; - } - - /** - * Creates new {@link Literal}. - * - * @param value must not be {@literal null}. - * @return - */ - public static Literal asLiteral(Object value) { - - Assert.notNull(value, "Value must not be null!"); - return new Literal(value); - } - } - - /** - * {@link AggregationExpression} for {@code $dayOfYear}. - * - * @author Christoph Strobl - */ - class DayOfYear extends AbstractAggregationExpression { - - private DayOfYear(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$dayOfYear"; - } - - /** - * Creates new {@link DayOfYear}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static DayOfYear dayOfYear(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new DayOfYear(Fields.field(fieldReference)); - } - - /** - * Creates new {@link DayOfYear}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$dayOfMonth"; - } - - /** - * Creates new {@link DayOfMonth}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static DayOfMonth dayOfMonth(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new DayOfMonth(Fields.field(fieldReference)); - } - - /** - * Creates new {@link DayOfMonth}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$dayOfWeek"; - } - - /** - * Creates new {@link DayOfWeek}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static DayOfWeek dayOfWeek(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new DayOfWeek(Fields.field(fieldReference)); - } - - /** - * Creates new {@link DayOfWeek}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$year"; - } - - /** - * Creates new {@link Year}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Year yearOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Year(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Year}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$month"; - } - - /** - * Creates new {@link Month}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Month monthOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Month(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Month}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$week"; - } - - /** - * Creates new {@link Week}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Week weekOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Week(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Week}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$hour"; - } - - /** - * Creates new {@link Hour}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Hour hourOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Hour(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Hour}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected String getMongoMethod() { - return "$minute"; - } - - /** - * Creates new {@link Minute}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static Minute minuteOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Minute(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Minute}. - * - * @param expression must not be {@literal null}. - * @return - */ - 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 - protected 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 - protected 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 - protected 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 java.util.Map argumentMap(Object date, String format) { - - java.util.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 $isoDayOfWeek}. - * - * @author Christoph Strobl - */ - class IsoDayOfWeek extends AbstractAggregationExpression { - - private IsoDayOfWeek(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$isoDayOfWeek"; - } - - /** - * Creates new {@link IsoDayOfWeek}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static IsoDayOfWeek isoDayOfWeek(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new IsoDayOfWeek(Fields.field(fieldReference)); - } - - /** - * Creates new {@link IsoDayOfWeek}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static IsoDayOfWeek isoDayOfWeek(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new IsoDayOfWeek(expression); - } - } - - /** - * {@link AggregationExpression} for {@code $isoWeek}. - * - * @author Christoph Strobl - */ - class IsoWeek extends AbstractAggregationExpression { - - private IsoWeek(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$isoWeek"; - } - - /** - * Creates new {@link IsoWeek}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static IsoWeek isoWeekOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new IsoWeek(Fields.field(fieldReference)); - } - - /** - * Creates new {@link IsoWeek}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static IsoWeek isoWeekOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new IsoWeek(expression); - } - } - - /** - * {@link AggregationExpression} for {@code $isoWeekYear}. - * - * @author Christoph Strobl - */ - class IsoWeekYear extends AbstractAggregationExpression { - - private IsoWeekYear(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$isoWeekYear"; - } - - /** - * Creates new {@link IsoWeekYear}. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - public static IsoWeekYear isoWeekYearOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new IsoWeekYear(Fields.field(fieldReference)); - } - - /** - * Creates new {@link Millisecond}. - * - * @param expression must not be {@literal null}. - * @return - */ - public static IsoWeekYear isoWeekYearOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new IsoWeekYear(expression); - } - } - - /** - * {@link AggregationExpression} for {@code $sum}. - * - * @author Christoph Strobl - */ - class Sum extends AbstractAggregationExpression { - - private Sum(Object value) { - super(value); - } - - @Override - protected 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)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @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 - protected 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)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @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 - protected 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)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @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 - protected 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)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @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 - protected 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)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @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 - protected 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)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.AbstractAggregationExpression#toDbObject(java.lang.Object, org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @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 - protected 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 - protected 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 - protected 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 - protected 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 - protected 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 - protected 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 - protected 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 - protected 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 - protected 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 - protected 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)); - } - } - - /** - * {@link AggregationExpression} for {@code $map}. - */ - class Map implements AggregationExpression { - - private Object sourceArray; - private String itemVariableName; - private AggregationExpression functionToApply; - - private Map(Object sourceArray, String itemVariableName, AggregationExpression functionToApply) { - - Assert.notNull(sourceArray, "SourceArray must not be null!"); - Assert.notNull(itemVariableName, "ItemVariableName must not be null!"); - Assert.notNull(functionToApply, "FunctionToApply must not be null!"); - - this.sourceArray = sourceArray; - this.itemVariableName = itemVariableName; - this.functionToApply = functionToApply; - } - - /** - * Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array - * and returns an array with the applied results. - * - * @param fieldReference must not be {@literal null}. - * @return - */ - static AsBuilder itemsOf(final String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - - return new AsBuilder() { - - @Override - public FunctionBuilder as(final String variableName) { - - Assert.notNull(variableName, "VariableName must not be null!"); - - return new FunctionBuilder() { - - @Override - public Map andApply(final AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression must not be null!"); - return new Map(Fields.field(fieldReference), variableName, expression); - } - }; - } - - }; - }; - - /** - * Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array - * and returns an array with the applied results. - * - * @param source must not be {@literal null}. - * @return - */ - public static AsBuilder itemsOf(final AggregationExpression source) { - - Assert.notNull(source, "AggregationExpression must not be null!"); - - return new AsBuilder() { - - @Override - public FunctionBuilder as(final String variableName) { - - Assert.notNull(variableName, "VariableName must not be null!"); - - return new FunctionBuilder() { - - @Override - public Map andApply(final AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression must not be null!"); - return new Map(source, variableName, expression); - } - }; - } - }; - } - - /* (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 toMap(ExposedFields.synthetic(Fields.fields(itemVariableName)), context); - } - - private DBObject toMap(ExposedFields exposedFields, AggregationOperationContext context) { - - BasicDBObject map = new BasicDBObject(); - InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext( - exposedFields, context); - - BasicDBObject input; - if (sourceArray instanceof Field) { - input = new BasicDBObject("input", context.getReference((Field) sourceArray).toString()); - } else { - input = new BasicDBObject("input", ((AggregationExpression) sourceArray).toDbObject(context)); - } - - map.putAll(context.getMappedObject(input)); - map.put("as", itemVariableName); - map.put("in", - functionToApply.toDbObject(new NestedDelegatingExpressionAggregationOperationContext(operationContext))); - - return new BasicDBObject("$map", map); - } - - interface AsBuilder { - - /** - * Define the {@literal variableName} for addressing items within the array. - * - * @param variableName must not be {@literal null}. - * @return - */ - FunctionBuilder as(String variableName); - } - - interface FunctionBuilder { - - /** - * Creates new {@link Map} that applies the given {@link AggregationExpression} to each item of the referenced - * array and returns an array with the applied results. - * - * @param expression must not be {@literal null}. - * @return - */ - Map andApply(AggregationExpression expression); - } - } - - /** - * Encapsulates the aggregation framework {@code $ifNull} operator. Replacement values can be either {@link Field - * field references}, {@link AggregationExpression expressions}, values of simple MongoDB types or values that can be - * converted to a simple MongoDB type. - * - * @see http://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/ - * @author Mark Paluch - */ - class IfNull implements AggregationExpression { - - private final Object condition; - private final Object value; - - private IfNull(Object condition, Object value) { - - this.condition = condition; - this.value = value; - } - - /** - * Creates new {@link IfNull}. - * - * @param fieldReference the field to check for a {@literal null} value, field reference must not be {@literal null} - * . - * @return - */ - public static ThenBuilder ifNull(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new IfNullOperatorBuilder().ifNull(fieldReference); - } - - /** - * Creates new {@link IfNull}. - * - * @param expression the expression to check for a {@literal null} value, field reference must not be - * {@literal null}. - * @return - */ - public static ThenBuilder ifNull(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new IfNullOperatorBuilder().ifNull(expression); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @Override - public DBObject toDbObject(AggregationOperationContext context) { - - List list = new ArrayList(); - - if (condition instanceof Field) { - list.add(context.getReference((Field) condition).toString()); - } else if (condition instanceof AggregationExpression) { - list.add(((AggregationExpression) condition).toDbObject(context)); - } else { - list.add(condition); - } - - list.add(resolve(value, context)); - - return new BasicDBObject("$ifNull", list); - } - - private Object resolve(Object value, AggregationOperationContext context) { - - if (value instanceof Field) { - return context.getReference((Field) value).toString(); - } else if (value instanceof AggregationExpression) { - return ((AggregationExpression) value).toDbObject(context); - } else if (value instanceof DBObject) { - return value; - } - - return context.getMappedObject(new BasicDBObject("$set", value)).get("$set"); - } - - /** - * @author Mark Paluch - */ - public static interface IfNullBuilder { - - /** - * @param fieldReference the field to check for a {@literal null} value, field reference must not be - * {@literal null}. - * @return the {@link ThenBuilder} - */ - ThenBuilder ifNull(String fieldReference); - - /** - * @param expression the expression to check for a {@literal null} value, field name must not be {@literal null} - * or empty. - * @return the {@link ThenBuilder} - */ - ThenBuilder ifNull(AggregationExpression expression); - } - - /** - * @author Mark Paluch - */ - public static interface ThenBuilder { - - /** - * @param value the value to be used if the {@code $ifNull} condition evaluates {@literal true}. Can be a - * {@link DBObject}, a value that is supported by MongoDB or a value that can be converted to a MongoDB - * representation but must not be {@literal null}. - * @return - */ - IfNull then(Object value); - - /** - * @param fieldReference the field holding the replacement value, must not be {@literal null}. - * @return - */ - IfNull thenValueOf(String fieldReference); - - /** - * @param expression the expression yielding to the replacement value, must not be {@literal null}. - * @return - */ - public IfNull thenValueOf(AggregationExpression expression); - } - - /** - * Builder for fluent {@link IfNullOperator} creation. - * - * @author Mark Paluch - */ - static final class IfNullOperatorBuilder implements IfNullBuilder, ThenBuilder { - - private Object condition; - - private IfNullOperatorBuilder() {} - - /** - * Creates a new builder for {@link IfNullOperator}. - * - * @return never {@literal null}. - */ - public static IfNullOperatorBuilder newBuilder() { - return new IfNullOperatorBuilder(); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.IfNullBuilder#ifNull(java.lang.String) - */ - public ThenBuilder ifNull(String fieldReference) { - - Assert.hasText(fieldReference, "FieldReference name must not be null or empty!"); - this.condition = Fields.field(fieldReference); - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.IfNullBuilder#ifNull(org.springframework.data.mongodb.core.aggregation.AggregationExpression) - */ - @Override - public ThenBuilder ifNull(AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression name must not be null or empty!"); - this.condition = expression; - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#then(java.lang.Object) - */ - public IfNull then(Object value) { - return new IfNull(condition, value); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#thenValueOf(java.lang.String) - */ - public IfNull thenValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new IfNull(condition, Fields.field(fieldReference)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#thenValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression) - */ - public IfNull thenValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new IfNull(condition, expression); - } - } - } - - /** - * Encapsulates the aggregation framework {@code $cond} operator. A {@link Cond} allows nested conditions - * {@code if-then[if-then-else]-else} using {@link Field}, {@link CriteriaDefinition}, {@link AggregationExpression} - * or a {@link DBObject custom} condition. Replacement values can be either {@link Field field references}, - * {@link AggregationExpression expressions}, values of simple MongoDB types or values that can be converted to a - * simple MongoDB type. - * - * @see http://docs.mongodb.com/manual/reference/operator/aggregation/cond/ - * @author Mark Paluch - * @author Christoph Strobl - */ - class Cond implements AggregationExpression { - - private final Object condition; - private final Object thenValue; - private final Object otherwiseValue; - - /** - * Creates a new {@link Cond} for a given {@link Field} and {@code then}/{@code otherwise} values. - * - * @param condition must not be {@literal null}. - * @param thenValue must not be {@literal null}. - * @param otherwiseValue must not be {@literal null}. - */ - private Cond(Field condition, Object thenValue, Object otherwiseValue) { - this((Object) condition, thenValue, otherwiseValue); - } - - /** - * Creates a new {@link Cond} for a given {@link CriteriaDefinition} and {@code then}/{@code otherwise} values. - * - * @param condition must not be {@literal null}. - * @param thenValue must not be {@literal null}. - * @param otherwiseValue must not be {@literal null}. - */ - private Cond(CriteriaDefinition condition, Object thenValue, Object otherwiseValue) { - this((Object) condition, thenValue, otherwiseValue); - } - - private Cond(Object condition, Object thenValue, Object otherwiseValue) { - - Assert.notNull(condition, "Condition must not be null!"); - Assert.notNull(thenValue, "Then value must not be null!"); - Assert.notNull(otherwiseValue, "Otherwise value must not be null!"); - - assertNotBuilder(condition, "Condition"); - assertNotBuilder(thenValue, "Then value"); - assertNotBuilder(otherwiseValue, "Otherwise value"); - - this.condition = condition; - this.thenValue = thenValue; - this.otherwiseValue = otherwiseValue; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @Override - public DBObject toDbObject(AggregationOperationContext context) { - - BasicDBObject condObject = new BasicDBObject(); - - condObject.append("if", resolveCriteria(context, condition)); - condObject.append("then", resolveValue(context, thenValue)); - condObject.append("else", resolveValue(context, otherwiseValue)); - - return new BasicDBObject("$cond", condObject); - } - - private Object resolveValue(AggregationOperationContext context, Object value) { - - if (value instanceof DBObject || value instanceof Field) { - return resolve(context, value); - } - - if (value instanceof AggregationExpression) { - return ((AggregationExpression) value).toDbObject(context); - } - - return context.getMappedObject(new BasicDBObject("$set", value)).get("$set"); - } - - private Object resolveCriteria(AggregationOperationContext context, Object value) { - - if (value instanceof DBObject || value instanceof Field) { - return resolve(context, value); - } - - if (value instanceof AggregationExpression) { - return ((AggregationExpression) value).toDbObject(context); - } - - if (value instanceof CriteriaDefinition) { - - DBObject mappedObject = context.getMappedObject(((CriteriaDefinition) value).getCriteriaObject()); - List clauses = new ArrayList(); - - clauses.addAll(getClauses(context, mappedObject)); - - return clauses.size() == 1 ? clauses.get(0) : clauses; - } - - throw new InvalidDataAccessApiUsageException( - String.format("Invalid value in condition. Supported: DBObject, Field references, Criteria, got: %s", value)); - } - - private List getClauses(AggregationOperationContext context, DBObject mappedObject) { - - List clauses = new ArrayList(); - - for (String key : mappedObject.keySet()) { - - Object predicate = mappedObject.get(key); - clauses.addAll(getClauses(context, key, predicate)); - } - - return clauses; - } - - private List getClauses(AggregationOperationContext context, String key, Object predicate) { - - List clauses = new ArrayList(); - - if (predicate instanceof List) { - - List args = new ArrayList(); - for (Object clause : (List) predicate) { - if (clause instanceof DBObject) { - args.addAll(getClauses(context, (DBObject) clause)); - } - } - - clauses.add(new BasicDBObject(key, args)); - - } else if (predicate instanceof DBObject) { - - DBObject nested = (DBObject) predicate; - - for (String s : nested.keySet()) { - - if (!isKeyword(s)) { - continue; - } - - List args = new ArrayList(); - args.add("$" + key); - args.add(nested.get(s)); - clauses.add(new BasicDBObject(s, args)); - } - - } else if (!isKeyword(key)) { - - List args = new ArrayList(); - args.add("$" + key); - args.add(predicate); - clauses.add(new BasicDBObject("$eq", args)); - } - - return clauses; - } - - /** - * Returns whether the given {@link String} is a MongoDB keyword. - * - * @param candidate - * @return - */ - private boolean isKeyword(String candidate) { - return candidate.startsWith("$"); - } - - private Object resolve(AggregationOperationContext context, Object value) { - - if (value instanceof DBObject) { - return context.getMappedObject((DBObject) value); - } - - return context.getReference((Field) value).toString(); - } - - private void assertNotBuilder(Object toCheck, String name) { - Assert.isTrue(!ClassUtils.isAssignableValue(ConditionalExpressionBuilder.class, toCheck), - String.format("%s must not be of type %s", name, ConditionalExpressionBuilder.class.getSimpleName())); - } - - /** - * Get a builder that allows fluent creation of {@link Cond}. - * - * @return never {@literal null}. - */ - public static WhenBuilder newBuilder() { - return ConditionalExpressionBuilder.newBuilder(); - } - - /** - * Start creating new {@link Cond} by providing the boolean expression used in {@code if}. - * - * @param booleanExpression must not be {@literal null}. - * @return never {@literal null}. - */ - public static ThenBuilder when(DBObject booleanExpression) { - return ConditionalExpressionBuilder.newBuilder().when(booleanExpression); - } - - /** - * Start creating new {@link Cond} by providing the {@link AggregationExpression} used in {@code if}. - * - * @param expression expression that yields in a boolean result, must not be {@literal null}. - * @return never {@literal null}. - */ - public static ThenBuilder when(AggregationExpression expression) { - return ConditionalExpressionBuilder.newBuilder().when(expression); - } - - /** - * Start creating new {@link Cond} by providing the field reference used in {@code if}. - * - * @param booleanField name of a field holding a boolean value, must not be {@literal null}. - * @return never {@literal null}. - */ - public static ThenBuilder when(String booleanField) { - return ConditionalExpressionBuilder.newBuilder().when(booleanField); - } - - /** - * Start creating new {@link Cond} by providing the {@link CriteriaDefinition} used in {@code if}. - * - * @param criteria criteria to evaluate, must not be {@literal null}. - * @return the {@link ThenBuilder} - */ - public static ThenBuilder when(CriteriaDefinition criteria) { - return ConditionalExpressionBuilder.newBuilder().when(criteria); - } - - /** - * @author Mark Paluch - */ - public static interface WhenBuilder { - - /** - * @param booleanExpression expression that yields in a boolean result, must not be {@literal null}. - * @return the {@link ThenBuilder} - */ - ThenBuilder when(DBObject booleanExpression); - - /** - * @param expression expression that yields in a boolean result, must not be {@literal null}. - * @return the {@link ThenBuilder} - */ - ThenBuilder when(AggregationExpression expression); - - /** - * @param booleanField name of a field holding a boolean value, must not be {@literal null}. - * @return the {@link ThenBuilder} - */ - ThenBuilder when(String booleanField); - - /** - * @param criteria criteria to evaluate, must not be {@literal null}. - * @return the {@link ThenBuilder} - */ - ThenBuilder when(CriteriaDefinition criteria); - } - - /** - * @author Mark Paluch - */ - public static interface ThenBuilder { - - /** - * @param value the value to be used if the condition evaluates {@literal true}. Can be a {@link DBObject}, a - * value that is supported by MongoDB or a value that can be converted to a MongoDB representation but - * must not be {@literal null}. - * @return the {@link OtherwiseBuilder} - */ - OtherwiseBuilder then(Object value); - - /** - * @param fieldReference must not be {@literal null}. - * @return the {@link OtherwiseBuilder} - */ - OtherwiseBuilder thenValueOf(String fieldReference); - - /** - * @param expression must not be {@literal null}. - * @return the {@link OtherwiseBuilder} - */ - OtherwiseBuilder thenValueOf(AggregationExpression expression); - } - - /** - * @author Mark Paluch - */ - public static interface OtherwiseBuilder { - - /** - * @param value the value to be used if the condition evaluates {@literal false}. Can be a {@link DBObject}, a - * value that is supported by MongoDB or a value that can be converted to a MongoDB representation but - * must not be {@literal null}. - * @return the {@link Cond} - */ - Cond otherwise(Object value); - - /** - * @param fieldReference must not be {@literal null}. - * @return the {@link Cond} - */ - Cond otherwiseValueOf(String fieldReference); - - /** - * @param expression must not be {@literal null}. - * @return the {@link Cond} - */ - Cond otherwiseValueOf(AggregationExpression expression); - } - - /** - * Builder for fluent {@link Cond} creation. - * - * @author Mark Paluch - */ - static class ConditionalExpressionBuilder implements WhenBuilder, ThenBuilder, OtherwiseBuilder { - - private Object condition; - private Object thenValue; - - private ConditionalExpressionBuilder() {} - - /** - * Creates a new builder for {@link Cond}. - * - * @return never {@literal null}. - */ - public static ConditionalExpressionBuilder newBuilder() { - return new ConditionalExpressionBuilder(); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(com.mongodb.DBObject) - */ - @Override - public ConditionalExpressionBuilder when(DBObject booleanExpression) { - - Assert.notNull(booleanExpression, "'Boolean expression' must not be null!"); - - this.condition = booleanExpression; - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(org.springframework.data.mongodb.core.query.CriteriaDefinition) - */ - @Override - public ThenBuilder when(CriteriaDefinition criteria) { - - Assert.notNull(criteria, "Criteria must not be null!"); - this.condition = criteria; - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(org.springframework.data.mongodb.core.aggregation.AggregationExpression) - */ - @Override - public ThenBuilder when(AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression field must not be null!"); - this.condition = expression; - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(java.lang.String) - */ - @Override - public ThenBuilder when(String booleanField) { - - Assert.hasText(booleanField, "Boolean field name must not be null or empty!"); - this.condition = Fields.field(booleanField); - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#then(java.lang.Object) - */ - @Override - public OtherwiseBuilder then(Object thenValue) { - - Assert.notNull(thenValue, "Then-value must not be null!"); - this.thenValue = thenValue; - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#thenValueOf(java.lang.String) - */ - @Override - public OtherwiseBuilder thenValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - this.thenValue = Fields.field(fieldReference); - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#thenValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression) - */ - @Override - public OtherwiseBuilder thenValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression must not be null!"); - this.thenValue = expression; - return this; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwise(java.lang.Object) - */ - @Override - public Cond otherwise(Object otherwiseValue) { - - Assert.notNull(otherwiseValue, "Value must not be null!"); - return new Cond(condition, thenValue, otherwiseValue); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwiseValueOf(java.lang.String) - */ - @Override - public Cond otherwiseValueOf(String fieldReference) { - - Assert.notNull(fieldReference, "FieldReference must not be null!"); - return new Cond(condition, thenValue, Fields.field(fieldReference)); - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwiseValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression) - */ - @Override - public Cond otherwiseValueOf(AggregationExpression expression) { - - Assert.notNull(expression, "AggregationExpression must not be null!"); - return new Cond(condition, thenValue, expression); - } - } - } - - /** - * {@link AggregationExpression} for {@code $let} that binds {@link AggregationExpression} to variables for use in the - * specified {@code in} expression, and returns the result of the expression. - * - * @author Christoph Strobl - * @since 1.10 - */ - class Let implements AggregationExpression { - - private final List vars; - private final AggregationExpression expression; - - private Let(List vars, AggregationExpression expression) { - - this.vars = vars; - this.expression = expression; - } - - /** - * Start creating new {@link Let} by defining the variables for {@code $vars}. - * - * @param variables must not be {@literal null}. - * @return - */ - public static LetBuilder define(final Collection variables) { - - Assert.notNull(variables, "Variables must not be null!"); - - return new LetBuilder() { - - @Override - public Let andApply(final AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Let(new ArrayList(variables), expression); - } - }; - } - - /** - * Start creating new {@link Let} by defining the variables for {@code $vars}. - * - * @param variables must not be {@literal null}. - * @return - */ - public static LetBuilder define(final ExpressionVariable... variables) { - - Assert.notNull(variables, "Variables must not be null!"); - - return new LetBuilder() { - - @Override - public Let andApply(final AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new Let(Arrays.asList(variables), expression); - } - }; - } - - public interface LetBuilder { - - /** - * Define the {@link AggregationExpression} to evaluate. - * - * @param expression must not be {@literal null}. - * @return - */ - Let andApply(AggregationExpression expression); - } - - /* (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 toLet(ExposedFields.synthetic(Fields.fields(getVariableNames())), context); - } - - private String[] getVariableNames() { - - String[] varNames = new String[this.vars.size()]; - for (int i = 0; i < this.vars.size(); i++) { - varNames[i] = this.vars.get(i).variableName; - } - - return varNames; - } - - private DBObject toLet(ExposedFields exposedFields, AggregationOperationContext context) { - - DBObject letExpression = new BasicDBObject(); - DBObject mappedVars = new BasicDBObject(); - InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext( - exposedFields, context); - - for (ExpressionVariable var : this.vars) { - mappedVars.putAll(getMappedVariable(var, context)); - } - - letExpression.put("vars", mappedVars); - letExpression.put("in", getMappedIn(operationContext)); - - return new BasicDBObject("$let", letExpression); - } - - private DBObject getMappedVariable(ExpressionVariable var, AggregationOperationContext context) { - - return new BasicDBObject(var.variableName, var.expression instanceof AggregationExpression - ? ((AggregationExpression) var.expression).toDbObject(context) : var.expression); - } - - private Object getMappedIn(AggregationOperationContext context) { - return expression.toDbObject(new NestedDelegatingExpressionAggregationOperationContext(context)); - } - - /** - * @author Christoph Strobl - */ - public static class ExpressionVariable { - - private final String variableName; - private final Object expression; - - /** - * Creates new {@link ExpressionVariable}. - * - * @param variableName can be {@literal null}. - * @param expression can be {@literal null}. - */ - private ExpressionVariable(String variableName, Object expression) { - - this.variableName = variableName; - this.expression = expression; - } - - /** - * Create a new {@link ExpressionVariable} with given name. - * - * @param variableName must not be {@literal null}. - * @return never {@literal null}. - */ - public static ExpressionVariable newVariable(String variableName) { - - Assert.notNull(variableName, "VariableName must not be null!"); - return new ExpressionVariable(variableName, null); - } - - /** - * Create a new {@link ExpressionVariable} with current name and given {@literal expression}. - * - * @param expression must not be {@literal null}. - * @return never {@literal null}. - */ - public ExpressionVariable forExpression(AggregationExpression expression) { - - Assert.notNull(expression, "Expression must not be null!"); - return new ExpressionVariable(variableName, expression); - } - - /** - * Create a new {@link ExpressionVariable} with current name and given {@literal expressionObject}. - * - * @param expressionObject must not be {@literal null}. - * @return never {@literal null}. - */ - public ExpressionVariable forExpression(DBObject expressionObject) { - - Assert.notNull(expressionObject, "Expression must not be null!"); - return new ExpressionVariable(variableName, expressionObject); - } - } - } - - /** - * {@link AggregationExpression} for {@code $switch}. - * - * @author Christoph Strobl - */ - class Switch extends AbstractAggregationExpression { - - private Switch(java.util.Map values) { - super(values); - } - - @Override - protected String getMongoMethod() { - return "$switch"; - } - - /** - * Creates new {@link Switch}. - * - * @param conditions must not be {@literal null}. - */ - public static Switch switchCases(CaseOperator... conditions) { - - Assert.notNull(conditions, "Conditions must not be null!"); - return switchCases(Arrays.asList(conditions)); - } - - /** - * Creates new {@link Switch}. - * - * @param conditions must not be {@literal null}. - */ - public static Switch switchCases(List conditions) { - - Assert.notNull(conditions, "Conditions must not be null!"); - return new Switch(Collections. singletonMap("branches", new ArrayList(conditions))); - } - - public Switch defaultTo(Object value) { - return new Switch(append("default", value)); - } - - /** - * Encapsulates the aggregation framework case document inside a {@code $switch}-operation. - */ - public static class CaseOperator implements AggregationExpression { - - private final AggregationExpression when; - private final Object then; - - private CaseOperator(AggregationExpression when, Object then) { - - this.when = when; - this.then = then; - } - - public static ThenBuilder when(final AggregationExpression condition) { - - Assert.notNull(condition, "Condition must not be null!"); - - return new ThenBuilder() { - - @Override - public CaseOperator then(Object value) { - - Assert.notNull(value, "Value must not be null!"); - return new CaseOperator(condition, value); - } - }; - } - - /* (non-Javadoc) - * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) - */ - @Override - public DBObject toDbObject(AggregationOperationContext context) { - - DBObject dbo = new BasicDBObject("case", when.toDbObject(context)); - - if (then instanceof AggregationExpression) { - dbo.put("then", ((AggregationExpression) then).toDbObject(context)); - } else if (then instanceof Field) { - dbo.put("then", context.getReference((Field) then).toString()); - } else { - dbo.put("then", then); - } - - return dbo; - } - - /** - * @author Christoph Strobl - */ - public interface ThenBuilder { - - /** - * Set the then {@literal value}. - * - * @param value must not be {@literal null}. - * @return - */ - CaseOperator then(Object value); - } - } - } - - /** - * {@link AggregationExpression} for {@code $type}. - * - * @author Christoph Strobl - */ - class Type extends AbstractAggregationExpression { - - private Type(Object value) { - super(value); - } - - @Override - protected String getMongoMethod() { - return "$type"; - } - - /** - * Creates new {@link Type}. - * - * @param field must not be {@literal null}. - * @return - */ - public static Type typeOf(String field) { - - Assert.notNull(field, "Field must not be null!"); - return new Type(Fields.field(field)); - } - } -} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java new file mode 100644 index 0000000000..12124f0a57 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java @@ -0,0 +1,1421 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.Collections; +import java.util.List; + +import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Avg; +import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Max; +import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Min; +import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.StdDevPop; +import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.StdDevSamp; +import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum; +import org.springframework.util.Assert; + +/** + * Gateway to {@literal Arithmetic} aggregation operations that perform math operations on numbers. + * + * @author Christoph Strobl + * @since 1.10 + */ +public class ArithmeticOperators { + + /** + * Take the field 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 value resulting from the given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static ArithmeticOperatorFactory valueOf(AggregationExpression expression) { + return new ArithmeticOperatorFactory(expression); + } + + /** + * @author Christoph Strobl + */ + 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 ? AccumulatorOperators.Sum.sumOf(fieldReference) + : AccumulatorOperators.Sum.sumOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that returns the average value of the numeric values. + * + * @return + */ + public Avg avg() { + return fieldReference != null ? AccumulatorOperators.Avg.avgOf(fieldReference) + : AccumulatorOperators.Avg.avgOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that returns the maximum value. + * + * @return + */ + public Max max() { + return fieldReference != null ? AccumulatorOperators.Max.maxOf(fieldReference) + : AccumulatorOperators.Max.maxOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that returns the minimum value. + * + * @return + */ + public Min min() { + return fieldReference != null ? AccumulatorOperators.Min.minOf(fieldReference) + : AccumulatorOperators.Min.minOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that calculates the population standard deviation of the input values. + * + * @return + */ + public StdDevPop stdDevPop() { + return fieldReference != null ? AccumulatorOperators.StdDevPop.stdDevPopOf(fieldReference) + : AccumulatorOperators.StdDevPop.stdDevPopOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that calculates the sample standard deviation of the input values. + * + * @return + */ + public StdDevSamp stdDevSamp() { + return fieldReference != null ? AccumulatorOperators.StdDevSamp.stdDevSampOf(fieldReference) + : AccumulatorOperators.StdDevSamp.stdDevSampOf(expression); + } + } + + /** + * {@link AggregationExpression} for {@code $abs}. + * + * @author Christoph Strobl + */ + public static class Abs extends AbstractAggregationExpression { + + private Abs(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$abs"; + } + + /** + * Creates new {@link Abs}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Abs absoluteValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Abs(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Abs}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Abs absoluteValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Abs(expression); + } + + /** + * Creates new {@link Abs}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Abs absoluteValueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + return new Abs(value); + } + } + + /** + * {@link AggregationExpression} for {@code $add}. + * + * @author Christoph Strobl + */ + public static class Add extends AbstractAggregationExpression { + + protected Add(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$add"; + } + + /** + * Creates new {@link Add}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Add valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Add(asFields(fieldReference)); + } + + /** + * Creates new {@link Add}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Add valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Add(Collections.singletonList(expression)); + } + + /** + * Creates new {@link Add}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Add valueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + 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 + */ + public static class Ceil extends AbstractAggregationExpression { + + private Ceil(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$ceil"; + } + + /** + * Creates new {@link Ceil}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Ceil ceilValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Ceil(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Ceil}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Ceil ceilValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Ceil(expression); + } + + /** + * Creates new {@link Ceil}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Ceil ceilValueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + return new Ceil(value); + } + } + + /** + * {@link AggregationExpression} for {@code $divide}. + * + * @author Christoph Strobl + */ + public static class Divide extends AbstractAggregationExpression { + + private Divide(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$divide"; + } + + /** + * Creates new {@link Divide}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Divide valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Divide(asFields(fieldReference)); + } + + /** + * Creates new {@link Divide}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Divide valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Divide(Collections.singletonList(expression)); + } + + /** + * Creates new {@link Divide}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Divide valueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + 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 + */ + public static class Exp extends AbstractAggregationExpression { + + private Exp(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$exp"; + } + + /** + * Creates new {@link Exp}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Exp expValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Exp(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Exp}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Exp expValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Exp(expression); + } + + /** + * Creates new {@link Exp}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Exp expValueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + return new Exp(value); + } + } + + /** + * {@link AggregationExpression} for {@code $floor}. + * + * @author Christoph Strobl + */ + public static class Floor extends AbstractAggregationExpression { + + private Floor(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$floor"; + } + + /** + * Creates new {@link Floor}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Floor floorValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Floor(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Floor}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Floor floorValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Floor(expression); + } + + /** + * Creates new {@link Floor}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Floor floorValueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + return new Floor(value); + } + } + + /** + * {@link AggregationExpression} for {@code $ln}. + * + * @author Christoph Strobl + */ + public static class Ln extends AbstractAggregationExpression { + + private Ln(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$ln"; + } + + /** + * Creates new {@link Ln}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Ln lnValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Ln(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Ln}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Ln lnValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Ln(expression); + } + + /** + * Creates new {@link Ln}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Ln lnValueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + return new Ln(value); + } + } + + /** + * {@link AggregationExpression} for {@code $log}. + * + * @author Christoph Strobl + */ + public static class Log extends AbstractAggregationExpression { + + private Log(List values) { + super(values); + } + + @Override + protected String getMongoMethod() { + return "$log"; + } + + /** + * Creates new {@link Min}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Log valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Log(asFields(fieldReference)); + } + + /** + * Creates new {@link Log}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Log valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Log(Collections.singletonList(expression)); + } + + /** + * Creates new {@link Log}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Log valueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + 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 + */ + public static class Log10 extends AbstractAggregationExpression { + + private Log10(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$log10"; + } + + /** + * Creates new {@link Log10}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Log10 log10ValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Log10(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Log10}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Log10 log10ValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Log10(expression); + } + + /** + * Creates new {@link Log10}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Log10 log10ValueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + return new Log10(value); + } + } + + /** + * {@link AggregationExpression} for {@code $mod}. + * + * @author Christoph Strobl + */ + public static class Mod extends AbstractAggregationExpression { + + private Mod(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$mod"; + } + + /** + * Creates new {@link Mod}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Mod valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Mod(asFields(fieldReference)); + } + + /** + * Creates new {@link Mod}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Mod valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Mod(Collections.singletonList(expression)); + } + + /** + * Creates new {@link Mod}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Mod valueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + 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 + */ + public static class Multiply extends AbstractAggregationExpression { + + private Multiply(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$multiply"; + } + + /** + * Creates new {@link Multiply}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Multiply valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Multiply(asFields(fieldReference)); + } + + /** + * Creates new {@link Multiply}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Multiply valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Multiply(Collections.singletonList(expression)); + } + + /** + * Creates new {@link Multiply}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Multiply valueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + 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 + */ + public static class Pow extends AbstractAggregationExpression { + + private Pow(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$pow"; + } + + /** + * Creates new {@link Pow}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Pow valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Pow(asFields(fieldReference)); + } + + /** + * Creates new {@link Pow}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Pow valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Pow(Collections.singletonList(expression)); + } + + /** + * Creates new {@link Pow}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Pow valueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + 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 + */ + public static class Sqrt extends AbstractAggregationExpression { + + private Sqrt(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$sqrt"; + } + + /** + * Creates new {@link Sqrt}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Sqrt sqrtOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Sqrt(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Sqrt}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Sqrt sqrtOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Sqrt(expression); + } + + /** + * Creates new {@link Sqrt}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Sqrt sqrtOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + return new Sqrt(value); + } + } + + /** + * {@link AggregationExpression} for {@code $subtract}. + * + * @author Christoph Strobl + */ + public static class Subtract extends AbstractAggregationExpression { + + private Subtract(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$subtract"; + } + + /** + * Creates new {@link Subtract}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Subtract valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Subtract(asFields(fieldReference)); + } + + /** + * Creates new {@link Subtract}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Subtract valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Subtract(Collections.singletonList(expression)); + } + + /** + * Creates new {@link Subtract}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Subtract valueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + 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 + */ + public static class Trunc extends AbstractAggregationExpression { + + private Trunc(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$trunc"; + } + + /** + * Creates new {@link Trunc}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Trunc truncValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Trunc(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Trunc}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Trunc truncValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Trunc(expression); + } + + /** + * Creates new {@link Trunc}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Trunc truncValueOf(Number value) { + + Assert.notNull(value, "Value must not be null!"); + return new Trunc(value); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java new file mode 100644 index 0000000000..b549269ac0 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java @@ -0,0 +1,1517 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.springframework.data.domain.Range; +import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.AsBuilder; +import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.PropertyExpression; +import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; +import org.springframework.util.Assert; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; + +/** + * Gateway to {@literal array} aggregation operations. + * + * @author Christoph Strobl + * @since 1.0 + */ +public 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 resulting from the given {@link AggregationExpression}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static ArrayOperatorFactory arrayOf(AggregationExpression expression) { + return new ArrayOperatorFactory(expression); + } + + /** + * @author Christoph Strobl + */ + public static class ArrayOperatorFactory { + + private final String fieldReference; + private final AggregationExpression expression; + + /** + * Creates new {@link ArrayOperatorFactory} for given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + */ + public ArrayOperatorFactory(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + this.fieldReference = fieldReference; + this.expression = null; + } + + /** + * Creates new {@link ArrayOperatorFactory} for given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + */ + public ArrayOperatorFactory(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + 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 ArrayElemAt 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 ArrayElemAt 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 ArrayElemAt elementAt(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return createArrayElemAt().elementAt(fieldReference); + } + + private ArrayElemAt createArrayElemAt() { + return usesFieldRef() ? ArrayElemAt.arrayOf(fieldReference) : ArrayElemAt.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); + } + + /** + * Creates new {@link AggregationExpressions} that searches the associated array for an occurrence of a specified + * value and returns the array index (zero-based) of the first occurrence. + * + * @param value must not be {@literal null}. + * @return + */ + public IndexOfArray indexOf(Object value) { + return usesFieldRef() ? IndexOfArray.arrayOf(fieldReference).indexOf(value) + : IndexOfArray.arrayOf(expression).indexOf(value); + } + + /** + * Creates new {@link AggregationExpressions} that returns an array with the elements in reverse order. + * + * @return + */ + public ReverseArray reverse() { + return usesFieldRef() ? ReverseArray.reverseArrayOf(fieldReference) : ReverseArray.reverseArrayOf(expression); + } + + /** + * Start creating new {@link AggregationExpressions} that applies an {@link AggregationExpression} to each element + * in an array and combines them into a single value. + * + * @param expression must not be {@literal null}. + * @return + */ + public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(final AggregationExpression expression) { + return new ArrayOperatorFactory.ReduceInitialValueBuilder() { + + @Override + public Reduce startingWith(Object initialValue) { + return (usesFieldRef() ? Reduce.arrayOf(fieldReference) : Reduce.arrayOf(expression)) + .withInitialValue(initialValue).reduce(expression); + } + }; + } + + /** + * Start creating new {@link AggregationExpressions} that applies an {@link AggregationExpression} to each element + * in an array and combines them into a single value. + * + * @param expressions + * @return + */ + public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(final PropertyExpression... expressions) { + + return new ArrayOperatorFactory.ReduceInitialValueBuilder() { + + @Override + public Reduce startingWith(Object initialValue) { + return (usesFieldRef() ? Reduce.arrayOf(fieldReference) : Reduce.arrayOf(expression)) + .withInitialValue(initialValue).reduce(expressions); + } + }; + } + + /** + * Creates new {@link AggregationExpressions} that transposes an array of input arrays so that the first element + * of the output array would be an array containing, the first element of the first input array, the first element + * of the second input array, etc. + * + * @param arrays must not be {@literal null}. + * @return + */ + public Zip zipWith(Object... arrays) { + return (usesFieldRef() ? Zip.arrayOf(fieldReference) : Zip.arrayOf(expression)).zip(arrays); + } + + /** + * Creates new {@link AggregationExpressions} that returns a boolean indicating whether a specified value is in + * the associated array. + * + * @param value must not be {@literal null}. + * @return + */ + public In containsValue(Object value) { + return (usesFieldRef() ? In.arrayOf(fieldReference) : In.arrayOf(expression)).containsValue(value); + } + + /** + * @author Christoph Strobl + */ + public interface ReduceInitialValueBuilder { + + /** + * Define the initial cumulative value set before in is applied to the first element of the input array. + * + * @param initialValue must not be {@literal null}. + * @return + */ + Reduce startingWith(Object initialValue); + } + + private boolean usesFieldRef() { + return fieldReference != null; + } + } + + /** + * {@link AggregationExpression} for {@code $arrayElementAt}. + * + * @author Christoph Strobl + */ + public static class ArrayElemAt extends AbstractAggregationExpression { + + private ArrayElemAt(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$arrayElemAt"; + } + + /** + * Creates new {@link ArrayElemAt}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static ArrayElemAt arrayOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new ArrayElemAt(asFields(fieldReference)); + } + + /** + * Creates new {@link ArrayElemAt}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static ArrayElemAt arrayOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new ArrayElemAt(Collections.singletonList(expression)); + } + + public ArrayElemAt elementAt(int index) { + return new ArrayElemAt(append(index)); + } + + public ArrayElemAt elementAt(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new ArrayElemAt(append(expression)); + } + + public ArrayElemAt elementAt(String arrayFieldReference) { + + Assert.notNull(arrayFieldReference, "ArrayReference must not be null!"); + return new ArrayElemAt(append(Fields.field(arrayFieldReference))); + } + } + + /** + * {@link AggregationExpression} for {@code $concatArrays}. + * + * @author Christoph Strobl + */ + public static class ConcatArrays extends AbstractAggregationExpression { + + private ConcatArrays(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$concatArrays"; + } + + /** + * Creates new {@link ConcatArrays}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static ConcatArrays arrayOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new ConcatArrays(asFields(fieldReference)); + } + + /** + * Creates new {@link ConcatArrays}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static 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(ExposedFields.from(as), context); + } + + private DBObject toFilter(ExposedFields exposedFields, AggregationOperationContext context) { + + DBObject filterExpression = new BasicDBObject(); + InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext( + exposedFields, context); + + filterExpression.putAll(context.getMappedObject(new BasicDBObject("input", getMappedInput(context)))); + filterExpression.put("as", as.getTarget()); + + filterExpression.putAll(context.getMappedObject(new BasicDBObject("cond", getMappedCondition(operationContext)))); + + 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 + */ + public static class IsArray extends AbstractAggregationExpression { + + private IsArray(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$isArray"; + } + + /** + * Creates new {@link IsArray}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static IsArray isArray(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new IsArray(Fields.field(fieldReference)); + } + + /** + * Creates new {@link IsArray}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class Size extends AbstractAggregationExpression { + + private Size(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$size"; + } + + /** + * Creates new {@link Size}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Size lengthOfArray(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Size(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Size}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class Slice extends AbstractAggregationExpression { + + private Slice(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$slice"; + } + + /** + * Creates new {@link Slice}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Slice sliceArrayOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Slice(asFields(fieldReference)); + } + + /** + * Creates new {@link Slice}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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); + } + }; + } + + /** + * @author Christoph Strobl + */ + public interface SliceElementsBuilder { + + /** + * Set the number of elements given {@literal nrElements}. + * + * @param nrElements + * @return + */ + Slice itemCount(int nrElements); + } + } + + /** + * {@link AggregationExpression} for {@code $indexOfArray}. + * + * @author Christoph Strobl + */ + public static class IndexOfArray extends AbstractAggregationExpression { + + private IndexOfArray(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$indexOfArray"; + } + + /** + * Start creating new {@link IndexOfArray}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static IndexOfArrayBuilder arrayOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new IndexOfArrayBuilder(Fields.field(fieldReference)); + } + + /** + * Start creating new {@link IndexOfArray}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static IndexOfArrayBuilder arrayOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new IndexOfArrayBuilder(expression); + } + + public IndexOfArray within(Range range) { + + Assert.notNull(range, "Range must not be null!"); + + List rangeValues = new ArrayList(2); + rangeValues.add(range.getLowerBound()); + if (range.getUpperBound() != null) { + rangeValues.add(range.getUpperBound()); + } + + return new IndexOfArray(append(rangeValues)); + } + + /** + * @author Christoph Strobl + */ + public static class IndexOfArrayBuilder { + + private final Object targetArray; + + private IndexOfArrayBuilder(Object targetArray) { + this.targetArray = targetArray; + } + + /** + * Set the {@literal value} to check for its index in the array. + * + * @param value must not be {@literal null}. + * @return + */ + public IndexOfArray indexOf(Object value) { + + Assert.notNull(value, "Value must not be null!"); + return new IndexOfArray(Arrays.asList(targetArray, value)); + } + } + } + + /** + * {@link AggregationExpression} for {@code $range}. + * + * @author Christoph Strobl + */ + public static class RangeOperator extends AbstractAggregationExpression { + + private RangeOperator(List values) { + super(values); + } + + @Override + protected String getMongoMethod() { + return "$range"; + } + + /** + * Start creating new {@link RangeOperator}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static RangeOperatorBuilder rangeStartingAt(String fieldReference) { + return new RangeOperatorBuilder(Fields.field(fieldReference)); + } + + /** + * Start creating new {@link RangeOperator}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static RangeOperatorBuilder rangeStartingAt(AggregationExpression expression) { + return new RangeOperatorBuilder(expression); + } + + /** + * Start creating new {@link RangeOperator}. + * + * @param value + * @return + */ + public static RangeOperatorBuilder rangeStartingAt(long value) { + return new RangeOperatorBuilder(value); + } + + public RangeOperator withStepSize(long stepSize) { + return new RangeOperator(append(stepSize)); + } + + public static class RangeOperatorBuilder { + + private final Object startPoint; + + private RangeOperatorBuilder(Object startPoint) { + this.startPoint = startPoint; + } + + /** + * Creates new {@link RangeOperator}. + * + * @param index + * @return + */ + public RangeOperator to(long index) { + return new RangeOperator(Arrays.asList(startPoint, index)); + } + + /** + * Creates new {@link RangeOperator}. + * + * @param expression must not be {@literal null}. + * @return + */ + public RangeOperator to(AggregationExpression expression) { + return new RangeOperator(Arrays.asList(startPoint, expression)); + } + + /** + * Creates new {@link RangeOperator}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public RangeOperator to(String fieldReference) { + return new RangeOperator(Arrays.asList(startPoint, Fields.field(fieldReference))); + } + } + } + + /** + * {@link AggregationExpression} for {@code $reverseArray}. + * + * @author Christoph Strobl + */ + public static class ReverseArray extends AbstractAggregationExpression { + + private ReverseArray(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$reverseArray"; + } + + /** + * Creates new {@link ReverseArray} given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static ReverseArray reverseArrayOf(String fieldReference) { + return new ReverseArray(Fields.field(fieldReference)); + } + + /** + * Creates new {@link ReverseArray} given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static ReverseArray reverseArrayOf(AggregationExpression expression) { + return new ReverseArray(expression); + } + } + + /** + * {@link AggregationExpression} for {@code $reduce}. + * + * @author Christoph Strobl + */ + public static class Reduce implements AggregationExpression { + + private final Object input; + private final Object initialValue; + private final List reduceExpressions; + + private Reduce(Object input, Object initialValue, List reduceExpressions) { + + this.input = input; + this.initialValue = initialValue; + this.reduceExpressions = reduceExpressions; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @Override + public DBObject toDbObject(AggregationOperationContext context) { + + DBObject dbo = new BasicDBObject(); + + dbo.put("input", getMappedValue(input, context)); + dbo.put("initialValue", getMappedValue(initialValue, context)); + + if (reduceExpressions.iterator().next() instanceof PropertyExpression) { + + DBObject properties = new BasicDBObject(); + for (AggregationExpression e : reduceExpressions) { + properties.putAll(e.toDbObject(context)); + } + dbo.put("in", properties); + } else { + dbo.put("in", (reduceExpressions.iterator().next()).toDbObject(context)); + } + + return new BasicDBObject("$reduce", dbo); + } + + private Object getMappedValue(Object value, AggregationOperationContext context) { + + if (value instanceof DBObject) { + return value; + } + if (value instanceof AggregationExpression) { + return ((AggregationExpression) value).toDbObject(context); + } else if (value instanceof Field) { + return context.getReference(((Field) value)).toString(); + } else { + return context.getMappedObject(new BasicDBObject("###val###", value)).get("###val###"); + } + } + + /** + * Start creating new {@link Reduce}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static InitialValueBuilder arrayOf(final String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null"); + + return new InitialValueBuilder() { + + @Override + public ReduceBuilder withInitialValue(final Object initialValue) { + + Assert.notNull(initialValue, "Initial value must not be null"); + + return new ReduceBuilder() { + + @Override + public Reduce reduce(AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression must not be null"); + return new Reduce(Fields.field(fieldReference), initialValue, Collections.singletonList(expression)); + } + + @Override + public Reduce reduce(PropertyExpression... expressions) { + + Assert.notNull(expressions, "PropertyExpressions must not be null"); + + return new Reduce(Fields.field(fieldReference), initialValue, + Arrays. asList(expressions)); + } + }; + } + }; + } + + /** + * Start creating new {@link Reduce}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static InitialValueBuilder arrayOf(final AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression must not be null"); + + return new InitialValueBuilder() { + + @Override + public ReduceBuilder withInitialValue(final Object initialValue) { + + Assert.notNull(initialValue, "Initial value must not be null"); + + return new ReduceBuilder() { + + @Override + public Reduce reduce(AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression must not be null"); + return new Reduce(expression, initialValue, Collections.singletonList(expression)); + } + + @Override + public Reduce reduce(PropertyExpression... expressions) { + + Assert.notNull(expressions, "PropertyExpressions must not be null"); + return new Reduce(expression, initialValue, Arrays. asList(expressions)); + } + }; + } + }; + } + + /** + * @author Christoph Strobl + */ + public interface InitialValueBuilder { + + /** + * Define the initial cumulative value set before in is applied to the first element of the input array. + * + * @param initialValue must not be {@literal null}. + * @return + */ + ReduceBuilder withInitialValue(Object initialValue); + } + + /** + * @author Christoph Strobl + */ + public interface ReduceBuilder { + + /** + * Define the {@link AggregationExpression} to apply to each element in the input array in left-to-right order. + *
+ * NOTE: During evaluation of the in expression the variable references {@link Variable#THIS} and + * {@link Variable#VALUE} are available. + * + * @param expression must not be {@literal null}. + * @return + */ + Reduce reduce(AggregationExpression expression); + + /** + * Define the {@link PropertyExpression}s to apply to each element in the input array in left-to-right order. + *
+ * NOTE: During evaluation of the in expression the variable references {@link Variable#THIS} and + * {@link Variable#VALUE} are available. + * + * @param expression must not be {@literal null}. + * @return + */ + Reduce reduce(PropertyExpression... expressions); + } + + /** + * @author Christoph Strobl + */ + public static class PropertyExpression implements AggregationExpression { + + private final String propertyName; + private final AggregationExpression aggregationExpression; + + protected PropertyExpression(String propertyName, AggregationExpression aggregationExpression) { + + Assert.notNull(propertyName, "Property name must not be null!"); + Assert.notNull(aggregationExpression, "AggregationExpression must not be null!"); + + this.propertyName = propertyName; + this.aggregationExpression = aggregationExpression; + } + + /** + * Define a result property for an {@link AggregationExpression} used in {@link Reduce}. + * + * @param name must not be {@literal null}. + * @return + */ + public static AsBuilder property(final String name) { + + return new AsBuilder() { + + @Override + public PropertyExpression definedAs(AggregationExpression expression) { + return new PropertyExpression(name, expression); + } + }; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @Override + public DBObject toDbObject(AggregationOperationContext context) { + return new BasicDBObject(propertyName, aggregationExpression.toDbObject(context)); + } + + /** + * @author Christoph Strobl + */ + public interface AsBuilder { + + /** + * Set the {@link AggregationExpression} resulting in the properties value. + * + * @param expression must not be {@literal null}. + * @return + */ + PropertyExpression definedAs(AggregationExpression expression); + } + } + + public enum Variable implements Field { + + THIS { + @Override + public String getName() { + return "$$this"; + } + + @Override + public String getTarget() { + return "$$this"; + } + + @Override + public boolean isAliased() { + return false; + } + + @Override + public String toString() { + return getName(); + } + }, + + VALUE { + @Override + public String getName() { + return "$$value"; + } + + @Override + public String getTarget() { + return "$$value"; + } + + @Override + public boolean isAliased() { + return false; + } + + @Override + public String toString() { + return getName(); + } + }; + + /** + * Create a {@link Field} reference to a given {@literal property} prefixed with the {@link Variable} identifier. + * eg. {@code $$value.product} + * + * @param property must not be {@literal null}. + * @return + */ + public Field referringTo(final String property) { + + return new Field() { + @Override + public String getName() { + return Variable.this.getName() + "." + property; + } + + @Override + public String getTarget() { + return Variable.this.getTarget() + "." + property; + } + + @Override + public boolean isAliased() { + return false; + } + + @Override + public String toString() { + return getName(); + } + }; + } + } + } + + /** + * {@link AggregationExpression} for {@code $zip}. + * + * @author Christoph Strobl + */ + public static class Zip extends AbstractAggregationExpression { + + protected Zip(java.util.Map value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$zip"; + } + + /** + * Start creating new {@link Zip}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static ZipBuilder arrayOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new ZipBuilder(Fields.field(fieldReference)); + } + + /** + * Start creating new {@link Zip}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static ZipBuilder arrayOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new ZipBuilder(expression); + } + + /** + * Create new {@link Zip} and set the {@code useLongestLength} property to {@literal true}. + * + * @return + */ + public Zip useLongestLength() { + return new Zip(append("useLongestLength", true)); + } + + /** + * Optionally provide a default value. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public Zip defaultTo(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Zip(append("defaults", Fields.field(fieldReference))); + } + + /** + * Optionally provide a default value. + * + * @param expression must not be {@literal null}. + * @return + */ + public Zip defaultTo(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Zip(append("defaults", expression)); + } + + /** + * Optionally provide a default value. + * + * @param array must not be {@literal null}. + * @return + */ + public Zip defaultTo(Object[] array) { + + Assert.notNull(array, "Array must not be null!"); + return new Zip(append("defaults", array)); + } + + public static class ZipBuilder { + + private final List sourceArrays; + + private ZipBuilder(Object sourceArray) { + + this.sourceArrays = new ArrayList(); + this.sourceArrays.add(sourceArray); + } + + /** + * Creates new {@link Zip} that transposes an array of input arrays so that the first element of the output array + * would be an array containing, the first element of the first input array, the first element of the second input + * array, etc. + * + * @param arrays arrays to zip the referenced one with. must not be {@literal null}. + * @return + */ + public Zip zip(Object... arrays) { + + Assert.notNull(arrays, "Arrays must not be null!"); + for (Object value : arrays) { + + if (value instanceof String) { + sourceArrays.add(Fields.field((String) value)); + } else { + sourceArrays.add(value); + } + } + + return new Zip(Collections. singletonMap("inputs", sourceArrays)); + } + } + } + + /** + * {@link AggregationExpression} for {@code $in}. + * + * @author Christoph Strobl + */ + public static class In extends AbstractAggregationExpression { + + private In(List values) { + super(values); + } + + @Override + protected String getMongoMethod() { + return "$in"; + } + + /** + * Start creating {@link In}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static InBuilder arrayOf(final String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + + return new InBuilder() { + + @Override + public In containsValue(Object value) { + + Assert.notNull(value, "Value must not be null!"); + return new In(Arrays.asList(value, Fields.field(fieldReference))); + } + }; + } + + /** + * Start creating {@link In}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static InBuilder arrayOf(final AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + + return new InBuilder() { + + @Override + public In containsValue(Object value) { + + Assert.notNull(value, "Value must not be null!"); + return new In(Arrays.asList(value, expression)); + } + }; + } + + /** + * @author Christoph Strobl + */ + public interface InBuilder { + + /** + * Set the {@literal value} to check for existence in the array. + * + * @param value must not be {@literal value}. + * @return + */ + In containsValue(Object value); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java new file mode 100644 index 0000000000..7e6ef68728 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/BooleanOperators.java @@ -0,0 +1,353 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.springframework.util.Assert; + +/** + * 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 + */ +public class BooleanOperators { + + /** + * Take the array referenced by given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static BooleanOperatorFactory valueOf(String fieldReference) { + return new BooleanOperatorFactory(fieldReference); + } + + /** + * Take the value resulting of the given {@link AggregationExpression}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static BooleanOperatorFactory valueOf(AggregationExpression fieldReference) { + return new BooleanOperatorFactory(fieldReference); + } + + /** + * Creates new {@link AggregationExpressions} 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) { + return Not.not(fieldReference); + } + + /** + * Creates new {@link AggregationExpressions} that evaluates the boolean value of {@link AggregationExpression} result + * and returns the opposite boolean value. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Not not(AggregationExpression expression) { + return Not.not(expression); + } + + /** + * @author Christoph Strobl + */ + public static class BooleanOperatorFactory { + + private final String fieldReference; + private final AggregationExpression expression; + + /** + * Creates new {@link BooleanOperatorFactory} for given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + */ + public BooleanOperatorFactory(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + this.fieldReference = fieldReference; + this.expression = null; + } + + /** + * Creates new {@link BooleanOperatorFactory} for given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + */ + public BooleanOperatorFactory(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + this.fieldReference = null; + this.expression = expression; + } + + /** + * 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) { + + Assert.notNull(expression, "Expression must not be null!"); + return createAnd().andExpression(expression); + } + + /** + * 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) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return createAnd().andField(fieldReference); + } + + private And createAnd() { + return usesFieldRef() ? And.and(Fields.field(fieldReference)) : And.and(expression); + } + + /** + * 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) { + + Assert.notNull(expression, "Expression must not be null!"); + return createOr().orExpression(expression); + } + + /** + * Creates new {@link AggregationExpressions} that evaluates one or more expressions and returns {@literal true} if + * any of the expressions are {@literal true}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + 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); + } + + /** + * Creates new {@link AggregationExpression} that evaluates a boolean and returns the opposite boolean value. + * + * @return + */ + public Not not() { + return usesFieldRef() ? Not.not(fieldReference) : Not.not(expression); + } + + private boolean usesFieldRef() { + return this.fieldReference != null; + } + } + + /** + * {@link AggregationExpression} for {@code $and}. + * + * @author Christoph Strobl + */ + public static class And extends AbstractAggregationExpression { + + private And(List values) { + super(values); + } + + @Override + protected 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 + */ + public static class Or extends AbstractAggregationExpression { + + private Or(List values) { + super(values); + } + + @Override + protected 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 + */ + public static class Not extends AbstractAggregationExpression { + + private Not(Object value) { + super(value); + } + + @Override + protected 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/ComparisonOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java new file mode 100644 index 0000000000..8196b506ab --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ComparisonOperators.java @@ -0,0 +1,879 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.Collections; +import java.util.List; + +import org.springframework.util.Assert; + +/** + * Gateway to {@literal comparison expressions}. + * + * @author Christoph Strobl + * @since 1.10 + */ +public class ComparisonOperators { + + /** + * Take the field 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 value resulting from the given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static ComparisonOperatorFactory valueOf(AggregationExpression expression) { + return new ComparisonOperatorFactory(expression); + } + + 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; + } + + /** + * Creates 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; + } + } + + /** + * {@link AggregationExpression} for {@code $cmp}. + * + * @author Christoph Strobl + */ + public static class Cmp extends AbstractAggregationExpression { + + private Cmp(List value) { + super(value); + } + + @Override + protected 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 + */ + public static class Eq extends AbstractAggregationExpression { + + private Eq(List value) { + super(value); + } + + @Override + protected 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 + */ + public static class Gt extends AbstractAggregationExpression { + + private Gt(List value) { + super(value); + } + + @Override + protected 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 + */ + public static class Lt extends AbstractAggregationExpression { + + private Lt(List value) { + super(value); + } + + @Override + protected 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 + */ + public static class Gte extends AbstractAggregationExpression { + + private Gte(List value) { + super(value); + } + + @Override + protected 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 + */ + public static class Lte extends AbstractAggregationExpression { + + private Lte(List value) { + super(value); + } + + @Override + protected 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 + */ + public static class Ne extends AbstractAggregationExpression { + + private Ne(List value) { + super(value); + } + + @Override + protected 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)); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java new file mode 100644 index 0000000000..31d412f5a9 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperators.java @@ -0,0 +1,975 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.springframework.dao.InvalidDataAccessApiUsageException; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.OtherwiseBuilder; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.ThenBuilder; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator; +import org.springframework.data.mongodb.core.query.CriteriaDefinition; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; + +/** + * Gateway to {@literal conditional expressions} that evaluate their argument expressions as booleans to a value. + * + * @author Mark Paluch + */ +public class ConditionalOperators { + + /** + * Take the field referenced by given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static ConditionalOperatorFactory when(String fieldReference) { + return new ConditionalOperatorFactory(fieldReference); + } + + /** + * Take the value resulting from the given {@literal expression}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static ConditionalOperatorFactory when(AggregationExpression expression) { + return new ConditionalOperatorFactory(expression); + } + + /** + * Take the value resulting from the given {@literal criteriaDefinition}. + * + * @param criteriaDefinition must not be {@literal null}. + * @return + */ + public static ConditionalOperatorFactory when(CriteriaDefinition criteriaDefinition) { + return new ConditionalOperatorFactory(criteriaDefinition); + } + + /** + * Creates new {@link AggregationExpressions} that evaluates an expression and returns the value of the expression if + * the expression evaluates to a non-null value. If the expression evaluates to a {@literal null} value, including + * instances of undefined values or missing fields, returns the value of the replacement expression. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static IfNull.ThenBuilder ifNull(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return IfNull.ifNull(fieldReference); + } + + /** + * Creates new {@link AggregationExpressions} that evaluates an expression and returns the value of the expression if + * the expression evaluates to a non-null value. If the expression evaluates to a {@literal null} value, including + * instances of undefined values or missing fields, returns the value of the replacement expression. + * + * @param expression must not be {@literal null}. + * @return + */ + public static IfNull.ThenBuilder ifNull(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return IfNull.ifNull(expression); + } + + /** + * Creates new {@link AggregationExpression} that evaluates a series of {@link CaseOperator} expressions. When it + * finds an expression which evaluates to {@literal true}, {@code $switch} executes a specified expression and breaks + * out of the control flow. + * + * @param conditions must not be {@literal null}. + * @return + */ + public static Switch switchCases(CaseOperator... conditions) { + return Switch.switchCases(conditions); + } + + /** + * Creates new {@link AggregationExpression} that evaluates a series of {@link CaseOperator} expressions. When it + * finds an expression which evaluates to {@literal true}, {@code $switch} executes a specified expression and breaks + * out of the control flow. + * + * @param conditions must not be {@literal null}. + * @return + */ + public static Switch switchCases(List conditions) { + return Switch.switchCases(conditions); + } + + public static class ConditionalOperatorFactory { + + private final String fieldReference; + private final AggregationExpression expression; + private final CriteriaDefinition criteriaDefinition; + + /** + * Creates new {@link ConditionalOperatorFactory} for given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + */ + public ConditionalOperatorFactory(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + + this.fieldReference = fieldReference; + this.expression = null; + this.criteriaDefinition = null; + } + + /** + * Creates new {@link ConditionalOperatorFactory} for given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + */ + public ConditionalOperatorFactory(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + + this.fieldReference = null; + this.expression = expression; + this.criteriaDefinition = null; + } + + /** + * Creates new {@link ConditionalOperatorFactory} for given {@link CriteriaDefinition}. + * + * @param criteriaDefinition must not be {@literal null}. + */ + public ConditionalOperatorFactory(CriteriaDefinition criteriaDefinition) { + + Assert.notNull(criteriaDefinition, "CriteriaDefinition must not be null!"); + + this.fieldReference = null; + this.expression = null; + this.criteriaDefinition = criteriaDefinition; + } + + /** + * Creates new {@link AggregationExpression} that evaluates a boolean expression to return one of the two specified + * return expressions. + * + * @param value must not be {@literal null}. + * @return + */ + public OtherwiseBuilder then(Object value) { + + Assert.notNull(value, "Value must not be null!"); + return createThenBuilder().then(value); + } + + /** + * Creates new {@link AggregationExpressions} that evaluates a boolean expression to return one of the two specified + * return expressions. + * + * @param expression must not be {@literal null}. + * @return + */ + public OtherwiseBuilder thenValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return createThenBuilder().then(expression); + } + + /** + * Creates new {@link AggregationExpressions} that evaluates a boolean expression to return one of the two specified + * return expressions. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public OtherwiseBuilder thenValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return createThenBuilder().then(fieldReference); + } + + private ThenBuilder createThenBuilder() { + + if (usesFieldRef()) { + return Cond.newBuilder().when(fieldReference); + } + + return usesCriteriaDefinition() ? Cond.newBuilder().when(criteriaDefinition) : Cond.newBuilder().when(expression); + } + + private boolean usesFieldRef() { + return this.fieldReference != null; + } + + private boolean usesCriteriaDefinition() { + return this.criteriaDefinition != null; + } + } + + /** + * Encapsulates the aggregation framework {@code $ifNull} operator. Replacement values can be either {@link Field + * field references}, {@link AggregationExpression expressions}, values of simple MongoDB types or values that can be + * converted to a simple MongoDB type. + * + * @see http://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/ + * @author Mark Paluch + */ + public static class IfNull implements AggregationExpression { + + private final Object condition; + private final Object value; + + private IfNull(Object condition, Object value) { + + this.condition = condition; + this.value = value; + } + + /** + * Creates new {@link IfNull}. + * + * @param fieldReference the field to check for a {@literal null} value, field reference must not be {@literal null} + * . + * @return + */ + public static ThenBuilder ifNull(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new IfNullOperatorBuilder().ifNull(fieldReference); + } + + /** + * Creates new {@link IfNull}. + * + * @param expression the expression to check for a {@literal null} value, field reference must not be + * {@literal null}. + * @return + */ + public static ThenBuilder ifNull(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new IfNullOperatorBuilder().ifNull(expression); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @Override + public DBObject toDbObject(AggregationOperationContext context) { + + List list = new ArrayList(); + + if (condition instanceof Field) { + list.add(context.getReference((Field) condition).toString()); + } else if (condition instanceof AggregationExpression) { + list.add(((AggregationExpression) condition).toDbObject(context)); + } else { + list.add(condition); + } + + list.add(resolve(value, context)); + + return new BasicDBObject("$ifNull", list); + } + + private Object resolve(Object value, AggregationOperationContext context) { + + if (value instanceof Field) { + return context.getReference((Field) value).toString(); + } else if (value instanceof AggregationExpression) { + return ((AggregationExpression) value).toDbObject(context); + } else if (value instanceof DBObject) { + return value; + } + + return context.getMappedObject(new BasicDBObject("$set", value)).get("$set"); + } + + /** + * @author Mark Paluch + */ + public static interface IfNullBuilder { + + /** + * @param fieldReference the field to check for a {@literal null} value, field reference must not be + * {@literal null}. + * @return the {@link ThenBuilder} + */ + ThenBuilder ifNull(String fieldReference); + + /** + * @param expression the expression to check for a {@literal null} value, field name must not be {@literal null} + * or empty. + * @return the {@link ThenBuilder} + */ + ThenBuilder ifNull(AggregationExpression expression); + } + + /** + * @author Mark Paluch + */ + public static interface ThenBuilder { + + /** + * @param value the value to be used if the {@code $ifNull} condition evaluates {@literal true}. Can be a + * {@link DBObject}, a value that is supported by MongoDB or a value that can be converted to a MongoDB + * representation but must not be {@literal null}. + * @return + */ + IfNull then(Object value); + + /** + * @param fieldReference the field holding the replacement value, must not be {@literal null}. + * @return + */ + IfNull thenValueOf(String fieldReference); + + /** + * @param expression the expression yielding to the replacement value, must not be {@literal null}. + * @return + */ + public IfNull thenValueOf(AggregationExpression expression); + } + + /** + * Builder for fluent {@link IfNullOperator} creation. + * + * @author Mark Paluch + */ + static final class IfNullOperatorBuilder implements IfNullBuilder, ThenBuilder { + + private Object condition; + + private IfNullOperatorBuilder() {} + + /** + * Creates a new builder for {@link IfNullOperator}. + * + * @return never {@literal null}. + */ + public static IfNullOperatorBuilder newBuilder() { + return new IfNullOperatorBuilder(); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.IfNullBuilder#ifNull(java.lang.String) + */ + public ThenBuilder ifNull(String fieldReference) { + + Assert.hasText(fieldReference, "FieldReference name must not be null or empty!"); + this.condition = Fields.field(fieldReference); + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.IfNullBuilder#ifNull(org.springframework.data.mongodb.core.aggregation.AggregationExpression) + */ + @Override + public ThenBuilder ifNull(AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression name must not be null or empty!"); + this.condition = expression; + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#then(java.lang.Object) + */ + public IfNull then(Object value) { + return new IfNull(condition, value); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#thenValueOf(java.lang.String) + */ + public IfNull thenValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new IfNull(condition, Fields.field(fieldReference)); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull.ThenBuilder#thenValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression) + */ + public IfNull thenValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new IfNull(condition, expression); + } + } + } + + /** + * {@link AggregationExpression} for {@code $switch}. + * + * @author Christoph Strobl + */ + public static class Switch extends AbstractAggregationExpression { + + private Switch(java.util.Map values) { + super(values); + } + + @Override + protected String getMongoMethod() { + return "$switch"; + } + + /** + * Creates new {@link Switch}. + * + * @param conditions must not be {@literal null}. + */ + public static Switch switchCases(CaseOperator... conditions) { + + Assert.notNull(conditions, "Conditions must not be null!"); + return switchCases(Arrays.asList(conditions)); + } + + /** + * Creates new {@link Switch}. + * + * @param conditions must not be {@literal null}. + */ + public static Switch switchCases(List conditions) { + + Assert.notNull(conditions, "Conditions must not be null!"); + return new Switch(Collections. singletonMap("branches", new ArrayList(conditions))); + } + + public Switch defaultTo(Object value) { + return new Switch(append("default", value)); + } + + /** + * Encapsulates the aggregation framework case document inside a {@code $switch}-operation. + */ + public static class CaseOperator implements AggregationExpression { + + private final AggregationExpression when; + private final Object then; + + private CaseOperator(AggregationExpression when, Object then) { + + this.when = when; + this.then = then; + } + + public static ThenBuilder when(final AggregationExpression condition) { + + Assert.notNull(condition, "Condition must not be null!"); + + return new ThenBuilder() { + + @Override + public CaseOperator then(Object value) { + + Assert.notNull(value, "Value must not be null!"); + return new CaseOperator(condition, value); + } + }; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @Override + public DBObject toDbObject(AggregationOperationContext context) { + + DBObject dbo = new BasicDBObject("case", when.toDbObject(context)); + + if (then instanceof AggregationExpression) { + dbo.put("then", ((AggregationExpression) then).toDbObject(context)); + } else if (then instanceof Field) { + dbo.put("then", context.getReference((Field) then).toString()); + } else { + dbo.put("then", then); + } + + return dbo; + } + + /** + * @author Christoph Strobl + */ + public interface ThenBuilder { + + /** + * Set the then {@literal value}. + * + * @param value must not be {@literal null}. + * @return + */ + CaseOperator then(Object value); + } + } + } + + /** + * Encapsulates the aggregation framework {@code $cond} operator. A {@link Cond} allows nested conditions + * {@code if-then[if-then-else]-else} using {@link Field}, {@link CriteriaDefinition}, {@link AggregationExpression} + * or a {@link DBObject custom} condition. Replacement values can be either {@link Field field references}, + * {@link AggregationExpression expressions}, values of simple MongoDB types or values that can be converted to a + * simple MongoDB type. + * + * @see http://docs.mongodb.com/manual/reference/operator/aggregation/cond/ + * @author Mark Paluch + * @author Christoph Strobl + */ + public static class Cond implements AggregationExpression { + + private final Object condition; + private final Object thenValue; + private final Object otherwiseValue; + + /** + * Creates a new {@link Cond} for a given {@link Field} and {@code then}/{@code otherwise} values. + * + * @param condition must not be {@literal null}. + * @param thenValue must not be {@literal null}. + * @param otherwiseValue must not be {@literal null}. + */ + private Cond(Field condition, Object thenValue, Object otherwiseValue) { + this((Object) condition, thenValue, otherwiseValue); + } + + /** + * Creates a new {@link Cond} for a given {@link CriteriaDefinition} and {@code then}/{@code otherwise} values. + * + * @param condition must not be {@literal null}. + * @param thenValue must not be {@literal null}. + * @param otherwiseValue must not be {@literal null}. + */ + private Cond(CriteriaDefinition condition, Object thenValue, Object otherwiseValue) { + this((Object) condition, thenValue, otherwiseValue); + } + + private Cond(Object condition, Object thenValue, Object otherwiseValue) { + + Assert.notNull(condition, "Condition must not be null!"); + Assert.notNull(thenValue, "Then value must not be null!"); + Assert.notNull(otherwiseValue, "Otherwise value must not be null!"); + + assertNotBuilder(condition, "Condition"); + assertNotBuilder(thenValue, "Then value"); + assertNotBuilder(otherwiseValue, "Otherwise value"); + + this.condition = condition; + this.thenValue = thenValue; + this.otherwiseValue = otherwiseValue; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext) + */ + @Override + public DBObject toDbObject(AggregationOperationContext context) { + + BasicDBObject condObject = new BasicDBObject(); + + condObject.append("if", resolveCriteria(context, condition)); + condObject.append("then", resolveValue(context, thenValue)); + condObject.append("else", resolveValue(context, otherwiseValue)); + + return new BasicDBObject("$cond", condObject); + } + + private Object resolveValue(AggregationOperationContext context, Object value) { + + if (value instanceof DBObject || value instanceof Field) { + return resolve(context, value); + } + + if (value instanceof AggregationExpression) { + return ((AggregationExpression) value).toDbObject(context); + } + + return context.getMappedObject(new BasicDBObject("$set", value)).get("$set"); + } + + private Object resolveCriteria(AggregationOperationContext context, Object value) { + + if (value instanceof DBObject || value instanceof Field) { + return resolve(context, value); + } + + if (value instanceof AggregationExpression) { + return ((AggregationExpression) value).toDbObject(context); + } + + if (value instanceof CriteriaDefinition) { + + DBObject mappedObject = context.getMappedObject(((CriteriaDefinition) value).getCriteriaObject()); + List clauses = new ArrayList(); + + clauses.addAll(getClauses(context, mappedObject)); + + return clauses.size() == 1 ? clauses.get(0) : clauses; + } + + throw new InvalidDataAccessApiUsageException( + String.format("Invalid value in condition. Supported: DBObject, Field references, Criteria, got: %s", value)); + } + + private List getClauses(AggregationOperationContext context, DBObject mappedObject) { + + List clauses = new ArrayList(); + + for (String key : mappedObject.keySet()) { + + Object predicate = mappedObject.get(key); + clauses.addAll(getClauses(context, key, predicate)); + } + + return clauses; + } + + private List getClauses(AggregationOperationContext context, String key, Object predicate) { + + List clauses = new ArrayList(); + + if (predicate instanceof List) { + + List args = new ArrayList(); + for (Object clause : (List) predicate) { + if (clause instanceof DBObject) { + args.addAll(getClauses(context, (DBObject) clause)); + } + } + + clauses.add(new BasicDBObject(key, args)); + + } else if (predicate instanceof DBObject) { + + DBObject nested = (DBObject) predicate; + + for (String s : nested.keySet()) { + + if (!isKeyword(s)) { + continue; + } + + List args = new ArrayList(); + args.add("$" + key); + args.add(nested.get(s)); + clauses.add(new BasicDBObject(s, args)); + } + + } else if (!isKeyword(key)) { + + List args = new ArrayList(); + args.add("$" + key); + args.add(predicate); + clauses.add(new BasicDBObject("$eq", args)); + } + + return clauses; + } + + /** + * Returns whether the given {@link String} is a MongoDB keyword. + * + * @param candidate + * @return + */ + private boolean isKeyword(String candidate) { + return candidate.startsWith("$"); + } + + private Object resolve(AggregationOperationContext context, Object value) { + + if (value instanceof DBObject) { + return context.getMappedObject((DBObject) value); + } + + return context.getReference((Field) value).toString(); + } + + private void assertNotBuilder(Object toCheck, String name) { + Assert.isTrue(!ClassUtils.isAssignableValue(ConditionalExpressionBuilder.class, toCheck), + String.format("%s must not be of type %s", name, ConditionalExpressionBuilder.class.getSimpleName())); + } + + /** + * Get a builder that allows fluent creation of {@link Cond}. + * + * @return never {@literal null}. + */ + public static WhenBuilder newBuilder() { + return ConditionalExpressionBuilder.newBuilder(); + } + + /** + * Start creating new {@link Cond} by providing the boolean expression used in {@code if}. + * + * @param booleanExpression must not be {@literal null}. + * @return never {@literal null}. + */ + public static ThenBuilder when(DBObject booleanExpression) { + return ConditionalExpressionBuilder.newBuilder().when(booleanExpression); + } + + /** + * Start creating new {@link Cond} by providing the {@link AggregationExpression} used in {@code if}. + * + * @param expression expression that yields in a boolean result, must not be {@literal null}. + * @return never {@literal null}. + */ + public static ThenBuilder when(AggregationExpression expression) { + return ConditionalExpressionBuilder.newBuilder().when(expression); + } + + /** + * Start creating new {@link Cond} by providing the field reference used in {@code if}. + * + * @param booleanField name of a field holding a boolean value, must not be {@literal null}. + * @return never {@literal null}. + */ + public static ThenBuilder when(String booleanField) { + return ConditionalExpressionBuilder.newBuilder().when(booleanField); + } + + /** + * Start creating new {@link Cond} by providing the {@link CriteriaDefinition} used in {@code if}. + * + * @param criteria criteria to evaluate, must not be {@literal null}. + * @return the {@link ThenBuilder} + */ + public static ThenBuilder when(CriteriaDefinition criteria) { + return ConditionalExpressionBuilder.newBuilder().when(criteria); + } + + /** + * @author Mark Paluch + */ + public static interface WhenBuilder { + + /** + * @param booleanExpression expression that yields in a boolean result, must not be {@literal null}. + * @return the {@link ThenBuilder} + */ + ThenBuilder when(DBObject booleanExpression); + + /** + * @param expression expression that yields in a boolean result, must not be {@literal null}. + * @return the {@link ThenBuilder} + */ + ThenBuilder when(AggregationExpression expression); + + /** + * @param booleanField name of a field holding a boolean value, must not be {@literal null}. + * @return the {@link ThenBuilder} + */ + ThenBuilder when(String booleanField); + + /** + * @param criteria criteria to evaluate, must not be {@literal null}. + * @return the {@link ThenBuilder} + */ + ThenBuilder when(CriteriaDefinition criteria); + } + + /** + * @author Mark Paluch + */ + public static interface ThenBuilder { + + /** + * @param value the value to be used if the condition evaluates {@literal true}. Can be a {@link DBObject}, a + * value that is supported by MongoDB or a value that can be converted to a MongoDB representation but + * must not be {@literal null}. + * @return the {@link OtherwiseBuilder} + */ + OtherwiseBuilder then(Object value); + + /** + * @param fieldReference must not be {@literal null}. + * @return the {@link OtherwiseBuilder} + */ + OtherwiseBuilder thenValueOf(String fieldReference); + + /** + * @param expression must not be {@literal null}. + * @return the {@link OtherwiseBuilder} + */ + OtherwiseBuilder thenValueOf(AggregationExpression expression); + } + + /** + * @author Mark Paluch + */ + public static interface OtherwiseBuilder { + + /** + * @param value the value to be used if the condition evaluates {@literal false}. Can be a {@link DBObject}, a + * value that is supported by MongoDB or a value that can be converted to a MongoDB representation but + * must not be {@literal null}. + * @return the {@link Cond} + */ + Cond otherwise(Object value); + + /** + * @param fieldReference must not be {@literal null}. + * @return the {@link Cond} + */ + Cond otherwiseValueOf(String fieldReference); + + /** + * @param expression must not be {@literal null}. + * @return the {@link Cond} + */ + Cond otherwiseValueOf(AggregationExpression expression); + } + + /** + * Builder for fluent {@link Cond} creation. + * + * @author Mark Paluch + */ + static class ConditionalExpressionBuilder implements WhenBuilder, ThenBuilder, OtherwiseBuilder { + + private Object condition; + private Object thenValue; + + private ConditionalExpressionBuilder() {} + + /** + * Creates a new builder for {@link Cond}. + * + * @return never {@literal null}. + */ + public static ConditionalExpressionBuilder newBuilder() { + return new ConditionalExpressionBuilder(); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(com.mongodb.DBObject) + */ + @Override + public ConditionalExpressionBuilder when(DBObject booleanExpression) { + + Assert.notNull(booleanExpression, "'Boolean expression' must not be null!"); + + this.condition = booleanExpression; + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(org.springframework.data.mongodb.core.query.CriteriaDefinition) + */ + @Override + public ThenBuilder when(CriteriaDefinition criteria) { + + Assert.notNull(criteria, "Criteria must not be null!"); + this.condition = criteria; + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(org.springframework.data.mongodb.core.aggregation.AggregationExpression) + */ + @Override + public ThenBuilder when(AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression field must not be null!"); + this.condition = expression; + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.WhenBuilder#when(java.lang.String) + */ + @Override + public ThenBuilder when(String booleanField) { + + Assert.hasText(booleanField, "Boolean field name must not be null or empty!"); + this.condition = Fields.field(booleanField); + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#then(java.lang.Object) + */ + @Override + public OtherwiseBuilder then(Object thenValue) { + + Assert.notNull(thenValue, "Then-value must not be null!"); + this.thenValue = thenValue; + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#thenValueOf(java.lang.String) + */ + @Override + public OtherwiseBuilder thenValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + this.thenValue = Fields.field(fieldReference); + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.ThenBuilder#thenValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression) + */ + @Override + public OtherwiseBuilder thenValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression must not be null!"); + this.thenValue = expression; + return this; + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwise(java.lang.Object) + */ + @Override + public Cond otherwise(Object otherwiseValue) { + + Assert.notNull(otherwiseValue, "Value must not be null!"); + return new Cond(condition, thenValue, otherwiseValue); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwiseValueOf(java.lang.String) + */ + @Override + public Cond otherwiseValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Cond(condition, thenValue, Fields.field(fieldReference)); + } + + /* (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.OtherwiseBuilder#otherwiseValueOf(org.springframework.data.mongodb.core.aggregation.AggregationExpression) + */ + @Override + public Cond otherwiseValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression must not be null!"); + return new Cond(condition, thenValue, expression); + } + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DataTypeOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DataTypeOperators.java new file mode 100644 index 0000000000..9a83753a17 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DataTypeOperators.java @@ -0,0 +1,67 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import org.springframework.util.Assert; + +/** + * Gateway to {@literal data type} expressions. + * + * @author Christoph Strobl + * @since 1.10 + * @soundtrack Clawfinger - Catch Me + */ +public class DataTypeOperators { + + /** + * Return the BSON data type of the given {@literal field}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Type typeOf(String fieldReference) { + return Type.typeOf(fieldReference); + } + + /** + * {@link AggregationExpression} for {@code $type}. + * + * @author Christoph Strobl + */ + public static class Type extends AbstractAggregationExpression { + + private Type(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$type"; + } + + /** + * Creates new {@link Type}. + * + * @param field must not be {@literal null}. + * @return + */ + public static Type typeOf(String field) { + + Assert.notNull(field, "Field must not be null!"); + return new Type(Fields.field(field)); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java new file mode 100644 index 0000000000..88fca85f46 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java @@ -0,0 +1,838 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.LinkedHashMap; + +import org.springframework.data.mongodb.core.aggregation.ArithmeticOperators.ArithmeticOperatorFactory; +import org.springframework.util.Assert; + +/** + * Gateway to {@literal Date} aggregation operations. + * + * @author Christoph Strobl + * @since 1.10 + */ +public 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 {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static DateOperatorFactory dateOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new DateOperatorFactory(expression); + } + + /** + * @author Christoph Strobl + */ + public static class DateOperatorFactory { + + private final String fieldReference; + private final AggregationExpression expression; + + /** + * Creates new {@link ArithmeticOperatorFactory} for given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + */ + public DateOperatorFactory(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 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); + } + + /** + * Creates new {@link AggregationExpressions} that returns the weekday number in ISO 8601 format, ranging from 1 + * (for Monday) to 7 (for Sunday). + * + * @return + */ + public IsoDayOfWeek isoDayOfWeek() { + return usesFieldRef() ? IsoDayOfWeek.isoDayOfWeek(fieldReference) : IsoDayOfWeek.isoDayOfWeek(expression); + } + + /** + * Creates new {@link AggregationExpressions} that returns the week number in ISO 8601 format, ranging from 1 to 53. + * + * @return + */ + public IsoWeek isoWeek() { + return usesFieldRef() ? IsoWeek.isoWeekOf(fieldReference) : IsoWeek.isoWeekOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that returns the year number in ISO 8601 format. + * + * @return + */ + public IsoWeekYear isoWeekYear() { + return usesFieldRef() ? IsoWeekYear.isoWeekYearOf(fieldReference) : IsoWeekYear.isoWeekYearOf(expression); + } + + private boolean usesFieldRef() { + return fieldReference != null; + } + } + + /** + * {@link AggregationExpression} for {@code $dayOfYear}. + * + * @author Christoph Strobl + */ + public static class DayOfYear extends AbstractAggregationExpression { + + private DayOfYear(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$dayOfYear"; + } + + /** + * Creates new {@link DayOfYear}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static DayOfYear dayOfYear(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new DayOfYear(Fields.field(fieldReference)); + } + + /** + * Creates new {@link DayOfYear}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class DayOfMonth extends AbstractAggregationExpression { + + private DayOfMonth(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$dayOfMonth"; + } + + /** + * Creates new {@link DayOfMonth}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static DayOfMonth dayOfMonth(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new DayOfMonth(Fields.field(fieldReference)); + } + + /** + * Creates new {@link DayOfMonth}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class DayOfWeek extends AbstractAggregationExpression { + + private DayOfWeek(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$dayOfWeek"; + } + + /** + * Creates new {@link DayOfWeek}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static DayOfWeek dayOfWeek(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new DayOfWeek(Fields.field(fieldReference)); + } + + /** + * Creates new {@link DayOfWeek}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class Year extends AbstractAggregationExpression { + + private Year(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$year"; + } + + /** + * Creates new {@link Year}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Year yearOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Year(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Year}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class Month extends AbstractAggregationExpression { + + private Month(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$month"; + } + + /** + * Creates new {@link Month}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Month monthOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Month(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Month}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class Week extends AbstractAggregationExpression { + + private Week(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$week"; + } + + /** + * Creates new {@link Week}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Week weekOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Week(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Week}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class Hour extends AbstractAggregationExpression { + + private Hour(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$hour"; + } + + /** + * Creates new {@link Hour}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Hour hourOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Hour(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Hour}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class Minute extends AbstractAggregationExpression { + + private Minute(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$minute"; + } + + /** + * Creates new {@link Minute}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Minute minuteOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Minute(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Minute}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class Second extends AbstractAggregationExpression { + + private Second(Object value) { + super(value); + } + + @Override + protected 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 + */ + public static class Millisecond extends AbstractAggregationExpression { + + private Millisecond(Object value) { + super(value); + } + + @Override + protected 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 + */ + public static class DateToString extends AbstractAggregationExpression { + + private DateToString(Object value) { + super(value); + } + + @Override + protected 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 java.util.Map argumentMap(Object date, String format) { + + java.util.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 $isoDayOfWeek}. + * + * @author Christoph Strobl + */ + public static class IsoDayOfWeek extends AbstractAggregationExpression { + + private IsoDayOfWeek(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$isoDayOfWeek"; + } + + /** + * Creates new {@link IsoDayOfWeek}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static IsoDayOfWeek isoDayOfWeek(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new IsoDayOfWeek(Fields.field(fieldReference)); + } + + /** + * Creates new {@link IsoDayOfWeek}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static IsoDayOfWeek isoDayOfWeek(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new IsoDayOfWeek(expression); + } + } + + /** + * {@link AggregationExpression} for {@code $isoWeek}. + * + * @author Christoph Strobl + */ + public static class IsoWeek extends AbstractAggregationExpression { + + private IsoWeek(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$isoWeek"; + } + + /** + * Creates new {@link IsoWeek}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static IsoWeek isoWeekOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new IsoWeek(Fields.field(fieldReference)); + } + + /** + * Creates new {@link IsoWeek}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static IsoWeek isoWeekOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new IsoWeek(expression); + } + } + + /** + * {@link AggregationExpression} for {@code $isoWeekYear}. + * + * @author Christoph Strobl + */ + public static class IsoWeekYear extends AbstractAggregationExpression { + + private IsoWeekYear(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$isoWeekYear"; + } + + /** + * Creates new {@link IsoWeekYear}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static IsoWeekYear isoWeekYearOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new IsoWeekYear(Fields.field(fieldReference)); + } + + /** + * Creates new {@link Millisecond}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static IsoWeekYear isoWeekYearOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new IsoWeekYear(expression); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LiteralOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LiteralOperators.java new file mode 100644 index 0000000000..a91b4e935c --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/LiteralOperators.java @@ -0,0 +1,96 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import org.springframework.util.Assert; + +/** + * Gateway to {@literal literal} aggregation operations. + * + * @author Christoph Strobl + * @since 1.10 + */ +public 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); + } + + /** + * @author Christoph Strobl + */ + public static class LiteralOperatorFactory { + + private final Object value; + + /** + * Creates new {@link LiteralOperatorFactory} for given {@literal value}. + * + * @param value must not be {@literal null}. + */ + public LiteralOperatorFactory(Object value) { + + Assert.notNull(value, "Value must not be null!"); + this.value = value; + } + + /** + * Creates new {@link AggregationExpressions} that returns the associated value without parsing. + * + * @return + */ + public Literal asLiteral() { + return Literal.asLiteral(value); + } + } + + /** + * {@link AggregationExpression} for {@code $literal}. + * + * @author Christoph Strobl + */ + public static class Literal extends AbstractAggregationExpression { + + private Literal(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$literal"; + } + + /** + * Creates new {@link Literal}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Literal asLiteral(Object value) { + + Assert.notNull(value, "Value must not be null!"); + return new Literal(value); + } + } +} 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 f1d67b02c4..bc18aed8ad 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 @@ -21,9 +21,9 @@ import java.util.Collections; import java.util.List; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Let.ExpressionVariable; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.IfNull; +import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.IfNull; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder.FieldProjection; @@ -790,7 +790,7 @@ public ProjectionOperationBuilder slice(int count, int offset) { * @since 1.10 */ public ProjectionOperationBuilder filter(String as, AggregationExpression condition) { - return this.operation.and(AggregationExpressions.Filter.filter(name).as(as).by(condition)); + return this.operation.and(ArrayOperators.Filter.filter(name).as(as).by(condition)); } // SET OPERATORS @@ -895,7 +895,7 @@ public ProjectionOperationBuilder allElementsInArrayTrue() { * @since 1.10 */ public ProjectionOperationBuilder absoluteValue() { - return this.operation.and(AggregationExpressions.Abs.absoluteValueOf(name)); + return this.operation.and(ArithmeticOperators.Abs.absoluteValueOf(name)); } /** @@ -906,7 +906,7 @@ public ProjectionOperationBuilder absoluteValue() { * @since 1.10 */ public ProjectionOperationBuilder ceil() { - return this.operation.and(AggregationExpressions.Ceil.ceilValueOf(name)); + return this.operation.and(ArithmeticOperators.Ceil.ceilValueOf(name)); } /** @@ -917,7 +917,7 @@ public ProjectionOperationBuilder ceil() { * @since 1.10 */ public ProjectionOperationBuilder exp() { - return this.operation.and(AggregationExpressions.Exp.expValueOf(name)); + return this.operation.and(ArithmeticOperators.Exp.expValueOf(name)); } /** @@ -928,7 +928,7 @@ public ProjectionOperationBuilder exp() { * @since 1.10 */ public ProjectionOperationBuilder floor() { - return this.operation.and(AggregationExpressions.Floor.floorValueOf(name)); + return this.operation.and(ArithmeticOperators.Floor.floorValueOf(name)); } /** @@ -939,7 +939,7 @@ public ProjectionOperationBuilder floor() { * @since 1.10 */ public ProjectionOperationBuilder ln() { - return this.operation.and(AggregationExpressions.Ln.lnValueOf(name)); + return this.operation.and(ArithmeticOperators.Ln.lnValueOf(name)); } /** @@ -951,7 +951,7 @@ public ProjectionOperationBuilder ln() { * @since 1.10 */ public ProjectionOperationBuilder log(String baseFieldRef) { - return this.operation.and(AggregationExpressions.Log.valueOf(name).log(baseFieldRef)); + return this.operation.and(ArithmeticOperators.Log.valueOf(name).log(baseFieldRef)); } /** @@ -963,7 +963,7 @@ public ProjectionOperationBuilder log(String baseFieldRef) { * @since 1.10 */ public ProjectionOperationBuilder log(Number base) { - return this.operation.and(AggregationExpressions.Log.valueOf(name).log(base)); + return this.operation.and(ArithmeticOperators.Log.valueOf(name).log(base)); } /** @@ -975,7 +975,7 @@ public ProjectionOperationBuilder log(Number base) { * @since 1.10 */ public ProjectionOperationBuilder log(AggregationExpression base) { - return this.operation.and(AggregationExpressions.Log.valueOf(name).log(base)); + return this.operation.and(ArithmeticOperators.Log.valueOf(name).log(base)); } /** @@ -986,7 +986,7 @@ public ProjectionOperationBuilder log(AggregationExpression base) { * @since 1.10 */ public ProjectionOperationBuilder log10() { - return this.operation.and(AggregationExpressions.Log10.log10ValueOf(name)); + return this.operation.and(ArithmeticOperators.Log10.log10ValueOf(name)); } /** @@ -998,7 +998,7 @@ public ProjectionOperationBuilder log10() { * @since 1.10 */ public ProjectionOperationBuilder pow(String exponentFieldRef) { - return this.operation.and(AggregationExpressions.Pow.valueOf(name).pow(exponentFieldRef)); + return this.operation.and(ArithmeticOperators.Pow.valueOf(name).pow(exponentFieldRef)); } /** @@ -1010,7 +1010,7 @@ public ProjectionOperationBuilder pow(String exponentFieldRef) { * @since 1.10 */ public ProjectionOperationBuilder pow(Number exponent) { - return this.operation.and(AggregationExpressions.Pow.valueOf(name).pow(exponent)); + return this.operation.and(ArithmeticOperators.Pow.valueOf(name).pow(exponent)); } /** @@ -1022,7 +1022,7 @@ public ProjectionOperationBuilder pow(Number exponent) { * @since 1.10 */ public ProjectionOperationBuilder pow(AggregationExpression exponentExpression) { - return this.operation.and(AggregationExpressions.Pow.valueOf(name).pow(exponentExpression)); + return this.operation.and(ArithmeticOperators.Pow.valueOf(name).pow(exponentExpression)); } /** @@ -1033,7 +1033,7 @@ public ProjectionOperationBuilder pow(AggregationExpression exponentExpression) * @since 1.10 */ public ProjectionOperationBuilder sqrt() { - return this.operation.and(AggregationExpressions.Sqrt.sqrtOf(name)); + return this.operation.and(ArithmeticOperators.Sqrt.sqrtOf(name)); } /** @@ -1043,7 +1043,7 @@ public ProjectionOperationBuilder sqrt() { * @since 1.10 */ public ProjectionOperationBuilder trunc() { - return this.operation.and(AggregationExpressions.Trunc.truncValueOf(name)); + return this.operation.and(ArithmeticOperators.Trunc.truncValueOf(name)); } /** @@ -1090,7 +1090,7 @@ public ProjectionOperationBuilder substring(int start, int nrOfChars) { * @since 1.10 */ public ProjectionOperationBuilder toLower() { - return this.operation.and(AggregationExpressions.ToLower.lowerValueOf(name)); + return this.operation.and(StringOperators.ToLower.lowerValueOf(name)); } /** @@ -1101,7 +1101,7 @@ public ProjectionOperationBuilder toLower() { * @since 1.10 */ public ProjectionOperationBuilder toUpper() { - return this.operation.and(AggregationExpressions.ToUpper.upperValueOf(name)); + return this.operation.and(StringOperators.ToUpper.upperValueOf(name)); } /** @@ -1172,7 +1172,7 @@ public ProjectionOperationBuilder concatArrays(String... fields) { * @since 1.10 */ public ProjectionOperationBuilder isArray() { - return this.operation.and(AggregationExpressions.IsArray.isArray(name)); + return this.operation.and(ArrayOperators.IsArray.isArray(name)); } /** @@ -1182,7 +1182,7 @@ public ProjectionOperationBuilder isArray() { * @since 1.10 */ public ProjectionOperationBuilder asLiteral() { - return this.operation.and(AggregationExpressions.Literal.asLiteral(name)); + return this.operation.and(LiteralOperators.Literal.asLiteral(name)); } /** @@ -1194,7 +1194,7 @@ public ProjectionOperationBuilder asLiteral() { * @since 1.10 */ public ProjectionOperationBuilder dateAsFormattedString(String format) { - return this.operation.and(AggregationExpressions.DateToString.dateOf(name).toString(format)); + return this.operation.and(DateOperators.DateToString.dateOf(name).toString(format)); } /** @@ -1209,7 +1209,7 @@ public ProjectionOperationBuilder dateAsFormattedString(String format) { */ public ProjectionOperationBuilder let(AggregationExpression valueExpression, String variableName, AggregationExpression in) { - return this.operation.and(AggregationExpressions.Let.define(ExpressionVariable.newVariable(variableName).forExpression(valueExpression)).andApply(in)); + return this.operation.and(VariableOperators.Let.define(ExpressionVariable.newVariable(variableName).forExpression(valueExpression)).andApply(in)); } /** @@ -1222,7 +1222,7 @@ public ProjectionOperationBuilder let(AggregationExpression valueExpression, Str * @since 1.10 */ public ProjectionOperationBuilder let(Collection variables, AggregationExpression in) { - return this.operation.and(AggregationExpressions.Let.define(variables).andApply(in)); + return this.operation.and(VariableOperators.Let.define(variables).andApply(in)); } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java new file mode 100644 index 0000000000..598c1e35ba --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperators.java @@ -0,0 +1,666 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Sum; +import org.springframework.util.Assert; + +/** + * Gateway to {@literal Set expressions} which perform {@literal set} operation on arrays, treating arrays as sets. + * + * @author Christoph Strobl + * @since 1.10 + */ +public class SetOperators { + + /** + * Take the array referenced by given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + 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); + } + + /** + * @author Christoph Strobl + */ + public static class SetOperatorFactory { + + private final String fieldReference; + private final AggregationExpression expression; + + /** + * Creates new {@link SetOperatorFactory} for given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + */ + public SetOperatorFactory(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + this.fieldReference = fieldReference; + this.expression = null; + } + + /** + * Creates new {@link SetOperatorFactory} for given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + */ + public SetOperatorFactory(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + this.fieldReference = null; + this.expression = expression; + } + + /** + * 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 arrayReferences must not be {@literal null}. + * @return + */ + public SetEquals isEqualTo(String... arrayReferences) { + return createSetEquals().isEqualTo(arrayReferences); + } + + /** + * 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 expressions must not be {@literal null}. + * @return + */ + public SetEquals isEqualTo(AggregationExpression... expressions) { + return createSetEquals().isEqualTo(expressions); + } + + private SetEquals createSetEquals() { + return usesFieldRef() ? SetEquals.arrayAsSet(fieldReference) : SetEquals.arrayAsSet(expression); + } + + /** + * 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); + } + + /** + * 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); + } + + private SetIntersection createSetIntersection() { + return usesFieldRef() ? SetIntersection.arrayAsSet(fieldReference) : SetIntersection.arrayAsSet(expression); + } + + /** + * 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 + */ + public SetUnion union(String... arrayReferences) { + return createSetUnion().union(arrayReferences); + } + + /** + * 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 + */ + public SetUnion union(AggregationExpression... expressions) { + return createSetUnion().union(expressions); + } + + private SetUnion createSetUnion() { + return usesFieldRef() ? SetUnion.arrayAsSet(fieldReference) : SetUnion.arrayAsSet(expression); + } + + /** + * 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 + */ + public SetDifference differenceTo(String arrayReference) { + return createSetDifference().differenceTo(arrayReference); + } + + /** + * 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 + */ + public SetDifference differenceTo(AggregationExpression expression) { + return createSetDifference().differenceTo(expression); + } + + private SetDifference createSetDifference() { + return usesFieldRef() ? SetDifference.arrayAsSet(fieldReference) : SetDifference.arrayAsSet(expression); + } + + /** + * 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 + */ + public SetIsSubset isSubsetOf(String arrayReference) { + return createSetIsSubset().isSubsetOf(arrayReference); + } + + /** + * 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 + */ + public SetIsSubset isSubsetOf(AggregationExpression expression) { + return createSetIsSubset().isSubsetOf(expression); + } + + 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; + } + } + + /** + * {@link AggregationExpression} for {@code $setEquals}. + * + * @author Christoph Strobl + */ + public static class SetEquals extends AbstractAggregationExpression { + + private SetEquals(List arrays) { + super(arrays); + } + + @Override + protected 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 + */ + public static class SetIntersection extends AbstractAggregationExpression { + + private SetIntersection(List arrays) { + super(arrays); + } + + @Override + protected 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 + */ + public static class SetUnion extends AbstractAggregationExpression { + + private SetUnion(Object value) { + super(value); + } + + @Override + protected 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 expression 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 + */ + public static class SetDifference extends AbstractAggregationExpression { + + private SetDifference(Object value) { + super(value); + } + + @Override + protected 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 + */ + public static class SetIsSubset extends AbstractAggregationExpression { + + private SetIsSubset(Object value) { + super(value); + } + + @Override + protected 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 + */ + public static class AnyElementTrue extends AbstractAggregationExpression { + + private AnyElementTrue(Object value) { + super(value); + } + + @Override + protected 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)); + } + + /** + * Creates 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 + */ + public static class AllElementsTrue extends AbstractAggregationExpression { + + private AllElementsTrue(Object value) { + super(value); + } + + @Override + protected 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; + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java new file mode 100644 index 0000000000..e79f25b231 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java @@ -0,0 +1,1089 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.springframework.data.domain.Range; +import org.springframework.util.Assert; + +/** + * Gateway to {@literal String} aggregation operations. + * + * @author Christoph Strobl + * @since 1.10 + */ +public 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); + } + + /** + * @author Christoph Strobl + */ + public static class StringOperatorFactory { + + private final String fieldReference; + private final AggregationExpression expression; + + /** + * Creates new {@link StringOperatorFactory} for given {@literal fieldReference}. + * + * @param fieldReference must not be {@literal null}. + */ + public StringOperatorFactory(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + this.fieldReference = fieldReference; + this.expression = null; + } + + /** + * Creates new {@link StringOperatorFactory} for given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + */ + public StringOperatorFactory(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + 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); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a string + * for an occurrence of a given {@literal substring} and returns the UTF-8 byte index (zero-based) of the first + * occurrence. + * + * @param substring must not be {@literal null}. + * @return + */ + public IndexOfBytes indexOf(String substring) { + + Assert.notNull(substring, "Substring must not be null!"); + return createIndexOfBytesSubstringBuilder().indexOf(substring); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a string + * for an occurrence of a substring contained in the given {@literal field reference} and returns the UTF-8 byte + * index (zero-based) of the first occurrence. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public IndexOfBytes indexOf(Field fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return createIndexOfBytesSubstringBuilder().indexOf(fieldReference); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a string + * for an occurrence of a substring resulting from the given {@link AggregationExpression} and returns the UTF-8 + * byte index (zero-based) of the first occurrence. + * + * @param expression must not be {@literal null}. + * @return + */ + public IndexOfBytes indexOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return createIndexOfBytesSubstringBuilder().indexOf(expression); + } + + private IndexOfBytes.SubstringBuilder createIndexOfBytesSubstringBuilder() { + return fieldReference != null ? IndexOfBytes.valueOf(fieldReference) : IndexOfBytes.valueOf(expression); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a string + * for an occurrence of a given {@literal substring} and returns the UTF-8 code point index (zero-based) of the + * first occurrence. + * + * @param substring must not be {@literal null}. + * @return + */ + public IndexOfCP indexOfCP(String substring) { + + Assert.notNull(substring, "Substring must not be null!"); + return createIndexOfCPSubstringBuilder().indexOf(substring); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a string + * for an occurrence of a substring contained in the given {@literal field reference} and returns the UTF-8 code + * point index (zero-based) of the first occurrence. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public IndexOfCP indexOfCP(Field fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return createIndexOfCPSubstringBuilder().indexOf(fieldReference); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated string representation and searches a string + * for an occurrence of a substring resulting from the given {@link AggregationExpression} and returns the UTF-8 + * code point index (zero-based) of the first occurrence. + * + * @param expression must not be {@literal null}. + * @return + */ + public IndexOfCP indexOfCP(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return createIndexOfCPSubstringBuilder().indexOf(expression); + } + + private IndexOfCP.SubstringBuilder createIndexOfCPSubstringBuilder() { + return fieldReference != null ? IndexOfCP.valueOf(fieldReference) : IndexOfCP.valueOf(expression); + } + + /** + * Creates new {@link AggregationExpression} that divides the associated string representation into an array of + * substrings based on the given delimiter. + * + * @param delimiter must not be {@literal null}. + * @return + */ + public Split split(String delimiter) { + return createSplit().split(delimiter); + } + + /** + * Creates new {@link AggregationExpression} that divides the associated string representation into an array of + * substrings based on the delimiter resulting from the referenced field.. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public Split split(Field fieldReference) { + return createSplit().split(fieldReference); + } + + /** + * Creates new {@link AggregationExpression} that divides the associated string representation into an array of + * substrings based on a delimiter resulting from the given {@link AggregationExpression}. + * + * @param expression must not be {@literal null}. + * @return + */ + public Split split(AggregationExpression expression) { + return createSplit().split(expression); + } + + private Split createSplit() { + return fieldReference != null ? Split.valueOf(fieldReference) : Split.valueOf(expression); + } + + /** + * Creates new {@link AggregationExpression} that returns the number of UTF-8 bytes in the associated string + * representation. + * + * @return + */ + public StrLenBytes length() { + return fieldReference != null ? StrLenBytes.stringLengthOf(fieldReference) + : StrLenBytes.stringLengthOf(expression); + } + + /** + * Creates new {@link AggregationExpression} that returns the number of UTF-8 code points in the associated string + * representation. + * + * @return + */ + public StrLenCP lengthCP() { + return fieldReference != null ? StrLenCP.stringLengthOfCP(fieldReference) : StrLenCP.stringLengthOfCP(expression); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated string representation and returns a + * substring starting at a specified code point index position. + * + * @param codePointStart + * @return + */ + public SubstrCP substringCP(int codePointStart) { + return substringCP(codePointStart, -1); + } + + /** + * Creates new {@link AggregationExpressions} that takes the associated string representation and returns a + * substring starting at a specified code point index position including the specified number of code points. + * + * @param codePointStart + * @param nrOfCodePoints + * @return + */ + public SubstrCP substringCP(int codePointStart, int nrOfCodePoints) { + return createSubstrCP().substringCP(codePointStart, nrOfCodePoints); + } + + private SubstrCP createSubstrCP() { + return fieldReference != null ? SubstrCP.valueOf(fieldReference) : SubstrCP.valueOf(expression); + } + } + + /** + * {@link AggregationExpression} for {@code $concat}. + * + * @author Christoph Strobl + */ + public static class Concat extends AbstractAggregationExpression { + + private Concat(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$concat"; + } + + /** + * Creates new {@link Concat}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Concat valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Concat(asFields(fieldReference)); + } + + /** + * Creates new {@link Concat}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Concat valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Concat(Collections.singletonList(expression)); + } + + /** + * Creates new {@link Concat}. + * + * @param value must not be {@literal null}. + * @return + */ + public static Concat stringValue(String value) { + + Assert.notNull(value, "Value must not be null!"); + 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 + */ + public static class Substr extends AbstractAggregationExpression { + + private Substr(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$substr"; + } + + /** + * Creates new {@link Substr}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Substr valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Substr(asFields(fieldReference)); + } + + /** + * Creates new {@link Substr}. + * + * @param expression must not be {@literal null}. + * @return + */ + 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 + */ + public static class ToLower extends AbstractAggregationExpression { + + private ToLower(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$toLower"; + } + + /** + * Creates new {@link ToLower}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static ToLower lowerValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new ToLower(Fields.field(fieldReference)); + } + + /** + * Creates new {@link ToLower}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static ToLower lowerValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new ToLower(Collections.singletonList(expression)); + } + + /** + * Creates new {@link ToLower}. + * + * @param value must not be {@literal null}. + * @return + */ + public static ToLower lower(String value) { + + Assert.notNull(value, "Value must not be null!"); + return new ToLower(value); + } + } + + /** + * {@link AggregationExpression} for {@code $toUpper}. + * + * @author Christoph Strobl + */ + public static class ToUpper extends AbstractAggregationExpression { + + private ToUpper(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$toUpper"; + } + + /** + * Creates new {@link ToUpper}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static ToUpper upperValueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new ToUpper(Fields.field(fieldReference)); + } + + /** + * Creates new {@link ToUpper}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static ToUpper upperValueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new ToUpper(Collections.singletonList(expression)); + } + + /** + * Creates new {@link ToUpper}. + * + * @param value must not be {@literal null}. + * @return + */ + public static ToUpper upper(String value) { + + Assert.notNull(value, "Value must not be null!"); + return new ToUpper(value); + } + } + + /** + * {@link AggregationExpression} for {@code $strcasecmp}. + * + * @author Christoph Strobl + */ + public static class StrCaseCmp extends AbstractAggregationExpression { + + private StrCaseCmp(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$strcasecmp"; + } + + /** + * Creates new {@link StrCaseCmp}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static StrCaseCmp valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new StrCaseCmp(asFields(fieldReference)); + } + + /** + * Creates new {@link StrCaseCmp}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static StrCaseCmp valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new StrCaseCmp(Collections.singletonList(expression)); + } + + /** + * Creates new {@link StrCaseCmp}. + * + * @param value must not be {@literal null}. + * @return + */ + public static StrCaseCmp stringValue(String value) { + + Assert.notNull(value, "Value must not be null!"); + 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 $indexOfBytes}. + * + * @author Christoph Strobl + */ + public static class IndexOfBytes extends AbstractAggregationExpression { + + private IndexOfBytes(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$indexOfBytes"; + } + + /** + * Start creating a new {@link IndexOfBytes}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static SubstringBuilder valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new SubstringBuilder(Fields.field(fieldReference)); + } + + /** + * Start creating a new {@link IndexOfBytes}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static SubstringBuilder valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new SubstringBuilder(expression); + } + + /** + * Optionally define the substring search start and end position. + * + * @param range must not be {@literal null}. + * @return + */ + public IndexOfBytes within(Range range) { + + Assert.notNull(range, "Range must not be null!"); + + List rangeValues = new ArrayList(2); + rangeValues.add(range.getLowerBound()); + if (range.getUpperBound() != null) { + rangeValues.add(range.getUpperBound()); + } + + return new IndexOfBytes(append(rangeValues)); + } + + public static class SubstringBuilder { + + private final Object stringExpression; + + private SubstringBuilder(Object stringExpression) { + this.stringExpression = stringExpression; + } + + /** + * Creates a new {@link IndexOfBytes} given {@literal substring}. + * + * @param substring must not be {@literal null}. + * @return + */ + public IndexOfBytes indexOf(String substring) { + return new IndexOfBytes(Arrays.asList(stringExpression, substring)); + } + + /** + * Creates a new {@link IndexOfBytes} given {@link AggregationExpression} that resolves to the substring. + * + * @param expression must not be {@literal null}. + * @return + */ + public IndexOfBytes indexOf(AggregationExpression expression) { + return new IndexOfBytes(Arrays.asList(stringExpression, expression)); + } + + /** + * Creates a new {@link IndexOfBytes} given {@link Field} that resolves to the substring. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public IndexOfBytes indexOf(Field fieldReference) { + return new IndexOfBytes(Arrays.asList(stringExpression, fieldReference)); + } + } + } + + /** + * {@link AggregationExpression} for {@code $indexOfCP}. + * + * @author Christoph Strobl + */ + public static class IndexOfCP extends AbstractAggregationExpression { + + private IndexOfCP(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$indexOfCP"; + } + + /** + * Start creating a new {@link IndexOfCP}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static SubstringBuilder valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new SubstringBuilder(Fields.field(fieldReference)); + } + + /** + * Start creating a new {@link IndexOfCP}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static SubstringBuilder valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new SubstringBuilder(expression); + } + + /** + * Optionally define the substring search start and end position. + * + * @param range must not be {@literal null}. + * @return + */ + public IndexOfCP within(Range range) { + + Assert.notNull(range, "Range must not be null!"); + + List rangeValues = new ArrayList(2); + rangeValues.add(range.getLowerBound()); + if (range.getUpperBound() != null) { + rangeValues.add(range.getUpperBound()); + } + + return new IndexOfCP(append(rangeValues)); + } + + public static class SubstringBuilder { + + private final Object stringExpression; + + private SubstringBuilder(Object stringExpression) { + this.stringExpression = stringExpression; + } + + /** + * Creates a new {@link IndexOfCP} given {@literal substring}. + * + * @param substring must not be {@literal null}. + * @return + */ + public IndexOfCP indexOf(String substring) { + return new IndexOfCP(Arrays.asList(stringExpression, substring)); + } + + /** + * Creates a new {@link IndexOfCP} given {@link AggregationExpression} that resolves to the substring. + * + * @param expression must not be {@literal null}. + * @return + */ + public IndexOfCP indexOf(AggregationExpression expression) { + return new IndexOfCP(Arrays.asList(stringExpression, expression)); + } + + /** + * Creates a new {@link IndexOfCP} given {@link Field} that resolves to the substring. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public IndexOfCP indexOf(Field fieldReference) { + return new IndexOfCP(Arrays.asList(stringExpression, fieldReference)); + } + } + } + + /** + * {@link AggregationExpression} for {@code $split}. + * + * @author Christoph Strobl + */ + public static class Split extends AbstractAggregationExpression { + + private Split(List values) { + super(values); + } + + @Override + protected String getMongoMethod() { + return "$split"; + } + + /** + * Start creating a new {@link Split}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Split valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Split(asFields(fieldReference)); + } + + /** + * Start creating a new {@link Split}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Split valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Split(Collections.singletonList(expression)); + } + + /** + * Use given {@link String} as delimiter. + * + * @param delimiter must not be {@literal null}. + * @return + */ + public Split split(String delimiter) { + + Assert.notNull(delimiter, "Delimiter must not be null!"); + return new Split(append(delimiter)); + } + + /** + * Use value of referenced field as delimiter. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public Split split(Field fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new Split(append(fieldReference)); + } + + /** + * Use value resulting from {@link AggregationExpression} as delimiter. + * + * @param expression must not be {@literal null}. + * @return + */ + public Split split(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Split(append(expression)); + } + } + + /** + * {@link AggregationExpression} for {@code $strLenBytes}. + * + * @author Christoph Strobl + */ + public static class StrLenBytes extends AbstractAggregationExpression { + + private StrLenBytes(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$strLenBytes"; + } + + /** + * Creates new {@link StrLenBytes}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static StrLenBytes stringLengthOf(String fieldReference) { + return new StrLenBytes(Fields.field(fieldReference)); + } + + /** + * Creates new {@link StrLenBytes}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static StrLenBytes stringLengthOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new StrLenBytes(expression); + } + } + + /** + * {@link AggregationExpression} for {@code $strLenCP}. + * + * @author Christoph Strobl + */ + public static class StrLenCP extends AbstractAggregationExpression { + + private StrLenCP(Object value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$strLenCP"; + } + + /** + * Creates new {@link StrLenCP}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static StrLenCP stringLengthOfCP(String fieldReference) { + return new StrLenCP(Fields.field(fieldReference)); + } + + /** + * Creates new {@link StrLenCP}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static StrLenCP stringLengthOfCP(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new StrLenCP(expression); + } + } + + /** + * {@link AggregationExpression} for {@code $substrCP}. + * + * @author Christoph Strobl + */ + public static class SubstrCP extends AbstractAggregationExpression { + + private SubstrCP(List value) { + super(value); + } + + @Override + protected String getMongoMethod() { + return "$substrCP"; + } + + /** + * Creates new {@link SubstrCP}. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static SubstrCP valueOf(String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + return new SubstrCP(asFields(fieldReference)); + } + + /** + * Creates new {@link SubstrCP}. + * + * @param expression must not be {@literal null}. + * @return + */ + public static SubstrCP valueOf(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new SubstrCP(Collections.singletonList(expression)); + } + + public SubstrCP substringCP(int start) { + return substringCP(start, -1); + } + + public SubstrCP substringCP(int start, int nrOfChars) { + return new SubstrCP(append(Arrays.asList(start, nrOfChars))); + } + } +} diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java new file mode 100644 index 0000000000..79e8a5289a --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/VariableOperators.java @@ -0,0 +1,391 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; +import org.springframework.util.Assert; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; + +/** + * Gateway to {@literal variable} aggregation operations. + * + * @author Christoph Strobl + * @author Mark Paluch + * @since 1.10 + */ +public class VariableOperators { + + /** + * Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array + * and returns an array with the applied results. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static Map.AsBuilder mapItemsOf(String fieldReference) { + return Map.itemsOf(fieldReference); + } + + /** + * Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array + * and returns an array with the applied results. + * + * @param expression must not be {@literal null}. + * @return + */ + public static Map.AsBuilder mapItemsOf(AggregationExpression expression) { + return Map.itemsOf(expression); + } + + /** + * Start creating new {@link Let} that allows definition of {@link ExpressionVariable} that can be used within a + * nested {@link AggregationExpression}. + * + * @param variables must not be {@literal null}. + * @return + */ + public static Let.LetBuilder define(ExpressionVariable... variables) { + return Let.define(variables); + } + + /** + * Start creating new {@link Let} that allows definition of {@link ExpressionVariable} that can be used within a + * nested {@link AggregationExpression}. + * + * @param variables must not be {@literal null}. + * @return + */ + public static Let.LetBuilder define(Collection variables) { + return Let.define(variables); + } + + /** + * {@link AggregationExpression} for {@code $map}. + */ + public static class Map implements AggregationExpression { + + private Object sourceArray; + private String itemVariableName; + private AggregationExpression functionToApply; + + private Map(Object sourceArray, String itemVariableName, AggregationExpression functionToApply) { + + Assert.notNull(sourceArray, "SourceArray must not be null!"); + Assert.notNull(itemVariableName, "ItemVariableName must not be null!"); + Assert.notNull(functionToApply, "FunctionToApply must not be null!"); + + this.sourceArray = sourceArray; + this.itemVariableName = itemVariableName; + this.functionToApply = functionToApply; + } + + /** + * Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array + * and returns an array with the applied results. + * + * @param fieldReference must not be {@literal null}. + * @return + */ + public static AsBuilder itemsOf(final String fieldReference) { + + Assert.notNull(fieldReference, "FieldReference must not be null!"); + + return new AsBuilder() { + + @Override + public FunctionBuilder as(final String variableName) { + + Assert.notNull(variableName, "VariableName must not be null!"); + + return new FunctionBuilder() { + + @Override + public Map andApply(final AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression must not be null!"); + return new Map(Fields.field(fieldReference), variableName, expression); + } + }; + } + + }; + }; + + /** + * Starts building new {@link Map} that applies an {@link AggregationExpression} to each item of a referenced array + * and returns an array with the applied results. + * + * @param source must not be {@literal null}. + * @return + */ + public static AsBuilder itemsOf(final AggregationExpression source) { + + Assert.notNull(source, "AggregationExpression must not be null!"); + + return new AsBuilder() { + + @Override + public FunctionBuilder as(final String variableName) { + + Assert.notNull(variableName, "VariableName must not be null!"); + + return new FunctionBuilder() { + + @Override + public Map andApply(final AggregationExpression expression) { + + Assert.notNull(expression, "AggregationExpression must not be null!"); + return new Map(source, variableName, expression); + } + }; + } + }; + } + + /* (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 toMap(ExposedFields.synthetic(Fields.fields(itemVariableName)), context); + } + + private DBObject toMap(ExposedFields exposedFields, AggregationOperationContext context) { + + BasicDBObject map = new BasicDBObject(); + InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext( + exposedFields, context); + + BasicDBObject input; + if (sourceArray instanceof Field) { + input = new BasicDBObject("input", context.getReference((Field) sourceArray).toString()); + } else { + input = new BasicDBObject("input", ((AggregationExpression) sourceArray).toDbObject(context)); + } + + map.putAll(context.getMappedObject(input)); + map.put("as", itemVariableName); + map.put("in", + functionToApply.toDbObject(new NestedDelegatingExpressionAggregationOperationContext(operationContext))); + + return new BasicDBObject("$map", map); + } + + public interface AsBuilder { + + /** + * Define the {@literal variableName} for addressing items within the array. + * + * @param variableName must not be {@literal null}. + * @return + */ + FunctionBuilder as(String variableName); + } + + public interface FunctionBuilder { + + /** + * Creates new {@link Map} that applies the given {@link AggregationExpression} to each item of the referenced + * array and returns an array with the applied results. + * + * @param expression must not be {@literal null}. + * @return + */ + Map andApply(AggregationExpression expression); + } + } + + /** + * {@link AggregationExpression} for {@code $let} that binds {@link AggregationExpression} to variables for use in the + * specified {@code in} expression, and returns the result of the expression. + * + * @author Christoph Strobl + * @since 1.10 + */ + public static class Let implements AggregationExpression { + + private final List vars; + private final AggregationExpression expression; + + private Let(List vars, AggregationExpression expression) { + + this.vars = vars; + this.expression = expression; + } + + /** + * Start creating new {@link Let} by defining the variables for {@code $vars}. + * + * @param variables must not be {@literal null}. + * @return + */ + public static LetBuilder define(final Collection variables) { + + Assert.notNull(variables, "Variables must not be null!"); + + return new LetBuilder() { + + @Override + public Let andApply(final AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Let(new ArrayList(variables), expression); + } + }; + } + + /** + * Start creating new {@link Let} by defining the variables for {@code $vars}. + * + * @param variables must not be {@literal null}. + * @return + */ + public static LetBuilder define(final ExpressionVariable... variables) { + + Assert.notNull(variables, "Variables must not be null!"); + + return new LetBuilder() { + + @Override + public Let andApply(final AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new Let(Arrays.asList(variables), expression); + } + }; + } + + public interface LetBuilder { + + /** + * Define the {@link AggregationExpression} to evaluate. + * + * @param expression must not be {@literal null}. + * @return + */ + Let andApply(AggregationExpression expression); + } + + /* (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 toLet(ExposedFields.synthetic(Fields.fields(getVariableNames())), context); + } + + private String[] getVariableNames() { + + String[] varNames = new String[this.vars.size()]; + for (int i = 0; i < this.vars.size(); i++) { + varNames[i] = this.vars.get(i).variableName; + } + + return varNames; + } + + private DBObject toLet(ExposedFields exposedFields, AggregationOperationContext context) { + + DBObject letExpression = new BasicDBObject(); + DBObject mappedVars = new BasicDBObject(); + InheritingExposedFieldsAggregationOperationContext operationContext = new InheritingExposedFieldsAggregationOperationContext( + exposedFields, context); + + for (ExpressionVariable var : this.vars) { + mappedVars.putAll(getMappedVariable(var, context)); + } + + letExpression.put("vars", mappedVars); + letExpression.put("in", getMappedIn(operationContext)); + + return new BasicDBObject("$let", letExpression); + } + + private DBObject getMappedVariable(ExpressionVariable var, AggregationOperationContext context) { + + return new BasicDBObject(var.variableName, var.expression instanceof AggregationExpression + ? ((AggregationExpression) var.expression).toDbObject(context) : var.expression); + } + + private Object getMappedIn(AggregationOperationContext context) { + return expression.toDbObject(new NestedDelegatingExpressionAggregationOperationContext(context)); + } + + /** + * @author Christoph Strobl + */ + public static class ExpressionVariable { + + private final String variableName; + private final Object expression; + + /** + * Creates new {@link ExpressionVariable}. + * + * @param variableName can be {@literal null}. + * @param expression can be {@literal null}. + */ + private ExpressionVariable(String variableName, Object expression) { + + this.variableName = variableName; + this.expression = expression; + } + + /** + * Create a new {@link ExpressionVariable} with given name. + * + * @param variableName must not be {@literal null}. + * @return never {@literal null}. + */ + public static ExpressionVariable newVariable(String variableName) { + + Assert.notNull(variableName, "VariableName must not be null!"); + return new ExpressionVariable(variableName, null); + } + + /** + * Create a new {@link ExpressionVariable} with current name and given {@literal expression}. + * + * @param expression must not be {@literal null}. + * @return never {@literal null}. + */ + public ExpressionVariable forExpression(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null!"); + return new ExpressionVariable(variableName, expression); + } + + /** + * Create a new {@link ExpressionVariable} with current name and given {@literal expressionObject}. + * + * @param expressionObject must not be {@literal null}. + * @return never {@literal null}. + */ + public ExpressionVariable forExpression(DBObject expressionObject) { + + Assert.notNull(expressionObject, "Expression must not be null!"); + return new ExpressionVariable(variableName, expressionObject); + } + } + } +} diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java index 6112c86a9c..9a46581696 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java @@ -57,11 +57,7 @@ import org.springframework.data.mongodb.core.CollectionCallback; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.Venue; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Let; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Let.ExpressionVariable; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Multiply; +import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; import org.springframework.data.mongodb.core.aggregation.AggregationTests.CarDescriptor.Entry; import org.springframework.data.mongodb.core.aggregation.BucketAutoOperation.Granularities; import org.springframework.data.mongodb.core.index.GeospatialIndex; @@ -535,7 +531,7 @@ public void aggregationUsingConditionalProjectionToCalculateDiscount() { TypedAggregation aggregation = newAggregation(InventoryItem.class, // project("item") // .and("discount")// - .applyCondition(Cond.newBuilder().when(Criteria.where("qty").gte(250)) // + .applyCondition(ConditionalOperators.Cond.newBuilder().when(Criteria.where("qty").gte(250)) // .then(30) // .otherwise(20))); @@ -1622,11 +1618,11 @@ public void letShouldBeAppliedCorrectly() { ExpressionVariable total = ExpressionVariable.newVariable("total") .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))); ExpressionVariable discounted = ExpressionVariable.newVariable("discounted") - .forExpression(Cond.when("applyDiscount").then(0.9D).otherwise(1.0D)); + .forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D)); TypedAggregation agg = Aggregation.newAggregation(Sales2.class, Aggregation.project() - .and(Let.define(total, discounted).andApply( + .and(VariableOperators.Let.define(total, discounted).andApply( AggregationFunctionExpressions.MULTIPLY.of(Fields.field("total"), Fields.field("discounted")))) .as("finalTotal")); @@ -1726,7 +1722,7 @@ public void bucketAutoShouldCollectDocumentsIntoABucket() { mongoTemplate.insert(Arrays.asList(a1, a2, a3, a4), Art.class); TypedAggregation aggregation = newAggregation(Art.class, // - bucketAuto(Multiply.valueOf("price").multiplyBy(10), 3) // + bucketAuto(ArithmeticOperators.Multiply.valueOf("price").multiplyBy(10), 3) // .withGranularity(Granularities.E12) // .andOutputCount().as("count") // .andOutput("title").push().as("titles") // @@ -1762,7 +1758,7 @@ public void facetShouldCreateFacets() { mongoTemplate.insert(Arrays.asList(a1, a2, a3, a4), Art.class); - BucketAutoOperation bucketPrice = bucketAuto(Multiply.valueOf("price").multiplyBy(10), 3) // + BucketAutoOperation bucketPrice = bucketAuto(ArithmeticOperators.Multiply.valueOf("price").multiplyBy(10), 3) // .withGranularity(Granularities.E12) // .andOutputCount().as("count") // .andOutput("title").push().as("titles") // diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java index c865f2a269..de2c44dd67 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java @@ -30,8 +30,6 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.data.domain.Sort.Direction; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators; import org.springframework.data.mongodb.core.query.Criteria; import com.mongodb.BasicDBObject; @@ -427,7 +425,7 @@ public void conditionExpressionBasedFieldsShouldBeReferencableInFollowingOperati public void shouldRenderProjectionConditionalExpressionCorrectly() { DBObject agg = Aggregation.newAggregation(// - project().and(Cond.newBuilder() // + project().and(ConditionalOperators.Cond.newBuilder() // .when("isYellow") // .then("bright") // .otherwise("dark")).as("color")) @@ -450,7 +448,7 @@ public void shouldRenderProjectionConditionalCorrectly() { DBObject agg = Aggregation.newAggregation(// project().and("color") - .applyCondition(Cond.newBuilder() // + .applyCondition(ConditionalOperators.Cond.newBuilder() // .when("isYellow") // .then("bright") // .otherwise("dark"))) @@ -474,7 +472,7 @@ public void shouldRenderProjectionConditionalWithCriteriaCorrectly() { DBObject agg = Aggregation .newAggregation(project()// .and("color")// - .applyCondition(Cond.newBuilder().when(Criteria.where("key").gt(5)) // + .applyCondition(ConditionalOperators.Cond.newBuilder().when(Criteria.where("key").gt(5)) // .then("bright").otherwise("dark"))) // .toDbObject("foo", Aggregation.DEFAULT_CONTEXT); @@ -522,7 +520,7 @@ public void referencingProjectionAliasesShouldRenderProjectionConditionalWithCri .newAggregation(// project().and("color").as("chroma"), project().and("luminosity") // - .applyCondition(Cond.newBuilder() + .applyCondition(ConditionalOperators.Cond.newBuilder() .when(Criteria.where("chroma") // .is(100)) // .then("bright").otherwise("dark"))) // diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketOperationUnitTests.java index c978f37c1c..989267e7b9 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/BucketOperationUnitTests.java @@ -22,7 +22,6 @@ import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; import org.junit.Test; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ArithmeticOperators; import com.mongodb.DBObject; import com.mongodb.util.JSON; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CondExpressionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CondExpressionUnitTests.java index b86fb27d87..009cf79e4d 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CondExpressionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/CondExpressionUnitTests.java @@ -16,14 +16,13 @@ package org.springframework.data.mongodb.core.aggregation; import static org.junit.Assert.*; -import static org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond.*; +import static org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond.*; import static org.springframework.data.mongodb.test.util.IsBsonObject.*; import java.util.Arrays; import org.junit.Test; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Cond; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Cond; import org.springframework.data.mongodb.core.query.Criteria; import com.mongodb.BasicDBObject; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java index 5cc4e799f8..6bb7219046 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java @@ -17,7 +17,7 @@ import static org.hamcrest.core.Is.*; import static org.junit.Assert.*; -import static org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.*; +import static org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.*; import java.util.Arrays; import java.util.List; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperationUnitTests.java index f91d141225..3400911a87 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/GraphLookupOperationUnitTests.java @@ -21,7 +21,6 @@ import org.junit.Test; import org.springframework.data.mongodb.core.Person; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Literal; import org.springframework.data.mongodb.core.query.Criteria; import com.mongodb.BasicDBObject; @@ -111,7 +110,7 @@ public void shouldRenderMixedArrayOfStartsWithCorrectly() { GraphLookupOperation graphLookupOperation = GraphLookupOperation.builder() // .from("employees") // - .startWith("reportsTo", Literal.asLiteral("$boss")) // + .startWith("reportsTo", LiteralOperators.Literal.asLiteral("$boss")) // .connectFrom("reportsTo") // .connectTo("name") // .as("reportingHierarchy"); @@ -145,7 +144,7 @@ public void shouldRenderStartWithAggregationExpressions() { GraphLookupOperation graphLookupOperation = GraphLookupOperation.builder() // .from("employees") // - .startWith(Literal.asLiteral("hello")) // + .startWith(LiteralOperators.Literal.asLiteral("hello")) // .connectFrom("reportsTo") // .connectTo("name") // .as("reportingHierarchy"); 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 63d825134c..e5b4a4857e 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 @@ -19,7 +19,7 @@ import static org.hamcrest.core.Is.is; import static org.junit.Assert.*; import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; -import static org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Let.ExpressionVariable.*; +import static org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable.*; 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.*; @@ -32,28 +32,11 @@ import org.junit.Test; import org.springframework.data.domain.Range; import org.springframework.data.mongodb.core.DBObjectTestUtils; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.And; -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.Avg; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.BooleanOperators; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ComparisonOperators; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.DateOperators; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Gte; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Let.ExpressionVariable; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.LiteralOperators; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Lt; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.RangeOperator; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Reduce.PropertyExpression; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Reduce.Variable; -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.AggregationExpressions.Switch.CaseOperator; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Type; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.VariableOperators; +import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable; +import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.PropertyExpression; +import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.Variable; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator; import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.*; import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; @@ -1773,7 +1756,7 @@ public void shouldRenderLetExpressionCorrectly() { .define( newVariable("total") .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))), - newVariable("discounted").forExpression(Cond.when("applyDiscount").then(0.9D).otherwise(1.0D))) + newVariable("discounted").forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D))) .andApply(AggregationFunctionExpressions.MULTIPLY.of(Fields.field("total"), Fields.field("discounted")))) // .as("finalTotal").toDBObject(Aggregation.DEFAULT_CONTEXT); @@ -1797,7 +1780,7 @@ public void shouldRenderLetExpressionCorrectlyWhenUsingLetOnProjectionBuilder() .forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax"))); ExpressionVariable var2 = newVariable("discounted") - .forExpression(Cond.when("applyDiscount").then(0.9D).otherwise(1.0D)); + .forExpression(ConditionalOperators.Cond.when("applyDiscount").then(0.9D).otherwise(1.0D)); DBObject agg = Aggregation.project().and("foo") .let(Arrays.asList(var1, var2), @@ -1930,7 +1913,7 @@ public void shouldRenderIndexOfArrayCorrectly() { @Test public void shouldRenderRangeCorrectly() { - DBObject agg = project().and(RangeOperator.rangeStartingAt(0L).to("distance").withStepSize(25L)).as("rest_stops") + DBObject agg = project().and(ArrayOperators.RangeOperator.rangeStartingAt(0L).to("distance").withStepSize(25L)).as("rest_stops") .toDBObject(Aggregation.DEFAULT_CONTEXT); assertThat(agg, isBsonObject().containing("$project.rest_stops.$range.[0]", 0L) @@ -2077,11 +2060,11 @@ public void shouldRenderSwitchCorrectly() { " }\n" + // "}"; - CaseOperator cond1 = CaseOperator.when(Gte.valueOf(Avg.avgOf("scores")).greaterThanEqualToValue(90)) + CaseOperator cond1 = CaseOperator.when(ComparisonOperators.Gte.valueOf(AccumulatorOperators.Avg.avgOf("scores")).greaterThanEqualToValue(90)) .then("Doing great!"); - CaseOperator cond2 = CaseOperator.when(And.and(Gte.valueOf(Avg.avgOf("scores")).greaterThanEqualToValue(80), - Lt.valueOf(Avg.avgOf("scores")).lessThanValue(90))).then("Doing pretty well."); - CaseOperator cond3 = CaseOperator.when(Lt.valueOf(Avg.avgOf("scores")).lessThanValue(80)) + CaseOperator cond2 = CaseOperator.when(BooleanOperators.And.and(ComparisonOperators.Gte.valueOf(AccumulatorOperators.Avg.avgOf("scores")).greaterThanEqualToValue(80), + ComparisonOperators.Lt.valueOf(AccumulatorOperators.Avg.avgOf("scores")).lessThanValue(90))).then("Doing pretty well."); + CaseOperator cond3 = CaseOperator.when(ComparisonOperators.Lt.valueOf(AccumulatorOperators.Avg.avgOf("scores")).lessThanValue(80)) .then("Needs improvement."); DBObject agg = project().and(ConditionalOperators.switchCases(cond1, cond2, cond3).defaultTo("No scores found.")) @@ -2096,7 +2079,7 @@ public void shouldRenderSwitchCorrectly() { @Test public void shouldTypeCorrectly() { - DBObject agg = project().and(Type.typeOf("a")).as("a") + DBObject agg = project().and(DataTypeOperators.Type.typeOf("a")).as("a") .toDBObject(Aggregation.DEFAULT_CONTEXT); assertThat(agg, Matchers.is(JSON.parse("{ $project : { a: { $type: \"$a\" } } }"))); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java index 5a68303364..49223ccb7e 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java @@ -19,7 +19,6 @@ import static org.junit.Assert.*; import org.junit.Test; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.VariableOperators; import org.springframework.data.mongodb.core.aggregation.ReplaceRootOperation.ReplaceRootDocumentOperation; import com.mongodb.BasicDBObject; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java index e80bb17815..b35cf59614 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java @@ -36,7 +36,6 @@ import org.springframework.data.annotation.PersistenceConstructor; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mapping.model.MappingException; -import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators; import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;