Browse Source

Polishing.

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

See #3721
Original pull request: #3746.
pull/3765/head
Mark Paluch 5 years ago
parent
commit
2a3a4cf030
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 118
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
  2. 11
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
  3. 51
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
  4. 2
      src/main/asciidoc/reference/aggregation-framework.adoc

118
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java

@ -155,6 +155,46 @@ public class ArithmeticOperators {
return usesFieldRef() ? Ceil.ceilValueOf(fieldReference) : Ceil.ceilValueOf(expression); 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 * Creates new {@link AggregationExpression} that ivides the associated number by number referenced via
* {@literal fieldReference}. * {@literal fieldReference}.
@ -226,6 +266,21 @@ public class ArithmeticOperators {
return usesFieldRef() ? Integral.integralOf(fieldReference) : Integral.integralOf(expression); 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. * Creates new {@link AggregationExpression} that calculates the approximation for the mathematical integral value.
* *
@ -234,6 +289,9 @@ public class ArithmeticOperators {
* @since 3.3 * @since 3.3
*/ */
public Integral integral(String unit) { public Integral integral(String unit) {
Assert.hasText(unit, "Unit must not be empty!");
return integral().unit(unit); return integral().unit(unit);
} }
@ -618,46 +676,6 @@ public class ArithmeticOperators {
return round().place(place); 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() { private boolean usesFieldRef() {
return fieldReference != null; 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 { public static class Derivative extends AbstractAggregationExpression {
private Derivative(Object value) { private Derivative(Object value) {
super(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) { public static Derivative derivativeOf(String fieldReference) {
return new Derivative(Collections.singletonMap("input", Fields.field(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) { public static Derivative derivativeOf(AggregationExpression expression) {
return new Derivative(Collections.singletonMap("input", expression)); return new Derivative(Collections.singletonMap("input", expression));
} }

11
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java

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

51
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java

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

2
src/main/asciidoc/reference/aggregation-framework.adoc

@ -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` | `addToSet`, `covariancePop`, `covarianceSamp`, `expMovingAvg`, `first`, `last`, `max`, `min`, `avg`, `push`, `sum`, `count` (+++*+++), `stdDevPop`, `stdDevSamp`
| Arithmetic Aggregation Operators | 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 | String Aggregation Operators
| `concat`, `substr`, `toLower`, `toUpper`, `stcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim` | `concat`, `substr`, `toLower`, `toUpper`, `stcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`

Loading…
Cancel
Save