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
index 13913caac..ba2c34eda 100644
--- 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
@@ -199,11 +199,61 @@ public class AccumulatorOperators {
: CovarianceSamp.covarianceSampOf(expression);
}
+ /**
+ * Creates new {@link ExpMovingAvgBuilder} that to build {@link AggregationExpression expMovingAvg} that calculates
+ * the exponential moving average of numeric values
+ *
+ * @return new instance of {@link ExpMovingAvg}.
+ * @since 3.3
+ */
+ public ExpMovingAvgBuilder expMovingAvg() {
+
+ ExpMovingAvg expMovingAvg = usesFieldRef() ? ExpMovingAvg.expMovingAvgOf(fieldReference)
+ : ExpMovingAvg.expMovingAvgOf(expression);
+ return new ExpMovingAvgBuilder() {
+
+ @Override
+ public ExpMovingAvg historicalDocuments(int numberOfHistoricalDocuments) {
+ return expMovingAvg.n(numberOfHistoricalDocuments);
+ }
+
+ @Override
+ public ExpMovingAvg alpha(double exponentialDecayValue) {
+ return expMovingAvg.alpha(exponentialDecayValue);
+ }
+ };
+ }
+
private boolean usesFieldRef() {
return fieldReference != null;
}
}
+ /**
+ * Builder for {@link ExpMovingAvg}.
+ *
+ * @since 3.3
+ */
+ public interface ExpMovingAvgBuilder {
+
+ /**
+ * Define the number of historical documents with significant mathematical weight.
+ *
+ * @param numberOfHistoricalDocuments
+ * @return new instance of {@link ExpMovingAvg}.
+ */
+ ExpMovingAvg historicalDocuments(int numberOfHistoricalDocuments);
+
+ /**
+ * Define the exponential decay value.
+ *
+ * @param exponentialDecayValue
+ * @return new instance of {@link ExpMovingAvg}.
+ */
+ ExpMovingAvg alpha(double exponentialDecayValue);
+
+ }
+
/**
* {@link AggregationExpression} for {@code $sum}.
*
@@ -835,4 +885,65 @@ public class AccumulatorOperators {
return "$covarianceSamp";
}
}
+
+ /**
+ * {@link ExpMovingAvg} calculates the exponential moving average of numeric values.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+ public static class ExpMovingAvg extends AbstractAggregationExpression {
+
+ private ExpMovingAvg(Object value) {
+ super(value);
+ }
+
+ /**
+ * Create a new {@link ExpMovingAvg} by defining the field holding the value to be used as input.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link ExpMovingAvg}.
+ */
+ public static ExpMovingAvg expMovingAvgOf(String fieldReference) {
+ return new ExpMovingAvg(Collections.singletonMap("input", Fields.field(fieldReference)));
+ }
+
+ /**
+ * Create a new {@link ExpMovingAvg} by defining the {@link AggregationExpression expression} to compute the value
+ * to be used as input.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link ExpMovingAvg}.
+ */
+ public static ExpMovingAvg expMovingAvgOf(AggregationExpression expression) {
+ return new ExpMovingAvg(Collections.singletonMap("input", expression));
+ }
+
+ /**
+ * Define the number of historical documents with significant mathematical weight.
+ * Specify either {@link #n(int) N} or {@link #alpha(double) aplha}. Not both!
+ *
+ * @param numberOfHistoricalDocuments
+ * @return new instance of {@link ExpMovingAvg}.
+ */
+ public ExpMovingAvg n/*umber of historical documents*/(int numberOfHistoricalDocuments) {
+ return new ExpMovingAvg(append("N", numberOfHistoricalDocuments));
+ }
+
+ /**
+ * Define the exponential decay value.
+ * Specify either {@link #alpha(double) aplha} or {@link #n(int) N}. Not both!
+ *
+ * @param exponentialDecayValue
+ * @return new instance of {@link ExpMovingAvg}.
+ */
+ public ExpMovingAvg alpha(double exponentialDecayValue) {
+ return new ExpMovingAvg(append("alpha", exponentialDecayValue));
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$expMovingAvg";
+ }
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperatorsUnitTests.java
index 6948255d1..27bd87625 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperatorsUnitTests.java
@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.core.aggregation;
import static org.assertj.core.api.Assertions.*;
+import static org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.*;
import java.util.Arrays;
import java.util.Date;
@@ -46,7 +47,7 @@ class AccumulatorOperatorsUnitTests {
assertThat(AccumulatorOperators.valueOf(Year.yearOf("birthdate")).covariancePop("midichlorianCount")
.toDocument(TestAggregationContext.contextFor(Jedi.class)))
- .isEqualTo(new Document("$covariancePop", Arrays.asList(new Document("$year", "$birthdate"), "$force")));
+ .isEqualTo(new Document("$covariancePop", Arrays.asList(new Document("$year", "$birthdate"), "$force")));
}
@Test // GH-3712
@@ -54,7 +55,7 @@ class AccumulatorOperatorsUnitTests {
assertThat(AccumulatorOperators.valueOf("balance").covarianceSamp("midichlorianCount")
.toDocument(TestAggregationContext.contextFor(Jedi.class)))
- .isEqualTo(new Document("$covarianceSamp", Arrays.asList("$balance", "$force")));
+ .isEqualTo(new Document("$covarianceSamp", Arrays.asList("$balance", "$force")));
}
@Test // GH-3712
@@ -62,7 +63,21 @@ class AccumulatorOperatorsUnitTests {
assertThat(AccumulatorOperators.valueOf(Year.yearOf("birthdate")).covarianceSamp("midichlorianCount")
.toDocument(TestAggregationContext.contextFor(Jedi.class)))
- .isEqualTo(new Document("$covarianceSamp", Arrays.asList(new Document("$year", "$birthdate"), "$force")));
+ .isEqualTo(new Document("$covarianceSamp", Arrays.asList(new Document("$year", "$birthdate"), "$force")));
+ }
+
+ @Test // GH-3718
+ void rendersExpMovingAvgWithNumberOfHistoricDocuments() {
+
+ assertThat(valueOf("price").expMovingAvg().historicalDocuments(2).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $expMovingAvg: { input: \"$price\", N: 2 } }"));
+ }
+
+ @Test // GH-3718
+ void rendersExpMovingAvgWithAlpha() {
+
+ assertThat(valueOf("price").expMovingAvg().alpha(0.75).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $expMovingAvg: { input: \"$price\", alpha: 0.75 } }"));
}
static class Jedi {
@@ -71,8 +86,7 @@ class AccumulatorOperatorsUnitTests {
Date birthdate;
- @Field("force")
- Integer midichlorianCount;
+ @Field("force") Integer midichlorianCount;
Integer balance;
}
diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc
index 3bfa50073..dfa87dd3c 100644
--- a/src/main/asciidoc/reference/mongodb.adoc
+++ b/src/main/asciidoc/reference/mongodb.adoc
@@ -2503,7 +2503,7 @@ At the time of this writing, we provide support for the following Aggregation Op
| `setEquals`, `setIntersection`, `setUnion`, `setDifference`, `setIsSubset`, `anyElementTrue`, `allElementsTrue`
| Group/Accumulator Aggregation Operators
-| `addToSet`, `covariancePop`, `covarianceSamp`, `first`, `last`, `max`, `min`, `avg`, `push`, `sum`, `(*count)`, `stdDevPop`, `stdDevSamp`
+| `addToSet`, `covariancePop`, `covarianceSamp`, `expMovingAvg`, `first`, `last`, `max`, `min`, `avg`, `push`, `sum`, `(*count)`, `stdDevPop`, `stdDevSamp`
| Arithmetic Aggregation Operators
| `abs`, `add` (*via `plus`), `ceil`, `divide`, `exp`, `floor`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (*via `minus`), `trunc`