Polishing.

Fix method order from earlier merges. Add missing Javadoc. Simplify tests. Update documentation.

See #3721
Original pull request: #3746.
This commit is contained in:
Mark Paluch
2021-08-24 14:59:58 +02:00
parent df2b2a2f68
commit 2a3a4cf030
4 changed files with 109 additions and 73 deletions

View File

@@ -155,6 +155,46 @@ public class ArithmeticOperators {
return usesFieldRef() ? Ceil.ceilValueOf(fieldReference) : Ceil.ceilValueOf(expression);
}
/**
* Creates new {@link AggregationExpression} that calculates the mathematical derivative value.
*
* @return new instance of {@link Derivative}.
* @since 3.3
*/
public Derivative derivative() {
return derivative((String) null);
}
/**
* Creates new {@link AggregationExpression} that calculates the mathematical derivative value.
*
* @param unit The time unit ({@link WindowUnits#WEEK}, {@link WindowUnits#DAY}, {@link WindowUnits#HOUR},
* {@link WindowUnits#MINUTE}, {@link WindowUnits#SECOND}, {@link WindowUnits#MILLISECOND}) to apply.
* @return new instance of {@link Derivative}.
* @since 3.3
*/
public Derivative derivative(WindowUnit unit) {
Assert.notNull(unit, "Window unit must not be null");
return derivative(unit.name().toLowerCase(Locale.ROOT));
}
/**
* Creates new {@link AggregationExpression} that calculates the mathematical derivative value.
*
* @param unit The time unit ({@literal week, day, hour, minute, second, millisecond}) to apply can be
* {@literal null}.
* @return new instance of {@link Derivative}.
* @since 3.3
*/
public Derivative derivative(@Nullable String unit) {
Derivative derivative = usesFieldRef() ? Derivative.derivativeOf(fieldReference)
: Derivative.derivativeOf(expression);
return StringUtils.hasText(unit) ? derivative.unit(unit) : derivative;
}
/**
* Creates new {@link AggregationExpression} that ivides the associated number by number referenced via
* {@literal fieldReference}.
@@ -226,6 +266,21 @@ public class ArithmeticOperators {
return usesFieldRef() ? Integral.integralOf(fieldReference) : Integral.integralOf(expression);
}
/**
* Creates new {@link AggregationExpression} that calculates the approximation for the mathematical integral value.
*
* @param unit The time unit ({@link WindowUnits#WEEK}, {@link WindowUnits#DAY}, {@link WindowUnits#HOUR},
* {@link WindowUnits#MINUTE}, {@link WindowUnits#SECOND}, {@link WindowUnits#MILLISECOND}) to apply.
* @return new instance of {@link Derivative}.
* @since 3.3
*/
public Integral integral(WindowUnit unit) {
Assert.notNull(unit, "Window unit must not be null");
return integral(unit.name().toLowerCase(Locale.ROOT));
}
/**
* Creates new {@link AggregationExpression} that calculates the approximation for the mathematical integral value.
*
@@ -234,6 +289,9 @@ public class ArithmeticOperators {
* @since 3.3
*/
public Integral integral(String unit) {
Assert.hasText(unit, "Unit must not be empty!");
return integral().unit(unit);
}
@@ -618,46 +676,6 @@ public class ArithmeticOperators {
return round().place(place);
}
/**
* Creates new {@link AggregationExpression} that calculates the mathematical derivative value.
*
* @return new instance of {@link Derivative}.
* @since 3.3
*/
public Derivative derivative() {
return derivative((String) null);
}
/**
* Creates new {@link AggregationExpression} that calculates the mathematical derivative value.
*
* @param unit The time unit ({@link WindowUnits#WEEK}, {@link WindowUnits#DAY}, {@link WindowUnits#HOUR},
* {@link WindowUnits#MINUTE}, {@link WindowUnits#SECOND}, {@link WindowUnits#MILLISECOND}) to apply.
* @return new instance of {@link Derivative}.
* @since 3.3
*/
public Derivative derivative(WindowUnit unit) {
Assert.notNull(unit, "Window unit must not be null");
return derivative(unit.name().toLowerCase(Locale.ROOT));
}
/**
* Creates new {@link AggregationExpression} that calculates the mathematical derivative value.
*
* @param unit The time unit ({@literal week, day, hour, minute, second, millisecond}) to apply can be
* {@literal null}.
* @return new instance of {@link Derivative}.
* @since 3.3
*/
public Derivative derivative(@Nullable String unit) {
Derivative derivative = usesFieldRef() ? Derivative.derivativeOf(fieldReference)
: Derivative.derivativeOf(expression);
return StringUtils.hasText(unit) ? derivative.unit(unit) : derivative;
}
private boolean usesFieldRef() {
return fieldReference != null;
}
@@ -1792,16 +1810,36 @@ public class ArithmeticOperators {
}
}
/**
* Value object to represent an {@link AggregationExpression expression} that calculates the average rate of change
* within the specified window.
*
* @author Christoph Strobl
* @since 3.3
*/
public static class Derivative extends AbstractAggregationExpression {
private Derivative(Object value) {
super(value);
}
/**
* Create a new instance of {@link Derivative} for the value stored at the given field holding a numeric value.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link Derivative}.
*/
public static Derivative derivativeOf(String fieldReference) {
return new Derivative(Collections.singletonMap("input", Fields.field(fieldReference)));
}
/**
* Create a new instance of {@link Derivative} 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 Derivative}.
*/
public static Derivative derivativeOf(AggregationExpression expression) {
return new Derivative(Collections.singletonMap("input", expression));
}

View File

@@ -15,8 +15,8 @@
*/
package org.springframework.data.mongodb.core.aggregation;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.aggregation.ArithmeticOperators.*;
import static org.springframework.data.mongodb.test.util.Assertions.*;
import java.util.Arrays;
import java.util.Collections;
@@ -66,16 +66,19 @@ class ArithmeticOperatorsUnitTests {
assertThat(
valueOf("miles").derivative(SetWindowFieldsOperation.WindowUnits.HOUR).toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo(Document.parse("{ $derivative: { input: \"$miles\", unit: \"hour\" } }"));
.isEqualTo("{ $derivative: { input: \"$miles\", unit: \"hour\" } }");
}
@Test // GH-3721
void rendersIntegral() {
assertThat(valueOf("kilowatts").integral().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse("{ $integral : { input : \"$kilowatts\" } }"));
assertThat(valueOf("kilowatts").integral().toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $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\" } }"));
assertThat(valueOf("kilowatts").integral(SetWindowFieldsOperation.WindowUnits.HOUR)
.toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $integral : { input : \"$kilowatts\", unit : \"hour\" } }");
}
}

View File

@@ -23,8 +23,8 @@ import org.bson.Document;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.data.mongodb.core.Person;
import org.springframework.lang.Nullable;
/**
* Unit tests for {@link SpelExpressionTransformer}.
@@ -152,8 +152,8 @@ public class SpelExpressionTransformerUnitTests {
@Test // DATAMONGO-774
void shouldRenderConsecutiveOperationsInComplexExpression() {
assertThat(transform("1 + 1 + (1 + 1 + 1) / q")).isEqualTo(
Document.parse("{ \"$add\" : [ 1 , 1 , { \"$divide\" : [ { \"$add\" : [ 1 , 1 , 1]} , \"$q\"]}]}"));
assertThat(transform("1 + 1 + (1 + 1 + 1) / q"))
.isEqualTo(Document.parse("{ \"$add\" : [ 1 , 1 , { \"$divide\" : [ { \"$add\" : [ 1 , 1 , 1]} , \"$q\"]}]}"));
}
@Test // DATAMONGO-774
@@ -189,8 +189,7 @@ public class SpelExpressionTransformerUnitTests {
Person person = new Person();
person.setAge(10);
assertThat(transform("[0].age + a.c", person))
.isEqualTo(Document.parse("{ \"$add\" : [ 10 , \"$a.c\"] }"));
assertThat(transform("[0].age + a.c", person)).isEqualTo(Document.parse("{ \"$add\" : [ 10 , \"$a.c\"] }"));
}
@Test // DATAMONGO-840
@@ -216,8 +215,7 @@ public class SpelExpressionTransformerUnitTests {
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeSetEquals() {
assertThat(transform("setEquals(a, b)"))
.isEqualTo(Document.parse("{ \"$setEquals\" : [ \"$a\" , \"$b\"]}"));
assertThat(transform("setEquals(a, b)")).isEqualTo(Document.parse("{ \"$setEquals\" : [ \"$a\" , \"$b\"]}"));
}
@Test // DATAMONGO-1530
@@ -379,8 +377,7 @@ public class SpelExpressionTransformerUnitTests {
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeConcat() {
assertThat(transform("concat(a, b, 'c')"))
.isEqualTo(Document.parse("{ \"$concat\" : [ \"$a\" , \"$b\" , \"c\"]}"));
assertThat(transform("concat(a, b, 'c')")).isEqualTo(Document.parse("{ \"$concat\" : [ \"$a\" , \"$b\" , \"c\"]}"));
}
@Test // DATAMONGO-1530
@@ -400,8 +397,7 @@ public class SpelExpressionTransformerUnitTests {
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeStrCaseCmp() {
assertThat(transform("strcasecmp(a, b)"))
.isEqualTo(Document.parse("{ \"$strcasecmp\" : [ \"$a\" , \"$b\"]}"));
assertThat(transform("strcasecmp(a, b)")).isEqualTo(Document.parse("{ \"$strcasecmp\" : [ \"$a\" , \"$b\"]}"));
}
@Test // DATAMONGO-1530
@@ -411,8 +407,7 @@ public class SpelExpressionTransformerUnitTests {
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeArrayElemAt() {
assertThat(transform("arrayElemAt(a, 10)"))
.isEqualTo(Document.parse("{ \"$arrayElemAt\" : [ \"$a\" , 10]}"));
assertThat(transform("arrayElemAt(a, 10)")).isEqualTo(Document.parse("{ \"$arrayElemAt\" : [ \"$a\" , 10]}"));
}
@Test // DATAMONGO-1530
@@ -511,15 +506,14 @@ public class SpelExpressionTransformerUnitTests {
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceDateToString() {
assertThat(transform("dateToString('%Y-%m-%d', $date)")).isEqualTo(
Document.parse("{ \"$dateToString\" : { \"format\" : \"%Y-%m-%d\" , \"date\" : \"$date\"}}"));
assertThat(transform("dateToString('%Y-%m-%d', $date)"))
.isEqualTo(Document.parse("{ \"$dateToString\" : { \"format\" : \"%Y-%m-%d\" , \"date\" : \"$date\"}}"));
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceCond() {
assertThat(transform("cond(qty > 250, 30, 20)")).isEqualTo(
Document
.parse("{ \"$cond\" : { \"if\" : { \"$gt\" : [ \"$qty\" , 250]} , \"then\" : 30 , \"else\" : 20}}"));
Document.parse("{ \"$cond\" : { \"if\" : { \"$gt\" : [ \"$qty\" , 250]} , \"then\" : 30 , \"else\" : 20}}"));
}
@Test // DATAMONGO-1530
@@ -633,8 +627,7 @@ public class SpelExpressionTransformerUnitTests {
@Test // DATAMONGO-1530
void shouldRenderComplexOperationNodeAnd() {
assertThat(transform("1+2 && concat(a, b) && true")).isEqualTo(
Document
.parse("{ \"$and\" : [ { \"$add\" : [ 1 , 2]} , { \"$concat\" : [ \"$a\" , \"$b\"]} , true]}"));
Document.parse("{ \"$and\" : [ { \"$add\" : [ 1 , 2]} , { \"$concat\" : [ \"$a\" , \"$b\"]} , true]}"));
}
@Test // DATAMONGO-1530
@@ -644,8 +637,7 @@ public class SpelExpressionTransformerUnitTests {
@Test // DATAMONGO-1530
void shouldRenderComplexNotCorrectly() {
assertThat(transform("!(foo > 10)"))
.isEqualTo(Document.parse("{ \"$not\" : [ { \"$gt\" : [ \"$foo\" , 10]}]}"));
assertThat(transform("!(foo > 10)")).isEqualTo(Document.parse("{ \"$not\" : [ { \"$gt\" : [ \"$foo\" , 10]}]}"));
}
@Test // DATAMONGO-1548
@@ -951,12 +943,14 @@ public class SpelExpressionTransformerUnitTests {
@Test // GH-3712
void shouldRenderCovariancePop() {
assertThat(transform("covariancePop(field1, field2)")).isEqualTo(Document.parse("{ \"$covariancePop\" : [\"$field1\", \"$field2\"]}"));
assertThat(transform("covariancePop(field1, field2)"))
.isEqualTo(Document.parse("{ \"$covariancePop\" : [\"$field1\", \"$field2\"]}"));
}
@Test // GH-3712
void shouldRenderCovarianceSamp() {
assertThat(transform("covarianceSamp(field1, field2)")).isEqualTo(Document.parse("{ \"$covarianceSamp\" : [\"$field1\", \"$field2\"]}"));
assertThat(transform("covarianceSamp(field1, field2)"))
.isEqualTo(Document.parse("{ \"$covarianceSamp\" : [\"$field1\", \"$field2\"]}"));
}
@Test // GH-3715
@@ -988,20 +982,21 @@ public class SpelExpressionTransformerUnitTests {
.isEqualTo(Document.parse("{ $shift: { output: \"$quantity\", by: 1, default: \"Not available\" } }"));
}
@Nullable
@Test // GH-3716
void shouldRenderDerivative() {
assertThat(transform("derivative(miles, 'hour')")).isEqualTo(Document.parse("{ \"$derivative\" : { input : '$miles', unit : 'hour'} }"));
assertThat(transform("derivative(miles, 'hour')"))
.isEqualTo(Document.parse("{ \"$derivative\" : { input : '$miles', unit : 'hour'} }"));
}
@Test // GH-3721
public void shouldRenderIntegral() {
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\" }}"));
void shouldRenderIntegralWithUnit() {
assertThat(transform("integral(field, 'hour')"))
.isEqualTo(Document.parse("{ \"$integral\" : { \"input\" : \"$field\", \"unit\" : \"hour\" }}"));
}
private Object transform(String expression, Object... params) {

View File

@@ -85,7 +85,7 @@ At the time of this writing, we provide support for the following Aggregation Op
| `addToSet`, `covariancePop`, `covarianceSamp`, `expMovingAvg`, `first`, `last`, `max`, `min`, `avg`, `push`, `sum`, `count` (+++*+++), `stdDevPop`, `stdDevSamp`
| Arithmetic Aggregation Operators
| `abs`, `add` (+++*+++ via `plus`), `ceil`, `derivative`, `divide`, `exp`, `floor`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `trunc`
| `abs`, `add` (+++*+++ via `plus`), `ceil`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `trunc`
| String Aggregation Operators
| `concat`, `substr`, `toLower`, `toUpper`, `stcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`