Add support for $integral aggregation operator.

Closes: #3721
Original pull request: #3746.
This commit is contained in:
Christoph Strobl
2021-07-23 10:46:27 +02:00
committed by Mark Paluch
parent fd0a402c99
commit df2b2a2f68
4 changed files with 100 additions and 8 deletions

View File

@@ -216,6 +216,27 @@ public class ArithmeticOperators {
return usesFieldRef() ? Floor.floorValueOf(fieldReference) : Floor.floorValueOf(expression);
}
/**
* Creates new {@link AggregationExpression} that calculates the approximation for the mathematical integral value.
*
* @return new instance of {@link Integral}.
* @since 3.3
*/
public Integral integral() {
return usesFieldRef() ? Integral.integralOf(fieldReference) : Integral.integralOf(expression);
}
/**
* Creates new {@link AggregationExpression} that calculates the approximation for the mathematical integral value.
*
* @param unit the unit of measure.
* @return new instance of {@link Integral}.
* @since 3.3
*/
public Integral integral(String unit) {
return integral().unit(unit);
}
/**
* Creates new {@link AggregationExpression} that calculates the natural logarithm ln (i.e loge) of the assoicated
* number.
@@ -520,8 +541,8 @@ public class ArithmeticOperators {
}
/**
* Creates new {@link AggregationExpression} that uses the previous input (field/expression) and the value of the given
* field to calculate the population covariance of the two.
* Creates new {@link AggregationExpression} that uses the previous input (field/expression) and the value of the
* given field to calculate the population covariance of the two.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link CovariancePop}.
@@ -532,8 +553,8 @@ public class ArithmeticOperators {
}
/**
* Creates new {@link AggregationExpression} that uses the previous input (field/expression) and the result of the given
* {@link AggregationExpression expression} to calculate the population covariance of the two.
* Creates new {@link AggregationExpression} that uses the previous input (field/expression) and the result of the
* given {@link AggregationExpression expression} to calculate the population covariance of the two.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link CovariancePop}.
@@ -548,8 +569,8 @@ public class ArithmeticOperators {
}
/**
* Creates new {@link AggregationExpression} that uses the previous input (field/expression) and the value of the given
* field to calculate the sample covariance of the two.
* Creates new {@link AggregationExpression} that uses the previous input (field/expression) and the value of the
* given field to calculate the sample covariance of the two.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link CovariancePop}.
@@ -560,8 +581,8 @@ public class ArithmeticOperators {
}
/**
* Creates new {@link AggregationExpression} that uses the previous input (field/expression) and the result of the given
* {@link AggregationExpression expression} to calculate the sample covariance of the two.
* Creates new {@link AggregationExpression} that uses the previous input (field/expression) and the result of the
* given {@link AggregationExpression expression} to calculate the sample covariance of the two.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link CovariancePop}.
@@ -1798,4 +1819,54 @@ public class ArithmeticOperators {
return "$derivative";
}
}
/**
* Value object to represent an {@link AggregationExpression expression} that calculates the approximation for the
* mathematical integral value.
*
* @author Christoph Strobl
* @since 3.3
*/
public static class Integral extends AbstractAggregationExpression {
private Integral(Object value) {
super(value);
}
/**
* Create a new instance of {@link Integral} for the value stored at the given field holding a numeric value.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Integral}.
*/
public static Integral integralOf(String fieldReference) {
return new Integral(Collections.singletonMap("input", Fields.field(fieldReference)));
}
/**
* Create a new instance of {@link Integral} for the value provided by the given expression that resolves to a
* numeric value.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Integral}.
*/
public static Integral integralOf(AggregationExpression expression) {
return new Integral(Collections.singletonMap("input", expression));
}
/**
* Set the unit of measure.
*
* @param unit the unit of measure.
* @return new instance of {@link Integral}.
*/
public Integral unit(String unit) {
return new Integral(append("unit", unit));
}
@Override
protected String getMongoMethod() {
return "$integral";
}
}
}

View File

@@ -92,6 +92,7 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("trunc", singleArgRef().forOperator("$trunc"));
map.put("round", arrayArgRef().forOperator("$round"));
map.put("derivative", mapArgRef().forOperator("$derivative").mappingParametersTo("input", "unit"));
map.put("integral", mapArgRef().forOperator("$integral").mappingParametersTo("input", "unit"));
// STRING OPERATORS
map.put("concat", arrayArgRef().forOperator("$concat"));

View File

@@ -68,4 +68,14 @@ class ArithmeticOperatorsUnitTests {
valueOf("miles").derivative(SetWindowFieldsOperation.WindowUnits.HOUR).toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo(Document.parse("{ $derivative: { input: \"$miles\", unit: \"hour\" } }"));
}
@Test // GH-3721
void rendersIntegral() {
assertThat(valueOf("kilowatts").integral().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse("{ $integral : { input : \"$kilowatts\" } }"));
}
@Test // GH-3721
void rendersIntegralWithUnit() {
assertThat(valueOf("kilowatts").integral("hour").toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse("{ $integral : { input : \"$kilowatts\", unit : \"hour\" } }"));
}
}

View File

@@ -994,6 +994,16 @@ public class SpelExpressionTransformerUnitTests {
assertThat(transform("derivative(miles, 'hour')")).isEqualTo(Document.parse("{ \"$derivative\" : { input : '$miles', unit : 'hour'} }"));
}
@Test // GH-3721
public void shouldRenderIntegral() {
assertThat(transform("integral(field)")).isEqualTo(Document.parse("{ \"$integral\" : { \"input\" : \"$field\" }}"));
}
@Test // GH-3721
public void shouldIntegralWithUnit() {
assertThat(transform("integral(field, 'hour')")).isEqualTo(Document.parse("{ \"$integral\" : { \"input\" : \"$field\", \"unit\" : \"hour\" }}"));
}
private Object transform(String expression, Object... params) {
Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
return result == null ? null : (!(result instanceof org.bson.Document) ? result.toString() : result);