diff --git a/pom.xml b/pom.xml
index b688f3ee50..d5c2f77edf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3718-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml
index 0033bd11d5..d39d80ff7a 100644
--- a/spring-data-mongodb-benchmarks/pom.xml
+++ b/spring-data-mongodb-benchmarks/pom.xml
@@ -7,7 +7,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3718-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index f62c8dc7f4..19c03281b5 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -14,7 +14,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3718-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 1f157e75bc..28bcd00e31 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 3.3.0-SNAPSHOT
+ 3.3.0-GH-3718-SNAPSHOT
../pom.xml
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 6698b932f8..7c467d56dc 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
@@ -142,6 +142,36 @@ public StdDevSamp stdDevSamp() {
return usesFieldRef() ? StdDevSamp.stdDevSampOf(fieldReference) : StdDevSamp.stdDevSampOf(expression);
}
+ /**
+ * Creates new {@link AggregationExpression} that calculates the exponential moving average of numeric values
+ * considering the given number of historical documents with significant weight.
+ *
+ * @param numberOfHistoricalDocuments the number of historical documents.
+ * @return new instance of {@link ExpMovingAvg}.
+ * @since 3.3
+ */
+ public ExpMovingAvg expMovingAvg(int numberOfHistoricalDocuments) {
+
+ ExpMovingAvg expMovingAvg = usesFieldRef() ? ExpMovingAvg.expMovingAvgOf(fieldReference)
+ : ExpMovingAvg.expMovingAvgOf(expression);
+ return expMovingAvg.N(numberOfHistoricalDocuments);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the exponential moving average of numeric values
+ * applying the given exponential decay value.
+ *
+ * @param exponentialDecayValue the decay value.
+ * @return new instance of {@link ExpMovingAvg}.
+ * @since 3.3
+ */
+ public ExpMovingAvg expMovingAvg(double exponentialDecayValue) {
+
+ ExpMovingAvg expMovingAvg = usesFieldRef() ? ExpMovingAvg.expMovingAvgOf(fieldReference)
+ : ExpMovingAvg.expMovingAvgOf(expression);
+ return expMovingAvg.alpha(exponentialDecayValue);
+ }
+
private boolean usesFieldRef() {
return fieldReference != null;
}
@@ -658,4 +688,65 @@ public Document toDocument(Object value, AggregationOperationContext context) {
return super.toDocument(value, context);
}
}
+
+ /**
+ * {@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
new file mode 100644
index 0000000000..047c508426
--- /dev/null
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AccumulatorOperatorsUnitTests.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021 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
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.mongodb.core.aggregation;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.*;
+
+import org.bson.Document;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Christoph Strobl
+ */
+class AccumulatorOperatorsUnitTests {
+
+ @Test // GH-3718
+ void rendersExpMovingAvgWithN/*umberOfHistoricDocuments*/() {
+
+ assertThat(valueOf("price").expMovingAvg(2).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $expMovingAvg: { input: \"$price\", N: 2 } }"));
+ }
+
+ @Test // GH-3718
+ void rendersExpMovingAvgWithAlpha() {
+
+ assertThat(valueOf("price").expMovingAvg(0.75).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $expMovingAvg: { input: \"$price\", alpha: 0.75 } }"));
+ }
+
+}