Add support for $expMovingAvg aggregation operator.

The SpEL support for this one is missing due to the differing argument map (N, alpha).

Closes: #3718
Original pull request: #3744.
This commit is contained in:
Christoph Strobl
2021-07-23 09:10:35 +02:00
committed by Mark Paluch
parent dbfd4e5c62
commit f3e067f59f
3 changed files with 131 additions and 6 deletions

View File

@@ -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. <br />
* 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. <br />
* 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";
}
}
}

View File

@@ -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;
}

View File

@@ -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`