.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sin sinOf(String fieldReference) {
+ return sinOf(fieldReference, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sin sinOf(String fieldReference, AngularDimension unit) {
+ return sin(Fields.field(fieldReference), unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sin sinOf(AggregationExpression expression) {
+ return sinOf(expression, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sin sinOf(AggregationExpression expression, AngularDimension unit) {
+ return sin(expression, unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value
+ * @return new instance of {@link Sin}.
+ */
+ public static Sin sin(Object value) {
+ return sin(value, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sin sin(Object value, AngularDimension unit) {
+
+ if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ return new Sin(ConvertOperators.DegreesToRadians.degreesToRadians(value));
+ }
+ return new Sin(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$sin";
+ }
+ }
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the hyperbolic sine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+ public static class Sinh extends AbstractAggregationExpression {
+
+ private Sinh(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sinh sinhOf(String fieldReference) {
+ return sinhOf(fieldReference, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * Use {@code sinhOf("angle", DEGREES)} as shortcut for
{ $sinh : { $degreesToRadians : "$angle" } }
.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sinh sinhOf(String fieldReference, AngularDimension unit) {
+ return sinh(Fields.field(fieldReference), unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg. {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sinh sinhOf(AggregationExpression expression) {
+ return sinhOf(expression, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sinh sinhOf(AggregationExpression expression, AngularDimension unit) {
+ return sinh(expression, unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sinh sinh(Object value) {
+ return sinh(value, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Sin}.
+ */
+ public static Sinh sinh(Object value, AngularDimension unit) {
+
+ if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ return new Sinh(ConvertOperators.DegreesToRadians.degreesToRadians(value));
+ }
+ return new Sinh(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$sinh";
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index f0799a1af6..9ee12be1eb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -93,6 +93,8 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("round", arrayArgRef().forOperator("$round"));
map.put("derivative", mapArgRef().forOperator("$derivative").mappingParametersTo("input", "unit"));
map.put("integral", mapArgRef().forOperator("$integral").mappingParametersTo("input", "unit"));
+ map.put("sin", singleArgRef().forOperator("$sin"));
+ map.put("sinh", singleArgRef().forOperator("$sinh"));
// STRING OPERATORS
map.put("concat", arrayArgRef().forOperator("$concat"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index d57363d91c..cc32a94323 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -81,4 +81,32 @@ void rendersIntegralWithUnit() {
.toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $integral : { input : \"$kilowatts\", unit : \"hour\" } }");
}
+
+ @Test // GH-3728
+ void rendersSin() {
+
+ assertThat(valueOf("angle").sin().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $sin : \"$angle\" }"));
+ }
+
+ @Test // GH-3728
+ void rendersSinWithValueInDegrees() {
+
+ assertThat(valueOf("angle").sin(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $sin : { $degreesToRadians : \"$angle\" } }"));
+ }
+
+ @Test // GH-3728
+ void rendersSinh() {
+
+ assertThat(valueOf("angle").sinh().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $sinh : \"$angle\" }"));
+ }
+
+ @Test // GH-3728
+ void rendersSinhWithValueInDegrees() {
+
+ assertThat(valueOf("angle").sinh(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $sinh : { $degreesToRadians : \"$angle\" } }"));
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index 9cad6cbb15..e250241558 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1004,6 +1004,16 @@ void shouldRenderIntegralWithUnit() {
.isEqualTo(Document.parse("{ \"$integral\" : { \"input\" : \"$field\", \"unit\" : \"hour\" }}"));
}
+ @Test // GH-3728
+ void shouldRenderSin() {
+ assertThat(transform("sin(angle)")).isEqualTo(Document.parse("{ \"$sin\" : \"$angle\"}"));
+ }
+
+ @Test // GH-3728
+ void shouldRenderSinh() {
+ assertThat(transform("sinh(angle)")).isEqualTo(Document.parse("{ \"$sinh\" : \"$angle\"}"));
+ }
+
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);
From 73d5886aae13082d24b7f49db91da322da509952 Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Wed, 28 Jul 2021 09:54:05 +0200
Subject: [PATCH 027/920] Add support for `$tan` and `$tanh` aggregation
operators.
Closes: #3730
Original pull request: #3755.
---
.../core/aggregation/ArithmeticOperators.java | 251 ++++++++++++++++++
.../core/spel/MethodReferenceNode.java | 2 +
.../ArithmeticOperatorsUnitTests.java | 29 ++
.../SpelExpressionTransformerUnitTests.java | 10 +
4 files changed, 292 insertions(+)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index db328338e3..e26e41f651 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -719,6 +719,51 @@ public Sinh sinh(AngularDimension unit) {
return usesFieldRef() ? Sinh.sinhOf(fieldReference, unit) : Sinh.sinhOf(expression, unit);
}
+ /**
+ * Creates new {@link AggregationExpression} that calculates the tangent of a numeric value given in
+ * {@link AngularDimension#RADIANS radians}.
+ *
+ * @return new instance of {@link Sin}.
+ * @since 3.3
+ */
+ public Tan tan() {
+ return tan(AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the tangent of a numeric value in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param unit the unit of measure.
+ * @return new instance of {@link Sin}.
+ * @since 3.3
+ */
+ public Tan tan(AngularDimension unit) {
+ return usesFieldRef() ? Tan.tanOf(fieldReference, unit) : Tan.tanOf(expression, unit);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the hyperbolic tangent of a numeric value given in
+ * {@link AngularDimension#RADIANS radians}.
+ *
+ * @return new instance of {@link Sin}.
+ * @since 3.3
+ */
+ public Tanh tanh() {
+ return tanh(AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the hyperbolic tangent of a numeric value.
+ *
+ * @param unit the unit of measure.
+ * @return new instance of {@link Sin}.
+ * @since 3.3
+ */
+ public Tanh tanh(AngularDimension unit) {
+ return usesFieldRef() ? Tanh.tanhOf(fieldReference, unit) : Tanh.tanhOf(expression, unit);
+ }
+
private boolean usesFieldRef() {
return fieldReference != null;
}
@@ -2153,4 +2198,210 @@ protected String getMongoMethod() {
return "$sinh";
}
}
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the tangent of a value that is measured in radians.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+ public static class Tan extends AbstractAggregationExpression {
+
+ private Tan(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in
+ * {@link AngularDimension#RADIANS radians}.
+ *
+ * Use {@code tanOf("angle", DEGREES)} as shortcut for
+ *
+ *
+ *
+ * .
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link Tan}.
+ */
+ public static Tan tanOf(String fieldReference) {
+ return tanOf(fieldReference, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Tan}.
+ */
+ public static Tan tanOf(String fieldReference, AngularDimension unit) {
+ return tan(Fields.field(fieldReference), unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link Tan}.
+ */
+ public static Tan tanOf(AggregationExpression expression) {
+ return tanOf(expression, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Tan}.
+ */
+ public static Tan tanOf(AggregationExpression expression, AngularDimension unit) {
+ return tan(expression, unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value
+ * @return new instance of {@link Tan}.
+ */
+ public static Tan tan(Object value) {
+ return tan(value, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Tan}.
+ */
+ public static Tan tan(Object value, AngularDimension unit) {
+
+ if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ return new Tan(ConvertOperators.DegreesToRadians.degreesToRadians(value));
+ }
+ return new Tan(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$tan";
+ }
+ }
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the hyperbolic tangent of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+ public static class Tanh extends AbstractAggregationExpression {
+
+ private Tanh(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link Tanh}.
+ */
+ public static Tanh tanhOf(String fieldReference) {
+ return tanhOf(fieldReference, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * Use {@code tanhOf("angle", DEGREES)} as shortcut for
+ *
+ *
+ *
+ * .
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Tanh}.
+ */
+ public static Tanh tanhOf(String fieldReference, AngularDimension unit) {
+ return tanh(Fields.field(fieldReference), unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg.
+ * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link Tanh}.
+ */
+ public static Tanh tanhOf(AggregationExpression expression) {
+ return tanhOf(expression, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Tanh}.
+ */
+ public static Tanh tanhOf(AggregationExpression expression, AngularDimension unit) {
+ return tanh(expression, unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @return new instance of {@link Tanh}.
+ */
+ public static Tanh tanh(Object value) {
+ return tanh(value, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Tanh}.
+ */
+ public static Tanh tanh(Object value, AngularDimension unit) {
+
+ if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ return new Tanh(ConvertOperators.DegreesToRadians.degreesToRadians(value));
+ }
+ return new Tanh(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$tanh";
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index 9ee12be1eb..a2d011d6ad 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -95,6 +95,8 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("integral", mapArgRef().forOperator("$integral").mappingParametersTo("input", "unit"));
map.put("sin", singleArgRef().forOperator("$sin"));
map.put("sinh", singleArgRef().forOperator("$sinh"));
+ map.put("tan", singleArgRef().forOperator("$tan"));
+ map.put("tanh", singleArgRef().forOperator("$tanh"));
// STRING OPERATORS
map.put("concat", arrayArgRef().forOperator("$concat"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index cc32a94323..9a77d093c4 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -109,4 +109,33 @@ void rendersSinhWithValueInDegrees() {
assertThat(valueOf("angle").sinh(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo(Document.parse("{ $sinh : { $degreesToRadians : \"$angle\" } }"));
}
+
+ @Test // GH-3730
+ void rendersTan() {
+
+ assertThat(valueOf("angle").tan().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $tan : \"$angle\" }"));
+ }
+
+ @Test // GH-3730
+ void rendersTanWithValueInDegrees() {
+
+ assertThat(valueOf("angle").tan(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $tan : { $degreesToRadians : \"$angle\" } }"));
+ }
+
+ @Test // GH-3730
+ void rendersTanh() {
+
+ assertThat(valueOf("angle").tanh().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $tanh : \"$angle\" }"));
+ }
+
+ @Test // GH-3730
+ void rendersTanhWithValueInDegrees() {
+
+ assertThat(valueOf("angle").tanh(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $tanh : { $degreesToRadians : \"$angle\" } }"));
+ }
+
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index e250241558..cc59a91700 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1014,6 +1014,16 @@ void shouldRenderSinh() {
assertThat(transform("sinh(angle)")).isEqualTo(Document.parse("{ \"$sinh\" : \"$angle\"}"));
}
+ @Test // GH-3730
+ void shouldRenderTan() {
+ assertThat(transform("tan(angle)")).isEqualTo(Document.parse("{ \"$tan\" : \"$angle\"}"));
+ }
+
+ @Test // GH-3730
+ void shouldRenderTanh() {
+ assertThat(transform("tanh(angle)")).isEqualTo(Document.parse("{ \"$tanh\" : \"$angle\"}"));
+ }
+
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);
From c4c6267d91b89fd299f47a1a8604d86e9e87e53d Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Wed, 28 Jul 2021 10:04:29 +0200
Subject: [PATCH 028/920] Add support for `$cos` and `$cosh` aggregation
operators.
Closes: #3710
Original pull request: #3755.
---
.../core/aggregation/ArithmeticOperators.java | 251 ++++++++++++++++++
.../core/spel/MethodReferenceNode.java | 2 +
.../ArithmeticOperatorsUnitTests.java | 28 ++
.../SpelExpressionTransformerUnitTests.java | 10 +
4 files changed, 291 insertions(+)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index e26e41f651..4de258b4eb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -719,6 +719,51 @@ public Sinh sinh(AngularDimension unit) {
return usesFieldRef() ? Sinh.sinhOf(fieldReference, unit) : Sinh.sinhOf(expression, unit);
}
+ /**
+ * Creates new {@link AggregationExpression} that calculates the cosine of a numeric value given in
+ * {@link AngularDimension#RADIANS radians}.
+ *
+ * @return new instance of {@link Sin}.
+ * @since 3.3
+ */
+ public Cos cos() {
+ return cos(AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the cosine of a numeric value in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param unit the unit of measure.
+ * @return new instance of {@link Sin}.
+ * @since 3.3
+ */
+ public Cos cos(AngularDimension unit) {
+ return usesFieldRef() ? Cos.cosOf(fieldReference, unit) : Cos.cosOf(expression, unit);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the hyperbolic cosine of a numeric value given in
+ * {@link AngularDimension#RADIANS radians}.
+ *
+ * @return new instance of {@link Sin}.
+ * @since 3.3
+ */
+ public Cosh cosh() {
+ return cosh(AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the hyperbolic cosine of a numeric value.
+ *
+ * @param unit the unit of measure.
+ * @return new instance of {@link Sin}.
+ * @since 3.3
+ */
+ public Cosh cosh(AngularDimension unit) {
+ return usesFieldRef() ? Cosh.coshOf(fieldReference, unit) : Cosh.coshOf(expression, unit);
+ }
+
/**
* Creates new {@link AggregationExpression} that calculates the tangent of a numeric value given in
* {@link AngularDimension#RADIANS radians}.
@@ -2199,6 +2244,212 @@ protected String getMongoMethod() {
}
}
+ /**
+ * An {@link AggregationExpression expression} that calculates the cosine of a value that is measured in radians.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+ public static class Cos extends AbstractAggregationExpression {
+
+ private Cos(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in
+ * {@link AngularDimension#RADIANS radians}.
+ *
+ * Use {@code cosOf("angle", DEGREES)} as shortcut for
+ *
+ *
+ *
+ * .
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link Cos}.
+ */
+ public static Cos cosOf(String fieldReference) {
+ return cosOf(fieldReference, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Cos}.
+ */
+ public static Cos cosOf(String fieldReference, AngularDimension unit) {
+ return cos(Fields.field(fieldReference), unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link Cos}.
+ */
+ public static Cos cosOf(AggregationExpression expression) {
+ return cosOf(expression, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Cos}.
+ */
+ public static Cos cosOf(AggregationExpression expression, AngularDimension unit) {
+ return cos(expression, unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value
+ * @return new instance of {@link Cos}.
+ */
+ public static Cos cos(Object value) {
+ return cos(value, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in the given
+ * {@link AngularDimension unit}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Cos}.
+ */
+ public static Cos cos(Object value, AngularDimension unit) {
+
+ if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ return new Cos(ConvertOperators.DegreesToRadians.degreesToRadians(value));
+ }
+ return new Cos(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$cos";
+ }
+ }
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the hyperbolic cosine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+ public static class Cosh extends AbstractAggregationExpression {
+
+ private Cosh(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link Cosh}.
+ */
+ public static Cosh coshOf(String fieldReference) {
+ return coshOf(fieldReference, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * Use {@code coshOf("angle", DEGREES)} as shortcut for
+ *
+ *
+ *
+ * .
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Cosh}.
+ */
+ public static Cosh coshOf(String fieldReference, AngularDimension unit) {
+ return cosh(Fields.field(fieldReference), unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg.
+ * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link Cosh}.
+ */
+ public static Cosh coshOf(AggregationExpression expression) {
+ return coshOf(expression, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Cosh}.
+ */
+ public static Cosh coshOf(AggregationExpression expression, AngularDimension unit) {
+ return cosh(expression, unit);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
+ * {@link AngularDimension#RADIANS}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @return new instance of {@link Cosh}.
+ */
+ public static Cosh cosh(Object value) {
+ return cosh(value, AngularDimension.RADIANS);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
+ * the given {@link AngularDimension unit}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value
+ * @param unit the unit of measure used by the value of the given field.
+ * @return new instance of {@link Cosh}.
+ */
+ public static Cosh cosh(Object value, AngularDimension unit) {
+
+ if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ return new Cosh(ConvertOperators.DegreesToRadians.degreesToRadians(value));
+ }
+ return new Cosh(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$cosh";
+ }
+ }
+
/**
* An {@link AggregationExpression expression} that calculates the tangent of a value that is measured in radians.
*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index a2d011d6ad..1efe94c757 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -95,6 +95,8 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("integral", mapArgRef().forOperator("$integral").mappingParametersTo("input", "unit"));
map.put("sin", singleArgRef().forOperator("$sin"));
map.put("sinh", singleArgRef().forOperator("$sinh"));
+ map.put("cos", singleArgRef().forOperator("$cos"));
+ map.put("cosh", singleArgRef().forOperator("$cosh"));
map.put("tan", singleArgRef().forOperator("$tan"));
map.put("tanh", singleArgRef().forOperator("$tanh"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index 9a77d093c4..55d1647568 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -110,6 +110,34 @@ void rendersSinhWithValueInDegrees() {
.isEqualTo(Document.parse("{ $sinh : { $degreesToRadians : \"$angle\" } }"));
}
+ @Test // GH-3710
+ void rendersCos() {
+
+ assertThat(valueOf("angle").cos().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $cos : \"$angle\" }"));
+ }
+
+ @Test // GH-3710
+ void rendersCosWithValueInDegrees() {
+
+ assertThat(valueOf("angle").cos(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $cos : { $degreesToRadians : \"$angle\" } }"));
+ }
+
+ @Test // GH-3710
+ void rendersCosh() {
+
+ assertThat(valueOf("angle").cosh().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $cosh : \"$angle\" }"));
+ }
+
+ @Test // GH-3710
+ void rendersCoshWithValueInDegrees() {
+
+ assertThat(valueOf("angle").cosh(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $cosh : { $degreesToRadians : \"$angle\" } }"));
+ }
+
@Test // GH-3730
void rendersTan() {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index cc59a91700..e30f7f9fb9 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1014,6 +1014,16 @@ void shouldRenderSinh() {
assertThat(transform("sinh(angle)")).isEqualTo(Document.parse("{ \"$sinh\" : \"$angle\"}"));
}
+ @Test // GH-3710
+ void shouldRenderCos() {
+ assertThat(transform("cos(angle)")).isEqualTo(Document.parse("{ \"$cos\" : \"$angle\"}"));
+ }
+
+ @Test // GH-3710
+ void shouldRenderCosh() {
+ assertThat(transform("cosh(angle)")).isEqualTo(Document.parse("{ \"$cosh\" : \"$angle\"}"));
+ }
+
@Test // GH-3730
void shouldRenderTan() {
assertThat(transform("tan(angle)")).isEqualTo(Document.parse("{ \"$tan\" : \"$angle\"}"));
From df0372eee1368a5b0c03de1ad3bda9e1aaecf9e7 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Tue, 24 Aug 2021 16:11:53 +0200
Subject: [PATCH 029/920] Polishing.
Rename AngularDimension to AngularUnit. Tweak Javadoc. Simplify tests. Update reference docs.
See: #3710, #3714, #3728, #3730
Original pull request: #3755.
---
.../core/aggregation/ArithmeticOperators.java | 244 ++++++-----
.../core/aggregation/ConvertOperators.java | 3 +-
.../ArithmeticOperatorsUnitTests.java | 36 +-
.../SpelExpressionTransformerUnitTests.java | 402 +++++++++---------
.../reference/aggregation-framework.adoc | 4 +-
5 files changed, 355 insertions(+), 334 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index 4de258b4eb..7896486abf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -678,34 +678,37 @@ public Round roundToPlace(int place) {
}
/**
- * Creates new {@link AggregationExpression} that calculates the sine of a numeric value given in {@link AngularDimension#RADIANS radians}.
+ * Creates new {@link AggregationExpression} that calculates the sine of a numeric value given in
+ * {@link AngularUnit#RADIANS radians}.
*
* @return new instance of {@link Sin}.
* @since 3.3
*/
public Sin sin() {
- return sin(AngularDimension.RADIANS);
+ return sin(AngularUnit.RADIANS);
}
/**
- * Creates new {@link AggregationExpression} that calculates the sine of a numeric value in the given {@link AngularDimension unit}.
+ * Creates new {@link AggregationExpression} that calculates the sine of a numeric value in the given
+ * {@link AngularUnit unit}.
*
* @param unit the unit of measure.
* @return new instance of {@link Sin}.
* @since 3.3
*/
- public Sin sin(AngularDimension unit) {
+ public Sin sin(AngularUnit unit) {
return usesFieldRef() ? Sin.sinOf(fieldReference, unit) : Sin.sinOf(expression, unit);
}
/**
- * Creates new {@link AggregationExpression} that calculates the sine of a numeric value given in {@link AngularDimension#RADIANS radians}.
+ * Creates new {@link AggregationExpression} that calculates the sine of a numeric value given in
+ * {@link AngularUnit#RADIANS radians}.
*
* @return new instance of {@link Sin}.
* @since 3.3
*/
public Sinh sinh() {
- return sinh(AngularDimension.RADIANS);
+ return sinh(AngularUnit.RADIANS);
}
/**
@@ -715,42 +718,42 @@ public Sinh sinh() {
* @return new instance of {@link Sin}.
* @since 3.3
*/
- public Sinh sinh(AngularDimension unit) {
+ public Sinh sinh(AngularUnit unit) {
return usesFieldRef() ? Sinh.sinhOf(fieldReference, unit) : Sinh.sinhOf(expression, unit);
}
/**
* Creates new {@link AggregationExpression} that calculates the cosine of a numeric value given in
- * {@link AngularDimension#RADIANS radians}.
+ * {@link AngularUnit#RADIANS radians}.
*
* @return new instance of {@link Sin}.
* @since 3.3
*/
public Cos cos() {
- return cos(AngularDimension.RADIANS);
+ return cos(AngularUnit.RADIANS);
}
/**
* Creates new {@link AggregationExpression} that calculates the cosine of a numeric value in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param unit the unit of measure.
* @return new instance of {@link Sin}.
* @since 3.3
*/
- public Cos cos(AngularDimension unit) {
+ public Cos cos(AngularUnit unit) {
return usesFieldRef() ? Cos.cosOf(fieldReference, unit) : Cos.cosOf(expression, unit);
}
/**
* Creates new {@link AggregationExpression} that calculates the hyperbolic cosine of a numeric value given in
- * {@link AngularDimension#RADIANS radians}.
+ * {@link AngularUnit#RADIANS radians}.
*
* @return new instance of {@link Sin}.
* @since 3.3
*/
public Cosh cosh() {
- return cosh(AngularDimension.RADIANS);
+ return cosh(AngularUnit.RADIANS);
}
/**
@@ -760,42 +763,42 @@ public Cosh cosh() {
* @return new instance of {@link Sin}.
* @since 3.3
*/
- public Cosh cosh(AngularDimension unit) {
+ public Cosh cosh(AngularUnit unit) {
return usesFieldRef() ? Cosh.coshOf(fieldReference, unit) : Cosh.coshOf(expression, unit);
}
/**
* Creates new {@link AggregationExpression} that calculates the tangent of a numeric value given in
- * {@link AngularDimension#RADIANS radians}.
+ * {@link AngularUnit#RADIANS radians}.
*
* @return new instance of {@link Sin}.
* @since 3.3
*/
public Tan tan() {
- return tan(AngularDimension.RADIANS);
+ return tan(AngularUnit.RADIANS);
}
/**
* Creates new {@link AggregationExpression} that calculates the tangent of a numeric value in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param unit the unit of measure.
* @return new instance of {@link Sin}.
* @since 3.3
*/
- public Tan tan(AngularDimension unit) {
+ public Tan tan(AngularUnit unit) {
return usesFieldRef() ? Tan.tanOf(fieldReference, unit) : Tan.tanOf(expression, unit);
}
/**
* Creates new {@link AggregationExpression} that calculates the hyperbolic tangent of a numeric value given in
- * {@link AngularDimension#RADIANS radians}.
+ * {@link AngularUnit#RADIANS radians}.
*
* @return new instance of {@link Sin}.
* @since 3.3
*/
public Tanh tanh() {
- return tanh(AngularDimension.RADIANS);
+ return tanh(AngularUnit.RADIANS);
}
/**
@@ -805,7 +808,7 @@ public Tanh tanh() {
* @return new instance of {@link Sin}.
* @since 3.3
*/
- public Tanh tanh(AngularDimension unit) {
+ public Tanh tanh(AngularUnit unit) {
return usesFieldRef() ? Tanh.tanhOf(fieldReference, unit) : Tanh.tanhOf(expression, unit);
}
@@ -2047,7 +2050,7 @@ protected String getMongoMethod() {
* @author Christoph Strobl
* @since 3.3
*/
- public enum AngularDimension {
+ public enum AngularUnit {
RADIANS, DEGREES
}
@@ -2065,76 +2068,82 @@ private Sin(Object value) {
/**
* Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in
- * {@link AngularDimension#RADIANS radians}.
+ * {@link AngularUnit#RADIANS radians}.
*
- * Use {@code sinhOf("angle", DEGREES)} as shortcut for
{ $sinh : { $degreesToRadians : "$angle" } }
.
+ * Use {@code sinhOf("angle", DEGREES)} as shortcut for
+ *
+ *
+ *
+ * .
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link Sin}.
*/
public static Sin sinOf(String fieldReference) {
- return sinOf(fieldReference, AngularDimension.RADIANS);
+ return sinOf(fieldReference, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Sin}.
*/
- public static Sin sinOf(String fieldReference, AngularDimension unit) {
+ public static Sin sinOf(String fieldReference, AngularUnit unit) {
return sin(Fields.field(fieldReference), unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @return new instance of {@link Sin}.
*/
public static Sin sinOf(AggregationExpression expression) {
- return sinOf(expression, AngularDimension.RADIANS);
+ return sinOf(expression, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Sin}.
*/
- public static Sin sinOf(AggregationExpression expression, AngularDimension unit) {
+ public static Sin sinOf(AggregationExpression expression, AngularUnit unit) {
return sin(expression, unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value
* @return new instance of {@link Sin}.
*/
public static Sin sin(Object value) {
- return sin(value, AngularDimension.RADIANS);
+ return sin(value, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the sine of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Sin}.
*/
- public static Sin sin(Object value, AngularDimension unit) {
+ public static Sin sin(Object value, AngularUnit unit) {
- if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ if (ObjectUtils.nullSafeEquals(AngularUnit.DEGREES, unit)) {
return new Sin(ConvertOperators.DegreesToRadians.degreesToRadians(value));
}
return new Sin(value);
@@ -2148,7 +2157,7 @@ protected String getMongoMethod() {
/**
* An {@link AggregationExpression expression} that calculates the hyperbolic sine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @author Christoph Strobl
* @since 3.3
@@ -2161,78 +2170,85 @@ private Sinh(Object value) {
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link Sin}.
*/
public static Sinh sinhOf(String fieldReference) {
- return sinhOf(fieldReference, AngularDimension.RADIANS);
+ return sinhOf(fieldReference, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
- * Use {@code sinhOf("angle", DEGREES)} as shortcut for
{ $sinh : { $degreesToRadians : "$angle" } }
.
+ * Use {@code sinhOf("angle", DEGREES)} as shortcut for
+ *
+ *
+ *
+ * .
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Sin}.
*/
- public static Sinh sinhOf(String fieldReference, AngularDimension unit) {
+ public static Sinh sinhOf(String fieldReference, AngularUnit unit) {
return sinh(Fields.field(fieldReference), unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
- * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg. {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
+ * Use {@code sinhOf("angle", DEGREES)} as shortcut for eg.
+ * {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @return new instance of {@link Sin}.
*/
public static Sinh sinhOf(AggregationExpression expression) {
- return sinhOf(expression, AngularDimension.RADIANS);
+ return sinhOf(expression, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Sin}.
*/
- public static Sinh sinhOf(AggregationExpression expression, AngularDimension unit) {
+ public static Sinh sinhOf(AggregationExpression expression, AngularUnit unit) {
return sinh(expression, unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value.
* @return new instance of {@link Sin}.
*/
public static Sinh sinh(Object value) {
- return sinh(value, AngularDimension.RADIANS);
+ return sinh(value, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic sine of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Sin}.
*/
- public static Sinh sinh(Object value, AngularDimension unit) {
+ public static Sinh sinh(Object value, AngularUnit unit) {
- if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ if (ObjectUtils.nullSafeEquals(AngularUnit.DEGREES, unit)) {
return new Sinh(ConvertOperators.DegreesToRadians.degreesToRadians(value));
}
return new Sinh(value);
@@ -2258,82 +2274,82 @@ private Cos(Object value) {
/**
* Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in
- * {@link AngularDimension#RADIANS radians}.
+ * {@link AngularUnit#RADIANS radians}.
*
* Use {@code cosOf("angle", DEGREES)} as shortcut for
- *
+ *
*
* { $cos : { $degreesToRadians : "$angle" } }
*
- *
+ *
* .
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link Cos}.
*/
public static Cos cosOf(String fieldReference) {
- return cosOf(fieldReference, AngularDimension.RADIANS);
+ return cosOf(fieldReference, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Cos}.
*/
- public static Cos cosOf(String fieldReference, AngularDimension unit) {
+ public static Cos cosOf(String fieldReference, AngularUnit unit) {
return cos(Fields.field(fieldReference), unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @return new instance of {@link Cos}.
*/
public static Cos cosOf(AggregationExpression expression) {
- return cosOf(expression, AngularDimension.RADIANS);
+ return cosOf(expression, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Cos}.
*/
- public static Cos cosOf(AggregationExpression expression, AngularDimension unit) {
+ public static Cos cosOf(AggregationExpression expression, AngularUnit unit) {
return cos(expression, unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value
* @return new instance of {@link Cos}.
*/
public static Cos cos(Object value) {
- return cos(value, AngularDimension.RADIANS);
+ return cos(value, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the cosine of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Cos}.
*/
- public static Cos cos(Object value, AngularDimension unit) {
+ public static Cos cos(Object value, AngularUnit unit) {
- if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ if (ObjectUtils.nullSafeEquals(AngularUnit.DEGREES, unit)) {
return new Cos(ConvertOperators.DegreesToRadians.degreesToRadians(value));
}
return new Cos(value);
@@ -2347,7 +2363,7 @@ protected String getMongoMethod() {
/**
* An {@link AggregationExpression expression} that calculates the hyperbolic cosine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @author Christoph Strobl
* @since 3.3
@@ -2360,38 +2376,38 @@ private Cosh(Object value) {
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link Cosh}.
*/
public static Cosh coshOf(String fieldReference) {
- return coshOf(fieldReference, AngularDimension.RADIANS);
+ return coshOf(fieldReference, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
* Use {@code coshOf("angle", DEGREES)} as shortcut for
- *
+ *
*
* { $cosh : { $degreesToRadians : "$angle" } }
*
- *
+ *
* .
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Cosh}.
*/
- public static Cosh coshOf(String fieldReference, AngularDimension unit) {
+ public static Cosh coshOf(String fieldReference, AngularUnit unit) {
return cosh(Fields.field(fieldReference), unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* Use {@code sinhOf("angle", DEGREES)} as shortcut for eg.
* {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
@@ -2400,45 +2416,45 @@ public static Cosh coshOf(String fieldReference, AngularDimension unit) {
* @return new instance of {@link Cosh}.
*/
public static Cosh coshOf(AggregationExpression expression) {
- return coshOf(expression, AngularDimension.RADIANS);
+ return coshOf(expression, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Cosh}.
*/
- public static Cosh coshOf(AggregationExpression expression, AngularDimension unit) {
+ public static Cosh coshOf(AggregationExpression expression, AngularUnit unit) {
return cosh(expression, unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value.
* @return new instance of {@link Cosh}.
*/
public static Cosh cosh(Object value) {
- return cosh(value, AngularDimension.RADIANS);
+ return cosh(value, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic cosine of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Cosh}.
*/
- public static Cosh cosh(Object value, AngularDimension unit) {
+ public static Cosh cosh(Object value, AngularUnit unit) {
- if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ if (ObjectUtils.nullSafeEquals(AngularUnit.DEGREES, unit)) {
return new Cosh(ConvertOperators.DegreesToRadians.degreesToRadians(value));
}
return new Cosh(value);
@@ -2464,82 +2480,82 @@ private Tan(Object value) {
/**
* Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in
- * {@link AngularDimension#RADIANS radians}.
+ * {@link AngularUnit#RADIANS radians}.
*
* Use {@code tanOf("angle", DEGREES)} as shortcut for
- *
+ *
*
* { $tan : { $degreesToRadians : "$angle" } }
*
- *
+ *
* .
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link Tan}.
*/
public static Tan tanOf(String fieldReference) {
- return tanOf(fieldReference, AngularDimension.RADIANS);
+ return tanOf(fieldReference, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Tan}.
*/
- public static Tan tanOf(String fieldReference, AngularDimension unit) {
+ public static Tan tanOf(String fieldReference, AngularUnit unit) {
return tan(Fields.field(fieldReference), unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @return new instance of {@link Tan}.
*/
public static Tan tanOf(AggregationExpression expression) {
- return tanOf(expression, AngularDimension.RADIANS);
+ return tanOf(expression, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Tan}.
*/
- public static Tan tanOf(AggregationExpression expression, AngularDimension unit) {
+ public static Tan tanOf(AggregationExpression expression, AngularUnit unit) {
return tan(expression, unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value
* @return new instance of {@link Tan}.
*/
public static Tan tan(Object value) {
- return tan(value, AngularDimension.RADIANS);
+ return tan(value, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the tangent of a value that is measured in the given
- * {@link AngularDimension unit}.
+ * {@link AngularUnit unit}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Tan}.
*/
- public static Tan tan(Object value, AngularDimension unit) {
+ public static Tan tan(Object value, AngularUnit unit) {
- if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ if (ObjectUtils.nullSafeEquals(AngularUnit.DEGREES, unit)) {
return new Tan(ConvertOperators.DegreesToRadians.degreesToRadians(value));
}
return new Tan(value);
@@ -2553,7 +2569,7 @@ protected String getMongoMethod() {
/**
* An {@link AggregationExpression expression} that calculates the hyperbolic tangent of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @author Christoph Strobl
* @since 3.3
@@ -2566,38 +2582,38 @@ private Tanh(Object value) {
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link Tanh}.
*/
public static Tanh tanhOf(String fieldReference) {
- return tanhOf(fieldReference, AngularDimension.RADIANS);
+ return tanhOf(fieldReference, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
* Use {@code tanhOf("angle", DEGREES)} as shortcut for
- *
+ *
*
* { $tanh : { $degreesToRadians : "$angle" } }
*
- *
+ *
* .
*
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Tanh}.
*/
- public static Tanh tanhOf(String fieldReference, AngularDimension unit) {
+ public static Tanh tanhOf(String fieldReference, AngularUnit unit) {
return tanh(Fields.field(fieldReference), unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* Use {@code sinhOf("angle", DEGREES)} as shortcut for eg.
* {@code sinhOf(ConvertOperators.valueOf("angle").degreesToRadians())}.
@@ -2606,45 +2622,45 @@ public static Tanh tanhOf(String fieldReference, AngularDimension unit) {
* @return new instance of {@link Tanh}.
*/
public static Tanh tanhOf(AggregationExpression expression) {
- return tanhOf(expression, AngularDimension.RADIANS);
+ return tanhOf(expression, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Tanh}.
*/
- public static Tanh tanhOf(AggregationExpression expression, AngularDimension unit) {
+ public static Tanh tanhOf(AggregationExpression expression, AngularUnit unit) {
return tanh(expression, unit);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
- * {@link AngularDimension#RADIANS}.
+ * {@link AngularUnit#RADIANS}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value.
* @return new instance of {@link Tanh}.
*/
public static Tanh tanh(Object value) {
- return tanh(value, AngularDimension.RADIANS);
+ return tanh(value, AngularUnit.RADIANS);
}
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
- * the given {@link AngularDimension unit}.
+ * the given {@link AngularUnit unit}.
*
* @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
* numeric value
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Tanh}.
*/
- public static Tanh tanh(Object value, AngularDimension unit) {
+ public static Tanh tanh(Object value, AngularUnit unit) {
- if (ObjectUtils.nullSafeEquals(AngularDimension.DEGREES, unit)) {
+ if (ObjectUtils.nullSafeEquals(AngularUnit.DEGREES, unit)) {
return new Tanh(ConvertOperators.DegreesToRadians.degreesToRadians(value));
}
return new Tanh(value);
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java
index b34933444a..637ebd8d8f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConvertOperators.java
@@ -232,7 +232,8 @@ public ToString convertToString() {
}
/**
- * {@link AggregationExpression} for {@code $degreesToRadians} that converts an input value measured in degrees to radians.\
+ * {@link AggregationExpression} for {@code $degreesToRadians} that converts an input value measured in degrees to
+ * radians.
*
* @return new instance of {@link DegreesToRadians}.
* @since 3.3
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index 55d1647568..7cde7cd1c4 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -86,84 +86,84 @@ void rendersIntegralWithUnit() {
void rendersSin() {
assertThat(valueOf("angle").sin().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $sin : \"$angle\" }"));
+ .isEqualTo("{ $sin : \"$angle\" }");
}
@Test // GH-3728
void rendersSinWithValueInDegrees() {
- assertThat(valueOf("angle").sin(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $sin : { $degreesToRadians : \"$angle\" } }"));
+ assertThat(valueOf("angle").sin(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $sin : { $degreesToRadians : \"$angle\" } }");
}
@Test // GH-3728
void rendersSinh() {
assertThat(valueOf("angle").sinh().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $sinh : \"$angle\" }"));
+ .isEqualTo("{ $sinh : \"$angle\" }");
}
@Test // GH-3728
void rendersSinhWithValueInDegrees() {
- assertThat(valueOf("angle").sinh(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $sinh : { $degreesToRadians : \"$angle\" } }"));
+ assertThat(valueOf("angle").sinh(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $sinh : { $degreesToRadians : \"$angle\" } }");
}
@Test // GH-3710
void rendersCos() {
assertThat(valueOf("angle").cos().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $cos : \"$angle\" }"));
+ .isEqualTo("{ $cos : \"$angle\" }");
}
@Test // GH-3710
void rendersCosWithValueInDegrees() {
- assertThat(valueOf("angle").cos(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $cos : { $degreesToRadians : \"$angle\" } }"));
+ assertThat(valueOf("angle").cos(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $cos : { $degreesToRadians : \"$angle\" } }");
}
@Test // GH-3710
void rendersCosh() {
assertThat(valueOf("angle").cosh().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $cosh : \"$angle\" }"));
+ .isEqualTo("{ $cosh : \"$angle\" }");
}
@Test // GH-3710
void rendersCoshWithValueInDegrees() {
- assertThat(valueOf("angle").cosh(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $cosh : { $degreesToRadians : \"$angle\" } }"));
+ assertThat(valueOf("angle").cosh(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $cosh : { $degreesToRadians : \"$angle\" } }");
}
@Test // GH-3730
void rendersTan() {
assertThat(valueOf("angle").tan().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $tan : \"$angle\" }"));
+ .isEqualTo("{ $tan : \"$angle\" }");
}
@Test // GH-3730
void rendersTanWithValueInDegrees() {
- assertThat(valueOf("angle").tan(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $tan : { $degreesToRadians : \"$angle\" } }"));
+ assertThat(valueOf("angle").tan(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $tan : { $degreesToRadians : \"$angle\" } }");
}
@Test // GH-3730
void rendersTanh() {
assertThat(valueOf("angle").tanh().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $tanh : \"$angle\" }"));
+ .isEqualTo("{ $tanh : \"$angle\" }");
}
@Test // GH-3730
void rendersTanhWithValueInDegrees() {
- assertThat(valueOf("angle").tanh(AngularDimension.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $tanh : { $degreesToRadians : \"$angle\" } }"));
+ assertThat(valueOf("angle").tanh(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $tanh : { $degreesToRadians : \"$angle\" } }");
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index e30f7f9fb9..193ffb520d 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -15,7 +15,7 @@
*/
package org.springframework.data.mongodb.core.aggregation;
-import static org.assertj.core.api.Assertions.*;
+import static org.springframework.data.mongodb.test.util.Assertions.*;
import java.util.Arrays;
@@ -53,21 +53,21 @@ void beforeEach() {
@Test // DATAMONGO-774
void shouldRenderConstantExpression() {
- assertThat(transform("1")).isEqualTo("1");
- assertThat(transform("-1")).isEqualTo("-1");
- assertThat(transform("1.0")).isEqualTo("1.0");
- assertThat(transform("-1.0")).isEqualTo("-1.0");
- assertThat(transform("null")).isNull();
+ assertThat(transformValue("1")).isEqualTo("1");
+ assertThat(transformValue("-1")).isEqualTo("-1");
+ assertThat(transformValue("1.0")).isEqualTo("1.0");
+ assertThat(transformValue("-1.0")).isEqualTo("-1.0");
+ assertThat(transformValue("null")).isNull();
}
@Test // DATAMONGO-774
void shouldSupportKnownOperands() {
- assertThat(transform("a + b")).isEqualTo(Document.parse("{ \"$add\" : [ \"$a\" , \"$b\"]}"));
- assertThat(transform("a - b")).isEqualTo(Document.parse("{ \"$subtract\" : [ \"$a\" , \"$b\"]}"));
- assertThat(transform("a * b")).isEqualTo(Document.parse("{ \"$multiply\" : [ \"$a\" , \"$b\"]}"));
- assertThat(transform("a / b")).isEqualTo(Document.parse("{ \"$divide\" : [ \"$a\" , \"$b\"]}"));
- assertThat(transform("a % b")).isEqualTo(Document.parse("{ \"$mod\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("a + b")).isEqualTo("{ \"$add\" : [ \"$a\" , \"$b\"]}");
+ assertThat(transform("a - b")).isEqualTo("{ \"$subtract\" : [ \"$a\" , \"$b\"]}");
+ assertThat(transform("a * b")).isEqualTo("{ \"$multiply\" : [ \"$a\" , \"$b\"]}");
+ assertThat(transform("a / b")).isEqualTo("{ \"$divide\" : [ \"$a\" , \"$b\"]}");
+ assertThat(transform("a % b")).isEqualTo("{ \"$mod\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-774
@@ -77,35 +77,35 @@ void shouldThrowExceptionOnUnknownOperand() {
@Test // DATAMONGO-774
void shouldRenderSumExpression() {
- assertThat(transform("a + 1")).isEqualTo(Document.parse("{ \"$add\" : [ \"$a\" , 1]}"));
+ assertThat(transform("a + 1")).isEqualTo("{ \"$add\" : [ \"$a\" , 1]}");
}
@Test // DATAMONGO-774
void shouldRenderFormula() {
- assertThat(transform("(netPrice + surCharge) * taxrate + 42")).isEqualTo(Document.parse(
- "{ \"$add\" : [ { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\"]} , 42]}"));
+ assertThat(transform("(netPrice + surCharge) * taxrate + 42")).isEqualTo(
+ "{ \"$add\" : [ { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\"]} , 42]}");
}
@Test // DATAMONGO-774
void shouldRenderFormulaInCurlyBrackets() {
- assertThat(transform("{(netPrice + surCharge) * taxrate + 42}")).isEqualTo(Document.parse(
- "{ \"$add\" : [ { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\"]} , 42]}"));
+ assertThat(transform("{(netPrice + surCharge) * taxrate + 42}")).isEqualTo(
+ "{ \"$add\" : [ { \"$multiply\" : [ { \"$add\" : [ \"$netPrice\" , \"$surCharge\"]} , \"$taxrate\"]} , 42]}");
}
@Test // DATAMONGO-774
void shouldRenderFieldReference() {
- assertThat(transform("foo")).isEqualTo("$foo");
- assertThat(transform("$foo")).isEqualTo("$foo");
+ assertThat(transformValue("foo")).isEqualTo("$foo");
+ assertThat(transformValue("$foo")).isEqualTo("$foo");
}
@Test // DATAMONGO-774
void shouldRenderNestedFieldReference() {
- assertThat(transform("foo.bar")).isEqualTo("$foo.bar");
- assertThat(transform("$foo.bar")).isEqualTo("$foo.bar");
+ assertThat(transformValue("foo.bar")).isEqualTo("$foo.bar");
+ assertThat(transformValue("$foo.bar")).isEqualTo("$foo.bar");
}
@Test // DATAMONGO-774
@@ -113,52 +113,52 @@ void shouldRenderNestedFieldReference() {
void shouldRenderNestedIndexedFieldReference() {
// TODO add support for rendering nested indexed field references
- assertThat(transform("foo[3].bar")).isEqualTo("$foo[3].bar");
+ assertThat(transformValue("foo[3].bar")).isEqualTo("$foo[3].bar");
}
@Test // DATAMONGO-774
void shouldRenderConsecutiveOperation() {
- assertThat(transform("1 + 1 + 1")).isEqualTo(Document.parse("{ \"$add\" : [ 1 , 1 , 1]}"));
+ assertThat(transform("1 + 1 + 1")).isEqualTo("{ \"$add\" : [ 1 , 1 , 1]}");
}
@Test // DATAMONGO-774
void shouldRenderComplexExpression0() {
assertThat(transform("-(1 + q)"))
- .isEqualTo(Document.parse("{ \"$multiply\" : [ -1 , { \"$add\" : [ 1 , \"$q\"]}]}"));
+ .isEqualTo("{ \"$multiply\" : [ -1 , { \"$add\" : [ 1 , \"$q\"]}]}");
}
@Test // DATAMONGO-774
void shouldRenderComplexExpression1() {
- assertThat(transform("1 + (q + 1) / (q - 1)")).isEqualTo(Document.parse(
- "{ \"$add\" : [ 1 , { \"$divide\" : [ { \"$add\" : [ \"$q\" , 1]} , { \"$subtract\" : [ \"$q\" , 1]}]}]}"));
+ assertThat(transform("1 + (q + 1) / (q - 1)")).isEqualTo(
+ "{ \"$add\" : [ 1 , { \"$divide\" : [ { \"$add\" : [ \"$q\" , 1]} , { \"$subtract\" : [ \"$q\" , 1]}]}]}");
}
@Test // DATAMONGO-774
void shouldRenderComplexExpression2() {
- assertThat(transform("(q + 1 + 4 - 5) / (q + 1 + 3 + 4)")).isEqualTo(Document.parse(
- "{ \"$divide\" : [ { \"$subtract\" : [ { \"$add\" : [ \"$q\" , 1 , 4]} , 5]} , { \"$add\" : [ \"$q\" , 1 , 3 , 4]}]}"));
+ assertThat(transform("(q + 1 + 4 - 5) / (q + 1 + 3 + 4)")).isEqualTo(
+ "{ \"$divide\" : [ { \"$subtract\" : [ { \"$add\" : [ \"$q\" , 1 , 4]} , 5]} , { \"$add\" : [ \"$q\" , 1 , 3 , 4]}]}");
}
@Test // DATAMONGO-774
void shouldRenderBinaryExpressionWithMixedSignsCorrectly() {
- assertThat(transform("-4 + 1")).isEqualTo(Document.parse("{ \"$add\" : [ -4 , 1]}"));
- assertThat(transform("1 + -4")).isEqualTo(Document.parse("{ \"$add\" : [ 1 , -4]}"));
+ assertThat(transform("-4 + 1")).isEqualTo("{ \"$add\" : [ -4 , 1]}");
+ assertThat(transform("1 + -4")).isEqualTo("{ \"$add\" : [ 1 , -4]}");
}
@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\"]}]}"));
+ .isEqualTo("{ \"$add\" : [ 1 , 1 , { \"$divide\" : [ { \"$add\" : [ 1 , 1 , 1]} , \"$q\"]}]}");
}
@Test // DATAMONGO-774
void shouldRenderParameterExpressionResults() {
- assertThat(transform("[0] + [1] + [2]", 1, 2, 3)).isEqualTo(Document.parse("{ \"$add\" : [ 1 , 2 , 3]}"));
+ assertThat(transform("[0] + [1] + [2]", 1, 2, 3)).isEqualTo("{ \"$add\" : [ 1 , 2 , 3]}");
}
@Test // DATAMONGO-774
@@ -189,852 +189,856 @@ void shouldRenderCompoundExpressionsWithIndexerAndFieldReference() {
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("{ \"$add\" : [ 10 , \"$a.c\"] }");
}
@Test // DATAMONGO-840
void shouldRenderCompoundExpressionsWithOnlyFieldReferences() {
- assertThat(transform("a.b + a.c")).isEqualTo(Document.parse("{ \"$add\" : [ \"$a.b\" , \"$a.c\"]}"));
+ assertThat(transform("a.b + a.c")).isEqualTo("{ \"$add\" : [ \"$a.b\" , \"$a.c\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeAnd() {
- assertThat(transform("and(a, b)")).isEqualTo(Document.parse("{ \"$and\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("and(a, b)")).isEqualTo("{ \"$and\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeOr() {
- assertThat(transform("or(a, b)")).isEqualTo(Document.parse("{ \"$or\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("or(a, b)")).isEqualTo("{ \"$or\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeNot() {
- assertThat(transform("not(a)")).isEqualTo(Document.parse("{ \"$not\" : [ \"$a\"]}"));
+ assertThat(transform("not(a)")).isEqualTo("{ \"$not\" : [ \"$a\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeSetEquals() {
- assertThat(transform("setEquals(a, b)")).isEqualTo(Document.parse("{ \"$setEquals\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("setEquals(a, b)")).isEqualTo("{ \"$setEquals\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeSetEqualsForArrays() {
assertThat(transform("setEquals(new int[]{1,2,3}, new int[]{4,5,6})"))
- .isEqualTo(Document.parse("{ \"$setEquals\" : [ [ 1 , 2 , 3] , [ 4 , 5 , 6]]}"));
+ .isEqualTo("{ \"$setEquals\" : [ [ 1 , 2 , 3] , [ 4 , 5 , 6]]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeSetEqualsMixedArrays() {
assertThat(transform("setEquals(a, new int[]{4,5,6})"))
- .isEqualTo(Document.parse("{ \"$setEquals\" : [ \"$a\" , [ 4 , 5 , 6]]}"));
+ .isEqualTo("{ \"$setEquals\" : [ \"$a\" , [ 4 , 5 , 6]]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceSetIntersection() {
assertThat(transform("setIntersection(a, new int[]{4,5,6})"))
- .isEqualTo(Document.parse("{ \"$setIntersection\" : [ \"$a\" , [ 4 , 5 , 6]]}"));
+ .isEqualTo("{ \"$setIntersection\" : [ \"$a\" , [ 4 , 5 , 6]]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceSetUnion() {
assertThat(transform("setUnion(a, new int[]{4,5,6})"))
- .isEqualTo(Document.parse("{ \"$setUnion\" : [ \"$a\" , [ 4 , 5 , 6]]}"));
+ .isEqualTo("{ \"$setUnion\" : [ \"$a\" , [ 4 , 5 , 6]]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceSeDifference() {
assertThat(transform("setDifference(a, new int[]{4,5,6})"))
- .isEqualTo(Document.parse("{ \"$setDifference\" : [ \"$a\" , [ 4 , 5 , 6]]}"));
+ .isEqualTo("{ \"$setDifference\" : [ \"$a\" , [ 4 , 5 , 6]]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceSetIsSubset() {
assertThat(transform("setIsSubset(a, new int[]{4,5,6})"))
- .isEqualTo(Document.parse("{ \"$setIsSubset\" : [ \"$a\" , [ 4 , 5 , 6]]}"));
+ .isEqualTo("{ \"$setIsSubset\" : [ \"$a\" , [ 4 , 5 , 6]]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceAnyElementTrue() {
- assertThat(transform("anyElementTrue(a)")).isEqualTo(Document.parse("{ \"$anyElementTrue\" : [ \"$a\"]}"));
+ assertThat(transform("anyElementTrue(a)")).isEqualTo("{ \"$anyElementTrue\" : [ \"$a\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceAllElementsTrue() {
assertThat(transform("allElementsTrue(a, new int[]{4,5,6})"))
- .isEqualTo(Document.parse("{ \"$allElementsTrue\" : [ \"$a\" , [ 4 , 5 , 6]]}"));
+ .isEqualTo("{ \"$allElementsTrue\" : [ \"$a\" , [ 4 , 5 , 6]]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceCmp() {
- assertThat(transform("cmp(a, 250)")).isEqualTo(Document.parse("{ \"$cmp\" : [ \"$a\" , 250]}"));
+ assertThat(transform("cmp(a, 250)")).isEqualTo("{ \"$cmp\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceEq() {
- assertThat(transform("eq(a, 250)")).isEqualTo(Document.parse("{ \"$eq\" : [ \"$a\" , 250]}"));
+ assertThat(transform("eq(a, 250)")).isEqualTo("{ \"$eq\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceGt() {
- assertThat(transform("gt(a, 250)")).isEqualTo(Document.parse("{ \"$gt\" : [ \"$a\" , 250]}"));
+ assertThat(transform("gt(a, 250)")).isEqualTo("{ \"$gt\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceGte() {
- assertThat(transform("gte(a, 250)")).isEqualTo(Document.parse("{ \"$gte\" : [ \"$a\" , 250]}"));
+ assertThat(transform("gte(a, 250)")).isEqualTo("{ \"$gte\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceLt() {
- assertThat(transform("lt(a, 250)")).isEqualTo(Document.parse("{ \"$lt\" : [ \"$a\" , 250]}"));
+ assertThat(transform("lt(a, 250)")).isEqualTo("{ \"$lt\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceLte() {
- assertThat(transform("lte(a, 250)")).isEqualTo(Document.parse("{ \"$lte\" : [ \"$a\" , 250]}"));
+ assertThat(transform("lte(a, 250)")).isEqualTo("{ \"$lte\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNe() {
- assertThat(transform("ne(a, 250)")).isEqualTo(Document.parse("{ \"$ne\" : [ \"$a\" , 250]}"));
+ assertThat(transform("ne(a, 250)")).isEqualTo("{ \"$ne\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceAbs() {
- assertThat(transform("abs(1)")).isEqualTo(Document.parse("{ \"$abs\" : 1}"));
+ assertThat(transform("abs(1)")).isEqualTo("{ \"$abs\" : 1}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceAdd() {
- assertThat(transform("add(a, 250)")).isEqualTo(Document.parse("{ \"$add\" : [ \"$a\" , 250]}"));
+ assertThat(transform("add(a, 250)")).isEqualTo("{ \"$add\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceCeil() {
- assertThat(transform("ceil(7.8)")).isEqualTo(Document.parse("{ \"$ceil\" : 7.8}"));
+ assertThat(transform("ceil(7.8)")).isEqualTo("{ \"$ceil\" : 7.8}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceDivide() {
- assertThat(transform("divide(a, 250)")).isEqualTo(Document.parse("{ \"$divide\" : [ \"$a\" , 250]}"));
+ assertThat(transform("divide(a, 250)")).isEqualTo("{ \"$divide\" : [ \"$a\" , 250]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceExp() {
- assertThat(transform("exp(2)")).isEqualTo(Document.parse("{ \"$exp\" : 2}"));
+ assertThat(transform("exp(2)")).isEqualTo("{ \"$exp\" : 2}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceFloor() {
- assertThat(transform("floor(2)")).isEqualTo(Document.parse("{ \"$floor\" : 2}"));
+ assertThat(transform("floor(2)")).isEqualTo("{ \"$floor\" : 2}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceLn() {
- assertThat(transform("ln(2)")).isEqualTo(Document.parse("{ \"$ln\" : 2}"));
+ assertThat(transform("ln(2)")).isEqualTo("{ \"$ln\" : 2}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceLog() {
- assertThat(transform("log(100, 10)")).isEqualTo(Document.parse("{ \"$log\" : [ 100 , 10]}"));
+ assertThat(transform("log(100, 10)")).isEqualTo("{ \"$log\" : [ 100 , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceLog10() {
- assertThat(transform("log10(100)")).isEqualTo(Document.parse("{ \"$log10\" : 100}"));
+ assertThat(transform("log10(100)")).isEqualTo("{ \"$log10\" : 100}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeMod() {
- assertThat(transform("mod(a, b)")).isEqualTo(Document.parse("{ \"$mod\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("mod(a, b)")).isEqualTo("{ \"$mod\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeMultiply() {
- assertThat(transform("multiply(a, b)")).isEqualTo(Document.parse("{ \"$multiply\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("multiply(a, b)")).isEqualTo("{ \"$multiply\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodePow() {
- assertThat(transform("pow(a, 2)")).isEqualTo(Document.parse("{ \"$pow\" : [ \"$a\" , 2]}"));
+ assertThat(transform("pow(a, 2)")).isEqualTo("{ \"$pow\" : [ \"$a\" , 2]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceSqrt() {
- assertThat(transform("sqrt(2)")).isEqualTo(Document.parse("{ \"$sqrt\" : 2}"));
+ assertThat(transform("sqrt(2)")).isEqualTo("{ \"$sqrt\" : 2}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeSubtract() {
- assertThat(transform("subtract(a, b)")).isEqualTo(Document.parse("{ \"$subtract\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("subtract(a, b)")).isEqualTo("{ \"$subtract\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceTrunc() {
- assertThat(transform("trunc(2.1)")).isEqualTo(Document.parse("{ \"$trunc\" : 2.1}"));
+ assertThat(transform("trunc(2.1)")).isEqualTo("{ \"$trunc\" : 2.1}");
}
@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("{ \"$concat\" : [ \"$a\" , \"$b\" , \"c\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeSubstrc() {
- assertThat(transform("substr(a, 0, 1)")).isEqualTo(Document.parse("{ \"$substr\" : [ \"$a\" , 0 , 1]}"));
+ assertThat(transform("substr(a, 0, 1)")).isEqualTo("{ \"$substr\" : [ \"$a\" , 0 , 1]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceToLower() {
- assertThat(transform("toLower(a)")).isEqualTo(Document.parse("{ \"$toLower\" : \"$a\"}"));
+ assertThat(transform("toLower(a)")).isEqualTo("{ \"$toLower\" : \"$a\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceToUpper() {
- assertThat(transform("toUpper(a)")).isEqualTo(Document.parse("{ \"$toUpper\" : \"$a\"}"));
+ assertThat(transform("toUpper(a)")).isEqualTo("{ \"$toUpper\" : \"$a\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeStrCaseCmp() {
- assertThat(transform("strcasecmp(a, b)")).isEqualTo(Document.parse("{ \"$strcasecmp\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("strcasecmp(a, b)")).isEqualTo("{ \"$strcasecmp\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceMeta() {
- assertThat(transform("meta('textScore')")).isEqualTo(Document.parse("{ \"$meta\" : \"textScore\"}"));
+ assertThat(transform("meta('textScore')")).isEqualTo("{ \"$meta\" : \"textScore\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeArrayElemAt() {
- assertThat(transform("arrayElemAt(a, 10)")).isEqualTo(Document.parse("{ \"$arrayElemAt\" : [ \"$a\" , 10]}"));
+ assertThat(transform("arrayElemAt(a, 10)")).isEqualTo("{ \"$arrayElemAt\" : [ \"$a\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeConcatArrays() {
assertThat(transform("concatArrays(a, b, c)"))
- .isEqualTo(Document.parse("{ \"$concatArrays\" : [ \"$a\" , \"$b\" , \"$c\"]}"));
+ .isEqualTo("{ \"$concatArrays\" : [ \"$a\" , \"$b\" , \"$c\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeFilter() {
- assertThat(transform("filter(a, 'num', '$$num' > 10)")).isEqualTo(Document.parse(
- "{ \"$filter\" : { \"input\" : \"$a\" , \"as\" : \"num\" , \"cond\" : { \"$gt\" : [ \"$$num\" , 10]}}}"));
+ assertThat(transform("filter(a, 'num', '$$num' > 10)")).isEqualTo(
+ "{ \"$filter\" : { \"input\" : \"$a\" , \"as\" : \"num\" , \"cond\" : { \"$gt\" : [ \"$$num\" , 10]}}}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceIsArray() {
- assertThat(transform("isArray(a)")).isEqualTo(Document.parse("{ \"$isArray\" : \"$a\"}"));
+ assertThat(transform("isArray(a)")).isEqualTo("{ \"$isArray\" : \"$a\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceIsSize() {
- assertThat(transform("size(a)")).isEqualTo(Document.parse("{ \"$size\" : \"$a\"}"));
+ assertThat(transform("size(a)")).isEqualTo("{ \"$size\" : \"$a\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeSlice() {
- assertThat(transform("slice(a, 10)")).isEqualTo(Document.parse("{ \"$slice\" : [ \"$a\" , 10]}"));
+ assertThat(transform("slice(a, 10)")).isEqualTo("{ \"$slice\" : [ \"$a\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeMap() {
- assertThat(transform("map(quizzes, 'grade', '$$grade' + 2)")).isEqualTo(Document.parse(
- "{ \"$map\" : { \"input\" : \"$quizzes\" , \"as\" : \"grade\" , \"in\" : { \"$add\" : [ \"$$grade\" , 2]}}}"));
+ assertThat(transform("map(quizzes, 'grade', '$$grade' + 2)")).isEqualTo(
+ "{ \"$map\" : { \"input\" : \"$quizzes\" , \"as\" : \"grade\" , \"in\" : { \"$add\" : [ \"$$grade\" , 2]}}}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeLet() {
- assertThat(transform("let({low:1, high:'$$low'}, gt('$$low', '$$high'))")).isEqualTo(Document.parse(
- "{ \"$let\" : { \"vars\" : { \"low\" : 1 , \"high\" : \"$$low\"} , \"in\" : { \"$gt\" : [ \"$$low\" , \"$$high\"]}}}"));
+ assertThat(transform("let({low:1, high:'$$low'}, gt('$$low', '$$high'))")).isEqualTo(
+ "{ \"$let\" : { \"vars\" : { \"low\" : 1 , \"high\" : \"$$low\"} , \"in\" : { \"$gt\" : [ \"$$low\" , \"$$high\"]}}}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceLiteral() {
- assertThat(transform("literal($1)")).isEqualTo(Document.parse("{ \"$literal\" : \"$1\"}"));
+ assertThat(transform("literal($1)")).isEqualTo("{ \"$literal\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceDayOfYear() {
- assertThat(transform("dayOfYear($1)")).isEqualTo(Document.parse("{ \"$dayOfYear\" : \"$1\"}"));
+ assertThat(transform("dayOfYear($1)")).isEqualTo("{ \"$dayOfYear\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceDayOfMonth() {
- assertThat(transform("dayOfMonth($1)")).isEqualTo(Document.parse("{ \"$dayOfMonth\" : \"$1\"}"));
+ assertThat(transform("dayOfMonth($1)")).isEqualTo("{ \"$dayOfMonth\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceDayOfWeek() {
- assertThat(transform("dayOfWeek($1)")).isEqualTo(Document.parse("{ \"$dayOfWeek\" : \"$1\"}"));
+ assertThat(transform("dayOfWeek($1)")).isEqualTo("{ \"$dayOfWeek\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceYear() {
- assertThat(transform("year($1)")).isEqualTo(Document.parse("{ \"$year\" : \"$1\"}"));
+ assertThat(transform("year($1)")).isEqualTo("{ \"$year\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceMonth() {
- assertThat(transform("month($1)")).isEqualTo(Document.parse("{ \"$month\" : \"$1\"}"));
+ assertThat(transform("month($1)")).isEqualTo("{ \"$month\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceWeek() {
- assertThat(transform("week($1)")).isEqualTo(Document.parse("{ \"$week\" : \"$1\"}"));
+ assertThat(transform("week($1)")).isEqualTo("{ \"$week\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceHour() {
- assertThat(transform("hour($1)")).isEqualTo(Document.parse("{ \"$hour\" : \"$1\"}"));
+ assertThat(transform("hour($1)")).isEqualTo("{ \"$hour\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceMinute() {
- assertThat(transform("minute($1)")).isEqualTo(Document.parse("{ \"$minute\" : \"$1\"}"));
+ assertThat(transform("minute($1)")).isEqualTo("{ \"$minute\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceSecond() {
- assertThat(transform("second($1)")).isEqualTo(Document.parse("{ \"$second\" : \"$1\"}"));
+ assertThat(transform("second($1)")).isEqualTo("{ \"$second\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceMillisecond() {
- assertThat(transform("millisecond($1)")).isEqualTo(Document.parse("{ \"$millisecond\" : \"$1\"}"));
+ assertThat(transform("millisecond($1)")).isEqualTo("{ \"$millisecond\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceDateToString() {
assertThat(transform("dateToString('%Y-%m-%d', $date)"))
- .isEqualTo(Document.parse("{ \"$dateToString\" : { \"format\" : \"%Y-%m-%d\" , \"date\" : \"$date\"}}"));
+ .isEqualTo("{ \"$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}}"));
+ "{ \"$cond\" : { \"if\" : { \"$gt\" : [ \"$qty\" , 250]} , \"then\" : 30 , \"else\" : 20}}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeIfNull() {
- assertThat(transform("ifNull(a, 10)")).isEqualTo(Document.parse("{ \"$ifNull\" : [ \"$a\" , 10]}"));
+ assertThat(transform("ifNull(a, 10)")).isEqualTo("{ \"$ifNull\" : [ \"$a\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeSum() {
- assertThat(transform("sum(a, b)")).isEqualTo(Document.parse("{ \"$sum\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("sum(a, b)")).isEqualTo("{ \"$sum\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeAvg() {
- assertThat(transform("avg(a, b)")).isEqualTo(Document.parse("{ \"$avg\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("avg(a, b)")).isEqualTo("{ \"$avg\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceFirst() {
- assertThat(transform("first($1)")).isEqualTo(Document.parse("{ \"$first\" : \"$1\"}"));
+ assertThat(transform("first($1)")).isEqualTo("{ \"$first\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceLast() {
- assertThat(transform("last($1)")).isEqualTo(Document.parse("{ \"$last\" : \"$1\"}"));
+ assertThat(transform("last($1)")).isEqualTo("{ \"$last\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeMax() {
- assertThat(transform("max(a, b)")).isEqualTo(Document.parse("{ \"$max\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("max(a, b)")).isEqualTo("{ \"$max\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeMin() {
- assertThat(transform("min(a, b)")).isEqualTo(Document.parse("{ \"$min\" : [ \"$a\" , \"$b\"]}"));
+ assertThat(transform("min(a, b)")).isEqualTo("{ \"$min\" : [ \"$a\" , \"$b\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodePush() {
assertThat(transform("push({'item':'$item', 'quantity':'$qty'})"))
- .isEqualTo(Document.parse("{ \"$push\" : { \"item\" : \"$item\" , \"quantity\" : \"$qty\"}}"));
+ .isEqualTo("{ \"$push\" : { \"item\" : \"$item\" , \"quantity\" : \"$qty\"}}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceAddToSet() {
- assertThat(transform("addToSet($1)")).isEqualTo(Document.parse("{ \"$addToSet\" : \"$1\"}"));
+ assertThat(transform("addToSet($1)")).isEqualTo("{ \"$addToSet\" : \"$1\"}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeStdDevPop() {
assertThat(transform("stdDevPop(scores.score)"))
- .isEqualTo(Document.parse("{ \"$stdDevPop\" : [ \"$scores.score\"]}"));
+ .isEqualTo("{ \"$stdDevPop\" : [ \"$scores.score\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderMethodReferenceNodeStdDevSamp() {
- assertThat(transform("stdDevSamp(age)")).isEqualTo(Document.parse("{ \"$stdDevSamp\" : [ \"$age\"]}"));
+ assertThat(transform("stdDevSamp(age)")).isEqualTo("{ \"$stdDevSamp\" : [ \"$age\"]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodeEq() {
- assertThat(transform("foo == 10")).isEqualTo(Document.parse("{ \"$eq\" : [ \"$foo\" , 10]}"));
+ assertThat(transform("foo == 10")).isEqualTo("{ \"$eq\" : [ \"$foo\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodeNe() {
- assertThat(transform("foo != 10")).isEqualTo(Document.parse("{ \"$ne\" : [ \"$foo\" , 10]}"));
+ assertThat(transform("foo != 10")).isEqualTo("{ \"$ne\" : [ \"$foo\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodeGt() {
- assertThat(transform("foo > 10")).isEqualTo(Document.parse("{ \"$gt\" : [ \"$foo\" , 10]}"));
+ assertThat(transform("foo > 10")).isEqualTo("{ \"$gt\" : [ \"$foo\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodeGte() {
- assertThat(transform("foo >= 10")).isEqualTo(Document.parse("{ \"$gte\" : [ \"$foo\" , 10]}"));
+ assertThat(transform("foo >= 10")).isEqualTo("{ \"$gte\" : [ \"$foo\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodeLt() {
- assertThat(transform("foo < 10")).isEqualTo(Document.parse("{ \"$lt\" : [ \"$foo\" , 10]}"));
+ assertThat(transform("foo < 10")).isEqualTo("{ \"$lt\" : [ \"$foo\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodeLte() {
- assertThat(transform("foo <= 10")).isEqualTo(Document.parse("{ \"$lte\" : [ \"$foo\" , 10]}"));
+ assertThat(transform("foo <= 10")).isEqualTo("{ \"$lte\" : [ \"$foo\" , 10]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodePow() {
- assertThat(transform("foo^2")).isEqualTo(Document.parse("{ \"$pow\" : [ \"$foo\" , 2]}"));
+ assertThat(transform("foo^2")).isEqualTo("{ \"$pow\" : [ \"$foo\" , 2]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodeOr() {
- assertThat(transform("true || false")).isEqualTo(Document.parse("{ \"$or\" : [ true , false]}"));
+ assertThat(transform("true || false")).isEqualTo("{ \"$or\" : [ true , false]}");
}
@Test // DATAMONGO-1530
void shouldRenderComplexOperationNodeOr() {
assertThat(transform("1+2 || concat(a, b) || true")).isEqualTo(
- Document.parse("{ \"$or\" : [ { \"$add\" : [ 1 , 2]} , { \"$concat\" : [ \"$a\" , \"$b\"]} , true]}"));
+ "{ \"$or\" : [ { \"$add\" : [ 1 , 2]} , { \"$concat\" : [ \"$a\" , \"$b\"]} , true]}");
}
@Test // DATAMONGO-1530
void shouldRenderOperationNodeAnd() {
- assertThat(transform("true && false")).isEqualTo(Document.parse("{ \"$and\" : [ true , false]}"));
+ assertThat(transform("true && false")).isEqualTo("{ \"$and\" : [ true , false]}");
}
@Test // DATAMONGO-1530
void shouldRenderComplexOperationNodeAnd() {
assertThat(transform("1+2 && concat(a, b) && true")).isEqualTo(
- Document.parse("{ \"$and\" : [ { \"$add\" : [ 1 , 2]} , { \"$concat\" : [ \"$a\" , \"$b\"]} , true]}"));
+ "{ \"$and\" : [ { \"$add\" : [ 1 , 2]} , { \"$concat\" : [ \"$a\" , \"$b\"]} , true]}");
}
@Test // DATAMONGO-1530
void shouldRenderNotCorrectly() {
- assertThat(transform("!true")).isEqualTo(Document.parse("{ \"$not\" : [ true]}"));
+ assertThat(transform("!true")).isEqualTo("{ \"$not\" : [ true]}");
}
@Test // DATAMONGO-1530
void shouldRenderComplexNotCorrectly() {
- assertThat(transform("!(foo > 10)")).isEqualTo(Document.parse("{ \"$not\" : [ { \"$gt\" : [ \"$foo\" , 10]}]}"));
+ assertThat(transform("!(foo > 10)")).isEqualTo("{ \"$not\" : [ { \"$gt\" : [ \"$foo\" , 10]}]}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceIndexOfBytes() {
assertThat(transform("indexOfBytes(item, 'foo')"))
- .isEqualTo(Document.parse("{ \"$indexOfBytes\" : [ \"$item\" , \"foo\"]}"));
+ .isEqualTo("{ \"$indexOfBytes\" : [ \"$item\" , \"foo\"]}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceIndexOfCP() {
assertThat(transform("indexOfCP(item, 'foo')"))
- .isEqualTo(Document.parse("{ \"$indexOfCP\" : [ \"$item\" , \"foo\"]}"));
+ .isEqualTo("{ \"$indexOfCP\" : [ \"$item\" , \"foo\"]}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceSplit() {
- assertThat(transform("split(item, ',')")).isEqualTo(Document.parse("{ \"$split\" : [ \"$item\" , \",\"]}"));
+ assertThat(transform("split(item, ',')")).isEqualTo("{ \"$split\" : [ \"$item\" , \",\"]}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceStrLenBytes() {
- assertThat(transform("strLenBytes(item)")).isEqualTo(Document.parse("{ \"$strLenBytes\" : \"$item\"}"));
+ assertThat(transform("strLenBytes(item)")).isEqualTo("{ \"$strLenBytes\" : \"$item\"}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceStrLenCP() {
- assertThat(transform("strLenCP(item)")).isEqualTo(Document.parse("{ \"$strLenCP\" : \"$item\"}"));
+ assertThat(transform("strLenCP(item)")).isEqualTo("{ \"$strLenCP\" : \"$item\"}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodSubstrCP() {
- assertThat(transform("substrCP(item, 0, 5)")).isEqualTo(Document.parse("{ \"$substrCP\" : [ \"$item\" , 0 , 5]}"));
+ assertThat(transform("substrCP(item, 0, 5)")).isEqualTo("{ \"$substrCP\" : [ \"$item\" , 0 , 5]}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceReverseArray() {
- assertThat(transform("reverseArray(array)")).isEqualTo(Document.parse("{ \"$reverseArray\" : \"$array\"}"));
+ assertThat(transform("reverseArray(array)")).isEqualTo("{ \"$reverseArray\" : \"$array\"}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceReduce() {
- assertThat(transform("reduce(field, '', {'$concat':{'$$value','$$this'}})")).isEqualTo(Document.parse(
- "{ \"$reduce\" : { \"input\" : \"$field\" , \"initialValue\" : \"\" , \"in\" : { \"$concat\" : [ \"$$value\" , \"$$this\"]}}}"));
+ assertThat(transform("reduce(field, '', {'$concat':{'$$value','$$this'}})")).isEqualTo(
+ "{ \"$reduce\" : { \"input\" : \"$field\" , \"initialValue\" : \"\" , \"in\" : { \"$concat\" : [ \"$$value\" , \"$$this\"]}}}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceZip() {
assertThat(transform("zip(new String[]{'$array1', '$array2'})"))
- .isEqualTo(Document.parse("{ \"$zip\" : { \"inputs\" : [ \"$array1\" , \"$array2\"]}}"));
+ .isEqualTo("{ \"$zip\" : { \"inputs\" : [ \"$array1\" , \"$array2\"]}}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodReferenceZipWithOptionalArgs() {
- assertThat(transform("zip(new String[]{'$array1', '$array2'}, true, new int[]{1,2})")).isEqualTo(Document.parse(
- "{ \"$zip\" : { \"inputs\" : [ \"$array1\" , \"$array2\"] , \"useLongestLength\" : true , \"defaults\" : [ 1 , 2]}}"));
+ assertThat(transform("zip(new String[]{'$array1', '$array2'}, true, new int[]{1,2})")).isEqualTo(
+ "{ \"$zip\" : { \"inputs\" : [ \"$array1\" , \"$array2\"] , \"useLongestLength\" : true , \"defaults\" : [ 1 , 2]}}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodIn() {
- assertThat(transform("in('item', array)")).isEqualTo(Document.parse("{ \"$in\" : [ \"item\" , \"$array\"]}"));
+ assertThat(transform("in('item', array)")).isEqualTo("{ \"$in\" : [ \"item\" , \"$array\"]}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodRefereneIsoDayOfWeek() {
- assertThat(transform("isoDayOfWeek(date)")).isEqualTo(Document.parse("{ \"$isoDayOfWeek\" : \"$date\"}"));
+ assertThat(transform("isoDayOfWeek(date)")).isEqualTo("{ \"$isoDayOfWeek\" : \"$date\"}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodRefereneIsoWeek() {
- assertThat(transform("isoWeek(date)")).isEqualTo(Document.parse("{ \"$isoWeek\" : \"$date\"}"));
+ assertThat(transform("isoWeek(date)")).isEqualTo("{ \"$isoWeek\" : \"$date\"}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodRefereneIsoWeekYear() {
- assertThat(transform("isoWeekYear(date)")).isEqualTo(Document.parse("{ \"$isoWeekYear\" : \"$date\"}"));
+ assertThat(transform("isoWeekYear(date)")).isEqualTo("{ \"$isoWeekYear\" : \"$date\"}");
}
@Test // DATAMONGO-1548
void shouldRenderMethodRefereneType() {
- assertThat(transform("type(a)")).isEqualTo(Document.parse("{ \"$type\" : \"$a\"}"));
+ assertThat(transform("type(a)")).isEqualTo("{ \"$type\" : \"$a\"}");
}
@Test // DATAMONGO-2077
void shouldRenderArrayToObjectWithFieldReference() {
- assertThat(transform("arrayToObject(field)")).isEqualTo(Document.parse("{ \"$arrayToObject\" : \"$field\"}"));
+ assertThat(transform("arrayToObject(field)")).isEqualTo("{ \"$arrayToObject\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderArrayToObjectWithArray() {
assertThat(transform("arrayToObject(new String[]{'key', 'value'})"))
- .isEqualTo(Document.parse("{ \"$arrayToObject\" : [\"key\", \"value\"]}"));
+ .isEqualTo("{ \"$arrayToObject\" : [\"key\", \"value\"]}");
}
@Test // DATAMONGO-2077
void shouldRenderObjectToArrayWithFieldReference() {
- assertThat(transform("objectToArray(field)")).isEqualTo(Document.parse("{ \"$objectToArray\" : \"$field\"}"));
+ assertThat(transform("objectToArray(field)")).isEqualTo("{ \"$objectToArray\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderMergeObjects() {
assertThat(transform("mergeObjects(field1, $$ROOT)"))
- .isEqualTo(Document.parse("{ \"$mergeObjects\" : [\"$field1\", \"$$ROOT\"]}"));
+ .isEqualTo("{ \"$mergeObjects\" : [\"$field1\", \"$$ROOT\"]}");
}
@Test // DATAMONGO-2077
void shouldRenderTrimWithoutChars() {
- assertThat(transform("trim(field)")).isEqualTo(Document.parse("{ \"$trim\" : {\"input\" : \"$field\"}}"));
+ assertThat(transform("trim(field)")).isEqualTo("{ \"$trim\" : {\"input\" : \"$field\"}}");
}
@Test // DATAMONGO-2077
void shouldRenderTrimWithChars() {
assertThat(transform("trim(field, 'ie')"))
- .isEqualTo(Document.parse("{ \"$trim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}"));
+ .isEqualTo("{ \"$trim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderTrimWithCharsFromFieldReference() {
assertThat(transform("trim(field1, field2)"))
- .isEqualTo(Document.parse("{ \"$trim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}"));
+ .isEqualTo("{ \"$trim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderLtrimWithoutChars() {
- assertThat(transform("ltrim(field)")).isEqualTo(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field\"}}"));
+ assertThat(transform("ltrim(field)")).isEqualTo("{ \"$ltrim\" : {\"input\" : \"$field\"}}");
}
@Test // DATAMONGO-2077
void shouldRenderLtrimWithChars() {
assertThat(transform("ltrim(field, 'ie')"))
- .isEqualTo(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}"));
+ .isEqualTo("{ \"$ltrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderLtrimWithCharsFromFieldReference() {
assertThat(transform("ltrim(field1, field2)"))
- .isEqualTo(Document.parse("{ \"$ltrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}"));
+ .isEqualTo("{ \"$ltrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderRtrimWithoutChars() {
- assertThat(transform("rtrim(field)")).isEqualTo(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field\"}}"));
+ assertThat(transform("rtrim(field)")).isEqualTo("{ \"$rtrim\" : {\"input\" : \"$field\"}}");
}
@Test // DATAMONGO-2077
void shouldRenderRtrimWithChars() {
assertThat(transform("rtrim(field, 'ie')"))
- .isEqualTo(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}"));
+ .isEqualTo("{ \"$rtrim\" : {\"input\" : \"$field\", \"chars\" : \"ie\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderRtrimWithCharsFromFieldReference() {
assertThat(transform("rtrim(field1, field2)"))
- .isEqualTo(Document.parse("{ \"$rtrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}"));
+ .isEqualTo("{ \"$rtrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderConvertWithoutOptionalParameters() {
assertThat(transform("convert(field, 'string')"))
- .isEqualTo(Document.parse("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"string\" }}"));
+ .isEqualTo("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"string\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderConvertWithOnError() {
- assertThat(transform("convert(field, 'int', 'Not an integer.')")).isEqualTo(Document
- .parse("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\" }}"));
+ assertThat(transform("convert(field, 'int', 'Not an integer.')"))
+ .isEqualTo("{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderConvertWithOnErrorOnNull() {
- assertThat(transform("convert(field, 'int', 'Not an integer.', -1)")).isEqualTo(Document.parse(
- "{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\", \"onNull\" : -1 }}"));
+ assertThat(transform("convert(field, 'int', 'Not an integer.', -1)")).isEqualTo(
+ "{ \"$convert\" : {\"input\" : \"$field\", \"to\" : \"int\", \"onError\" : \"Not an integer.\", \"onNull\" : -1 }}");
}
@Test // DATAMONGO-2077
void shouldRenderToBool() {
- assertThat(transform("toBool(field)")).isEqualTo(Document.parse("{ \"$toBool\" : \"$field\"}"));
+ assertThat(transform("toBool(field)")).isEqualTo("{ \"$toBool\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderToDate() {
- assertThat(transform("toDate(field)")).isEqualTo(Document.parse("{ \"$toDate\" : \"$field\"}"));
+ assertThat(transform("toDate(field)")).isEqualTo("{ \"$toDate\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderToDecimal() {
- assertThat(transform("toDecimal(field)")).isEqualTo(Document.parse("{ \"$toDecimal\" : \"$field\"}"));
+ assertThat(transform("toDecimal(field)")).isEqualTo("{ \"$toDecimal\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderToDouble() {
- assertThat(transform("toDouble(field)")).isEqualTo(Document.parse("{ \"$toDouble\" : \"$field\"}"));
+ assertThat(transform("toDouble(field)")).isEqualTo("{ \"$toDouble\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderToInt() {
- assertThat(transform("toInt(field)")).isEqualTo(Document.parse("{ \"$toInt\" : \"$field\"}"));
+ assertThat(transform("toInt(field)")).isEqualTo("{ \"$toInt\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderToLong() {
- assertThat(transform("toLong(field)")).isEqualTo(Document.parse("{ \"$toLong\" : \"$field\"}"));
+ assertThat(transform("toLong(field)")).isEqualTo("{ \"$toLong\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderToObjectId() {
- assertThat(transform("toObjectId(field)")).isEqualTo(Document.parse("{ \"$toObjectId\" : \"$field\"}"));
+ assertThat(transform("toObjectId(field)")).isEqualTo("{ \"$toObjectId\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderToString() {
- assertThat(transform("toString(field)")).isEqualTo(Document.parse("{ \"$toString\" : \"$field\"}"));
+ assertThat(transform("toString(field)")).isEqualTo("{ \"$toString\" : \"$field\"}");
}
@Test // DATAMONGO-2077
void shouldRenderDateFromStringWithoutOptionalParameters() {
assertThat(transform("dateFromString(field)"))
- .isEqualTo(Document.parse("{ \"$dateFromString\" : {\"dateString\" : \"$field\" }}"));
+ .isEqualTo("{ \"$dateFromString\" : {\"dateString\" : \"$field\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderDateFromStringWithFormat() {
assertThat(transform("dateFromString(field, 'DD-MM-YYYY')")).isEqualTo(
- Document.parse("{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\" }}"));
+ "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderDateFromStringWithFormatAndTimezone() {
- assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC')")).isEqualTo(Document.parse(
- "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\" }}"));
+ assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC')")).isEqualTo(
+ "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\" }}");
}
@Test // DATAMONGO-2077
void shouldRenderDateFromStringWithFormatTimezoneAndOnError() {
- assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1)")).isEqualTo(Document.parse(
- "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1 }}"));
+ assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1)")).isEqualTo(
+ "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1 }}");
}
@Test // DATAMONGO-2077
void shouldRenderDateFromStringWithFormatTimezoneOnErrorAndOnNull() {
- assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1, -2)")).isEqualTo(Document.parse(
- "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1, \"onNull\" : -2}}"));
+ assertThat(transform("dateFromString(field, 'DD-MM-YYYY', 'UTC', -1, -2)")).isEqualTo(
+ "{ \"$dateFromString\" : {\"dateString\" : \"$field\", \"format\" : \"DD-MM-YYYY\", \"timezone\" : \"UTC\", \"onError\" : -1, \"onNull\" : -2}}");
}
@Test // DATAMONGO-2077, DATAMONGO-2671
void shouldRenderDateFromParts() {
- assertThat(transform("dateFromParts(y, m, d, h, mm, s, ms, 'UTC')")).isEqualTo(Document.parse(
- "{ \"$dateFromParts\" : {\"year\" : \"$y\", \"month\" : \"$m\", \"day\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"millisecond\" : \"$ms\", \"timezone\" : \"UTC\"}}"));
+ assertThat(transform("dateFromParts(y, m, d, h, mm, s, ms, 'UTC')")).isEqualTo(
+ "{ \"$dateFromParts\" : {\"year\" : \"$y\", \"month\" : \"$m\", \"day\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"millisecond\" : \"$ms\", \"timezone\" : \"UTC\"}}");
}
@Test // DATAMONGO-2077, DATAMONGO-2671
void shouldRenderIsoDateFromParts() {
- assertThat(transform("isoDateFromParts(y, m, d, h, mm, s, ms, 'UTC')")).isEqualTo(Document.parse(
- "{ \"$dateFromParts\" : {\"isoWeekYear\" : \"$y\", \"isoWeek\" : \"$m\", \"isoDayOfWeek\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"millisecond\" : \"$ms\", \"timezone\" : \"UTC\"}}"));
+ assertThat(transform("isoDateFromParts(y, m, d, h, mm, s, ms, 'UTC')")).isEqualTo(
+ "{ \"$dateFromParts\" : {\"isoWeekYear\" : \"$y\", \"isoWeek\" : \"$m\", \"isoDayOfWeek\" : \"$d\", \"hour\" : \"$h\", \"minute\" : \"$mm\", \"second\" : \"$s\", \"millisecond\" : \"$ms\", \"timezone\" : \"UTC\"}}");
}
@Test // DATAMONGO-2077
void shouldRenderDateToParts() {
assertThat(transform("dateToParts(field, 'UTC', false)")).isEqualTo(
- Document.parse("{ \"$dateToParts\" : {\"date\" : \"$field\", \"timezone\" : \"UTC\", \"iso8601\" : false}}"));
+ "{ \"$dateToParts\" : {\"date\" : \"$field\", \"timezone\" : \"UTC\", \"iso8601\" : false}}");
}
@Test // DATAMONGO-2077
void shouldRenderIndexOfArray() {
assertThat(transform("indexOfArray(field, 2)"))
- .isEqualTo(Document.parse("{ \"$indexOfArray\" : [\"$field\", 2 ]}"));
+ .isEqualTo("{ \"$indexOfArray\" : [\"$field\", 2 ]}");
}
@Test // DATAMONGO-2077
void shouldRenderRange() {
- assertThat(transform("range(0, 10, 2)")).isEqualTo(Document.parse("{ \"$range\" : [0, 10, 2 ]}"));
+ assertThat(transform("range(0, 10, 2)")).isEqualTo("{ \"$range\" : [0, 10, 2 ]}");
}
@Test // DATAMONGO-2370
void shouldRenderRound() {
- assertThat(transform("round(field)")).isEqualTo(Document.parse("{ \"$round\" : [\"$field\"]}"));
+ assertThat(transform("round(field)")).isEqualTo("{ \"$round\" : [\"$field\"]}");
}
@Test // DATAMONGO-2370
void shouldRenderRoundWithPlace() {
- assertThat(transform("round(field, 2)")).isEqualTo(Document.parse("{ \"$round\" : [\"$field\", 2]}"));
+ assertThat(transform("round(field, 2)")).isEqualTo("{ \"$round\" : [\"$field\", 2]}");
}
@Test // GH-3714
void shouldRenderDegreesToRadians() {
- assertThat(transform("degreesToRadians(angle_a)")).isEqualTo(Document.parse("{ \"$degreesToRadians\" : \"$angle_a\"}"));
+ assertThat(transform("degreesToRadians(angle_a)")).isEqualTo("{ \"$degreesToRadians\" : \"$angle_a\"}");
}
@Test // GH-3712
void shouldRenderCovariancePop() {
assertThat(transform("covariancePop(field1, field2)"))
- .isEqualTo(Document.parse("{ \"$covariancePop\" : [\"$field1\", \"$field2\"]}"));
+ .isEqualTo("{ \"$covariancePop\" : [\"$field1\", \"$field2\"]}");
}
@Test // GH-3712
void shouldRenderCovarianceSamp() {
assertThat(transform("covarianceSamp(field1, field2)"))
- .isEqualTo(Document.parse("{ \"$covarianceSamp\" : [\"$field1\", \"$field2\"]}"));
+ .isEqualTo("{ \"$covarianceSamp\" : [\"$field1\", \"$field2\"]}");
}
@Test // GH-3715
void shouldRenderRank() {
- assertThat(transform("rank()")).isEqualTo(Document.parse("{ $rank : {} }"));
+ assertThat(transform("rank()")).isEqualTo("{ $rank : {} }");
}
@Test // GH-3715
void shouldRenderDenseRank() {
- assertThat(transform("denseRank()")).isEqualTo(Document.parse("{ $denseRank : {} }"));
+ assertThat(transform("denseRank()")).isEqualTo("{ $denseRank : {} }");
}
@Test // GH-3717
void shouldRenderDocumentNumber() {
- assertThat(transform("documentNumber()")).isEqualTo(Document.parse("{ $documentNumber : {} }"));
+ assertThat(transform("documentNumber()")).isEqualTo("{ $documentNumber : {} }");
}
@Test // GH-3727
void rendersShift() {
assertThat(transform("shift(quantity, 1)"))
- .isEqualTo(Document.parse("{ $shift: { output: \"$quantity\", by: 1 } }"));
+ .isEqualTo("{ $shift: { output: \"$quantity\", by: 1 } }");
}
@Test // GH-3727
void rendersShiftWithDefault() {
assertThat(transform("shift(quantity, 1, 'Not available')"))
- .isEqualTo(Document.parse("{ $shift: { output: \"$quantity\", by: 1, default: \"Not available\" } }"));
+ .isEqualTo("{ $shift: { output: \"$quantity\", by: 1, default: \"Not available\" } }");
}
@Test // GH-3716
void shouldRenderDerivative() {
assertThat(transform("derivative(miles, 'hour')"))
- .isEqualTo(Document.parse("{ \"$derivative\" : { input : '$miles', unit : 'hour'} }"));
+ .isEqualTo("{ \"$derivative\" : { input : '$miles', unit : 'hour'} }");
}
@Test // GH-3721
void shouldRenderIntegral() {
- assertThat(transform("integral(field)")).isEqualTo(Document.parse("{ \"$integral\" : { \"input\" : \"$field\" }}"));
+ assertThat(transform("integral(field)")).isEqualTo("{ \"$integral\" : { \"input\" : \"$field\" }}");
}
@Test // GH-3721
void shouldRenderIntegralWithUnit() {
assertThat(transform("integral(field, 'hour')"))
- .isEqualTo(Document.parse("{ \"$integral\" : { \"input\" : \"$field\", \"unit\" : \"hour\" }}"));
+ .isEqualTo("{ \"$integral\" : { \"input\" : \"$field\", \"unit\" : \"hour\" }}");
}
@Test // GH-3728
void shouldRenderSin() {
- assertThat(transform("sin(angle)")).isEqualTo(Document.parse("{ \"$sin\" : \"$angle\"}"));
+ assertThat(transform("sin(angle)")).isEqualTo("{ \"$sin\" : \"$angle\"}");
}
@Test // GH-3728
void shouldRenderSinh() {
- assertThat(transform("sinh(angle)")).isEqualTo(Document.parse("{ \"$sinh\" : \"$angle\"}"));
+ assertThat(transform("sinh(angle)")).isEqualTo("{ \"$sinh\" : \"$angle\"}");
}
@Test // GH-3710
void shouldRenderCos() {
- assertThat(transform("cos(angle)")).isEqualTo(Document.parse("{ \"$cos\" : \"$angle\"}"));
+ assertThat(transform("cos(angle)")).isEqualTo("{ \"$cos\" : \"$angle\"}");
}
@Test // GH-3710
void shouldRenderCosh() {
- assertThat(transform("cosh(angle)")).isEqualTo(Document.parse("{ \"$cosh\" : \"$angle\"}"));
+ assertThat(transform("cosh(angle)")).isEqualTo("{ \"$cosh\" : \"$angle\"}");
}
@Test // GH-3730
void shouldRenderTan() {
- assertThat(transform("tan(angle)")).isEqualTo(Document.parse("{ \"$tan\" : \"$angle\"}"));
+ assertThat(transform("tan(angle)")).isEqualTo("{ \"$tan\" : \"$angle\"}");
}
@Test // GH-3730
void shouldRenderTanh() {
- assertThat(transform("tanh(angle)")).isEqualTo(Document.parse("{ \"$tanh\" : \"$angle\"}"));
+ assertThat(transform("tanh(angle)")).isEqualTo("{ \"$tanh\" : \"$angle\"}");
}
- private Object transform(String expression, Object... params) {
+ private Document transform(String expression, Object... params) {
+ return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
+ }
+
+ private Object transformValue(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);
}
diff --git a/src/main/asciidoc/reference/aggregation-framework.adoc b/src/main/asciidoc/reference/aggregation-framework.adoc
index 9b00811a7b..6373eb663c 100644
--- a/src/main/asciidoc/reference/aggregation-framework.adoc
+++ b/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`
| Arithmetic Aggregation Operators
-| `abs`, `add` (+++*+++ via `plus`), `ceil`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `trunc`
+| `abs`, `add` (+++*+++ via `plus`), `ceil`, `cos`, `cosh`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `sin`, `sinh`, `tan`, `tanh`, `trunc`
| String Aggregation Operators
| `concat`, `substr`, `toLower`, `toUpper`, `stcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`
@@ -112,7 +112,7 @@ At the time of this writing, we provide support for the following Aggregation Op
| `type`
| Convert Aggregation Operators
-| `convert`, `toBool`, `toDate`, `toDecimal`, `toDouble`, `toInt`, `toLong`, `toObjectId`, `toString`
+| `convert`, `degreesToRadians`, `toBool`, `toDate`, `toDecimal`, `toDouble`, `toInt`, `toLong`, `toObjectId`, `toString`
| Object Aggregation Operators
| `objectToArray`, `mergeObjects`
From aca403c11240a5e2253d36e4ecba627283dd9ee7 Mon Sep 17 00:00:00 2001
From: Ryan Gibb
Date: Fri, 30 Jul 2021 14:06:42 +0100
Subject: [PATCH 030/920] Fix a typo in `MongoConverter` javadoc.
Original pull request: #3758.
---
.../data/mongodb/core/convert/MongoConverter.java | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
index 8887a3bd03..20499d3173 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
@@ -40,13 +40,14 @@
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
+ * @author Ryan Gibb
*/
public interface MongoConverter
extends EntityConverter, MongoPersistentProperty, Object, Bson>, MongoWriter,
EntityReader {
/**
- * Returns thw {@link TypeMapper} being used to write type information into {@link Document}s created with that
+ * Returns the {@link TypeMapper} being used to write type information into {@link Document}s created with that
* converter.
*
* @return will never be {@literal null}.
From 869b88702db52a25417da5427859f75621d78e7d Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 25 Aug 2021 10:15:13 +0200
Subject: [PATCH 031/920] Polishing.
Fix typo in reference docs.
See #3758
---
src/main/asciidoc/reference/aggregation-framework.adoc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/asciidoc/reference/aggregation-framework.adoc b/src/main/asciidoc/reference/aggregation-framework.adoc
index 6373eb663c..f23b290697 100644
--- a/src/main/asciidoc/reference/aggregation-framework.adoc
+++ b/src/main/asciidoc/reference/aggregation-framework.adoc
@@ -88,7 +88,7 @@ At the time of this writing, we provide support for the following Aggregation Op
| `abs`, `add` (+++*+++ via `plus`), `ceil`, `cos`, `cosh`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `sin`, `sinh`, `tan`, `tanh`, `trunc`
| String Aggregation Operators
-| `concat`, `substr`, `toLower`, `toUpper`, `stcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`
+| `concat`, `substr`, `toLower`, `toUpper`, `strcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`
| Comparison Aggregation Operators
| `eq` (+++*+++ via `is`), `gt`, `gte`, `lt`, `lte`, `ne`
From afef243634e4efa215755cf14b678bebe10ff92a Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Mon, 26 Jul 2021 08:46:22 +0200
Subject: [PATCH 032/920] Add support for `$dateAdd` aggregation operator.
Closes: #3713
Original pull request: #3748.
---
.../core/aggregation/DateOperators.java | 144 +++++++++++++++++-
.../core/spel/MethodReferenceNode.java | 1 +
.../aggregation/DateOperatorsUnitTests.java | 44 ++++++
.../SpelExpressionTransformerUnitTests.java | 6 +
4 files changed, 189 insertions(+), 6 deletions(-)
create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
index 15f10f7d6c..f7abf88d72 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.core.aggregation;
import java.util.Collections;
+import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -156,7 +157,7 @@ public static Timezone none() {
* representing an Olson Timezone Identifier or UTC Offset.
*
* @param value the plain timezone {@link String}, a {@link Field} holding the timezone or an
- * {@link AggregationExpression} resulting in the timezone.
+ * {@link AggregationExpression} resulting in the timezone.
* @return new instance of {@link Timezone}.
*/
public static Timezone valueOf(Object value) {
@@ -274,6 +275,41 @@ public DateOperatorFactory withTimezone(Timezone timezone) {
return new DateOperatorFactory(fieldReference, expression, dateValue, timezone);
}
+ /**
+ * Creates new {@link AggregationExpression} that adds the value of the given {@link AggregationExpression
+ * expression} (in {@literal units). @param expression must not be {@literal null}.
+ *
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ * @since 3.3
+ */
+ public DateAdd addValueOf(AggregationExpression expression, String unit) {
+ return applyTimezone(DateAdd.addValueOf(expression, unit).toDate(dateReference()), timezone);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that adds the value stored at the given {@literal field} (in
+ * {@literal units). @param fieldReference must not be {@literal null}.
+ *
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ * @since 3.3
+ */
+ public DateAdd addValueOf(String fieldReference, String unit) {
+ return applyTimezone(DateAdd.addValueOf(fieldReference, unit).toDate(dateReference()), timezone);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that adds the given value (in {@literal units). @param value must not
+ * be {@literal null}. @param unit the unit of measure. Must not be {@literal null}.
+ *
+ * @return
+ * @since 3.3 new instance of {@link DateAdd}.
+ */
+ public DateAdd add(Object value, String unit) {
+ return applyTimezone(DateAdd.addValue(value, unit).toDate(dateReference()), timezone);
+ }
+
/**
* Creates new {@link AggregationExpression} that returns the day of the year for a date as a number between 1 and
* 366.
@@ -1480,7 +1516,6 @@ protected java.util.Map append(String key, Object value) {
} else {
clone.put("timezone", ((Timezone) value).value);
}
-
} else {
clone.put(key, value);
}
@@ -1911,7 +1946,7 @@ default T millisecondOf(AggregationExpression expression) {
* @author Matt Morrissette
* @author Christoph Strobl
* @see https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromParts/
+ * "https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromParts/">https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromParts/
* @since 2.1
*/
public static class DateFromParts extends TimezonedDateAggregationExpression implements DateParts {
@@ -2086,7 +2121,7 @@ default DateFromParts yearOf(AggregationExpression expression) {
* @author Matt Morrissette
* @author Christoph Strobl
* @see https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromParts/
+ * "https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromParts/">https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromParts/
* @since 2.1
*/
public static class IsoDateFromParts extends TimezonedDateAggregationExpression
@@ -2262,7 +2297,7 @@ default IsoDateFromParts isoWeekYearOf(AggregationExpression expression) {
* @author Matt Morrissette
* @author Christoph Strobl
* @see https://docs.mongodb.com/manual/reference/operator/aggregation/dateToParts/
+ * "https://docs.mongodb.com/manual/reference/operator/aggregation/dateToParts/">https://docs.mongodb.com/manual/reference/operator/aggregation/dateToParts/
* @since 2.1
*/
public static class DateToParts extends TimezonedDateAggregationExpression {
@@ -2343,7 +2378,7 @@ protected String getMongoMethod() {
* @author Matt Morrissette
* @author Christoph Strobl
* @see https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromString/
+ * "https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromString/">https://docs.mongodb.com/manual/reference/operator/aggregation/dateFromString/
* @since 2.1
*/
public static class DateFromString extends TimezonedDateAggregationExpression {
@@ -2418,6 +2453,103 @@ protected String getMongoMethod() {
}
}
+ /**
+ * {@link AggregationExpression} for {@code $dateAdd}.
+ * NOTE: Requires MongoDB 5.0 or later.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+ public static class DateAdd extends TimezonedDateAggregationExpression {
+
+ private DateAdd(Object value) {
+ super(value);
+ }
+
+ /**
+ * Add the number of {@literal units} of the result of the given {@link AggregationExpression expression} to a
+ * {@link #toDate(Object) start date}.
+ *
+ * @param expression must not be {@literal null}.
+ * @param unit must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public static DateAdd addValueOf(AggregationExpression expression, String unit) {
+ return addValue(expression, unit);
+ }
+
+ /**
+ * Add the number of {@literal units} from a {@literal field} to a {@link #toDate(Object) start date}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @param unit must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public static DateAdd addValueOf(String fieldReference, String unit) {
+ return addValue(Fields.field(fieldReference), unit);
+ }
+
+ /**
+ * Add the number of {@literal units} to a {@link #toDate(Object) start date}.
+ *
+ * @param value must not be {@literal null}.
+ * @param unit must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public static DateAdd addValue(Object value, String unit) {
+
+ Map args = new HashMap<>();
+ args.put("unit", unit);
+ args.put("amount", value);
+ return new DateAdd(args);
+ }
+
+ /**
+ * Define the start date, in UTC, for the addition operation.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public DateAdd toDateOf(AggregationExpression expression) {
+ return toDate(expression);
+ }
+
+ /**
+ * Define the start date, in UTC, for the addition operation.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public DateAdd toDateOf(String fieldReference) {
+ return toDate(Fields.field(fieldReference));
+ }
+
+ /**
+ * Define the start date, in UTC, for the addition operation.
+ *
+ * @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public DateAdd toDate(Object dateExpression) {
+ return new DateAdd(append("startDate", dateExpression));
+ }
+
+ /**
+ * Optionally set the {@link Timezone} to use. If not specified {@literal UTC} is used.
+ *
+ * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
+ * @return new instance of {@link DateAdd}.
+ */
+ public DateAdd withTimezone(Timezone timezone) {
+ return new DateAdd(appendTimezone(argumentMap(), timezone));
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$dateAdd";
+ }
+ }
+
@SuppressWarnings("unchecked")
private static T applyTimezone(T instance, Timezone timezone) {
return !ObjectUtils.nullSafeEquals(Timezone.none(), timezone) && !instance.hasTimezone()
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index 1efe94c757..6b4daa15b8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -144,6 +144,7 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("literal", singleArgRef().forOperator("$literal"));
// DATE OPERATORS
+ map.put("dateAdd", mapArgRef().forOperator("$dateAdd").mappingParametersTo("startDate", "unit", "amount", "timezone"));
map.put("dayOfYear", singleArgRef().forOperator("$dayOfYear"));
map.put("dayOfMonth", singleArgRef().forOperator("$dayOfMonth"));
map.put("dayOfWeek", singleArgRef().forOperator("$dayOfWeek"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
new file mode 100644
index 0000000000..036edfdce1
--- /dev/null
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
@@ -0,0 +1,44 @@
+/*
+ * 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 org.bson.Document;
+import org.junit.jupiter.api.Test;
+import org.springframework.data.mongodb.core.aggregation.DateOperators.Timezone;
+
+/**
+ * @author Christoph Strobl
+ */
+class DateOperatorsUnitTests {
+
+ @Test // GH-3713
+ void rendersDateAdd() {
+
+ assertThat(DateOperators.dateOf("purchaseDate").add(3, "day").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }"));
+ }
+
+ @Test // GH-3713
+ void rendersDateAddWithTimezone() {
+
+ assertThat(DateOperators.dateOf("purchaseDate").withTimezone(Timezone.valueOf("America/Chicago")).add(3, "day")
+ .toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
+ "{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }"));
+ }
+ }
+}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index 193ffb520d..337d61f984 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1039,6 +1039,12 @@ private Document transform(String expression, Object... params) {
}
private Object transformValue(String expression, Object... params) {
+ @Test // GH-3713
+ void shouldRenderDateAdd() {
+ assertThat(transform("dateAdd(purchaseDate, 'day', 3)")).isEqualTo(Document.parse("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }"));
+ }
+
+ 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);
}
From fc41793d5de7bf48f551638e9836ef0eaceb1c43 Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Mon, 26 Jul 2021 09:08:18 +0200
Subject: [PATCH 033/920] Add support for `$dateDiff` aggregation operator.
Closes: #3713
Original pull request: #3748.
---
.../core/aggregation/DateOperators.java | 144 ++++++++++++++++++
.../core/spel/MethodReferenceNode.java | 1 +
.../aggregation/DateOperatorsUnitTests.java | 16 ++
.../SpelExpressionTransformerUnitTests.java | 5 +
4 files changed, 166 insertions(+)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
index f7abf88d72..a9b1d411cd 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
@@ -340,6 +340,42 @@ public DayOfWeek dayOfWeek() {
return applyTimezone(DayOfWeek.dayOfWeek(dateReference()), timezone);
}
+ /**
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date
+ * computed by the given {@link AggregationExpression expression}. @param expression must not be {@literal null}.
+ *
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ * @since 3.3
+ */
+ public DateDiff diffValueOf(AggregationExpression expression, String unit) {
+ return applyTimezone(DateDiff.diffValueOf(expression, unit).toDate(dateReference()), timezone);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date stored
+ * at the given {@literal field}. @param expression must not be {@literal null}.
+ *
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ * @since 3.3
+ */
+ public DateDiff diffValueOf(String fieldReference, String unit) {
+ return applyTimezone(DateDiff.diffValueOf(fieldReference, unit).toDate(dateReference()), timezone);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date given
+ * {@literal value}. @param value anything the resolves to a valid date. Must not be {@literal null}.
+ *
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ * @since 3.3
+ */
+ public DateDiff diff(Object value, String unit) {
+ return applyTimezone(DateDiff.diffValue(value, unit).toDate(dateReference()), timezone);
+ }
+
/**
* Creates new {@link AggregationExpression} that returns the year portion of a date.
*
@@ -2550,6 +2586,114 @@ protected String getMongoMethod() {
}
}
+ /**
+ * {@link AggregationExpression} for {@code $dateDiff}.
+ * NOTE: Requires MongoDB 5.0 or later.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+ public static class DateDiff extends TimezonedDateAggregationExpression {
+
+ private DateDiff(Object value) {
+ super(value);
+ }
+
+ /**
+ * Add the number of {@literal units} of the result of the given {@link AggregationExpression expression} to a
+ * {@link #toDate(Object) start date}.
+ *
+ * @param expression must not be {@literal null}.
+ * @param unit must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public static DateDiff diffValueOf(AggregationExpression expression, String unit) {
+ return diffValue(expression, unit);
+ }
+
+ /**
+ * Add the number of {@literal units} from a {@literal field} to a {@link #toDate(Object) start date}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @param unit must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public static DateDiff diffValueOf(String fieldReference, String unit) {
+ return diffValue(Fields.field(fieldReference), unit);
+ }
+
+ /**
+ * Add the number of {@literal units} to a {@link #toDate(Object) start date}.
+ *
+ * @param value must not be {@literal null}.
+ * @param unit must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public static DateDiff diffValue(Object value, String unit) {
+
+ Map args = new HashMap<>();
+ args.put("unit", unit);
+ args.put("endDate", value);
+ return new DateDiff(args);
+ }
+
+ /**
+ * Define the start date, in UTC, for the addition operation.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public DateDiff toDateOf(AggregationExpression expression) {
+ return toDate(expression);
+ }
+
+ /**
+ * Define the start date, in UTC, for the addition operation.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public DateDiff toDateOf(String fieldReference) {
+ return toDate(Fields.field(fieldReference));
+ }
+
+ /**
+ * Define the start date, in UTC, for the addition operation.
+ *
+ * @param dateExpression anything that evaluates to a valid date. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}.
+ */
+ public DateDiff toDate(Object dateExpression) {
+ return new DateDiff(append("startDate", dateExpression));
+ }
+
+ /**
+ * Optionally set the {@link Timezone} to use. If not specified {@literal UTC} is used.
+ *
+ * @param timezone must not be {@literal null}. Consider {@link Timezone#none()} instead.
+ * @return new instance of {@link DateAdd}.
+ */
+ public DateDiff withTimezone(Timezone timezone) {
+ return new DateDiff(appendTimezone(argumentMap(), timezone));
+ }
+
+ /**
+ * Set the start day of the week if the unit if measure is set to {@literal week}. Uses {@literal Sunday} by
+ * default.
+ *
+ * @param day must not be {@literal null}.
+ * @return new instance of {@link DateDiff}.
+ */
+ public DateDiff startOfWeek(Object day) {
+ return new DateDiff(append("startOfWeek", day));
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$dateDiff";
+ }
+ }
+
@SuppressWarnings("unchecked")
private static T applyTimezone(T instance, Timezone timezone) {
return !ObjectUtils.nullSafeEquals(Timezone.none(), timezone) && !instance.hasTimezone()
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index 6b4daa15b8..6a60a7df1a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -145,6 +145,7 @@ public class MethodReferenceNode extends ExpressionNode {
// DATE OPERATORS
map.put("dateAdd", mapArgRef().forOperator("$dateAdd").mappingParametersTo("startDate", "unit", "amount", "timezone"));
+ map.put("dateDiff", mapArgRef().forOperator("$dateDiff").mappingParametersTo("startDate", "endDate", "unit","timezone", "startOfWeek"));
map.put("dayOfYear", singleArgRef().forOperator("$dayOfYear"));
map.put("dayOfMonth", singleArgRef().forOperator("$dayOfMonth"));
map.put("dayOfWeek", singleArgRef().forOperator("$dayOfWeek"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
index 036edfdce1..ab975b852a 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
@@ -40,5 +40,21 @@ void rendersDateAddWithTimezone() {
.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
"{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }"));
}
+
+ @Test // GH-3713
+ void rendersDateDiff() {
+
+ assertThat(
+ DateOperators.dateOf("purchaseDate").diffValueOf("delivered", "day").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document
+ .parse("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }"));
+ }
+
+ @Test // GH-3713
+ void rendersDateDiffWithTimezone() {
+
+ assertThat(DateOperators.dateOf("purchaseDate").withTimezone(Timezone.valueOf("America/Chicago"))
+ .diffValueOf("delivered", "day").toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
+ "{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\", timezone : \"America/Chicago\" } }"));
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index 337d61f984..33edab1d5a 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1044,6 +1044,11 @@ void shouldRenderDateAdd() {
assertThat(transform("dateAdd(purchaseDate, 'day', 3)")).isEqualTo(Document.parse("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }"));
}
+ @Test // GH-3713
+ void shouldRenderDateDiff() {
+ assertThat(transform("dateDiff(purchaseDate, delivered, 'day')")).isEqualTo(Document.parse("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }"));
+ }
+
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);
From 456c1ad26abb77d82595f7e1589ca02a9a780da9 Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Mon, 26 Jul 2021 09:18:16 +0200
Subject: [PATCH 034/920] Add shortcut for date aggregation operators working
with timezone.
See: #3713
Original pull request: #3748.
---
.../core/aggregation/DateOperators.java | 26 +++++++++++++++++++
.../aggregation/DateOperatorsUnitTests.java | 6 ++---
2 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
index a9b1d411cd..a97d64c52d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
@@ -46,6 +46,19 @@ public static DateOperatorFactory dateOf(String fieldReference) {
return new DateOperatorFactory(fieldReference);
}
+ /**
+ * Take the date referenced by given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link DateOperatorFactory}.
+ * @since 3.3
+ */
+ public static DateOperatorFactory zonedDateOf(String fieldReference, Timezone timezone) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new DateOperatorFactory(fieldReference).withTimezone(timezone);
+ }
+
/**
* Take the date resulting from the given {@link AggregationExpression}.
*
@@ -58,6 +71,19 @@ public static DateOperatorFactory dateOf(AggregationExpression expression) {
return new DateOperatorFactory(expression);
}
+ /**
+ * Take the date resulting from the given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link DateOperatorFactory}.
+ * @since 3.3
+ */
+ public static DateOperatorFactory zonedDateOf(AggregationExpression expression, Timezone timezone) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new DateOperatorFactory(expression).withTimezone(timezone);
+ }
+
/**
* Take the given value as date.
*
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
index ab975b852a..95f977ed73 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
@@ -35,8 +35,8 @@ void rendersDateAdd() {
@Test // GH-3713
void rendersDateAddWithTimezone() {
-
- assertThat(DateOperators.dateOf("purchaseDate").withTimezone(Timezone.valueOf("America/Chicago")).add(3, "day")
+
+ assertThat(DateOperators.zonedDateOf("purchaseDate", Timezone.valueOf("America/Chicago")).add(3, "day")
.toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
"{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }"));
}
@@ -53,7 +53,7 @@ void rendersDateDiff() {
@Test // GH-3713
void rendersDateDiffWithTimezone() {
- assertThat(DateOperators.dateOf("purchaseDate").withTimezone(Timezone.valueOf("America/Chicago"))
+ assertThat(DateOperators.zonedDateOf("purchaseDate", Timezone.valueOf("America/Chicago"))
.diffValueOf("delivered", "day").toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
"{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\", timezone : \"America/Chicago\" } }"));
}
From 24171b3ae27a4bc867fd619420543ce1b56344f5 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 25 Aug 2021 10:59:26 +0200
Subject: [PATCH 035/920] Polishing.
Introduce factory methods to convert TimeZone/ZoneId/ZoneOffset into Mongo Timezone. Introduce TemporalUnit abstraction and converters to convert ChronoUnit and TimeUnit into TemporalUnit for date operators accepting a unit parameter.
See #3713
Original pull request: #3748.
---
.../core/aggregation/DateOperators.java | 294 ++++++++++++++++--
.../aggregation/SetWindowFieldsOperation.java | 63 ++++
.../aggregation/DateOperatorsUnitTests.java | 48 ++-
.../SpelExpressionTransformerUnitTests.java | 11 +-
.../reference/aggregation-framework.adoc | 2 +-
5 files changed, 376 insertions(+), 42 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
index a97d64c52d..029b994f2e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DateOperators.java
@@ -15,10 +15,16 @@
*/
package org.springframework.data.mongodb.core.aggregation;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
+import java.util.Locale;
import java.util.Map;
+import java.util.TimeZone;
+import java.util.concurrent.TimeUnit;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -157,6 +163,7 @@ public static DateFromString dateFromString(String value) {
* NOTE: Support for timezones in aggregations Requires MongoDB 3.6 or later.
*
* @author Christoph Strobl
+ * @author Mark Paluch
* @since 2.1
*/
public static class Timezone {
@@ -192,6 +199,61 @@ public static Timezone valueOf(Object value) {
return new Timezone(value);
}
+ /**
+ * Create a {@link Timezone} for the given {@link TimeZone} rendering the offset as UTC offset.
+ *
+ * @param timeZone {@link TimeZone} rendering the offset as UTC offset.
+ * @return new instance of {@link Timezone}.
+ * @since 3.3
+ */
+ public static Timezone fromOffset(TimeZone timeZone) {
+
+ Assert.notNull(timeZone, "TimeZone must not be null!");
+
+ return fromOffset(
+ ZoneOffset.ofTotalSeconds(Math.toIntExact(TimeUnit.MILLISECONDS.toSeconds(timeZone.getRawOffset()))));
+ }
+
+ /**
+ * Create a {@link Timezone} for the given {@link ZoneOffset} rendering the offset as UTC offset.
+ *
+ * @param offset {@link ZoneOffset} rendering the offset as UTC offset.
+ * @return new instance of {@link Timezone}.
+ * @since 3.3
+ */
+ public static Timezone fromOffset(ZoneOffset offset) {
+
+ Assert.notNull(offset, "ZoneOffset must not be null!");
+ return new Timezone(offset.toString());
+ }
+
+ /**
+ * Create a {@link Timezone} for the given {@link TimeZone} rendering the offset as UTC offset.
+ *
+ * @param timeZone {@link Timezone} rendering the offset as zone identifier.
+ * @return new instance of {@link Timezone}.
+ * @since 3.3
+ */
+ public static Timezone fromZone(TimeZone timeZone) {
+
+ Assert.notNull(timeZone, "TimeZone must not be null!");
+
+ return valueOf(timeZone.getID());
+ }
+
+ /**
+ * Create a {@link Timezone} for the given {@link java.time.ZoneId} rendering the offset as UTC offset.
+ *
+ * @param zoneId {@link ZoneId} rendering the offset as zone identifier.
+ * @return new instance of {@link Timezone}.
+ * @since 3.3
+ */
+ public static Timezone fromZone(ZoneId zoneId) {
+
+ Assert.notNull(zoneId, "ZoneId must not be null!");
+ return new Timezone(zoneId.toString());
+ }
+
/**
* Create a {@link Timezone} for the {@link Field} reference holding the Olson Timezone Identifier or UTC Offset.
*
@@ -212,6 +274,11 @@ public static Timezone ofField(String fieldReference) {
public static Timezone ofExpression(AggregationExpression expression) {
return valueOf(expression);
}
+
+ @Nullable
+ Object getValue() {
+ return value;
+ }
}
/**
@@ -303,32 +370,64 @@ public DateOperatorFactory withTimezone(Timezone timezone) {
/**
* Creates new {@link AggregationExpression} that adds the value of the given {@link AggregationExpression
- * expression} (in {@literal units). @param expression must not be {@literal null}.
- *
+ * expression} (in {@literal units}).
+ *
+ * @param expression must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
- * @return new instance of {@link DateAdd}.
- * @since 3.3
+ * @return new instance of {@link DateAdd}. @since 3.3
*/
public DateAdd addValueOf(AggregationExpression expression, String unit) {
return applyTimezone(DateAdd.addValueOf(expression, unit).toDate(dateReference()), timezone);
}
+ /**
+ * Creates new {@link AggregationExpression} that adds the value of the given {@link AggregationExpression
+ * expression} (in {@literal units}).
+ *
+ * @param expression must not be {@literal null}.
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}. @since 3.3
+ */
+ public DateAdd addValueOf(AggregationExpression expression, TemporalUnit unit) {
+
+ Assert.notNull(unit, "TemporalUnit must not be null");
+ return applyTimezone(DateAdd.addValueOf(expression, unit.name().toLowerCase(Locale.ROOT)).toDate(dateReference()),
+ timezone);
+ }
+
/**
* Creates new {@link AggregationExpression} that adds the value stored at the given {@literal field} (in
- * {@literal units). @param fieldReference must not be {@literal null}.
- *
+ * {@literal units}).
+ *
+ * @param fieldReference must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
- * @return new instance of {@link DateAdd}.
- * @since 3.3
+ * @return new instance of {@link DateAdd}. @since 3.3
*/
public DateAdd addValueOf(String fieldReference, String unit) {
return applyTimezone(DateAdd.addValueOf(fieldReference, unit).toDate(dateReference()), timezone);
}
/**
- * Creates new {@link AggregationExpression} that adds the given value (in {@literal units). @param value must not
- * be {@literal null}. @param unit the unit of measure. Must not be {@literal null}.
- *
+ * Creates new {@link AggregationExpression} that adds the value stored at the given {@literal field} (in
+ * {@literal units}).
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}. @since 3.3
+ */
+ public DateAdd addValueOf(String fieldReference, TemporalUnit unit) {
+
+ Assert.notNull(unit, "TemporalUnit must not be null");
+
+ return applyTimezone(
+ DateAdd.addValueOf(fieldReference, unit.name().toLowerCase(Locale.ROOT)).toDate(dateReference()), timezone);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that adds the given value (in {@literal units}).
+ *
+ * @param value must not be {@literal null}.
+ * @param unit the unit of measure. Must not be {@literal null}.
* @return
* @since 3.3 new instance of {@link DateAdd}.
*/
@@ -336,6 +435,22 @@ public DateAdd add(Object value, String unit) {
return applyTimezone(DateAdd.addValue(value, unit).toDate(dateReference()), timezone);
}
+ /**
+ * Creates new {@link AggregationExpression} that adds the given value (in {@literal units}).
+ *
+ * @param value must not be {@literal null}.
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return
+ * @since 3.3 new instance of {@link DateAdd}.
+ */
+ public DateAdd add(Object value, TemporalUnit unit) {
+
+ Assert.notNull(unit, "TemporalUnit must not be null");
+
+ return applyTimezone(DateAdd.addValue(value, unit.name().toLowerCase(Locale.ROOT)).toDate(dateReference()),
+ timezone);
+ }
+
/**
* Creates new {@link AggregationExpression} that returns the day of the year for a date as a number between 1 and
* 366.
@@ -367,41 +482,89 @@ public DayOfWeek dayOfWeek() {
}
/**
- * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date
- * computed by the given {@link AggregationExpression expression}. @param expression must not be {@literal null}.
- *
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units}) to the date
+ * computed by the given {@link AggregationExpression expression}.
+ *
+ * @param expression must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
- * @return new instance of {@link DateAdd}.
- * @since 3.3
+ * @return new instance of {@link DateAdd}. @since 3.3
*/
public DateDiff diffValueOf(AggregationExpression expression, String unit) {
return applyTimezone(DateDiff.diffValueOf(expression, unit).toDate(dateReference()), timezone);
}
/**
- * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date stored
- * at the given {@literal field}. @param expression must not be {@literal null}.
- *
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units}) to the date
+ * computed by the given {@link AggregationExpression expression}.
+ *
+ * @param expression must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
- * @return new instance of {@link DateAdd}.
- * @since 3.3
+ * @return new instance of {@link DateAdd}. @since 3.3
+ */
+ public DateDiff diffValueOf(AggregationExpression expression, TemporalUnit unit) {
+
+ Assert.notNull(unit, "TemporalUnit must not be null");
+
+ return applyTimezone(
+ DateDiff.diffValueOf(expression, unit.name().toLowerCase(Locale.ROOT)).toDate(dateReference()), timezone);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units}) to the date stored
+ * at the given {@literal field}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}. @since 3.3
*/
public DateDiff diffValueOf(String fieldReference, String unit) {
return applyTimezone(DateDiff.diffValueOf(fieldReference, unit).toDate(dateReference()), timezone);
}
/**
- * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units) to the date given
- * {@literal value}. @param value anything the resolves to a valid date. Must not be {@literal null}.
- *
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units}) to the date stored
+ * at the given {@literal field}.
+ *
+ * @param fieldReference must not be {@literal null}.
* @param unit the unit of measure. Must not be {@literal null}.
- * @return new instance of {@link DateAdd}.
- * @since 3.3
+ * @return new instance of {@link DateAdd}. @since 3.3
+ */
+ public DateDiff diffValueOf(String fieldReference, TemporalUnit unit) {
+
+ Assert.notNull(unit, "TemporalUnit must not be null");
+
+ return applyTimezone(
+ DateDiff.diffValueOf(fieldReference, unit.name().toLowerCase(Locale.ROOT)).toDate(dateReference()), timezone);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units}) to the date given
+ * {@literal value}.
+ *
+ * @param value anything the resolves to a valid date. Must not be {@literal null}.
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}. @since 3.3
*/
public DateDiff diff(Object value, String unit) {
return applyTimezone(DateDiff.diffValue(value, unit).toDate(dateReference()), timezone);
}
+ /**
+ * Creates new {@link AggregationExpression} that calculates the difference (in {@literal units}) to the date given
+ * {@literal value}.
+ *
+ * @param value anything the resolves to a valid date. Must not be {@literal null}.
+ * @param unit the unit of measure. Must not be {@literal null}.
+ * @return new instance of {@link DateAdd}. @since 3.3
+ */
+ public DateDiff diff(Object value, TemporalUnit unit) {
+
+ Assert.notNull(unit, "TemporalUnit must not be null");
+
+ return applyTimezone(DateDiff.diffValue(value, unit.name().toLowerCase(Locale.ROOT)).toDate(dateReference()),
+ timezone);
+ }
+
/**
* Creates new {@link AggregationExpression} that returns the year portion of a date.
*
@@ -2720,6 +2883,85 @@ protected String getMongoMethod() {
}
}
+ /**
+ * Interface defining a temporal unit for date operators.
+ *
+ * @author Mark Paluch
+ * @since 3.3
+ */
+ public interface TemporalUnit {
+
+ String name();
+
+ /**
+ * Converts the given time unit into a {@link TemporalUnit}. Supported units are: days, hours, minutes, seconds, and
+ * milliseconds.
+ *
+ * @param timeUnit the time unit to convert, must not be {@literal null}.
+ * @return
+ * @throws IllegalArgumentException if the {@link TimeUnit} is {@literal null} or not supported for conversion.
+ */
+ static TemporalUnit from(TimeUnit timeUnit) {
+
+ Assert.notNull(timeUnit, "TimeUnit must not be null");
+
+ switch (timeUnit) {
+ case DAYS:
+ return TemporalUnits.DAY;
+ case HOURS:
+ return TemporalUnits.HOUR;
+ case MINUTES:
+ return TemporalUnits.MINUTE;
+ case SECONDS:
+ return TemporalUnits.SECOND;
+ case MILLISECONDS:
+ return TemporalUnits.MILLISECOND;
+ }
+
+ throw new IllegalArgumentException(String.format("Cannot create TemporalUnit from %s", timeUnit));
+ }
+
+ /**
+ * Converts the given chrono unit into a {@link TemporalUnit}. Supported units are: years, weeks, months, days,
+ * hours, minutes, seconds, and millis.
+ *
+ * @param chronoUnit the chrono unit to convert, must not be {@literal null}.
+ * @return
+ * @throws IllegalArgumentException if the {@link TimeUnit} is {@literal null} or not supported for conversion.
+ */
+ static TemporalUnit from(ChronoUnit chronoUnit) {
+
+ switch (chronoUnit) {
+ case YEARS:
+ return TemporalUnits.YEAR;
+ case WEEKS:
+ return TemporalUnits.WEEK;
+ case MONTHS:
+ return TemporalUnits.MONTH;
+ case DAYS:
+ return TemporalUnits.DAY;
+ case HOURS:
+ return TemporalUnits.HOUR;
+ case MINUTES:
+ return TemporalUnits.MINUTE;
+ case SECONDS:
+ return TemporalUnits.SECOND;
+ case MILLIS:
+ return TemporalUnits.MILLISECOND;
+ }
+
+ throw new IllegalArgumentException(String.format("Cannot create TemporalUnit from %s", chronoUnit));
+ }
+ }
+
+ /**
+ * Supported temporal units.
+ */
+ enum TemporalUnits implements TemporalUnit {
+ YEAR, QUARTER, WEEK, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND
+
+ }
+
@SuppressWarnings("unchecked")
private static T applyTimezone(T instance, Timezone timezone) {
return !ObjectUtils.nullSafeEquals(Timezone.none(), timezone) && !instance.hasTimezone()
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java
index 9c40a0b642..fa01b02b98 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetWindowFieldsOperation.java
@@ -15,9 +15,11 @@
*/
package org.springframework.data.mongodb.core.aggregation;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import org.bson.Document;
import org.springframework.data.domain.Sort;
@@ -626,7 +628,68 @@ public Document toDocument(AggregationOperationContext ctx) {
* The actual time unit to apply to a {@link Window}.
*/
public interface WindowUnit {
+
String name();
+
+ /**
+ * Converts the given time unit into a {@link WindowUnit}. Supported units are: days, hours, minutes, seconds, and
+ * milliseconds.
+ *
+ * @param timeUnit the time unit to convert, must not be {@literal null}.
+ * @return
+ * @throws IllegalArgumentException if the {@link TimeUnit} is {@literal null} or not supported for conversion.
+ */
+ static WindowUnit from(TimeUnit timeUnit) {
+
+ Assert.notNull(timeUnit, "TimeUnit must not be null");
+
+ switch (timeUnit) {
+ case DAYS:
+ return WindowUnits.DAY;
+ case HOURS:
+ return WindowUnits.HOUR;
+ case MINUTES:
+ return WindowUnits.MINUTE;
+ case SECONDS:
+ return WindowUnits.SECOND;
+ case MILLISECONDS:
+ return WindowUnits.MILLISECOND;
+ }
+
+ throw new IllegalArgumentException(String.format("Cannot create WindowUnit from %s", timeUnit));
+ }
+
+ /**
+ * Converts the given chrono unit into a {@link WindowUnit}. Supported units are: years, weeks, months, days, hours,
+ * minutes, seconds, and millis.
+ *
+ * @param chronoUnit the chrono unit to convert, must not be {@literal null}.
+ * @return
+ * @throws IllegalArgumentException if the {@link TimeUnit} is {@literal null} or not supported for conversion.
+ */
+ static WindowUnit from(ChronoUnit chronoUnit) {
+
+ switch (chronoUnit) {
+ case YEARS:
+ return WindowUnits.YEAR;
+ case WEEKS:
+ return WindowUnits.WEEK;
+ case MONTHS:
+ return WindowUnits.MONTH;
+ case DAYS:
+ return WindowUnits.DAY;
+ case HOURS:
+ return WindowUnits.HOUR;
+ case MINUTES:
+ return WindowUnits.MINUTE;
+ case SECONDS:
+ return WindowUnits.SECOND;
+ case MILLIS:
+ return WindowUnits.MILLISECOND;
+ }
+
+ throw new IllegalArgumentException(String.format("Cannot create WindowUnit from %s", chronoUnit));
+ }
}
/**
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
index 95f977ed73..6d63b954f8 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DateOperatorsUnitTests.java
@@ -15,14 +15,22 @@
*/
package org.springframework.data.mongodb.core.aggregation;
-import static org.assertj.core.api.Assertions.*;
+import static org.springframework.data.mongodb.test.util.Assertions.*;
+
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.temporal.ChronoUnit;
+import java.util.TimeZone;
-import org.bson.Document;
import org.junit.jupiter.api.Test;
+
import org.springframework.data.mongodb.core.aggregation.DateOperators.Timezone;
/**
+ * Unit tests for {@link DateOperators}.
+ *
* @author Christoph Strobl
+ * @author Mark Paluch
*/
class DateOperatorsUnitTests {
@@ -30,15 +38,15 @@ class DateOperatorsUnitTests {
void rendersDateAdd() {
assertThat(DateOperators.dateOf("purchaseDate").add(3, "day").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }"));
+ .isEqualTo("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }");
}
@Test // GH-3713
void rendersDateAddWithTimezone() {
assertThat(DateOperators.zonedDateOf("purchaseDate", Timezone.valueOf("America/Chicago")).add(3, "day")
- .toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
- "{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }"));
+ .toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(
+ "{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3, timezone : \"America/Chicago\" } }");
}
@Test // GH-3713
@@ -46,15 +54,37 @@ void rendersDateDiff() {
assertThat(
DateOperators.dateOf("purchaseDate").diffValueOf("delivered", "day").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document
- .parse("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }"));
+ .isEqualTo("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }");
}
@Test // GH-3713
void rendersDateDiffWithTimezone() {
assertThat(DateOperators.zonedDateOf("purchaseDate", Timezone.valueOf("America/Chicago"))
- .diffValueOf("delivered", "day").toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(Document.parse(
- "{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\", timezone : \"America/Chicago\" } }"));
+ .diffValueOf("delivered", DateOperators.TemporalUnit.from(ChronoUnit.DAYS))
+ .toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(
+ "{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\", timezone : \"America/Chicago\" } }");
+ }
+
+ @Test // GH-3713
+ void rendersTimezoneFromZoneOffset() {
+ assertThat(DateOperators.Timezone.fromOffset(ZoneOffset.ofHoursMinutes(3, 30)).getValue()).isEqualTo("+03:30");
+ }
+
+ @Test // GH-3713
+ void rendersTimezoneFromTimeZoneOffset() {
+ assertThat(DateOperators.Timezone.fromOffset(TimeZone.getTimeZone("America/Chicago")).getValue())
+ .isEqualTo("-06:00");
+ }
+
+ @Test // GH-3713
+ void rendersTimezoneFromTimeZoneId() {
+ assertThat(DateOperators.Timezone.fromZone(TimeZone.getTimeZone("America/Chicago")).getValue())
+ .isEqualTo("America/Chicago");
+ }
+
+ @Test // GH-3713
+ void rendersTimezoneFromZoneId() {
+ assertThat(DateOperators.Timezone.fromZone(ZoneId.of("America/Chicago")).getValue()).isEqualTo("America/Chicago");
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index 33edab1d5a..bc8da0a3c1 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1034,11 +1034,6 @@ void shouldRenderTanh() {
assertThat(transform("tanh(angle)")).isEqualTo("{ \"$tanh\" : \"$angle\"}");
}
- private Document transform(String expression, Object... params) {
- return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
- }
-
- private Object transformValue(String expression, Object... params) {
@Test // GH-3713
void shouldRenderDateAdd() {
assertThat(transform("dateAdd(purchaseDate, 'day', 3)")).isEqualTo(Document.parse("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }"));
@@ -1049,7 +1044,11 @@ void shouldRenderDateDiff() {
assertThat(transform("dateDiff(purchaseDate, delivered, 'day')")).isEqualTo(Document.parse("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }"));
}
- private Object transform(String expression, Object... params) {
+ private Document transform(String expression, Object... params) {
+ return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
+ }
+
+ private Object transformValue(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);
}
diff --git a/src/main/asciidoc/reference/aggregation-framework.adoc b/src/main/asciidoc/reference/aggregation-framework.adoc
index f23b290697..bc7a032e75 100644
--- a/src/main/asciidoc/reference/aggregation-framework.adoc
+++ b/src/main/asciidoc/reference/aggregation-framework.adoc
@@ -100,7 +100,7 @@ At the time of this writing, we provide support for the following Aggregation Op
| `literal`
| Date Aggregation Operators
-| `dayOfYear`, `dayOfMonth`, `dayOfWeek`, `year`, `month`, `week`, `hour`, `minute`, `second`, `millisecond`, `dateToString`, `dateFromString`, `dateFromParts`, `dateToParts`, `isoDayOfWeek`, `isoWeek`, `isoWeekYear`
+| `dayOfYear`, `dayOfMonth`, `dayOfWeek`, `year`, `month`, `week`, `hour`, `minute`, `second`, `millisecond`, `dateAdd`, `dateDiff`, `dateToString`, `dateFromString`, `dateFromParts`, `dateToParts`, `isoDayOfWeek`, `isoWeek`, `isoWeekYear`
| Variable Operators
| `map`
From 92cc2a582a8b5770996f11b409f9629678c2ce8e Mon Sep 17 00:00:00 2001
From: Mushtaq Ahmed
Date: Sat, 31 Jul 2021 16:52:38 +0530
Subject: [PATCH 036/920] Add support for `$rand` aggregation operator.
Closes #3724
Original pull request: #3759
---
.../core/aggregation/ArithmeticOperators.java | 25 +++++++++++++++++++
.../core/spel/MethodReferenceNode.java | 1 +
.../ArithmeticOperatorsUnitTests.java | 5 ++++
.../SpelExpressionTransformerUnitTests.java | 5 ++++
4 files changed, 36 insertions(+)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index 7896486abf..9610967830 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -19,6 +19,7 @@
import java.util.List;
import java.util.Locale;
+import org.bson.Document;
import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.Avg;
import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.CovariancePop;
import org.springframework.data.mongodb.core.aggregation.AccumulatorOperators.CovarianceSamp;
@@ -63,6 +64,16 @@ public static ArithmeticOperatorFactory valueOf(AggregationExpression expression
return new ArithmeticOperatorFactory(expression);
}
+ /**
+ * Creates new {@link AggregationExpression} that returns a random float between 0 and 1 each time it is called.
+ *
+ * @return new instance of {@link Rand}.
+ * @since 3.3
+ */
+ public static Rand rand() {
+ return new Rand();
+ }
+
/**
* @author Christoph Strobl
*/
@@ -2671,4 +2682,18 @@ protected String getMongoMethod() {
return "$tanh";
}
}
+
+ /**
+ * {@link Rand} returns a floating value between 0 and 1.
+ *
+ * @author Mushtaq Ahmed
+ * @since 3.3
+ */
+ public static class Rand implements AggregationExpression {
+
+ @Override
+ public Document toDocument(AggregationOperationContext context) {
+ return new Document("$rand", new Document());
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index 6a60a7df1a..a91358353c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -99,6 +99,7 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("cosh", singleArgRef().forOperator("$cosh"));
map.put("tan", singleArgRef().forOperator("$tan"));
map.put("tanh", singleArgRef().forOperator("$tanh"));
+ map.put("rand", emptyRef().forOperator("$rand"));
// STRING OPERATORS
map.put("concat", arrayArgRef().forOperator("$concat"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index 7cde7cd1c4..b589e152aa 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -166,4 +166,9 @@ void rendersTanhWithValueInDegrees() {
.isEqualTo("{ $tanh : { $degreesToRadians : \"$angle\" } }");
}
+
+ @Test // GH-3724
+ void rendersRank() {
+ assertThat(rand().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(new Document("$rand", new Document()));
+ }
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index bc8da0a3c1..cc20ffd121 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1044,6 +1044,11 @@ void shouldRenderDateDiff() {
assertThat(transform("dateDiff(purchaseDate, delivered, 'day')")).isEqualTo(Document.parse("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }"));
}
+ @Test // GH-3724
+ void shouldRenderRand() {
+ assertThat(transform("rand()")).isEqualTo(Document.parse("{ $rand : {} }"));
+ }
+
private Document transform(String expression, Object... params) {
return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
}
From 7c6e951c7c0088459ada9e6c5a7c3ad2a427fbaa Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 25 Aug 2021 11:13:02 +0200
Subject: [PATCH 037/920] Polishing.
Add author tags, tweak Javadoc style. Simplify tests. Document operator.
See #3724
Original pull request: #3759.
---
.../mongodb/core/aggregation/ArithmeticOperators.java | 4 +++-
.../core/aggregation/ArithmeticOperatorsUnitTests.java | 4 ++--
.../aggregation/SpelExpressionTransformerUnitTests.java | 8 +++++---
src/main/asciidoc/reference/aggregation-framework.adoc | 2 +-
4 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index 9610967830..8fe3d9120c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -40,6 +40,7 @@
*
* @author Christoph Strobl
* @author Mark Paluch
+ * @author Mushtaq Ahmed
* @since 1.10
*/
public class ArithmeticOperators {
@@ -65,7 +66,8 @@ public static ArithmeticOperatorFactory valueOf(AggregationExpression expression
}
/**
- * Creates new {@link AggregationExpression} that returns a random float between 0 and 1 each time it is called.
+ * Creates new {@link AggregationExpression} that returns a random float between {@code 0} and {@code 1} each time it
+ * is called.
*
* @return new instance of {@link Rand}.
* @since 3.3
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index b589e152aa..02f76d5c10 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -29,6 +29,7 @@
*
* @author Christoph Strobl
* @author Mark Paluch
+ * @author Mushtaq Ahmed
*/
class ArithmeticOperatorsUnitTests {
@@ -166,9 +167,8 @@ void rendersTanhWithValueInDegrees() {
.isEqualTo("{ $tanh : { $degreesToRadians : \"$angle\" } }");
}
-
@Test // GH-3724
- void rendersRank() {
+ void rendersRand() {
assertThat(rand().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(new Document("$rand", new Document()));
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index cc20ffd121..daba7a21cd 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1036,17 +1036,19 @@ void shouldRenderTanh() {
@Test // GH-3713
void shouldRenderDateAdd() {
- assertThat(transform("dateAdd(purchaseDate, 'day', 3)")).isEqualTo(Document.parse("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }"));
+ assertThat(transform("dateAdd(purchaseDate, 'day', 3)"))
+ .isEqualTo("{ $dateAdd: { startDate: \"$purchaseDate\", unit: \"day\", amount: 3 } }");
}
@Test // GH-3713
void shouldRenderDateDiff() {
- assertThat(transform("dateDiff(purchaseDate, delivered, 'day')")).isEqualTo(Document.parse("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }"));
+ assertThat(transform("dateDiff(purchaseDate, delivered, 'day')"))
+ .isEqualTo("{ $dateDiff: { startDate: \"$purchaseDate\", endDate: \"$delivered\", unit: \"day\" } }");
}
@Test // GH-3724
void shouldRenderRand() {
- assertThat(transform("rand()")).isEqualTo(Document.parse("{ $rand : {} }"));
+ assertThat(transform("rand()")).isEqualTo("{ $rand : {} }");
}
private Document transform(String expression, Object... params) {
diff --git a/src/main/asciidoc/reference/aggregation-framework.adoc b/src/main/asciidoc/reference/aggregation-framework.adoc
index bc7a032e75..f96719adde 100644
--- a/src/main/asciidoc/reference/aggregation-framework.adoc
+++ b/src/main/asciidoc/reference/aggregation-framework.adoc
@@ -76,7 +76,7 @@ At the time of this writing, we provide support for the following Aggregation Op
[cols="2*"]
|===
| Pipeline Aggregation Operators
-| `bucket`, `bucketAuto`, `count`, `facet`, `geoNear`, `graphLookup`, `group`, `limit`, `lookup`, `match`, `project`, `replaceRoot`, `skip`, `sort`, `unwind`
+| `bucket`, `bucketAuto`, `count`, `facet`, `geoNear`, `graphLookup`, `group`, `limit`, `lookup`, `match`, `project`, `rand`, `replaceRoot`, `skip`, `sort`, `unwind`
| Set Aggregation Operators
| `setEquals`, `setIntersection`, `setUnion`, `setDifference`, `setIsSubset`, `anyElementTrue`, `allElementsTrue`
From 302c8031f90f951fcae2e67b6d471f026f266089 Mon Sep 17 00:00:00 2001
From: sangyongchoi
Date: Tue, 3 Aug 2021 23:23:59 +0900
Subject: [PATCH 038/920] Add Criteria infix functions for `maxDistance` and
`minDistance`.
Closes: #3761
---
.../core/query/TypedCriteriaExtensions.kt | 22 +++++++++
.../query/TypedCriteriaExtensionsTests.kt | 48 +++++++++++++++++++
2 files changed, 70 insertions(+)
diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt
index b8762ffbe1..eb1868e300 100644
--- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt
+++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt
@@ -364,6 +364,28 @@ infix fun KProperty>.maxDistance(d: Double): Criteria =
infix fun KProperty>.minDistance(d: Double): Criteria =
Criteria(asString(this)).minDistance(d)
+/**
+ * Creates a geo-spatial criterion using a $maxDistance operation, for use with $near
+ *
+ * See [MongoDB Query operator:
+ * $maxDistance](https://docs.mongodb.com/manual/reference/operator/query/maxDistance/)
+ * @author Sangyong Choi
+ * @since 3.2
+ * @see Criteria.maxDistance
+ */
+infix fun Criteria.maxDistance(d: Double): Criteria =
+ this.maxDistance(d)
+
+/**
+ * Creates a geospatial criterion using a $minDistance operation, for use with $near or
+ * $nearSphere.
+ * @author Sangyong Choi
+ * @since 3.2
+ * @see Criteria.minDistance
+ */
+infix fun Criteria.minDistance(d: Double): Criteria =
+ this.minDistance(d)
+
/**
* Creates a criterion using the $elemMatch operator
*
diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt
index 54969476d3..3b9dfc9342 100644
--- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt
+++ b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt
@@ -317,6 +317,54 @@ class TypedCriteriaExtensionsTests {
assertThat(typed).isEqualTo(expected)
}
+ @Test
+ fun `maxDistance() should equal expected criteria with nearSphere`() {
+ val point = Point(0.0, 0.0)
+
+ val typed = Building::location nearSphere point maxDistance 3.0
+ val expected = Criteria("location")
+ .nearSphere(point)
+ .maxDistance(3.0)
+
+ assertThat(typed).isEqualTo(expected)
+ }
+
+ @Test
+ fun `minDistance() should equal expected criteria with nearSphere`() {
+ val point = Point(0.0, 0.0)
+
+ val typed = Building::location nearSphere point minDistance 3.0
+ val expected = Criteria("location")
+ .nearSphere(point)
+ .minDistance(3.0)
+
+ assertThat(typed).isEqualTo(expected)
+ }
+
+ @Test
+ fun `maxDistance() should equal expected criteria with near`() {
+ val point = Point(0.0, 0.0)
+
+ val typed = Building::location near point maxDistance 3.0
+ val expected = Criteria("location")
+ .near(point)
+ .maxDistance(3.0)
+
+ assertThat(typed).isEqualTo(expected)
+ }
+
+ @Test
+ fun `minDistance() should equal expected criteria with near`() {
+ val point = Point(0.0, 0.0)
+
+ val typed = Building::location near point minDistance 3.0
+ val expected = Criteria("location")
+ .near(point)
+ .minDistance(3.0)
+
+ assertThat(typed).isEqualTo(expected)
+ }
+
@Test
fun `elemMatch() should equal expected criteria`() {
From 467536cb34162f528ecba3d494e77414bb2cb333 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 25 Aug 2021 14:33:06 +0200
Subject: [PATCH 039/920] Polishing.
Update since version. Reformat code.
See: #3761.
---
.../mongodb/core/query/TypedCriteriaExtensions.kt | 4 ++--
.../core/query/TypedCriteriaExtensionsTests.kt | 11 +++++++----
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt
index eb1868e300..ab7e32fc03 100644
--- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt
+++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensions.kt
@@ -370,7 +370,7 @@ infix fun KProperty>.minDistance(d: Double): Criteria =
* See [MongoDB Query operator:
* $maxDistance](https://docs.mongodb.com/manual/reference/operator/query/maxDistance/)
* @author Sangyong Choi
- * @since 3.2
+ * @since 3.2.5
* @see Criteria.maxDistance
*/
infix fun Criteria.maxDistance(d: Double): Criteria =
@@ -380,7 +380,7 @@ infix fun Criteria.maxDistance(d: Double): Criteria =
* Creates a geospatial criterion using a $minDistance operation, for use with $near or
* $nearSphere.
* @author Sangyong Choi
- * @since 3.2
+ * @since 3.2.5
* @see Criteria.minDistance
*/
infix fun Criteria.minDistance(d: Double): Criteria =
diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt
index 3b9dfc9342..7a5c358fad 100644
--- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt
+++ b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/query/TypedCriteriaExtensionsTests.kt
@@ -25,8 +25,11 @@ import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type
import java.util.regex.Pattern
/**
+ * Unit tests for [Criteria] extensions.
+ *
* @author Tjeu Kayim
* @author Mark Paluch
+ * @author Sangyong Choi
*/
class TypedCriteriaExtensionsTests {
@@ -319,8 +322,8 @@ class TypedCriteriaExtensionsTests {
@Test
fun `maxDistance() should equal expected criteria with nearSphere`() {
- val point = Point(0.0, 0.0)
+ val point = Point(0.0, 0.0)
val typed = Building::location nearSphere point maxDistance 3.0
val expected = Criteria("location")
.nearSphere(point)
@@ -331,8 +334,8 @@ class TypedCriteriaExtensionsTests {
@Test
fun `minDistance() should equal expected criteria with nearSphere`() {
- val point = Point(0.0, 0.0)
+ val point = Point(0.0, 0.0)
val typed = Building::location nearSphere point minDistance 3.0
val expected = Criteria("location")
.nearSphere(point)
@@ -343,8 +346,8 @@ class TypedCriteriaExtensionsTests {
@Test
fun `maxDistance() should equal expected criteria with near`() {
- val point = Point(0.0, 0.0)
+ val point = Point(0.0, 0.0)
val typed = Building::location near point maxDistance 3.0
val expected = Criteria("location")
.near(point)
@@ -355,8 +358,8 @@ class TypedCriteriaExtensionsTests {
@Test
fun `minDistance() should equal expected criteria with near`() {
- val point = Point(0.0, 0.0)
+ val point = Point(0.0, 0.0)
val typed = Building::location near point minDistance 3.0
val expected = Criteria("location")
.near(point)
From 36e2d80d71634a134ed2c6d615be217692b59e9a Mon Sep 17 00:00:00 2001
From: Ivan Volzhev
Date: Sat, 21 Aug 2021 08:24:22 +0200
Subject: [PATCH 040/920] Relax requirement for GeoJsonMultiPoint construction
allowing creation using a single point.
Only 1 point is required per GeoJson RFC and Mongo works just fine with 1 point as well.
Closes #3776
Original pull request: #3777.
---
.../mongodb/core/geo/GeoJsonMultiPoint.java | 18 ++++++++++++++++--
.../data/mongodb/core/geo/GeoJsonTests.java | 16 ++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
index f42d38e0dc..c1c80b89e8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
@@ -28,6 +28,7 @@
* {@link GeoJsonMultiPoint} is defined as list of {@link Point}s.
*
* @author Christoph Strobl
+ * @author Ivan Volzhev
* @since 1.7
* @see https://geojson.org/geojson-spec.html#multipoint
*/
@@ -40,12 +41,12 @@ public class GeoJsonMultiPoint implements GeoJson> {
/**
* Creates a new {@link GeoJsonMultiPoint} for the given {@link Point}s.
*
- * @param points points must not be {@literal null} and have at least 2 entries.
+ * @param points points must not be {@literal null} and have at least 1 entry.
*/
public GeoJsonMultiPoint(List points) {
Assert.notNull(points, "Points must not be null.");
- Assert.isTrue(points.size() >= 2, "Minimum of 2 Points required.");
+ Assert.isTrue(points.size() >= 1, "Minimum of 1 Point required.");
this.points = new ArrayList(points);
}
@@ -69,6 +70,19 @@ public GeoJsonMultiPoint(Point first, Point second, Point... others) {
this.points.addAll(Arrays.asList(others));
}
+ /**
+ * Creates a new {@link GeoJsonMultiPoint} for the given {@link Point}.
+ *
+ * @param point must not be {@literal null}.
+ */
+ public GeoJsonMultiPoint(Point point) {
+
+ Assert.notNull(point, "First point must not be null!");
+
+ this.points = new ArrayList();
+ this.points.add(point);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getType()
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
index fa7115c098..6fa053dacd 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/geo/GeoJsonTests.java
@@ -63,6 +63,7 @@
/**
* @author Christoph Strobl
* @author Mark Paluch
+ * @author Ivan Volzhev
*/
@ExtendWith({ MongoClientExtension.class, SpringExtension.class })
@ContextConfiguration
@@ -329,6 +330,21 @@ public void shouldSaveAndRetrieveDocumentWithGeoJsonMultiPointTypeCorrectly() {
assertThat(result.geoJsonMultiPoint).isEqualTo(obj.geoJsonMultiPoint);
}
+ @Test // DATAMONGO-3776
+ public void shouldSaveAndRetrieveDocumentWithGeoJsonMultiPointTypeWithOnePointCorrectly() {
+
+ DocumentWithPropertyUsingGeoJsonType obj = new DocumentWithPropertyUsingGeoJsonType();
+ obj.id = "geoJsonMultiPoint";
+ obj.geoJsonMultiPoint = new GeoJsonMultiPoint(new Point(0, 0));
+
+ template.save(obj);
+
+ DocumentWithPropertyUsingGeoJsonType result = template.findOne(query(where("id").is(obj.id)),
+ DocumentWithPropertyUsingGeoJsonType.class);
+
+ assertThat(result.geoJsonMultiPoint).isEqualTo(obj.geoJsonMultiPoint);
+ }
+
@Test // DATAMONGO-1137
public void shouldSaveAndRetrieveDocumentWithGeoJsonMultiPolygonTypeCorrectly() {
From f71f1074455042e774030314e208950d9e570fad Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 25 Aug 2021 14:57:02 +0200
Subject: [PATCH 041/920] Polishing.
Reorder methods. Add since tag. Simplify assertions. Use diamond syntax.
See: #3776
Original pull request: #3777.
---
.../mongodb/core/geo/GeoJsonMultiPoint.java | 37 ++++++++++---------
1 file changed, 19 insertions(+), 18 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
index c1c80b89e8..30af9f7293 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/geo/GeoJsonMultiPoint.java
@@ -38,17 +38,31 @@ public class GeoJsonMultiPoint implements GeoJson> {
private final List points;
+ /**
+ * Creates a new {@link GeoJsonMultiPoint} for the given {@link Point}.
+ *
+ * @param point must not be {@literal null}.
+ * @since 3.2.5
+ */
+ public GeoJsonMultiPoint(Point point) {
+
+ Assert.notNull(point, "Point must not be null!");
+
+ this.points = new ArrayList<>();
+ this.points.add(point);
+ }
+
/**
* Creates a new {@link GeoJsonMultiPoint} for the given {@link Point}s.
*
- * @param points points must not be {@literal null} and have at least 1 entry.
+ * @param points points must not be {@literal null} and not empty
*/
public GeoJsonMultiPoint(List points) {
- Assert.notNull(points, "Points must not be null.");
- Assert.isTrue(points.size() >= 1, "Minimum of 1 Point required.");
+ Assert.notNull(points, "Points must not be null!");
+ Assert.notEmpty(points, "Points must contain at least one point!");
- this.points = new ArrayList(points);
+ this.points = new ArrayList<>(points);
}
/**
@@ -64,25 +78,12 @@ public GeoJsonMultiPoint(Point first, Point second, Point... others) {
Assert.notNull(second, "Second point must not be null!");
Assert.notNull(others, "Additional points must not be null!");
- this.points = new ArrayList();
+ this.points = new ArrayList<>();
this.points.add(first);
this.points.add(second);
this.points.addAll(Arrays.asList(others));
}
- /**
- * Creates a new {@link GeoJsonMultiPoint} for the given {@link Point}.
- *
- * @param point must not be {@literal null}.
- */
- public GeoJsonMultiPoint(Point point) {
-
- Assert.notNull(point, "First point must not be null!");
-
- this.points = new ArrayList();
- this.points.add(point);
- }
-
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.geo.GeoJson#getType()
From 297ef9823920008245f6271acc1e1212991d89e0 Mon Sep 17 00:00:00 2001
From: divya srivastava
Date: Mon, 23 Aug 2021 17:33:06 +0530
Subject: [PATCH 042/920] Add support for `$regexFind`, `$regexFindAll`, and
`$regexMatch` aggregation operators.
Closes #3725
Original pull request: #3781.
---
.../core/aggregation/StringOperators.java | 438 ++++++++++++++++++
.../core/spel/MethodReferenceNode.java | 3 +
.../ProjectionOperationUnitTests.java | 27 ++
.../SpelExpressionTransformerUnitTests.java | 64 +++
.../aggregation/StringOperatorsUnitTests.java | 106 +++++
5 files changed, 638 insertions(+)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
index 5a31f6b3fc..710c6c855e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
@@ -515,6 +515,120 @@ public RTrim rtrim(AggregationExpression expression) {
private RTrim createRTrim() {
return usesFieldRef() ? RTrim.valueOf(fieldReference) : RTrim.valueOf(expression);
}
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
+ * regular expression to find the document with the first match.
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind regexFind(String regex) {
+ return createRegexFind().regex(regex);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression resulting from the given {@link AggregationExpression} to find the document with the first match.
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind regexFind(AggregationExpression expression) {
+ return createRegexFind().regexOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression with the options specified in the argument to find the document with the first match.
+ *
+ * @param regex the regular expression to apply
+ * @param options the options to use
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind regexFind(String regex,String options) {
+ return createRegexFind().regex(regex).options(options);
+ }
+
+ private RegexFind createRegexFind() {
+ return usesFieldRef() ? RegexFind.valueOf(fieldReference) : RegexFind.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
+ * regular expression to find all the documents with the match.
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll regexFindAll(String regex) {
+ return createRegexFindAll().regex(regex);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression resulting from the given {@link AggregationExpression} to find all the documents with the match..
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll regexFindAll(AggregationExpression expression) {
+ return createRegexFindAll().regexOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression with the options specified in the argument to find all the documents with the match..
+ *
+ * @param regex the regular expression to apply
+ * @param options the options to use
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll regexFindAll(String regex,String options) {
+ return createRegexFindAll().regex(regex).options(options);
+ }
+
+ private RegexFindAll createRegexFindAll() {
+ return usesFieldRef() ? RegexFindAll.valueOf(fieldReference) : RegexFindAll.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
+ * regular expression to find if a match is found or not.
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch regexMatch(String regex) {
+ return createRegexMatch().regex(regex);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression resulting from the given {@link AggregationExpression} to find if a match is found or not.
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch regexMatch(AggregationExpression expression) {
+ return createRegexMatch().regexOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression with the options specified in the argument to find if a match is found or not.
+ *
+ * @param regex the regular expression to apply
+ * @param options the options to use
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch regexMatch(String regex,String options) {
+ return createRegexMatch().regex(regex).options(options);
+ }
+
+ private RegexMatch createRegexMatch() {
+ return usesFieldRef() ? RegexMatch.valueOf(fieldReference) : RegexMatch.valueOf(expression);
+ }
private boolean usesFieldRef() {
return fieldReference != null;
@@ -1477,4 +1591,328 @@ protected String getMongoMethod() {
return "$rtrim";
}
}
+
+ /**
+ * {@link AggregationExpression} for {@code $regexFind} which applies a regular expression (regex) to a string and
+ * returns information on the first matched substring.
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ */
+ public static class RegexFind extends AbstractAggregationExpression {
+
+ protected RegexFind(Object value) {
+ super(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$regexFind";
+ }
+
+ /**
+ * Creates new {@link RegexFind} using the value of the provided {@link Field fieldReference} as {@literal input} value.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public static RegexFind valueOf(String fieldReference) {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new RegexFind(Collections.singletonMap("input", Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link RegexFind} using the result of the provided {@link AggregationExpression} as {@literal input}
+ * value.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public static RegexFind valueOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexFind(Collections.singletonMap("input", expression));
+ }
+
+ /**
+ * Optional specify the options to use with the regular expression.
+ *
+ * @param options must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind options(String options) {
+ Assert.notNull(options, "Options must not be null!");
+ return new RegexFind(append("options", options));
+ }
+
+ /**
+ * Optional specify the reference to the {@link Field field} holding the options values to use with the regular expression.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind optionsOf(String fieldReference) {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new RegexFind(append("options", Fields.field(fieldReference)));
+ }
+
+ /**
+ * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular expression.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind optionsOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexFind(append("options", expression));
+ }
+
+ /**
+ * Optional specify the regular expression to apply.
+ *
+ * @param regex must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind regex(String regex) {
+ Assert.notNull(regex, "Regex must not be null!");
+ return new RegexFind(append("regex",regex));
+ }
+
+ /**
+ * Optional specify the reference to the {@link Field field} holding the regular expression to apply.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind regexOf(String fieldReference) {
+ Assert.notNull(fieldReference, "fieldReference must not be null!");
+ return new RegexFind(append("regex",Fields.field(fieldReference)));
+ }
+
+ /**
+ * Optional specify the {@link AggregationExpression} evaluating to the regular expression to apply.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind regexOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexFind(append("regex",expression));
+ }
+
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $regexFindAll} which applies a regular expression (regex) to a string and
+ * returns information on all the matched substrings.
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ */
+ public static class RegexFindAll extends AbstractAggregationExpression {
+
+ protected RegexFindAll(Object value) {
+ super(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$regexFindAll";
+ }
+
+ /**
+ * Creates new {@link RegexFindAll} using the value of the provided {@link Field fieldReference} as {@literal input} value.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public static RegexFindAll valueOf(String fieldReference) {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new RegexFindAll(Collections.singletonMap("input", Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link RegexFindAll} using the result of the provided {@link AggregationExpression} as {@literal input}
+ * value.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public static RegexFindAll valueOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexFindAll(Collections.singletonMap("input", expression));
+ }
+
+ /**
+ * Optional specify the options to use with the regular expression.
+ *
+ * @param options must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll options(String options) {
+ Assert.notNull(options, "Options must not be null!");
+ return new RegexFindAll(append("options", options));
+ }
+
+ /**
+ * Optional specify the reference to the {@link Field field} holding the options values to use with the regular expression.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll optionsOf(String fieldReference) {
+ Assert.notNull(fieldReference, "fieldReference must not be null!");
+ return new RegexFindAll(append("options", Fields.field(fieldReference)));
+ }
+
+ /**
+ * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular expression.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll optionsOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexFindAll(append("options", expression));
+ }
+
+ /**
+ * Optional specify the regular expression to apply.
+ *
+ * @param regex must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll regex(String regex) {
+ Assert.notNull(regex, "Regex must not be null!");
+ return new RegexFindAll(append("regex",regex));
+ }
+
+ /**
+ * Optional specify the reference to the {@link Field field} holding the regular expression to apply.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll regexOf(String fieldReference) {
+ Assert.notNull(fieldReference, "fieldReference must not be null!");
+ return new RegexFindAll(append("regex",Fields.field(fieldReference)));
+ }
+
+ /**
+ * Optional specify the {@link AggregationExpression} evaluating to the regular expression to apply.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll regexOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexFindAll(append("regex",expression));
+ }
+
+ }
+
+ /**
+ * {@link AggregationExpression} for {@code $regexMatch} which applies a regular expression (regex) to a string and
+ * returns a boolean that indicates if a match is found or not.
+ * NOTE: Requires MongoDB 4.0 or later.
+ *
+ */
+ public static class RegexMatch extends AbstractAggregationExpression {
+
+ protected RegexMatch(Object value) {
+ super(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$regexMatch";
+ }
+
+ /**
+ * Creates new {@link RegexMatch} using the value of the provided {@link Field fieldReference} as {@literal input} value.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public static RegexMatch valueOf(String fieldReference) {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new RegexMatch(Collections.singletonMap("input", Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates new {@link RegexMatch} using the result of the provided {@link AggregationExpression} as {@literal input}
+ * value.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public static RegexMatch valueOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexMatch(Collections.singletonMap("input", expression));
+ }
+
+ /**
+ * Optional specify the options to use with the regular expression.
+ *
+ * @param options must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch options(String options) {
+ Assert.notNull(options, "Options must not be null!");
+ return new RegexMatch(append("options", options));
+ }
+
+ /**
+ * Optional specify the reference to the {@link Field field} holding the options values to use with the regular expression.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch optionsOf(String fieldReference) {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new RegexMatch(append("options", Fields.field(fieldReference)));
+ }
+
+ /**
+ * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular expression.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch optionsOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexMatch(append("options", expression));
+ }
+
+ /**
+ * Optional specify the regular expression to apply.
+ *
+ * @param regex must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch regex(String regex) {
+ Assert.notNull(regex, "Regex must not be null!");
+ return new RegexMatch(append("regex",regex));
+ }
+
+ /**
+ * Optional specify the reference to the {@link Field field} holding the regular expression to apply.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch regexOf(String fieldReference) {
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new RegexMatch(append("regex",Fields.field(fieldReference)));
+ }
+
+ /**
+ * Optional specify the {@link AggregationExpression} evaluating to the regular expression to apply.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch regexOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new RegexMatch(append("regex",expression));
+ }
+
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index a91358353c..0fbfe51f09 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -116,6 +116,9 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("trim", mapArgRef().forOperator("$trim").mappingParametersTo("input", "chars"));
map.put("ltrim", mapArgRef().forOperator("$ltrim").mappingParametersTo("input", "chars"));
map.put("rtrim", mapArgRef().forOperator("$rtrim").mappingParametersTo("input", "chars"));
+ map.put("regexFind", mapArgRef().forOperator("$regexFind").mappingParametersTo("input", "regex" , "options"));
+ map.put("regexFindAll", mapArgRef().forOperator("$regexFindAll").mappingParametersTo("input", "regex" , "options"));
+ map.put("regexMatch", mapArgRef().forOperator("$regexMatch").mappingParametersTo("input", "regex" , "options"));
// TEXT SEARCH OPERATORS
map.put("meta", singleArgRef().forOperator("$meta"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
index 805fc10f38..ff6771d9f1 100755
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
@@ -1766,6 +1766,33 @@ public void shouldRenderSubstrCPCorrectly() {
assertThat(agg)
.isEqualTo(Document.parse("{ $project : { yearSubstring: { $substrCP: [ \"$quarter\", 0, 2 ] } } }"));
}
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindCorrectly() {
+
+ Document agg = project().and(StringOperators.valueOf("field1").regexFind("e")).as("regex")
+ .toDocument(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg).isEqualTo(Document.parse("{ $project : { regex: { $regexFind: { \"input\" : \"$field1\", \"regex\" : \"e\" } } } }"));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindAllCorrectly() {
+
+ Document agg = project().and(StringOperators.valueOf("field1").regexFindAll("e")).as("regex")
+ .toDocument(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg).isEqualTo(Document.parse("{ $project : { regex: { $regexFindAll: { \"input\" : \"$field1\", \"regex\" : \"e\" } } } }"));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexMatchCorrectly() {
+
+ Document agg = project().and(StringOperators.valueOf("field1").regexMatch("e")).as("regex")
+ .toDocument(Aggregation.DEFAULT_CONTEXT);
+
+ assertThat(agg).isEqualTo(Document.parse("{ $project : { regex: { $regexMatch: { \"input\" : \"$field1\", \"regex\" : \"e\" } } } }"));
+ }
@Test // DATAMONGO-1548
public void shouldRenderIndexOfArrayCorrectly() {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index daba7a21cd..41b0323636 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -800,6 +800,70 @@ void shouldRenderRtrimWithCharsFromFieldReference() {
assertThat(transform("rtrim(field1, field2)"))
.isEqualTo("{ \"$rtrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}");
}
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexFindWithoutOptions() {
+
+ assertThat(transform("regexFind(field1,'e')"))
+ .isEqualTo(Document.parse("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}"));
+ }
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexFindWithOptions() {
+
+ assertThat(transform("regexFind(field1,'e','i')"))
+ .isEqualTo(Document.parse("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}"));
+ }
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexFindWithOptionsFromFieldReference() {
+
+ assertThat(transform("regexFind(field1,'e',field2)"))
+ .isEqualTo(Document.parse("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}"));
+ }
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexFindAllWithoutOptions() {
+
+ assertThat(transform("regexFindAll(field1,'e')"))
+ .isEqualTo(Document.parse("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}"));
+ }
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexFindAllWithOptions() {
+
+ assertThat(transform("regexFindAll(field1,'e','i')"))
+ .isEqualTo(Document.parse("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}"));
+ }
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexFindAllWithOptionsFromFieldReference() {
+
+ assertThat(transform("regexFindAll(field1,'e',field2)"))
+ .isEqualTo(Document.parse("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}"));
+ }
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexMatchWithoutOptions() {
+
+ assertThat(transform("regexMatch(field1,'e')"))
+ .isEqualTo(Document.parse("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}"));
+ }
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexMatchWithOptions() {
+
+ assertThat(transform("regexMatch(field1,'e','i')"))
+ .isEqualTo(Document.parse("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}"));
+ }
+
+ @Test // DATAMONGO-3725
+ public void shouldRenderRegexMatchWithOptionsFromFieldReference() {
+
+ assertThat(transform("regexMatch(field1,'e',field2)"))
+ .isEqualTo(Document.parse("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}"));
+ }
+
@Test // DATAMONGO-2077
void shouldRenderConvertWithoutOptionalParameters() {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java
index 0dbe362ae4..cdd0b38dbc 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java
@@ -144,5 +144,111 @@ public void shouldRenderRTrimWithCharsExpression() {
assertThat(StringOperators.valueOf("shrewd").rtrim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } "));
}
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindAll() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFindAll("e").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }"));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindAllForExpression() {
+
+ assertThat(StringOperators.valueOf(EXPRESSION).regexFindAll("e").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindAllForRegexExpression() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFindAll(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindAllWithOptions() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFindAll("e").options("i").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindAllWithOptionsExpression() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFindAll("e").optionsOf(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING + " } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexMatch() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexMatch("e").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }"));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexMatchForExpression() {
+
+ assertThat(StringOperators.valueOf(EXPRESSION).regexMatch("e").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexMatchForRegexExpression() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexMatch(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexMatchWithOptions() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexMatch("e").options("i").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexMatchWithOptionsExpression() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexMatch("e").optionsOf(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING + " } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFind() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFind("e").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFind: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }"));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindForExpression() {
+
+ assertThat(StringOperators.valueOf(EXPRESSION).regexFind("e").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFind: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindForRegexExpression() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFind(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFind: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindWithOptions() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFind("e").options("i").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFind: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } "));
+ }
+
+ @Test // DATAMONGO - 3725
+ public void shouldRenderRegexFindWithOptionsExpression() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFind("e").optionsOf(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo(Document.parse("{ $regexFind: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING + " } } "));
+ }
+
}
From 69b582823a53cf3a1b8a07354da942bd94432f4f Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Thu, 26 Aug 2021 12:20:11 +0200
Subject: [PATCH 043/920] Polishing.
Add support for Pattern. Extract Regex flags translation from Criteria into RegexFlags utility class. Add since and author tags. Simplify tests. Update reference documentation.
See #3725.
Original pull request: #3781.
---
.../core/aggregation/StringOperators.java | 371 ++++++++++-----
.../data/mongodb/core/query/Criteria.java | 58 +--
.../data/mongodb/util/RegexFlags.java | 116 +++++
.../ProjectionOperationUnitTests.java | 433 +++++++++---------
.../SpelExpressionTransformerUnitTests.java | 89 ++--
.../aggregation/StringOperatorsUnitTests.java | 226 +++++----
.../reference/aggregation-framework.adoc | 2 +-
7 files changed, 776 insertions(+), 519 deletions(-)
create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
index 710c6c855e..8b6bb03875 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/StringOperators.java
@@ -18,8 +18,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
import org.springframework.data.domain.Range;
+import org.springframework.data.mongodb.util.RegexFlags;
import org.springframework.util.Assert;
/**
@@ -27,6 +30,7 @@
*
* @author Christoph Strobl
* @author Mark Paluch
+ * @author Divya Srivastava
* @since 1.10
*/
public class StringOperators {
@@ -515,117 +519,170 @@ public RTrim rtrim(AggregationExpression expression) {
private RTrim createRTrim() {
return usesFieldRef() ? RTrim.valueOf(fieldReference) : RTrim.valueOf(expression);
}
-
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
* regular expression to find the document with the first match.
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @param regex must not be {@literal null}.
* @return new instance of {@link RegexFind}.
+ * @since 3.3
*/
public RegexFind regexFind(String regex) {
return createRegexFind().regex(regex);
}
-
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
- * expression resulting from the given {@link AggregationExpression} to find the document with the first match.
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression resulting from the given {@link AggregationExpression} to find the document with the first
+ * match.
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @param expression must not be {@literal null}.
* @return new instance of {@link RegexFind}.
+ * @since 3.3
*/
public RegexFind regexFind(AggregationExpression expression) {
return createRegexFind().regexOf(expression);
}
-
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the {@link Pattern} and applies the regular expression with
+ * the options specified in the argument to find the document with the first match.
+ *
+ * @param pattern the pattern object to apply.
+ * @return new instance of {@link RegexFind}.
+ * @since 3.3
+ */
+ public RegexFind regexFind(Pattern pattern) {
+ return createRegexFind().pattern(pattern);
+ }
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
* expression with the options specified in the argument to find the document with the first match.
*
- * @param regex the regular expression to apply
- * @param options the options to use
+ * @param regex the regular expression to apply.
+ * @param options the options to use.
* @return new instance of {@link RegexFind}.
+ * @since 3.3
*/
- public RegexFind regexFind(String regex,String options) {
+ public RegexFind regexFind(String regex, String options) {
return createRegexFind().regex(regex).options(options);
}
-
+
private RegexFind createRegexFind() {
return usesFieldRef() ? RegexFind.valueOf(fieldReference) : RegexFind.valueOf(expression);
}
-
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
* regular expression to find all the documents with the match.
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @param regex must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
+ * @since 3.3
*/
public RegexFindAll regexFindAll(String regex) {
return createRegexFindAll().regex(regex);
}
-
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
- * expression resulting from the given {@link AggregationExpression} to find all the documents with the match..
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression resulting from the given {@link AggregationExpression} to find all the documents with the
+ * match..
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @param expression must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
+ * @since 3.3
*/
public RegexFindAll regexFindAll(AggregationExpression expression) {
return createRegexFindAll().regexOf(expression);
}
-
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
- * expression with the options specified in the argument to find all the documents with the match..
+ * Creates new {@link AggregationExpression} that takes a {@link Pattern} and applies the regular expression with
+ * the options specified in the argument to find all the documents with the match.
*
- * @param regex the regular expression to apply
- * @param options the options to use
+ * @param pattern the pattern object to apply.
* @return new instance of {@link RegexFindAll}.
+ * @since 3.3
*/
- public RegexFindAll regexFindAll(String regex,String options) {
+ public RegexFindAll regexFindAll(Pattern pattern) {
+ return createRegexFindAll().pattern(pattern);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * expression with the options specified in the argument to find all the documents with the match.
+ *
+ * @param regex the regular expression to apply.
+ * @param options the options to use.
+ * @return new instance of {@link RegexFindAll}.
+ * @since 3.3
+ */
+ public RegexFindAll regexFindAll(String regex, String options) {
return createRegexFindAll().regex(regex).options(options);
}
-
+
private RegexFindAll createRegexFindAll() {
return usesFieldRef() ? RegexFindAll.valueOf(fieldReference) : RegexFindAll.valueOf(expression);
}
-
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the given
* regular expression to find if a match is found or not.
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @param regex must not be {@literal null}.
* @return new instance of {@link RegexMatch}.
+ * @since 3.3
*/
public RegexMatch regexMatch(String regex) {
return createRegexMatch().regex(regex);
}
-
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
* expression resulting from the given {@link AggregationExpression} to find if a match is found or not.
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @param expression must not be {@literal null}.
* @return new instance of {@link RegexMatch}.
+ * @since 3.3
*/
public RegexMatch regexMatch(AggregationExpression expression) {
return createRegexMatch().regexOf(expression);
}
-
+
+ /**
+ * Creates new {@link AggregationExpression} that takes a {@link Pattern} and applies the regular expression with
+ * the options specified in the argument to find if a match is found or not.
+ *
+ * @param pattern the pattern object to apply.
+ * @return new instance of {@link RegexMatch}.
+ * @since 3.3
+ */
+ public RegexMatch regexMatch(Pattern pattern) {
+ return createRegexMatch().pattern(pattern);
+ }
+
/**
- * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
+ * Creates new {@link AggregationExpression} that takes the associated string representation and applies the regular
* expression with the options specified in the argument to find if a match is found or not.
*
- * @param regex the regular expression to apply
- * @param options the options to use
+ * @param regex the regular expression to apply.
+ * @param options the options to use.
* @return new instance of {@link RegexMatch}.
+ * @since 3.3
*/
- public RegexMatch regexMatch(String regex,String options) {
+ public RegexMatch regexMatch(String regex, String options) {
return createRegexMatch().regex(regex).options(options);
}
-
+
private RegexMatch createRegexMatch() {
return usesFieldRef() ? RegexMatch.valueOf(fieldReference) : RegexMatch.valueOf(expression);
}
@@ -1591,35 +1648,35 @@ protected String getMongoMethod() {
return "$rtrim";
}
}
-
+
/**
- * {@link AggregationExpression} for {@code $regexFind} which applies a regular expression (regex) to a string and
+ * {@link AggregationExpression} for {@code $regexFind} which applies a regular expression (regex) to a string and
* returns information on the first matched substring.
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @author Divya Srivastava
+ * @since 3.3
*/
public static class RegexFind extends AbstractAggregationExpression {
-
+
protected RegexFind(Object value) {
super(value);
}
- @Override
- protected String getMongoMethod() {
- return "$regexFind";
- }
-
/**
- * Creates new {@link RegexFind} using the value of the provided {@link Field fieldReference} as {@literal input} value.
+ * Creates new {@link RegexFind} using the value of the provided {@link Field fieldReference} as {@literal input}
+ * value.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexFind}.
*/
public static RegexFind valueOf(String fieldReference) {
+
Assert.notNull(fieldReference, "FieldReference must not be null!");
+
return new RegexFind(Collections.singletonMap("input", Fields.field(fieldReference)));
}
-
+
/**
* Creates new {@link RegexFind} using the result of the provided {@link AggregationExpression} as {@literal input}
* value.
@@ -1628,10 +1685,12 @@ public static RegexFind valueOf(String fieldReference) {
* @return new instance of {@link RegexFind}.
*/
public static RegexFind valueOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
+
return new RegexFind(Collections.singletonMap("input", expression));
}
-
+
/**
* Optional specify the options to use with the regular expression.
*
@@ -1639,72 +1698,108 @@ public static RegexFind valueOf(AggregationExpression expression) {
* @return new instance of {@link RegexFind}.
*/
public RegexFind options(String options) {
+
Assert.notNull(options, "Options must not be null!");
+
return new RegexFind(append("options", options));
}
-
+
/**
- * Optional specify the reference to the {@link Field field} holding the options values to use with the regular expression.
+ * Optional specify the reference to the {@link Field field} holding the options values to use with the regular
+ * expression.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexFind}.
*/
public RegexFind optionsOf(String fieldReference) {
+
Assert.notNull(fieldReference, "FieldReference must not be null!");
+
return new RegexFind(append("options", Fields.field(fieldReference)));
}
-
+
/**
- * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular expression.
+ * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular
+ * expression.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link RegexFind}.
*/
public RegexFind optionsOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
+
return new RegexFind(append("options", expression));
}
-
+
/**
- * Optional specify the regular expression to apply.
+ * Specify the regular expression to apply.
*
* @param regex must not be {@literal null}.
* @return new instance of {@link RegexFind}.
*/
public RegexFind regex(String regex) {
+
Assert.notNull(regex, "Regex must not be null!");
- return new RegexFind(append("regex",regex));
+
+ return new RegexFind(append("regex", regex));
}
-
+
/**
- * Optional specify the reference to the {@link Field field} holding the regular expression to apply.
+ * Apply a {@link Pattern} into {@code regex} and {@code options} fields.
+ *
+ * @param pattern must not be {@literal null}.
+ * @return new instance of {@link RegexFind}.
+ */
+ public RegexFind pattern(Pattern pattern) {
+
+ Assert.notNull(pattern, "Pattern must not be null!");
+
+ Map regex = append("regex", pattern.pattern());
+ regex.put("options", RegexFlags.toRegexOptions(pattern.flags()));
+
+ return new RegexFind(regex);
+ }
+
+ /**
+ * Specify the reference to the {@link Field field} holding the regular expression to apply.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexFind}.
*/
public RegexFind regexOf(String fieldReference) {
+
Assert.notNull(fieldReference, "fieldReference must not be null!");
- return new RegexFind(append("regex",Fields.field(fieldReference)));
+
+ return new RegexFind(append("regex", Fields.field(fieldReference)));
}
-
+
/**
- * Optional specify the {@link AggregationExpression} evaluating to the regular expression to apply.
+ * Specify the {@link AggregationExpression} evaluating to the regular expression to apply.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link RegexFind}.
*/
public RegexFind regexOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
- return new RegexFind(append("regex",expression));
+
+ return new RegexFind(append("regex", expression));
}
+ @Override
+ protected String getMongoMethod() {
+ return "$regexFind";
+ }
}
-
+
/**
- * {@link AggregationExpression} for {@code $regexFindAll} which applies a regular expression (regex) to a string and
+ * {@link AggregationExpression} for {@code $regexFindAll} which applies a regular expression (regex) to a string and
* returns information on all the matched substrings.
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @author Divya Srivastava
+ * @since 3.3
*/
public static class RegexFindAll extends AbstractAggregationExpression {
@@ -1712,13 +1807,9 @@ protected RegexFindAll(Object value) {
super(value);
}
- @Override
- protected String getMongoMethod() {
- return "$regexFindAll";
- }
-
/**
- * Creates new {@link RegexFindAll} using the value of the provided {@link Field fieldReference} as {@literal input} value.
+ * Creates new {@link RegexFindAll} using the value of the provided {@link Field fieldReference} as {@literal input}
+ * value.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
@@ -1727,19 +1818,21 @@ public static RegexFindAll valueOf(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new RegexFindAll(Collections.singletonMap("input", Fields.field(fieldReference)));
}
-
+
/**
- * Creates new {@link RegexFindAll} using the result of the provided {@link AggregationExpression} as {@literal input}
- * value.
+ * Creates new {@link RegexFindAll} using the result of the provided {@link AggregationExpression} as
+ * {@literal input} value.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
*/
public static RegexFindAll valueOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
+
return new RegexFindAll(Collections.singletonMap("input", expression));
}
-
+
/**
* Optional specify the options to use with the regular expression.
*
@@ -1747,72 +1840,108 @@ public static RegexFindAll valueOf(AggregationExpression expression) {
* @return new instance of {@link RegexFindAll}.
*/
public RegexFindAll options(String options) {
+
Assert.notNull(options, "Options must not be null!");
+
return new RegexFindAll(append("options", options));
}
-
+
/**
- * Optional specify the reference to the {@link Field field} holding the options values to use with the regular expression.
+ * Optional specify the reference to the {@link Field field} holding the options values to use with the regular
+ * expression.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
*/
public RegexFindAll optionsOf(String fieldReference) {
+
Assert.notNull(fieldReference, "fieldReference must not be null!");
+
return new RegexFindAll(append("options", Fields.field(fieldReference)));
}
-
+
/**
- * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular expression.
+ * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular
+ * expression.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
*/
public RegexFindAll optionsOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
+
return new RegexFindAll(append("options", expression));
}
-
+
/**
- * Optional specify the regular expression to apply.
+ * Apply a {@link Pattern} into {@code regex} and {@code options} fields.
+ *
+ * @param pattern must not be {@literal null}.
+ * @return new instance of {@link RegexFindAll}.
+ */
+ public RegexFindAll pattern(Pattern pattern) {
+
+ Assert.notNull(pattern, "Pattern must not be null!");
+
+ Map regex = append("regex", pattern.pattern());
+ regex.put("options", RegexFlags.toRegexOptions(pattern.flags()));
+
+ return new RegexFindAll(regex);
+ }
+
+ /**
+ * Specify the regular expression to apply.
*
* @param regex must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
*/
public RegexFindAll regex(String regex) {
+
Assert.notNull(regex, "Regex must not be null!");
- return new RegexFindAll(append("regex",regex));
+
+ return new RegexFindAll(append("regex", regex));
}
-
+
/**
- * Optional specify the reference to the {@link Field field} holding the regular expression to apply.
+ * Specify the reference to the {@link Field field} holding the regular expression to apply.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
*/
public RegexFindAll regexOf(String fieldReference) {
+
Assert.notNull(fieldReference, "fieldReference must not be null!");
- return new RegexFindAll(append("regex",Fields.field(fieldReference)));
+
+ return new RegexFindAll(append("regex", Fields.field(fieldReference)));
}
-
+
/**
- * Optional specify the {@link AggregationExpression} evaluating to the regular expression to apply.
+ * Specify the {@link AggregationExpression} evaluating to the regular expression to apply.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link RegexFindAll}.
*/
public RegexFindAll regexOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
- return new RegexFindAll(append("regex",expression));
+
+ return new RegexFindAll(append("regex", expression));
}
+ @Override
+ protected String getMongoMethod() {
+ return "$regexFindAll";
+ }
}
-
+
/**
- * {@link AggregationExpression} for {@code $regexMatch} which applies a regular expression (regex) to a string and
+ * {@link AggregationExpression} for {@code $regexMatch} which applies a regular expression (regex) to a string and
* returns a boolean that indicates if a match is found or not.
* NOTE: Requires MongoDB 4.0 or later.
*
+ * @author Divya Srivastava
+ * @since 3.3
*/
public static class RegexMatch extends AbstractAggregationExpression {
@@ -1820,22 +1949,20 @@ protected RegexMatch(Object value) {
super(value);
}
- @Override
- protected String getMongoMethod() {
- return "$regexMatch";
- }
-
/**
- * Creates new {@link RegexMatch} using the value of the provided {@link Field fieldReference} as {@literal input} value.
+ * Creates new {@link RegexMatch} using the value of the provided {@link Field fieldReference} as {@literal input}
+ * value.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexMatch}.
*/
public static RegexMatch valueOf(String fieldReference) {
+
Assert.notNull(fieldReference, "FieldReference must not be null!");
+
return new RegexMatch(Collections.singletonMap("input", Fields.field(fieldReference)));
}
-
+
/**
* Creates new {@link RegexMatch} using the result of the provided {@link AggregationExpression} as {@literal input}
* value.
@@ -1844,10 +1971,12 @@ public static RegexMatch valueOf(String fieldReference) {
* @return new instance of {@link RegexMatch}.
*/
public static RegexMatch valueOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
+
return new RegexMatch(Collections.singletonMap("input", expression));
}
-
+
/**
* Optional specify the options to use with the regular expression.
*
@@ -1855,54 +1984,82 @@ public static RegexMatch valueOf(AggregationExpression expression) {
* @return new instance of {@link RegexMatch}.
*/
public RegexMatch options(String options) {
+
Assert.notNull(options, "Options must not be null!");
+
return new RegexMatch(append("options", options));
}
-
+
/**
- * Optional specify the reference to the {@link Field field} holding the options values to use with the regular expression.
+ * Optional specify the reference to the {@link Field field} holding the options values to use with the regular
+ * expression.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexMatch}.
*/
public RegexMatch optionsOf(String fieldReference) {
+
Assert.notNull(fieldReference, "FieldReference must not be null!");
+
return new RegexMatch(append("options", Fields.field(fieldReference)));
}
-
+
/**
- * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular expression.
+ * Optional specify the {@link AggregationExpression} evaluating to the options values to use with the regular
+ * expression.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link RegexMatch}.
*/
public RegexMatch optionsOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
+
return new RegexMatch(append("options", expression));
}
-
+
/**
- * Optional specify the regular expression to apply.
+ * Apply a {@link Pattern} into {@code regex} and {@code options} fields.
+ *
+ * @param pattern must not be {@literal null}.
+ * @return new instance of {@link RegexMatch}.
+ */
+ public RegexMatch pattern(Pattern pattern) {
+
+ Assert.notNull(pattern, "Pattern must not be null!");
+
+ Map regex = append("regex", pattern.pattern());
+ regex.put("options", RegexFlags.toRegexOptions(pattern.flags()));
+
+ return new RegexMatch(regex);
+ }
+
+ /**
+ * Specify the regular expression to apply.
*
* @param regex must not be {@literal null}.
* @return new instance of {@link RegexMatch}.
*/
public RegexMatch regex(String regex) {
+
Assert.notNull(regex, "Regex must not be null!");
- return new RegexMatch(append("regex",regex));
+
+ return new RegexMatch(append("regex", regex));
}
-
+
/**
- * Optional specify the reference to the {@link Field field} holding the regular expression to apply.
+ * Specify the reference to the {@link Field field} holding the regular expression to apply.
*
* @param fieldReference must not be {@literal null}.
* @return new instance of {@link RegexMatch}.
*/
public RegexMatch regexOf(String fieldReference) {
+
Assert.notNull(fieldReference, "FieldReference must not be null!");
- return new RegexMatch(append("regex",Fields.field(fieldReference)));
+
+ return new RegexMatch(append("regex", Fields.field(fieldReference)));
}
-
+
/**
* Optional specify the {@link AggregationExpression} evaluating to the regular expression to apply.
*
@@ -1910,9 +2067,15 @@ public RegexMatch regexOf(String fieldReference) {
* @return new instance of {@link RegexMatch}.
*/
public RegexMatch regexOf(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
- return new RegexMatch(append("regex",expression));
+
+ return new RegexMatch(append("regex", expression));
}
+ @Override
+ protected String getMongoMethod() {
+ return "$regexMatch";
+ }
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
index 9b1e8df940..f9a354c38f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
@@ -42,6 +42,7 @@
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
import org.springframework.data.mongodb.core.schema.JsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
+import org.springframework.data.mongodb.util.RegexFlags;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;
@@ -71,20 +72,6 @@ public class Criteria implements CriteriaDefinition {
*/
private static final Object NOT_SET = new Object();
- private static final int[] FLAG_LOOKUP = new int[Character.MAX_VALUE];
-
- static {
- FLAG_LOOKUP['g'] = 256;
- FLAG_LOOKUP['i'] = Pattern.CASE_INSENSITIVE;
- FLAG_LOOKUP['m'] = Pattern.MULTILINE;
- FLAG_LOOKUP['s'] = Pattern.DOTALL;
- FLAG_LOOKUP['c'] = Pattern.CANON_EQ;
- FLAG_LOOKUP['x'] = Pattern.COMMENTS;
- FLAG_LOOKUP['d'] = Pattern.UNIX_LINES;
- FLAG_LOOKUP['t'] = Pattern.LITERAL;
- FLAG_LOOKUP['u'] = Pattern.UNICODE_CASE;
- }
-
private @Nullable String key;
private List criteriaChain;
private LinkedHashMap criteria = new LinkedHashMap();
@@ -530,7 +517,7 @@ private Pattern toPattern(String regex, @Nullable String options) {
Assert.notNull(regex, "Regex string must not be null!");
- return Pattern.compile(regex, regexFlags(options));
+ return Pattern.compile(regex, RegexFlags.toRegexFlags(options));
}
/**
@@ -1099,47 +1086,6 @@ private static boolean requiresGeoJsonFormat(Object value) {
|| (value instanceof GeoCommand && ((GeoCommand) value).getShape() instanceof GeoJson);
}
- /**
- * Lookup the MongoDB specific flags for a given regex option string.
- *
- * @param s the Regex option/flag to look up. Can be {@literal null}.
- * @return zero if given {@link String} is {@literal null} or empty.
- * @since 2.2
- */
- private static int regexFlags(@Nullable String s) {
-
- int flags = 0;
-
- if (s == null) {
- return flags;
- }
-
- for (final char f : s.toLowerCase().toCharArray()) {
- flags |= regexFlag(f);
- }
-
- return flags;
- }
-
- /**
- * Lookup the MongoDB specific flags for a given character.
- *
- * @param c the Regex option/flag to look up.
- * @return
- * @throws IllegalArgumentException for unknown flags
- * @since 2.2
- */
- private static int regexFlag(char c) {
-
- int flag = FLAG_LOOKUP[c];
-
- if (flag == 0) {
- throw new IllegalArgumentException(String.format("Unrecognized flag [%c]", c));
- }
-
- return flag;
- }
-
/**
* MongoDB specific bitwise query
* operators like {@code $bitsAllClear, $bitsAllSet,...} for usage with {@link Criteria#bits()} and {@link Query}.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
new file mode 100644
index 0000000000..dfee94954c
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
@@ -0,0 +1,116 @@
+/*
+ * 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.util;
+
+import java.util.regex.Pattern;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * Utility to translate {@link Pattern#flags() regex flags} to MongoDB regex options and vice versa.
+ *
+ * @author Mark Paluch
+ * @since 3.3
+ */
+public abstract class RegexFlags {
+
+ private static final int[] FLAG_LOOKUP = new int[Character.MAX_VALUE];
+
+ static {
+ FLAG_LOOKUP['g'] = 256;
+ FLAG_LOOKUP['i'] = Pattern.CASE_INSENSITIVE;
+ FLAG_LOOKUP['m'] = Pattern.MULTILINE;
+ FLAG_LOOKUP['s'] = Pattern.DOTALL;
+ FLAG_LOOKUP['c'] = Pattern.CANON_EQ;
+ FLAG_LOOKUP['x'] = Pattern.COMMENTS;
+ FLAG_LOOKUP['d'] = Pattern.UNIX_LINES;
+ FLAG_LOOKUP['t'] = Pattern.LITERAL;
+ FLAG_LOOKUP['u'] = Pattern.UNICODE_CASE;
+ }
+
+ private RegexFlags() {
+
+ }
+
+ /**
+ * Lookup the MongoDB specific options from given {@link Pattern#flags() flags}.
+ *
+ * @param flags the Regex flags to look up.
+ * @return the options string. May be empty.
+ */
+ public static String toRegexOptions(int flags) {
+
+ if (flags == 0) {
+ return "";
+ }
+
+ StringBuilder buf = new StringBuilder();
+
+ for (int i = 'a'; i < 'z'; i++) {
+
+ if (FLAG_LOOKUP[i] == 0) {
+ continue;
+ }
+
+ if ((flags & FLAG_LOOKUP[i]) > 0) {
+ buf.append((char) i);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ /**
+ * Lookup the MongoDB specific flags for a given regex option string.
+ *
+ * @param s the Regex option/flag to look up. Can be {@literal null}.
+ * @return zero if given {@link String} is {@literal null} or empty.
+ * @since 2.2
+ */
+ public static int toRegexFlags(@Nullable String s) {
+
+ int flags = 0;
+
+ if (s == null) {
+ return flags;
+ }
+
+ for (char f : s.toLowerCase().toCharArray()) {
+ flags |= toRegexFlag(f);
+ }
+
+ return flags;
+ }
+
+ /**
+ * Lookup the MongoDB specific flags for a given character.
+ *
+ * @param c the Regex option/flag to look up.
+ * @return
+ * @throws IllegalArgumentException for unknown flags
+ * @since 2.2
+ */
+ public static int toRegexFlag(char c) {
+
+ int flag = FLAG_LOOKUP[c];
+
+ if (flag == 0) {
+ throw new IllegalArgumentException(String.format("Unrecognized flag [%c]", c));
+ }
+
+ return flag;
+ }
+}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
index ff6771d9f1..9ef207c9ad 100755
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java
@@ -55,24 +55,25 @@
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
+ * @author Divya Srivastava
* @author Mark Paluch
*/
public class ProjectionOperationUnitTests {
- static final String MOD = "$mod";
- static final String ADD = "$add";
- static final String SUBTRACT = "$subtract";
- static final String MULTIPLY = "$multiply";
- static final String DIVIDE = "$divide";
- static final String PROJECT = "$project";
+ private static final String MOD = "$mod";
+ private static final String ADD = "$add";
+ private static final String SUBTRACT = "$subtract";
+ private static final String MULTIPLY = "$multiply";
+ private static final String DIVIDE = "$divide";
+ private static final String PROJECT = "$project";
@Test // DATAMONGO-586
- public void rejectsNullFields() {
+ void rejectsNullFields() {
assertThatIllegalArgumentException().isThrownBy(() -> new ProjectionOperation((Fields) null));
}
@Test // DATAMONGO-586
- public void declaresBackReferenceCorrectly() {
+ void declaresBackReferenceCorrectly() {
ProjectionOperation operation = new ProjectionOperation();
operation = operation.and("prop").previousOperation();
@@ -83,7 +84,7 @@ public void declaresBackReferenceCorrectly() {
}
@Test // DATAMONGO-586
- public void alwaysUsesExplicitReference() {
+ void alwaysUsesExplicitReference() {
ProjectionOperation operation = new ProjectionOperation(Fields.fields("foo").and("bar", "foobar"));
@@ -95,7 +96,7 @@ public void alwaysUsesExplicitReference() {
}
@Test // DATAMONGO-586
- public void aliasesSimpleFieldProjection() {
+ void aliasesSimpleFieldProjection() {
ProjectionOperation operation = new ProjectionOperation();
@@ -106,7 +107,7 @@ public void aliasesSimpleFieldProjection() {
}
@Test // DATAMONGO-586
- public void aliasesArithmeticProjection() {
+ void aliasesArithmeticProjection() {
ProjectionOperation operation = new ProjectionOperation();
@@ -121,7 +122,7 @@ public void aliasesArithmeticProjection() {
}
@Test // DATAMONGO-586
- public void arithmeticProjectionOperationWithoutAlias() {
+ void arithmeticProjectionOperationWithoutAlias() {
String fieldName = "a";
ProjectionOperationBuilder operation = new ProjectionOperation().and(fieldName).plus(1);
@@ -134,7 +135,7 @@ public void arithmeticProjectionOperationWithoutAlias() {
}
@Test // DATAMONGO-586
- public void arithmeticProjectionOperationPlus() {
+ void arithmeticProjectionOperationPlus() {
String fieldName = "a";
String fieldAlias = "b";
@@ -148,7 +149,7 @@ public void arithmeticProjectionOperationPlus() {
}
@Test // DATAMONGO-586
- public void arithmeticProjectionOperationMinus() {
+ void arithmeticProjectionOperationMinus() {
String fieldName = "a";
String fieldAlias = "b";
@@ -162,7 +163,7 @@ public void arithmeticProjectionOperationMinus() {
}
@Test // DATAMONGO-586
- public void arithmeticProjectionOperationMultiply() {
+ void arithmeticProjectionOperationMultiply() {
String fieldName = "a";
String fieldAlias = "b";
@@ -176,7 +177,7 @@ public void arithmeticProjectionOperationMultiply() {
}
@Test // DATAMONGO-586
- public void arithmeticProjectionOperationDivide() {
+ void arithmeticProjectionOperationDivide() {
String fieldName = "a";
String fieldAlias = "b";
@@ -190,12 +191,12 @@ public void arithmeticProjectionOperationDivide() {
}
@Test // DATAMONGO-586
- public void arithmeticProjectionOperationDivideByZeroException() {
+ void arithmeticProjectionOperationDivideByZeroException() {
assertThatIllegalArgumentException().isThrownBy(() -> new ProjectionOperation().and("a").divide(0));
}
@Test // DATAMONGO-586
- public void arithmeticProjectionOperationMod() {
+ void arithmeticProjectionOperationMod() {
String fieldName = "a";
String fieldAlias = "b";
@@ -209,7 +210,7 @@ public void arithmeticProjectionOperationMod() {
}
@Test // DATAMONGO-758, DATAMONGO-1893
- public void excludeShouldAllowExclusionOfFieldsOtherThanUnderscoreId/* since MongoDB 3.4 */() {
+ void excludeShouldAllowExclusionOfFieldsOtherThanUnderscoreId/* since MongoDB 3.4 */() {
ProjectionOperation projectionOp = new ProjectionOperation().andExclude("foo");
Document document = projectionOp.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -220,7 +221,7 @@ public void arithmeticProjectionOperationMod() {
}
@Test // DATAMONGO-1893
- public void includeShouldNotInheritFields() {
+ void includeShouldNotInheritFields() {
ProjectionOperation projectionOp = new ProjectionOperation().andInclude("foo");
@@ -228,7 +229,7 @@ public void includeShouldNotInheritFields() {
}
@Test // DATAMONGO-758
- public void excludeShouldAllowExclusionOfUnderscoreId() {
+ void excludeShouldAllowExclusionOfUnderscoreId() {
ProjectionOperation projectionOp = new ProjectionOperation().andExclude(Fields.UNDERSCORE_ID);
Document document = projectionOp.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -237,7 +238,7 @@ public void excludeShouldAllowExclusionOfUnderscoreId() {
}
@Test // DATAMONGO-1906
- public void rendersConditionalProjectionCorrectly() {
+ void rendersConditionalProjectionCorrectly() {
TypedAggregation aggregation = Aggregation.newAggregation(Book.class,
Aggregation.project("title")
@@ -252,7 +253,7 @@ public void rendersConditionalProjectionCorrectly() {
}
@Test // DATAMONGO-757
- public void usesImplictAndExplicitFieldAliasAndIncludeExclude() {
+ void usesImplictAndExplicitFieldAliasAndIncludeExclude() {
ProjectionOperation operation = Aggregation.project("foo").and("foobar").as("bar").andInclude("inc1", "inc2")
.andExclude("_id");
@@ -268,12 +269,12 @@ public void usesImplictAndExplicitFieldAliasAndIncludeExclude() {
}
@Test
- public void arithmeticProjectionOperationModByZeroException() {
+ void arithmeticProjectionOperationModByZeroException() {
assertThatIllegalArgumentException().isThrownBy(() -> new ProjectionOperation().and("a").mod(0));
}
@Test // DATAMONGO-769
- public void allowArithmeticOperationsWithFieldReferences() {
+ void allowArithmeticOperationsWithFieldReferences() {
ProjectionOperation operation = Aggregation.project() //
.and("foo").plus("bar").as("fooPlusBar") //
@@ -298,7 +299,7 @@ public void allowArithmeticOperationsWithFieldReferences() {
}
@Test // DATAMONGO-774
- public void projectionExpressions() {
+ void projectionExpressions() {
ProjectionOperation operation = Aggregation.project() //
.andExpression("(netPrice + surCharge) * taxrate * [0]", 2).as("grossSalesPrice") //
@@ -310,7 +311,7 @@ public void projectionExpressions() {
}
@Test // DATAMONGO-975
- public void shouldRenderDateTimeFragmentExtractionsForSimpleFieldProjectionsCorrectly() {
+ void shouldRenderDateTimeFragmentExtractionsForSimpleFieldProjectionsCorrectly() {
ProjectionOperation operation = Aggregation.project() //
.and("date").extractHour().as("hour") //
@@ -343,7 +344,7 @@ public void shouldRenderDateTimeFragmentExtractionsForSimpleFieldProjectionsCorr
}
@Test // DATAMONGO-975
- public void shouldRenderDateTimeFragmentExtractionsForExpressionProjectionsCorrectly() throws Exception {
+ void shouldRenderDateTimeFragmentExtractionsForExpressionProjectionsCorrectly() throws Exception {
ProjectionOperation operation = Aggregation.project() //
.andExpression("date + 86400000") //
@@ -360,7 +361,7 @@ public void shouldRenderDateTimeFragmentExtractionsForExpressionProjectionsCorre
}
@Test // DATAMONGO-979
- public void shouldRenderSizeExpressionInProjection() {
+ void shouldRenderSizeExpressionInProjection() {
ProjectionOperation operation = Aggregation //
.project() //
@@ -375,7 +376,7 @@ public void shouldRenderSizeExpressionInProjection() {
}
@Test // DATAMONGO-979
- public void shouldRenderGenericSizeExpressionInProjection() {
+ void shouldRenderGenericSizeExpressionInProjection() {
ProjectionOperation operation = Aggregation //
.project() //
@@ -389,7 +390,7 @@ public void shouldRenderGenericSizeExpressionInProjection() {
}
@Test // DATAMONGO-1457
- public void shouldRenderSliceCorrectly() throws Exception {
+ void shouldRenderSliceCorrectly() throws Exception {
ProjectionOperation operation = Aggregation.project().and("field").slice(10).as("renamed");
@@ -400,7 +401,7 @@ public void shouldRenderSliceCorrectly() throws Exception {
}
@Test // DATAMONGO-1457
- public void shouldRenderSliceWithPositionCorrectly() throws Exception {
+ void shouldRenderSliceWithPositionCorrectly() throws Exception {
ProjectionOperation operation = Aggregation.project().and("field").slice(10, 5).as("renamed");
@@ -411,7 +412,7 @@ public void shouldRenderSliceWithPositionCorrectly() throws Exception {
}
@Test // DATAMONGO-784
- public void shouldRenderCmpCorrectly() {
+ void shouldRenderCmpCorrectly() {
ProjectionOperation operation = Aggregation.project().and("field").cmp(10).as("cmp10");
@@ -420,7 +421,7 @@ public void shouldRenderCmpCorrectly() {
}
@Test // DATAMONGO-784
- public void shouldRenderEqCorrectly() {
+ void shouldRenderEqCorrectly() {
ProjectionOperation operation = Aggregation.project().and("field").eq(10).as("eq10");
@@ -429,7 +430,7 @@ public void shouldRenderEqCorrectly() {
}
@Test // DATAMONGO-784
- public void shouldRenderGtCorrectly() {
+ void shouldRenderGtCorrectly() {
ProjectionOperation operation = Aggregation.project().and("field").gt(10).as("gt10");
@@ -438,7 +439,7 @@ public void shouldRenderGtCorrectly() {
}
@Test // DATAMONGO-784
- public void shouldRenderGteCorrectly() {
+ void shouldRenderGteCorrectly() {
ProjectionOperation operation = Aggregation.project().and("field").gte(10).as("gte10");
@@ -447,7 +448,7 @@ public void shouldRenderGteCorrectly() {
}
@Test // DATAMONGO-784
- public void shouldRenderLtCorrectly() {
+ void shouldRenderLtCorrectly() {
ProjectionOperation operation = Aggregation.project().and("field").lt(10).as("lt10");
@@ -456,7 +457,7 @@ public void shouldRenderLtCorrectly() {
}
@Test // DATAMONGO-784
- public void shouldRenderLteCorrectly() {
+ void shouldRenderLteCorrectly() {
ProjectionOperation operation = Aggregation.project().and("field").lte(10).as("lte10");
@@ -465,7 +466,7 @@ public void shouldRenderLteCorrectly() {
}
@Test // DATAMONGO-784
- public void shouldRenderNeCorrectly() {
+ void shouldRenderNeCorrectly() {
ProjectionOperation operation = Aggregation.project().and("field").ne(10).as("ne10");
@@ -474,7 +475,7 @@ public void shouldRenderNeCorrectly() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetEquals() {
+ void shouldRenderSetEquals() {
Document agg = project("A", "B").and("A").equalsArrays("B").as("sameElements")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -484,7 +485,7 @@ public void shouldRenderSetEquals() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetEqualsAggregationExpresssion() {
+ void shouldRenderSetEqualsAggregationExpresssion() {
Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").isEqualTo("B")).as("sameElements")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -494,7 +495,7 @@ public void shouldRenderSetEqualsAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetIntersection() {
+ void shouldRenderSetIntersection() {
Document agg = project("A", "B").and("A").intersectsArrays("B").as("commonToBoth")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -504,7 +505,7 @@ public void shouldRenderSetIntersection() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetIntersectionAggregationExpresssion() {
+ void shouldRenderSetIntersectionAggregationExpresssion() {
Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").intersects("B")).as("commonToBoth")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -514,7 +515,7 @@ public void shouldRenderSetIntersectionAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetUnion() {
+ void shouldRenderSetUnion() {
Document agg = project("A", "B").and("A").unionArrays("B").as("allValues").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -523,7 +524,7 @@ public void shouldRenderSetUnion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetUnionAggregationExpresssion() {
+ void shouldRenderSetUnionAggregationExpresssion() {
Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").union("B")).as("allValues")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -533,7 +534,7 @@ public void shouldRenderSetUnionAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetDifference() {
+ void shouldRenderSetDifference() {
Document agg = project("A", "B").and("B").differenceToArray("A").as("inBOnly")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -543,7 +544,7 @@ public void shouldRenderSetDifference() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetDifferenceAggregationExpresssion() {
+ void shouldRenderSetDifferenceAggregationExpresssion() {
Document agg = project("A", "B").and(SetOperators.arrayAsSet("B").differenceTo("A")).as("inBOnly")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -553,7 +554,7 @@ public void shouldRenderSetDifferenceAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetIsSubset() {
+ void shouldRenderSetIsSubset() {
Document agg = project("A", "B").and("A").subsetOfArray("B").as("aIsSubsetOfB")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -563,7 +564,7 @@ public void shouldRenderSetIsSubset() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSetIsSubsetAggregationExpresssion() {
+ void shouldRenderSetIsSubsetAggregationExpresssion() {
Document agg = project("A", "B").and(SetOperators.arrayAsSet("A").isSubsetOf("B")).as("aIsSubsetOfB")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -573,7 +574,7 @@ public void shouldRenderSetIsSubsetAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAnyElementTrue() {
+ void shouldRenderAnyElementTrue() {
Document agg = project("responses").and("responses").anyElementInArrayTrue().as("isAnyTrue")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -583,7 +584,7 @@ public void shouldRenderAnyElementTrue() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAnyElementTrueAggregationExpresssion() {
+ void shouldRenderAnyElementTrueAggregationExpresssion() {
Document agg = project("responses").and(SetOperators.arrayAsSet("responses").anyElementTrue()).as("isAnyTrue")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -593,7 +594,7 @@ public void shouldRenderAnyElementTrueAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAllElementsTrue() {
+ void shouldRenderAllElementsTrue() {
Document agg = project("responses").and("responses").allElementsInArrayTrue().as("isAllTrue")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -603,7 +604,7 @@ public void shouldRenderAllElementsTrue() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAllElementsTrueAggregationExpresssion() {
+ void shouldRenderAllElementsTrueAggregationExpresssion() {
Document agg = project("responses").and(SetOperators.arrayAsSet("responses").allElementsTrue()).as("isAllTrue")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -613,7 +614,7 @@ public void shouldRenderAllElementsTrueAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAbs() {
+ void shouldRenderAbs() {
Document agg = project().and("anyNumber").absoluteValue().as("absoluteValue")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -622,7 +623,7 @@ public void shouldRenderAbs() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAbsAggregationExpresssion() {
+ void shouldRenderAbsAggregationExpresssion() {
Document agg = project()
.and(
@@ -634,7 +635,7 @@ public void shouldRenderAbsAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAddAggregationExpresssion() {
+ void shouldRenderAddAggregationExpresssion() {
Document agg = project().and(ArithmeticOperators.valueOf("price").add("fee")).as("total")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -643,7 +644,7 @@ public void shouldRenderAddAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderCeil() {
+ void shouldRenderCeil() {
Document agg = project().and("anyNumber").ceil().as("ceilValue").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -651,7 +652,7 @@ public void shouldRenderCeil() {
}
@Test // DATAMONGO-1536
- public void shouldRenderCeilAggregationExpresssion() {
+ void shouldRenderCeilAggregationExpresssion() {
Document agg = project().and(
ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).ceil())
@@ -662,7 +663,7 @@ public void shouldRenderCeilAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderDivide() {
+ void shouldRenderDivide() {
Document agg = project().and("value")
.divide(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).as("result")
@@ -673,7 +674,7 @@ public void shouldRenderDivide() {
}
@Test // DATAMONGO-1536
- public void shouldRenderDivideAggregationExpresssion() {
+ void shouldRenderDivideAggregationExpresssion() {
Document agg = project()
.and(ArithmeticOperators.valueOf("anyNumber")
@@ -685,7 +686,7 @@ public void shouldRenderDivideAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderExp() {
+ void shouldRenderExp() {
Document agg = project().and("value").exp().as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -693,7 +694,7 @@ public void shouldRenderExp() {
}
@Test // DATAMONGO-1536
- public void shouldRenderExpAggregationExpresssion() {
+ void shouldRenderExpAggregationExpresssion() {
Document agg = project()
.and(
@@ -705,7 +706,7 @@ public void shouldRenderExpAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderFloor() {
+ void shouldRenderFloor() {
Document agg = project().and("value").floor().as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -713,7 +714,7 @@ public void shouldRenderFloor() {
}
@Test // DATAMONGO-1536
- public void shouldRenderFloorAggregationExpresssion() {
+ void shouldRenderFloorAggregationExpresssion() {
Document agg = project().and(
ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).floor())
@@ -724,7 +725,7 @@ public void shouldRenderFloorAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLn() {
+ void shouldRenderLn() {
Document agg = project().and("value").ln().as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -732,7 +733,7 @@ public void shouldRenderLn() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLnAggregationExpresssion() {
+ void shouldRenderLnAggregationExpresssion() {
Document agg = project()
.and(ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).ln())
@@ -743,7 +744,7 @@ public void shouldRenderLnAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLog() {
+ void shouldRenderLog() {
Document agg = project().and("value").log(2).as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -751,7 +752,7 @@ public void shouldRenderLog() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLogAggregationExpresssion() {
+ void shouldRenderLogAggregationExpresssion() {
Document agg = project().and(
ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).log(2))
@@ -762,7 +763,7 @@ public void shouldRenderLogAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLog10() {
+ void shouldRenderLog10() {
Document agg = project().and("value").log10().as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -770,7 +771,7 @@ public void shouldRenderLog10() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLog10AggregationExpresssion() {
+ void shouldRenderLog10AggregationExpresssion() {
Document agg = project().and(
ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).log10())
@@ -781,7 +782,7 @@ public void shouldRenderLog10AggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMod() {
+ void shouldRenderMod() {
Document agg = project().and("value").mod(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end")))
.as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -791,7 +792,7 @@ public void shouldRenderMod() {
}
@Test // DATAMONGO-1536
- public void shouldRenderModAggregationExpresssion() {
+ void shouldRenderModAggregationExpresssion() {
Document agg = project().and(
ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).mod(2))
@@ -802,7 +803,7 @@ public void shouldRenderModAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMultiply() {
+ void shouldRenderMultiply() {
Document agg = project().and("value")
.multiply(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).as("result")
@@ -813,7 +814,7 @@ public void shouldRenderMultiply() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMultiplyAggregationExpresssion() {
+ void shouldRenderMultiplyAggregationExpresssion() {
Document agg = project()
.and(ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end")))
@@ -825,7 +826,7 @@ public void shouldRenderMultiplyAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderPow() {
+ void shouldRenderPow() {
Document agg = project().and("value").pow(2).as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -833,7 +834,7 @@ public void shouldRenderPow() {
}
@Test // DATAMONGO-1536
- public void shouldRenderPowAggregationExpresssion() {
+ void shouldRenderPowAggregationExpresssion() {
Document agg = project().and(
ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).pow(2))
@@ -844,7 +845,7 @@ public void shouldRenderPowAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSqrt() {
+ void shouldRenderSqrt() {
Document agg = project().and("value").sqrt().as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -852,7 +853,7 @@ public void shouldRenderSqrt() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSqrtAggregationExpresssion() {
+ void shouldRenderSqrtAggregationExpresssion() {
Document agg = project().and(
ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).sqrt())
@@ -863,7 +864,7 @@ public void shouldRenderSqrtAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSubtract() {
+ void shouldRenderSubtract() {
Document agg = project().and("numericField").minus(AggregationFunctionExpressions.SIZE.of(field("someArray")))
.as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -873,7 +874,7 @@ public void shouldRenderSubtract() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSubtractAggregationExpresssion() {
+ void shouldRenderSubtractAggregationExpresssion() {
Document agg = project()
.and(ArithmeticOperators.valueOf("numericField")
@@ -885,7 +886,7 @@ public void shouldRenderSubtractAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderTrunc() {
+ void shouldRenderTrunc() {
Document agg = project().and("value").trunc().as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -893,7 +894,7 @@ public void shouldRenderTrunc() {
}
@Test // DATAMONGO-1536
- public void shouldRenderTruncAggregationExpresssion() {
+ void shouldRenderTruncAggregationExpresssion() {
Document agg = project().and(
ArithmeticOperators.valueOf(AggregationFunctionExpressions.SUBTRACT.of(field("start"), field("end"))).trunc())
@@ -904,7 +905,7 @@ public void shouldRenderTruncAggregationExpresssion() {
}
@Test // DATAMONGO-1536
- public void shouldRenderConcat() {
+ void shouldRenderConcat() {
Document agg = project().and("item").concat(" - ", field("description")).as("itemDescription")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -915,7 +916,7 @@ public void shouldRenderConcat() {
}
@Test // DATAMONGO-1536
- public void shouldRenderConcatAggregationExpression() {
+ void shouldRenderConcatAggregationExpression() {
Document agg = project().and(StringOperators.valueOf("item").concat(" - ").concatValueOf("description"))
.as("itemDescription").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -926,7 +927,7 @@ public void shouldRenderConcatAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSubstr() {
+ void shouldRenderSubstr() {
Document agg = project().and("quarter").substring(0, 2).as("yearSubstring").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -934,7 +935,7 @@ public void shouldRenderSubstr() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSubstrAggregationExpression() {
+ void shouldRenderSubstrAggregationExpression() {
Document agg = project().and(StringOperators.valueOf("quarter").substring(0, 2)).as("yearSubstring")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -943,7 +944,7 @@ public void shouldRenderSubstrAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderToLower() {
+ void shouldRenderToLower() {
Document agg = project().and("item").toLower().as("item").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -951,7 +952,7 @@ public void shouldRenderToLower() {
}
@Test // DATAMONGO-1536
- public void shouldRenderToLowerAggregationExpression() {
+ void shouldRenderToLowerAggregationExpression() {
Document agg = project().and(StringOperators.valueOf("item").toLower()).as("item")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -960,7 +961,7 @@ public void shouldRenderToLowerAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderToUpper() {
+ void shouldRenderToUpper() {
Document agg = project().and("item").toUpper().as("item").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -968,7 +969,7 @@ public void shouldRenderToUpper() {
}
@Test // DATAMONGO-1536
- public void shouldRenderToUpperAggregationExpression() {
+ void shouldRenderToUpperAggregationExpression() {
Document agg = project().and(StringOperators.valueOf("item").toUpper()).as("item")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -977,7 +978,7 @@ public void shouldRenderToUpperAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderStrCaseCmp() {
+ void shouldRenderStrCaseCmp() {
Document agg = project().and("quarter").strCaseCmp("13q4").as("comparisonResult")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -987,7 +988,7 @@ public void shouldRenderStrCaseCmp() {
}
@Test // DATAMONGO-1536
- public void shouldRenderStrCaseCmpAggregationExpression() {
+ void shouldRenderStrCaseCmpAggregationExpression() {
Document agg = project().and(StringOperators.valueOf("quarter").strCaseCmp("13q4")).as("comparisonResult")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -997,7 +998,7 @@ public void shouldRenderStrCaseCmpAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderArrayElementAt() {
+ void shouldRenderArrayElementAt() {
Document agg = project().and("favorites").arrayElementAt(0).as("first").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1005,7 +1006,7 @@ public void shouldRenderArrayElementAt() {
}
@Test // DATAMONGO-1536
- public void shouldRenderArrayElementAtAggregationExpression() {
+ void shouldRenderArrayElementAtAggregationExpression() {
Document agg = project().and(ArrayOperators.arrayOf("favorites").elementAt(0)).as("first")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1014,7 +1015,7 @@ public void shouldRenderArrayElementAtAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderConcatArrays() {
+ void shouldRenderConcatArrays() {
Document agg = project().and("instock").concatArrays("ordered").as("items").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1023,7 +1024,7 @@ public void shouldRenderConcatArrays() {
}
@Test // DATAMONGO-1536
- public void shouldRenderConcatArraysAggregationExpression() {
+ void shouldRenderConcatArraysAggregationExpression() {
Document agg = project().and(ArrayOperators.arrayOf("instock").concat("ordered")).as("items")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1033,7 +1034,7 @@ public void shouldRenderConcatArraysAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderIsArray() {
+ void shouldRenderIsArray() {
Document agg = project().and("instock").isArray().as("isAnArray").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1041,7 +1042,7 @@ public void shouldRenderIsArray() {
}
@Test // DATAMONGO-1536
- public void shouldRenderIsArrayAggregationExpression() {
+ void shouldRenderIsArrayAggregationExpression() {
Document agg = project().and(ArrayOperators.arrayOf("instock").isArray()).as("isAnArray")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1050,7 +1051,7 @@ public void shouldRenderIsArrayAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSizeAggregationExpression() {
+ void shouldRenderSizeAggregationExpression() {
Document agg = project().and(ArrayOperators.arrayOf("instock").length()).as("arraySize")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1059,7 +1060,7 @@ public void shouldRenderSizeAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSliceAggregationExpression() {
+ void shouldRenderSliceAggregationExpression() {
Document agg = project().and(ArrayOperators.arrayOf("favorites").slice().itemCount(3)).as("threeFavorites")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1068,7 +1069,7 @@ public void shouldRenderSliceAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSliceWithPositionAggregationExpression() {
+ void shouldRenderSliceWithPositionAggregationExpression() {
Document agg = project().and(ArrayOperators.arrayOf("favorites").slice().offset(2).itemCount(3))
.as("threeFavorites").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1077,7 +1078,7 @@ public void shouldRenderSliceWithPositionAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLiteral() {
+ void shouldRenderLiteral() {
Document agg = project().and("$1").asLiteral().as("literalOnly").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1085,7 +1086,7 @@ public void shouldRenderLiteral() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLiteralAggregationExpression() {
+ void shouldRenderLiteralAggregationExpression() {
Document agg = project().and(LiteralOperators.valueOf("$1").asLiteral()).as("literalOnly")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1094,7 +1095,7 @@ public void shouldRenderLiteralAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderDayOfYearAggregationExpression() {
+ void shouldRenderDayOfYearAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").dayOfYear()).as("dayOfYear")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1103,7 +1104,7 @@ public void shouldRenderDayOfYearAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDayOfYearAggregationExpressionWithTimezone() {
+ void shouldRenderDayOfYearAggregationExpressionWithTimezone() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).dayOfYear()).as("dayOfYear")
@@ -1114,7 +1115,7 @@ public void shouldRenderDayOfYearAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1834
- public void shouldRenderTimeZoneFromField() {
+ void shouldRenderTimeZoneFromField() {
Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.ofField("tz")).dayOfYear())
.as("dayOfYear").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1124,7 +1125,7 @@ public void shouldRenderTimeZoneFromField() {
}
@Test // DATAMONGO-1834
- public void shouldRenderTimeZoneFromExpression() {
+ void shouldRenderTimeZoneFromExpression() {
Document agg = project()
.and(DateOperators.dateOf("date")
@@ -1136,7 +1137,7 @@ public void shouldRenderTimeZoneFromExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderDayOfMonthAggregationExpression() {
+ void shouldRenderDayOfMonthAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").dayOfMonth()).as("day")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1145,7 +1146,7 @@ public void shouldRenderDayOfMonthAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDayOfMonthAggregationExpressionWithTimezone() {
+ void shouldRenderDayOfMonthAggregationExpressionWithTimezone() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).dayOfMonth()).as("day")
@@ -1156,7 +1157,7 @@ public void shouldRenderDayOfMonthAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderDayOfWeekAggregationExpression() {
+ void shouldRenderDayOfWeekAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").dayOfWeek()).as("dayOfWeek")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1165,7 +1166,7 @@ public void shouldRenderDayOfWeekAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDayOfWeekAggregationExpressionWithTimezone() {
+ void shouldRenderDayOfWeekAggregationExpressionWithTimezone() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).dayOfWeek()).as("dayOfWeek")
@@ -1176,7 +1177,7 @@ public void shouldRenderDayOfWeekAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderYearAggregationExpression() {
+ void shouldRenderYearAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").year()).as("year")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1185,7 +1186,7 @@ public void shouldRenderYearAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderYearAggregationExpressionWithTimezone() {
+ void shouldRenderYearAggregationExpressionWithTimezone() {
Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).year())
.as("year").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1195,7 +1196,7 @@ public void shouldRenderYearAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMonthAggregationExpression() {
+ void shouldRenderMonthAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").month()).as("month")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1204,7 +1205,7 @@ public void shouldRenderMonthAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderMonthAggregationExpressionWithTimezone() {
+ void shouldRenderMonthAggregationExpressionWithTimezone() {
Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).month())
.as("month").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1214,7 +1215,7 @@ public void shouldRenderMonthAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderWeekAggregationExpression() {
+ void shouldRenderWeekAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").week()).as("week")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1223,7 +1224,7 @@ public void shouldRenderWeekAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderWeekAggregationExpressionWithTimezone() {
+ void shouldRenderWeekAggregationExpressionWithTimezone() {
Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).week())
.as("week").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1233,7 +1234,7 @@ public void shouldRenderWeekAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderHourAggregationExpression() {
+ void shouldRenderHourAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").hour()).as("hour")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1242,7 +1243,7 @@ public void shouldRenderHourAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderHourAggregationExpressionWithTimezone() {
+ void shouldRenderHourAggregationExpressionWithTimezone() {
Document agg = project().and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).hour())
.as("hour").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1252,7 +1253,7 @@ public void shouldRenderHourAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMinuteAggregationExpression() {
+ void shouldRenderMinuteAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").minute()).as("minute")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1261,7 +1262,7 @@ public void shouldRenderMinuteAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderMinuteAggregationExpressionWithTimezone() {
+ void shouldRenderMinuteAggregationExpressionWithTimezone() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).minute()).as("minute")
@@ -1272,7 +1273,7 @@ public void shouldRenderMinuteAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSecondAggregationExpression() {
+ void shouldRenderSecondAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").second()).as("second")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1281,7 +1282,7 @@ public void shouldRenderSecondAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderSecondAggregationExpressionWithTimezone() {
+ void shouldRenderSecondAggregationExpressionWithTimezone() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).second()).as("second")
@@ -1292,7 +1293,7 @@ public void shouldRenderSecondAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMillisecondAggregationExpression() {
+ void shouldRenderMillisecondAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").millisecond()).as("msec")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1301,7 +1302,7 @@ public void shouldRenderMillisecondAggregationExpression() {
}
@Test // DATAMONGO-1834
- public void shouldRenderMillisecondAggregationExpressionWithTimezone() {
+ void shouldRenderMillisecondAggregationExpressionWithTimezone() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).millisecond()).as("msec")
@@ -1312,7 +1313,7 @@ public void shouldRenderMillisecondAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderDateToString() {
+ void shouldRenderDateToString() {
Document agg = project().and("date").dateAsFormattedString("%H:%M:%S:%L").as("time")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1322,7 +1323,7 @@ public void shouldRenderDateToString() {
}
@Test // DATAMONGO-2047
- public void shouldRenderDateToStringWithoutFormatOption() {
+ void shouldRenderDateToStringWithoutFormatOption() {
Document agg = project().and("date").dateAsFormattedString().as("time").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1330,7 +1331,7 @@ public void shouldRenderDateToStringWithoutFormatOption() {
}
@Test // DATAMONGO-1536
- public void shouldRenderDateToStringAggregationExpression() {
+ void shouldRenderDateToStringAggregationExpression() {
Document agg = project().and(DateOperators.dateOf("date").toString("%H:%M:%S:%L")).as("time")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1340,7 +1341,7 @@ public void shouldRenderDateToStringAggregationExpression() {
}
@Test // DATAMONGO-1834, DATAMONGO-2047
- public void shouldRenderDateToStringAggregationExpressionWithTimezone() {
+ void shouldRenderDateToStringAggregationExpressionWithTimezone() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).toString("%H:%M:%S:%L"))
@@ -1358,7 +1359,7 @@ public void shouldRenderDateToStringAggregationExpressionWithTimezone() {
}
@Test // DATAMONGO-2047
- public void shouldRenderDateToStringWithOnNull() {
+ void shouldRenderDateToStringWithOnNull() {
Document agg = project()
.and(DateOperators.dateOf("date").toStringWithDefaultFormat().onNullReturnValueOf("fallback-field")).as("time")
@@ -1369,7 +1370,7 @@ public void shouldRenderDateToStringWithOnNull() {
}
@Test // DATAMONGO-2047
- public void shouldRenderDateToStringWithOnNullExpression() {
+ void shouldRenderDateToStringWithOnNullExpression() {
Document agg = project()
.and(DateOperators.dateOf("date").toStringWithDefaultFormat()
@@ -1381,7 +1382,7 @@ public void shouldRenderDateToStringWithOnNullExpression() {
}
@Test // DATAMONGO-2047
- public void shouldRenderDateToStringWithOnNullAndTimezone() {
+ void shouldRenderDateToStringWithOnNullAndTimezone() {
Document agg = project().and(DateOperators.dateOf("date").toStringWithDefaultFormat()
.onNullReturnValueOf("fallback-field").withTimezone(Timezone.ofField("foo"))).as("time")
@@ -1392,7 +1393,7 @@ public void shouldRenderDateToStringWithOnNullAndTimezone() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSumAggregationExpression() {
+ void shouldRenderSumAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("quizzes").sum()).as("quizTotal")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1401,7 +1402,7 @@ public void shouldRenderSumAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderSumWithMultipleArgsAggregationExpression() {
+ void shouldRenderSumWithMultipleArgsAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("final").sum().and("midterm")).as("examTotal")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1410,7 +1411,7 @@ public void shouldRenderSumWithMultipleArgsAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAvgAggregationExpression() {
+ void shouldRenderAvgAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("quizzes").avg()).as("quizAvg")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1419,7 +1420,7 @@ public void shouldRenderAvgAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderAvgWithMultipleArgsAggregationExpression() {
+ void shouldRenderAvgWithMultipleArgsAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("final").avg().and("midterm")).as("examAvg")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1428,7 +1429,7 @@ public void shouldRenderAvgWithMultipleArgsAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMaxAggregationExpression() {
+ void shouldRenderMaxAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("quizzes").max()).as("quizMax")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1437,7 +1438,7 @@ public void shouldRenderMaxAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMaxWithMultipleArgsAggregationExpression() {
+ void shouldRenderMaxWithMultipleArgsAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("final").max().and("midterm")).as("examMax")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1446,7 +1447,7 @@ public void shouldRenderMaxWithMultipleArgsAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMinAggregationExpression() {
+ void shouldRenderMinAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("quizzes").min()).as("quizMin")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1455,7 +1456,7 @@ public void shouldRenderMinAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderMinWithMultipleArgsAggregationExpression() {
+ void shouldRenderMinWithMultipleArgsAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("final").min().and("midterm")).as("examMin")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1464,7 +1465,7 @@ public void shouldRenderMinWithMultipleArgsAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderStdDevPopAggregationExpression() {
+ void shouldRenderStdDevPopAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("scores").stdDevPop()).as("stdDev")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1473,7 +1474,7 @@ public void shouldRenderStdDevPopAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderStdDevSampAggregationExpression() {
+ void shouldRenderStdDevSampAggregationExpression() {
Document agg = project().and(ArithmeticOperators.valueOf("scores").stdDevSamp()).as("stdDev")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1482,7 +1483,7 @@ public void shouldRenderStdDevSampAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderCmpAggregationExpression() {
+ void shouldRenderCmpAggregationExpression() {
Document agg = project().and(ComparisonOperators.valueOf("qty").compareToValue(250)).as("cmp250")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1491,7 +1492,7 @@ public void shouldRenderCmpAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderEqAggregationExpression() {
+ void shouldRenderEqAggregationExpression() {
Document agg = project().and(ComparisonOperators.valueOf("qty").equalToValue(250)).as("eq250")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1500,7 +1501,7 @@ public void shouldRenderEqAggregationExpression() {
}
@Test // DATAMONGO-2513
- public void shouldRenderEqAggregationExpressionWithListComparison() {
+ void shouldRenderEqAggregationExpressionWithListComparison() {
Document agg = project().and(ComparisonOperators.valueOf("qty").equalToValue(Arrays.asList(250))).as("eq250")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1509,7 +1510,7 @@ public void shouldRenderEqAggregationExpressionWithListComparison() {
}
@Test // DATAMONGO-1536
- public void shouldRenderGtAggregationExpression() {
+ void shouldRenderGtAggregationExpression() {
Document agg = project().and(ComparisonOperators.valueOf("qty").greaterThanValue(250)).as("gt250")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1518,7 +1519,7 @@ public void shouldRenderGtAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderGteAggregationExpression() {
+ void shouldRenderGteAggregationExpression() {
Document agg = project().and(ComparisonOperators.valueOf("qty").greaterThanEqualToValue(250)).as("gte250")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1527,7 +1528,7 @@ public void shouldRenderGteAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLtAggregationExpression() {
+ void shouldRenderLtAggregationExpression() {
Document agg = project().and(ComparisonOperators.valueOf("qty").lessThanValue(250)).as("lt250")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1536,7 +1537,7 @@ public void shouldRenderLtAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLteAggregationExpression() {
+ void shouldRenderLteAggregationExpression() {
Document agg = project().and(ComparisonOperators.valueOf("qty").lessThanEqualToValue(250)).as("lte250")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1545,7 +1546,7 @@ public void shouldRenderLteAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderNeAggregationExpression() {
+ void shouldRenderNeAggregationExpression() {
Document agg = project().and(ComparisonOperators.valueOf("qty").notEqualToValue(250)).as("ne250")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1554,7 +1555,7 @@ public void shouldRenderNeAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLogicAndAggregationExpression() {
+ void shouldRenderLogicAndAggregationExpression() {
Document agg = project()
.and(BooleanOperators.valueOf(ComparisonOperators.valueOf("qty").greaterThanValue(100))
@@ -1566,7 +1567,7 @@ public void shouldRenderLogicAndAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderLogicOrAggregationExpression() {
+ void shouldRenderLogicOrAggregationExpression() {
Document agg = project()
.and(BooleanOperators.valueOf(ComparisonOperators.valueOf("qty").greaterThanValue(250))
@@ -1578,7 +1579,7 @@ public void shouldRenderLogicOrAggregationExpression() {
}
@Test // DATAMONGO-1536
- public void shouldRenderNotAggregationExpression() {
+ void shouldRenderNotAggregationExpression() {
Document agg = project().and(BooleanOperators.not(ComparisonOperators.valueOf("qty").greaterThanValue(250)))
.as("result").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1587,7 +1588,7 @@ public void shouldRenderNotAggregationExpression() {
}
@Test // DATAMONGO-1540
- public void shouldRenderMapAggregationExpression() {
+ void shouldRenderMapAggregationExpression() {
Document agg = Aggregation.project()
.and(VariableOperators.mapItemsOf("quizzes").as("grade")
@@ -1599,7 +1600,7 @@ public void shouldRenderMapAggregationExpression() {
}
@Test // DATAMONGO-1540
- public void shouldRenderMapAggregationExpressionOnExpression() {
+ void shouldRenderMapAggregationExpressionOnExpression() {
Document agg = Aggregation.project()
.and(VariableOperators.mapItemsOf(AggregationFunctionExpressions.SIZE.of("foo")).as("grade")
@@ -1611,7 +1612,7 @@ public void shouldRenderMapAggregationExpressionOnExpression() {
}
@Test // DATAMONGO-861, DATAMONGO-1542
- public void shouldRenderIfNullConditionAggregationExpression() {
+ void shouldRenderIfNullConditionAggregationExpression() {
Document agg = project().and(
ConditionalOperators.ifNull(ArrayOperators.arrayOf("array").elementAt(1)).then("a more sophisticated value"))
@@ -1622,7 +1623,7 @@ public void shouldRenderIfNullConditionAggregationExpression() {
}
@Test // DATAMONGO-1542
- public void shouldRenderIfNullValueAggregationExpression() {
+ void shouldRenderIfNullValueAggregationExpression() {
Document agg = project()
.and(ConditionalOperators.ifNull("field").then(ArrayOperators.arrayOf("array").elementAt(1))).as("result")
@@ -1633,7 +1634,7 @@ public void shouldRenderIfNullValueAggregationExpression() {
}
@Test // DATAMONGO-861, DATAMONGO-1542
- public void fieldReplacementIfNullShouldRenderCorrectly() {
+ void fieldReplacementIfNullShouldRenderCorrectly() {
Document agg = project().and(ConditionalOperators.ifNull("optional").thenValueOf("$never-null")).as("result")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1643,7 +1644,7 @@ public void fieldReplacementIfNullShouldRenderCorrectly() {
}
@Test // DATAMONGO-1538
- public void shouldRenderLetExpressionCorrectly() {
+ void shouldRenderLetExpressionCorrectly() {
Document agg = Aggregation.project()
.and(VariableOperators
@@ -1665,7 +1666,7 @@ public void shouldRenderLetExpressionCorrectly() {
}
@Test // DATAMONGO-1538
- public void shouldRenderLetExpressionCorrectlyWhenUsingLetOnProjectionBuilder() {
+ void shouldRenderLetExpressionCorrectlyWhenUsingLetOnProjectionBuilder() {
ExpressionVariable var1 = newVariable("total")
.forExpression(AggregationFunctionExpressions.ADD.of(Fields.field("price"), Fields.field("tax")));
@@ -1688,7 +1689,7 @@ public void shouldRenderLetExpressionCorrectlyWhenUsingLetOnProjectionBuilder()
}
@Test // DATAMONGO-1548
- public void shouldRenderIndexOfBytesCorrectly() {
+ void shouldRenderIndexOfBytesCorrectly() {
Document agg = project().and(StringOperators.valueOf("item").indexOf("foo")).as("byteLocation")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1698,7 +1699,7 @@ public void shouldRenderIndexOfBytesCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderIndexOfBytesWithRangeCorrectly() {
+ void shouldRenderIndexOfBytesWithRangeCorrectly() {
Document agg = project()
.and(StringOperators.valueOf("item").indexOf("foo")
@@ -1710,7 +1711,7 @@ public void shouldRenderIndexOfBytesWithRangeCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderIndexOfCPCorrectly() {
+ void shouldRenderIndexOfCPCorrectly() {
Document agg = project().and(StringOperators.valueOf("item").indexOfCP("foo")).as("cpLocation")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1719,7 +1720,7 @@ public void shouldRenderIndexOfCPCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderIndexOfCPWithRangeCorrectly() {
+ void shouldRenderIndexOfCPWithRangeCorrectly() {
Document agg = project()
.and(StringOperators.valueOf("item").indexOfCP("foo")
@@ -1731,7 +1732,7 @@ public void shouldRenderIndexOfCPWithRangeCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderSplitCorrectly() {
+ void shouldRenderSplitCorrectly() {
Document agg = project().and(StringOperators.valueOf("city").split(", ")).as("city_state")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1740,7 +1741,7 @@ public void shouldRenderSplitCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderStrLenBytesCorrectly() {
+ void shouldRenderStrLenBytesCorrectly() {
Document agg = project().and(StringOperators.valueOf("name").length()).as("length")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1749,7 +1750,7 @@ public void shouldRenderStrLenBytesCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderStrLenCPCorrectly() {
+ void shouldRenderStrLenCPCorrectly() {
Document agg = project().and(StringOperators.valueOf("name").lengthCP()).as("length")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1758,7 +1759,7 @@ public void shouldRenderStrLenCPCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderSubstrCPCorrectly() {
+ void shouldRenderSubstrCPCorrectly() {
Document agg = project().and(StringOperators.valueOf("quarter").substringCP(0, 2)).as("yearSubstring")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1766,27 +1767,27 @@ public void shouldRenderSubstrCPCorrectly() {
assertThat(agg)
.isEqualTo(Document.parse("{ $project : { yearSubstring: { $substrCP: [ \"$quarter\", 0, 2 ] } } }"));
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindCorrectly() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindCorrectly() {
Document agg = project().and(StringOperators.valueOf("field1").regexFind("e")).as("regex")
.toDocument(Aggregation.DEFAULT_CONTEXT);
assertThat(agg).isEqualTo(Document.parse("{ $project : { regex: { $regexFind: { \"input\" : \"$field1\", \"regex\" : \"e\" } } } }"));
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindAllCorrectly() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllCorrectly() {
Document agg = project().and(StringOperators.valueOf("field1").regexFindAll("e")).as("regex")
.toDocument(Aggregation.DEFAULT_CONTEXT);
assertThat(agg).isEqualTo(Document.parse("{ $project : { regex: { $regexFindAll: { \"input\" : \"$field1\", \"regex\" : \"e\" } } } }"));
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexMatchCorrectly() {
+
+ @Test // GH-3725
+ void shouldRenderRegexMatchCorrectly() {
Document agg = project().and(StringOperators.valueOf("field1").regexMatch("e")).as("regex")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1795,7 +1796,7 @@ public void shouldRenderRegexMatchCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderIndexOfArrayCorrectly() {
+ void shouldRenderIndexOfArrayCorrectly() {
Document agg = project().and(ArrayOperators.arrayOf("items").indexOf(2)).as("index")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1804,7 +1805,7 @@ public void shouldRenderIndexOfArrayCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderRangeCorrectly() {
+ void shouldRenderRangeCorrectly() {
Document agg = project().and(ArrayOperators.RangeOperator.rangeStartingAt(0L).to("distance").withStepSize(25L))
.as("rest_stops").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1815,7 +1816,7 @@ public void shouldRenderRangeCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderReverseArrayCorrectly() {
+ void shouldRenderReverseArrayCorrectly() {
Document agg = project().and(ArrayOperators.arrayOf("favorites").reverse()).as("reverseFavorites")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1824,7 +1825,7 @@ public void shouldRenderReverseArrayCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderReduceWithSimpleObjectCorrectly() {
+ void shouldRenderReduceWithSimpleObjectCorrectly() {
Document agg = project()
.and(ArrayOperators.arrayOf("probabilityArr")
@@ -1836,7 +1837,7 @@ public void shouldRenderReduceWithSimpleObjectCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderReduceWithComplexObjectCorrectly() {
+ void shouldRenderReduceWithComplexObjectCorrectly() {
PropertyExpression sum = PropertyExpression.property("sum").definedAs(
ArithmeticOperators.valueOf(Variable.VALUE.referringTo("sum").getName()).add(Variable.THIS.getName()));
@@ -1853,7 +1854,7 @@ public void shouldRenderReduceWithComplexObjectCorrectly() {
}
@Test // DATAMONGO-1843
- public void shouldRenderReduceWithInputAndInExpressionsCorrectly() {
+ void shouldRenderReduceWithInputAndInExpressionsCorrectly() {
Document expected = Document.parse(
"{ \"$project\" : { \"results\" : { \"$reduce\" : { \"input\" : { \"$slice\" : [\"$array\", 5] }, \"initialValue\" : \"\", \"in\" : { \"$concat\" : [\"$$value\", \"/\", \"$$this\"] } } } } }");
@@ -1874,7 +1875,7 @@ public void shouldRenderReduceWithInputAndInExpressionsCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderZipCorrectly() {
+ void shouldRenderZipCorrectly() {
AggregationExpression elemAt0 = ArrayOperators.arrayOf("matrix").elementAt(0);
AggregationExpression elemAt1 = ArrayOperators.arrayOf("matrix").elementAt(1);
@@ -1889,7 +1890,7 @@ public void shouldRenderZipCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderInCorrectly() {
+ void shouldRenderInCorrectly() {
Document agg = project().and(ArrayOperators.arrayOf("in_stock").containsValue("bananas")).as("has_bananas")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1899,7 +1900,7 @@ public void shouldRenderInCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderIsoDayOfWeekCorrectly() {
+ void shouldRenderIsoDayOfWeekCorrectly() {
Document agg = project().and(DateOperators.dateOf("birthday").isoDayOfWeek()).as("dayOfWeek")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1908,7 +1909,7 @@ public void shouldRenderIsoDayOfWeekCorrectly() {
}
@Test // DATAMONGO-1834
- public void shouldRenderIsoDayOfWeekWithTimezoneCorrectly() {
+ void shouldRenderIsoDayOfWeekWithTimezoneCorrectly() {
Document agg = project()
.and(DateOperators.dateOf("birthday").withTimezone(Timezone.valueOf("America/Chicago")).isoDayOfWeek())
@@ -1919,7 +1920,7 @@ public void shouldRenderIsoDayOfWeekWithTimezoneCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderIsoWeekCorrectly() {
+ void shouldRenderIsoWeekCorrectly() {
Document agg = project().and(DateOperators.dateOf("date").isoWeek()).as("weekNumber")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1928,7 +1929,7 @@ public void shouldRenderIsoWeekCorrectly() {
}
@Test // DATAMONGO-1834
- public void shouldRenderIsoWeekWithTimezoneCorrectly() {
+ void shouldRenderIsoWeekWithTimezoneCorrectly() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).isoWeek()).as("weekNumber")
@@ -1939,7 +1940,7 @@ public void shouldRenderIsoWeekWithTimezoneCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderIsoWeekYearCorrectly() {
+ void shouldRenderIsoWeekYearCorrectly() {
Document agg = project().and(DateOperators.dateOf("date").isoWeekYear()).as("yearNumber")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -1948,7 +1949,7 @@ public void shouldRenderIsoWeekYearCorrectly() {
}
@Test // DATAMONGO-1834
- public void shouldRenderIsoWeekYearWithTimezoneCorrectly() {
+ void shouldRenderIsoWeekYearWithTimezoneCorrectly() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).isoWeekYear())
@@ -1959,7 +1960,7 @@ public void shouldRenderIsoWeekYearWithTimezoneCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldRenderSwitchCorrectly() {
+ void shouldRenderSwitchCorrectly() {
String expected = "$switch:\n" + //
"{\n" + //
@@ -2001,7 +2002,7 @@ public void shouldRenderSwitchCorrectly() {
}
@Test // DATAMONGO-1548
- public void shouldTypeCorrectly() {
+ void shouldTypeCorrectly() {
Document agg = project().and(DataTypeOperators.Type.typeOf("a")).as("a").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2009,7 +2010,7 @@ public void shouldTypeCorrectly() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDateFromPartsWithJustTheYear() {
+ void shouldRenderDateFromPartsWithJustTheYear() {
Document agg = project().and(DateOperators.dateFromParts().year(2018)).as("newDate")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2018,7 +2019,7 @@ public void shouldRenderDateFromPartsWithJustTheYear() {
}
@Test // DATAMONGO-1834, DATAMONGO-2671
- public void shouldRenderDateFromParts() {
+ void shouldRenderDateFromParts() {
Document agg = project()
.and(DateOperators.dateFromParts().year(2018).month(3).day(23).hour(14).minute(25).second(10).millisecond(2))
@@ -2029,7 +2030,7 @@ public void shouldRenderDateFromParts() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDateFromPartsWithTimezone() {
+ void shouldRenderDateFromPartsWithTimezone() {
Document agg = project()
.and(DateOperators.dateFromParts().withTimezone(Timezone.valueOf("America/Chicago")).year(2018)).as("newDate")
@@ -2040,7 +2041,7 @@ public void shouldRenderDateFromPartsWithTimezone() {
}
@Test // DATAMONGO-1834
- public void shouldRenderIsoDateFromPartsWithJustTheYear() {
+ void shouldRenderIsoDateFromPartsWithJustTheYear() {
Document agg = project().and(DateOperators.dateFromParts().isoWeekYear(2018)).as("newDate")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2049,7 +2050,7 @@ public void shouldRenderIsoDateFromPartsWithJustTheYear() {
}
@Test // DATAMONGO-1834, DATAMONGO-2671
- public void shouldRenderIsoDateFromParts() {
+ void shouldRenderIsoDateFromParts() {
Document agg = project().and(DateOperators.dateFromParts().isoWeekYear(2018).isoWeek(12).isoDayOfWeek(5).hour(14)
.minute(30).second(42).millisecond(2)).as("newDate").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2059,7 +2060,7 @@ public void shouldRenderIsoDateFromParts() {
}
@Test // DATAMONGO-1834
- public void shouldRenderIsoDateFromPartsWithTimezone() {
+ void shouldRenderIsoDateFromPartsWithTimezone() {
Document agg = project()
.and(DateOperators.dateFromParts().withTimezone(Timezone.valueOf("America/Chicago")).isoWeekYear(2018))
@@ -2070,7 +2071,7 @@ public void shouldRenderIsoDateFromPartsWithTimezone() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDateToParts() {
+ void shouldRenderDateToParts() {
Document agg = project().and(DateOperators.dateOf("date").toParts()).as("newDate")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2079,7 +2080,7 @@ public void shouldRenderDateToParts() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDateToIsoParts() {
+ void shouldRenderDateToIsoParts() {
Document agg = project().and(DateOperators.dateOf("date").toParts().iso8601()).as("newDate")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2089,7 +2090,7 @@ public void shouldRenderDateToIsoParts() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDateToPartsWithTimezone() {
+ void shouldRenderDateToPartsWithTimezone() {
Document agg = project()
.and(DateOperators.dateOf("date").withTimezone(Timezone.valueOf("America/Chicago")).toParts()).as("newDate")
@@ -2100,7 +2101,7 @@ public void shouldRenderDateToPartsWithTimezone() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDateFromString() {
+ void shouldRenderDateFromString() {
Document agg = project().and(DateOperators.dateFromString("2017-02-08T12:10:40.787")).as("newDate")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2110,7 +2111,7 @@ public void shouldRenderDateFromString() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDateFromStringWithFieldReference() {
+ void shouldRenderDateFromStringWithFieldReference() {
Document agg = project().and(DateOperators.dateOf("date").fromString()).as("newDate")
.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2120,7 +2121,7 @@ public void shouldRenderDateFromStringWithFieldReference() {
}
@Test // DATAMONGO-1834
- public void shouldRenderDateFromStringWithTimezone() {
+ void shouldRenderDateFromStringWithTimezone() {
Document agg = project()
.and(DateOperators.dateFromString("2017-02-08T12:10:40.787").withTimezone(Timezone.valueOf("America/Chicago")))
@@ -2131,7 +2132,7 @@ public void shouldRenderDateFromStringWithTimezone() {
}
@Test // DATAMONGO-2047
- public void shouldRenderDateFromStringWithFormat() {
+ void shouldRenderDateFromStringWithFormat() {
Document agg = project().and(DateOperators.dateFromString("2017-02-08T12:10:40.787").withFormat("dd/mm/yyyy"))
.as("newDate").toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -2141,7 +2142,7 @@ public void shouldRenderDateFromStringWithFormat() {
}
@Test // DATAMONGO-2200
- public void typeProjectionShouldIncludeTopLevelFieldsOfType() {
+ void typeProjectionShouldIncludeTopLevelFieldsOfType() {
ProjectionOperation operation = Aggregation.project(Book.class);
@@ -2155,7 +2156,7 @@ public void typeProjectionShouldIncludeTopLevelFieldsOfType() {
}
@Test // DATAMONGO-2200
- public void typeProjectionShouldMapFieldNames() {
+ void typeProjectionShouldMapFieldNames() {
MongoMappingContext mappingContext = new MongoMappingContext();
MongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
@@ -2171,7 +2172,7 @@ public void typeProjectionShouldMapFieldNames() {
}
@Test // DATAMONGO-2200
- public void typeProjectionShouldIncludeInterfaceProjectionValues() {
+ void typeProjectionShouldIncludeInterfaceProjectionValues() {
ProjectionOperation operation = Aggregation.project(ProjectionInterface.class);
@@ -2184,7 +2185,7 @@ public void typeProjectionShouldIncludeInterfaceProjectionValues() {
}
@Test // DATAMONGO-2200
- public void typeProjectionShouldBeEmptyIfNoPropertiesFound() {
+ void typeProjectionShouldBeEmptyIfNoPropertiesFound() {
ProjectionOperation operation = Aggregation.project(EmptyType.class);
@@ -2195,7 +2196,7 @@ public void typeProjectionShouldBeEmptyIfNoPropertiesFound() {
}
@Test // DATAMONGO-2312
- public void simpleFieldReferenceAsArray() {
+ void simpleFieldReferenceAsArray() {
org.bson.Document doc = Aggregation.newAggregation(project("x", "y", "someField").asArray("myArray"))
.toDocument("coll", Aggregation.DEFAULT_CONTEXT);
@@ -2205,7 +2206,7 @@ public void simpleFieldReferenceAsArray() {
}
@Test // DATAMONGO-2312
- public void mappedFieldReferenceAsArray() {
+ void mappedFieldReferenceAsArray() {
MongoMappingContext mappingContext = new MongoMappingContext();
@@ -2219,7 +2220,7 @@ public void mappedFieldReferenceAsArray() {
}
@Test // DATAMONGO-2312
- public void arrayWithNullValue() {
+ void arrayWithNullValue() {
Document doc = project() //
.andArrayOf(Fields.field("field-1"), null, "value").as("myArray") //
@@ -2229,7 +2230,7 @@ public void arrayWithNullValue() {
}
@Test // DATAMONGO-2312
- public void nestedArrayField() {
+ void nestedArrayField() {
Document doc = project("_id", "value") //
.andArrayOf(Fields.field("field-1"), "plain - string", ArithmeticOperators.valueOf("field-1").sum().and(10))
@@ -2241,7 +2242,7 @@ public void nestedArrayField() {
}
@Test // DATAMONGO-2312
- public void nestedMappedFieldReferenceInArrayField() {
+ void nestedMappedFieldReferenceInArrayField() {
MongoMappingContext mappingContext = new MongoMappingContext();
@@ -2289,7 +2290,7 @@ interface ProjectionInterface {
String getTitle();
}
- static class EmptyType {
+ private static class EmptyType {
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index 41b0323636..e92ea38336 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -32,6 +32,7 @@
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
+ * @author Divya Srivastava
*/
public class SpelExpressionTransformerUnitTests {
@@ -800,68 +801,68 @@ void shouldRenderRtrimWithCharsFromFieldReference() {
assertThat(transform("rtrim(field1, field2)"))
.isEqualTo("{ \"$rtrim\" : {\"input\" : \"$field1\", \"chars\" : \"$field2\" }}");
}
-
- @Test // DATAMONGO-3725
- public void shouldRenderRegexFindWithoutOptions() {
-
+
+ @Test // GH-3725
+ void shouldRenderRegexFindWithoutOptions() {
+
assertThat(transform("regexFind(field1,'e')"))
- .isEqualTo(Document.parse("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}"));
+ .isEqualTo("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}");
}
-
- @Test // DATAMONGO-3725
- public void shouldRenderRegexFindWithOptions() {
-
+
+ @Test // GH-3725
+ void shouldRenderRegexFindWithOptions() {
+
assertThat(transform("regexFind(field1,'e','i')"))
- .isEqualTo(Document.parse("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}"));
+ .isEqualTo("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}");
}
-
- @Test // DATAMONGO-3725
- public void shouldRenderRegexFindWithOptionsFromFieldReference() {
-
+
+ @Test // GH-3725
+ void shouldRenderRegexFindWithOptionsFromFieldReference() {
+
assertThat(transform("regexFind(field1,'e',field2)"))
- .isEqualTo(Document.parse("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}"));
+ .isEqualTo("{ \"$regexFind\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}");
}
-
- @Test // DATAMONGO-3725
- public void shouldRenderRegexFindAllWithoutOptions() {
-
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllWithoutOptions() {
+
assertThat(transform("regexFindAll(field1,'e')"))
- .isEqualTo(Document.parse("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}"));
+ .isEqualTo("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}");
}
-
- @Test // DATAMONGO-3725
- public void shouldRenderRegexFindAllWithOptions() {
-
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllWithOptions() {
+
assertThat(transform("regexFindAll(field1,'e','i')"))
- .isEqualTo(Document.parse("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}"));
+ .isEqualTo("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}");
}
-
- @Test // DATAMONGO-3725
- public void shouldRenderRegexFindAllWithOptionsFromFieldReference() {
-
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllWithOptionsFromFieldReference() {
+
assertThat(transform("regexFindAll(field1,'e',field2)"))
- .isEqualTo(Document.parse("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}"));
+ .isEqualTo("{ \"$regexFindAll\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}");
}
- @Test // DATAMONGO-3725
- public void shouldRenderRegexMatchWithoutOptions() {
-
+ @Test // GH-3725
+ void shouldRenderRegexMatchWithoutOptions() {
+
assertThat(transform("regexMatch(field1,'e')"))
- .isEqualTo(Document.parse("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}"));
+ .isEqualTo("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\"}}");
}
-
- @Test // DATAMONGO-3725
- public void shouldRenderRegexMatchWithOptions() {
-
+
+ @Test // GH-3725
+ void shouldRenderRegexMatchWithOptions() {
+
assertThat(transform("regexMatch(field1,'e','i')"))
- .isEqualTo(Document.parse("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}"));
+ .isEqualTo("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"i\"}}");
}
-
- @Test // DATAMONGO-3725
- public void shouldRenderRegexMatchWithOptionsFromFieldReference() {
-
+
+ @Test // GH-3725
+ void shouldRenderRegexMatchWithOptionsFromFieldReference() {
+
assertThat(transform("regexMatch(field1,'e',field2)"))
- .isEqualTo(Document.parse("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}"));
+ .isEqualTo("{ \"$regexMatch\" : {\"input\" : \"$field1\" , \"regex\" : \"e\" , \"options\" : \"$field2\"}}");
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java
index cdd0b38dbc..d8ba5129e0 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/StringOperatorsUnitTests.java
@@ -15,7 +15,9 @@
*/
package org.springframework.data.mongodb.core.aggregation;
-import static org.assertj.core.api.Assertions.*;
+import static org.springframework.data.mongodb.test.util.Assertions.*;
+
+import java.util.regex.Pattern;
import org.bson.Document;
import org.junit.jupiter.api.Test;
@@ -25,230 +27,258 @@
*
* @author Christoph Strobl
* @author Mark Paluch
+ * @author Divya Srivastava
* @currentRead Royal Assassin - Robin Hobb
*/
-public class StringOperatorsUnitTests {
+class StringOperatorsUnitTests {
- static final String EXPRESSION_STRING = "{ \"$fitz\" : \"chivalry\" }";
- static final Document EXPRESSION_DOC = Document.parse(EXPRESSION_STRING);
- static final AggregationExpression EXPRESSION = context -> EXPRESSION_DOC;
+ private static final String EXPRESSION_STRING = "{ \"$fitz\" : \"chivalry\" }";
+ private static final Document EXPRESSION_DOC = Document.parse(EXPRESSION_STRING);
+ private static final AggregationExpression EXPRESSION = context -> EXPRESSION_DOC;
@Test // DATAMONGO-2049
- public void shouldRenderTrim() {
+ void shouldRenderTrim() {
assertThat(StringOperators.valueOf("shrewd").trim().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\" } } "));
+ .isEqualTo("{ $trim: { \"input\" : \"$shrewd\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderTrimForExpression() {
+ void shouldRenderTrimForExpression() {
assertThat(StringOperators.valueOf(EXPRESSION).trim().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $trim: { \"input\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $trim: { \"input\" : " + EXPRESSION_STRING + " } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderTrimWithChars() {
+ void shouldRenderTrimWithChars() {
assertThat(StringOperators.valueOf("shrewd").trim("sh").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } "));
+ .isEqualTo("{ $trim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderTrimWithCharsExpression() {
+ void shouldRenderTrimWithCharsExpression() {
assertThat(StringOperators.valueOf("shrewd").trim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $trim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $trim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderTrimLeft() {
+ void shouldRenderTrimLeft() {
assertThat(StringOperators.valueOf("shrewd").trim().left().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\" } } "));
+ .isEqualTo("{ $ltrim: { \"input\" : \"$shrewd\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderTrimLeftWithChars() {
+ void shouldRenderTrimLeftWithChars() {
assertThat(StringOperators.valueOf("shrewd").trim("sh").left().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } "));
+ .isEqualTo("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderTrimRight() {
+ void shouldRenderTrimRight() {
assertThat(StringOperators.valueOf("shrewd").trim().right().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\" } } "));
+ .isEqualTo("{ $rtrim: { \"input\" : \"$shrewd\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderTrimRightWithChars() {
+ void shouldRenderTrimRightWithChars() {
assertThat(StringOperators.valueOf("shrewd").trim("sh").right().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } "));
+ .isEqualTo("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderLTrim() {
+ void shouldRenderLTrim() {
assertThat(StringOperators.valueOf("shrewd").ltrim().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\" } } "));
+ .isEqualTo("{ $ltrim: { \"input\" : \"$shrewd\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderLTrimForExpression() {
+ void shouldRenderLTrimForExpression() {
assertThat(StringOperators.valueOf(EXPRESSION).ltrim().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $ltrim: { \"input\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $ltrim: { \"input\" : " + EXPRESSION_STRING + " } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderLTrimWithChars() {
+ void shouldRenderLTrimWithChars() {
assertThat(StringOperators.valueOf("shrewd").ltrim("sh").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } "));
+ .isEqualTo("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderLTrimWithCharsExpression() {
+ void shouldRenderLTrimWithCharsExpression() {
assertThat(StringOperators.valueOf("shrewd").ltrim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $ltrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderRTrim() {
+ void shouldRenderRTrim() {
assertThat(StringOperators.valueOf("shrewd").rtrim().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\" } } "));
+ .isEqualTo("{ $rtrim: { \"input\" : \"$shrewd\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderRTrimForExpression() {
+ void shouldRenderRTrimForExpression() {
assertThat(StringOperators.valueOf(EXPRESSION).rtrim().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $rtrim: { \"input\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $rtrim: { \"input\" : " + EXPRESSION_STRING + " } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderRTrimWithChars() {
+ void shouldRenderRTrimWithChars() {
assertThat(StringOperators.valueOf("shrewd").rtrim("sh").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } "));
+ .isEqualTo("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : \"sh\" } } ");
}
@Test // DATAMONGO-2049
- public void shouldRenderRTrimWithCharsExpression() {
+ void shouldRenderRTrimWithCharsExpression() {
assertThat(StringOperators.valueOf("shrewd").rtrim(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $rtrim: { \"input\" : \"$shrewd\", \"chars\" : " + EXPRESSION_STRING + " } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindAll() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAll() {
assertThat(StringOperators.valueOf("shrewd").regexFindAll("e").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }"));
+ .isEqualTo("{ $regexFindAll: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindAllForExpression() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllForExpression() {
assertThat(StringOperators.valueOf(EXPRESSION).regexFindAll("e").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } "));
+ .isEqualTo("{ $regexFindAll: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindAllForRegexExpression() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllForRegexExpression() {
assertThat(StringOperators.valueOf("shrewd").regexFindAll(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $regexFindAll: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } ");
+ }
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllWithPattern() {
+
+ assertThat(StringOperators.valueOf("shrewd")
+ .regexFindAll(
+ Pattern.compile("foo", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL | Pattern.COMMENTS))
+ .toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $regexFindAll: { \"input\" : \"$shrewd\", \"regex\" : \"foo\" , \"options\" : \"imsx\" } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindAllWithOptions() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllWithOptions() {
assertThat(StringOperators.valueOf("shrewd").regexFindAll("e").options("i").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } "));
+ .isEqualTo("{ $regexFindAll: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindAllWithOptionsExpression() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindAllWithOptionsExpression() {
assertThat(StringOperators.valueOf("shrewd").regexFindAll("e").optionsOf(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFindAll: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $regexFindAll: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING
+ + " } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexMatch() {
+
+ @Test // GH-3725
+ void shouldRenderRegexMatch() {
assertThat(StringOperators.valueOf("shrewd").regexMatch("e").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }"));
+ .isEqualTo("{ $regexMatch: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexMatchForExpression() {
+
+ @Test // GH-3725
+ void shouldRenderRegexMatchForExpression() {
assertThat(StringOperators.valueOf(EXPRESSION).regexMatch("e").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } "));
+ .isEqualTo("{ $regexMatch: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexMatchForRegexExpression() {
+
+ @Test // GH-3725
+ void shouldRenderRegexMatchForRegexExpression() {
assertThat(StringOperators.valueOf("shrewd").regexMatch(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $regexMatch: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } ");
+ }
+
+ @Test // GH-3725
+ void shouldRenderRegexMatchForPattern() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexMatch(Pattern.compile("foo", Pattern.CASE_INSENSITIVE))
+ .toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $regexMatch: { \"input\" : \"$shrewd\" , \"regex\" : \"foo\", \"options\" : \"i\"} } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexMatchWithOptions() {
+
+ @Test // GH-3725
+ void shouldRenderRegexMatchWithOptions() {
assertThat(StringOperators.valueOf("shrewd").regexMatch("e").options("i").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } "));
+ .isEqualTo("{ $regexMatch: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexMatchWithOptionsExpression() {
+
+ @Test // GH-3725
+ void shouldRenderRegexMatchWithOptionsExpression() {
assertThat(StringOperators.valueOf("shrewd").regexMatch("e").optionsOf(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexMatch: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $regexMatch: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING
+ + " } } ");
}
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFind() {
+ @Test // GH-3725
+ void shouldRenderRegexFind() {
assertThat(StringOperators.valueOf("shrewd").regexFind("e").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFind: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }"));
+ .isEqualTo("{ $regexFind: { \"input\" : \"$shrewd\" , \"regex\" : \"e\" } }");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindForExpression() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindForExpression() {
assertThat(StringOperators.valueOf(EXPRESSION).regexFind("e").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFind: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } "));
+ .isEqualTo("{ $regexFind: { \"input\" : " + EXPRESSION_STRING + " , \"regex\" : \"e\" } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindForRegexExpression() {
+
+ @Test // GH-3725
+ void shouldRenderRegexFindForRegexExpression() {
assertThat(StringOperators.valueOf("shrewd").regexFind(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFind: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } "));
+ .isEqualTo("{ $regexFind: { \"input\" : \"$shrewd\" , \"regex\" : " + EXPRESSION_STRING + " } } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindWithOptions() {
- assertThat(StringOperators.valueOf("shrewd").regexFind("e").options("i").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFind: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } "));
+ @Test // GH-3725
+ void shouldRenderRegexFindForPattern() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFind(Pattern.compile("foo", Pattern.MULTILINE))
+ .toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $regexFind: { \"input\" : \"$shrewd\" , \"regex\" : \"foo\", \"options\" : \"m\"} } ");
}
-
- @Test // DATAMONGO - 3725
- public void shouldRenderRegexFindWithOptionsExpression() {
- assertThat(StringOperators.valueOf("shrewd").regexFind("e").optionsOf(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo(Document.parse("{ $regexFind: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING + " } } "));
+ @Test // GH-3725
+ void shouldRenderRegexFindWithOptions() {
+
+ assertThat(StringOperators.valueOf("shrewd").regexFind("e").options("i").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $regexFind: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : \"i\" } } ");
}
+ @Test // GH-3725
+ void shouldRenderRegexFindWithOptionsExpression() {
+ assertThat(StringOperators.valueOf("shrewd").regexFind("e").optionsOf(EXPRESSION).toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $regexFind: { \"input\" : \"$shrewd\", \"regex\" : \"e\" , \"options\" : " + EXPRESSION_STRING
+ + " } } ");
+ }
}
diff --git a/src/main/asciidoc/reference/aggregation-framework.adoc b/src/main/asciidoc/reference/aggregation-framework.adoc
index f96719adde..75ed415096 100644
--- a/src/main/asciidoc/reference/aggregation-framework.adoc
+++ b/src/main/asciidoc/reference/aggregation-framework.adoc
@@ -88,7 +88,7 @@ At the time of this writing, we provide support for the following Aggregation Op
| `abs`, `add` (+++*+++ via `plus`), `ceil`, `cos`, `cosh`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `sin`, `sinh`, `tan`, `tanh`, `trunc`
| String Aggregation Operators
-| `concat`, `substr`, `toLower`, `toUpper`, `strcasecmp`, `indexOfBytes`, `indexOfCP`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`
+| `concat`, `substr`, `toLower`, `toUpper`, `strcasecmp`, `indexOfBytes`, `indexOfCP`, `regexFind`, `regexFindAll`, `regexMatch`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`
| Comparison Aggregation Operators
| `eq` (+++*+++ via `is`), `gt`, `gte`, `lt`, `lte`, `ne`
From 62eb719b1e9adb050c33be254cbe9bf7a527415e Mon Sep 17 00:00:00 2001
From: James McNee
Date: Thu, 26 Aug 2021 21:25:36 +0100
Subject: [PATCH 044/920] Add support for `$sampleRate` criteria.
Closes #3726
Original pull request: #3765.
---
.../data/mongodb/core/query/Criteria.java | 16 ++++++++++++++++
.../mongodb/core/query/CriteriaUnitTests.java | 16 ++++++++++++++++
.../asciidoc/reference/mongo-repositories.adoc | 4 ++++
3 files changed, 36 insertions(+)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
index f9a354c38f..3ec4caf3c7 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
@@ -64,6 +64,7 @@
* @author Andreas Zink
* @author Ziemowit Stolarczyk
* @author Clément Petit
+ * @author James McNee
*/
public class Criteria implements CriteriaDefinition {
@@ -390,6 +391,21 @@ public Criteria exists(boolean value) {
return this;
}
+ /**
+ * Creates a criterion using the {@literal $sampleRate} operator.
+ *
+ * @param sampleRate sample rate to determine number of documents to be randomly selected from the input.
+ * @return this.
+ * @see MongoDB Query operator: $sampleRate
+ */
+ public Criteria sampleRate(double sampleRate) {
+ Assert.isTrue(sampleRate >= 0, "The sample rate must be greater than zero!");
+ Assert.isTrue(sampleRate <= 1, "The sample rate must not be greater than one!");
+
+ criteria.put("$sampleRate", sampleRate);
+ return this;
+ }
+
/**
* Creates a criterion using the {@literal $type} operator.
*
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java
index 9edf3c43fd..e24fc34bef 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java
@@ -156,6 +156,22 @@ public void shouldNegateFollowingSimpleExpression() {
assertThat(co).isEqualTo(Document.parse("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}"));
}
+ @Test // GH-3726
+ public void shouldBuildCorrectSampleRateOperation() {
+ Criteria c = new Criteria().sampleRate(0.4);
+ assertThat(c.getCriteriaObject()).isEqualTo(Document.parse("{ \"$sampleRate\" : 0.4 }"));
+ }
+
+ @Test // GH-3726
+ public void shouldThrowExceptionWhenSampleRateIsNegative() {
+ assertThatIllegalArgumentException().isThrownBy(() -> new Criteria().sampleRate(-1));
+ }
+
+ @Test // GH-3726
+ public void shouldThrowExceptionWhenSampleRateIsGreatedThanOne() {
+ assertThatIllegalArgumentException().isThrownBy(() -> new Criteria().sampleRate(1.01));
+ }
+
@Test // DATAMONGO-1068
public void getCriteriaObjectShouldReturnEmptyDocumentWhenNoCriteriaSpecified() {
diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc
index 328a547b5a..b847174c67 100644
--- a/src/main/asciidoc/reference/mongo-repositories.adoc
+++ b/src/main/asciidoc/reference/mongo-repositories.adoc
@@ -281,6 +281,10 @@ lower / upper bounds (`$gt` / `$gte` & `$lt` / `$lte`) according to `Range`
| `Exists`
| `findByLocationExists(boolean exists)`
| `{"location" : {"$exists" : exists }}`
+
+| `SampleRate`
+| `sampleRate(double sampleRate)`
+| `{"$sampleRate" : sampleRate }`
|===
NOTE: If the property criterion compares a document, the order of the fields and exact equality in the document matters.
From f662d7ca0d240a9f719ffec78243fc7661c544a1 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Fri, 27 Aug 2021 09:34:40 +0200
Subject: [PATCH 045/920] Polishing.
Tweak Javadoc. Add since tag, reformat code. Simplify tests. Move documentation bits into the right place.
See #3726.
Original pull request: #3765.
---
.../data/mongodb/core/query/Criteria.java | 8 ++++--
.../mongodb/core/query/CriteriaUnitTests.java | 27 ++++++++++---------
.../reference/mongo-repositories.adoc | 4 ---
src/main/asciidoc/reference/mongodb.adoc | 1 +
4 files changed, 21 insertions(+), 19 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
index 3ec4caf3c7..df167330a1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java
@@ -394,11 +394,15 @@ public Criteria exists(boolean value) {
/**
* Creates a criterion using the {@literal $sampleRate} operator.
*
- * @param sampleRate sample rate to determine number of documents to be randomly selected from the input.
+ * @param sampleRate sample rate to determine number of documents to be randomly selected from the input. Must be
+ * between {@code 0} and {@code 1}.
* @return this.
- * @see MongoDB Query operator: $sampleRate
+ * @see MongoDB Query operator:
+ * $sampleRate
+ * @since 3.3
*/
public Criteria sampleRate(double sampleRate) {
+
Assert.isTrue(sampleRate >= 0, "The sample rate must be greater than zero!");
Assert.isTrue(sampleRate <= 1, "The sample rate must not be greater than one!");
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java
index e24fc34bef..96253e4ac0 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaUnitTests.java
@@ -40,19 +40,20 @@
* @author Ziemowit Stolarczyk
* @author Clément Petit
* @author Mark Paluch
+ * @author James McNee
*/
public class CriteriaUnitTests {
@Test
public void testSimpleCriteria() {
Criteria c = new Criteria("name").is("Bubba");
- assertThat(c.getCriteriaObject()).isEqualTo(Document.parse("{ \"name\" : \"Bubba\"}"));
+ assertThat(c.getCriteriaObject()).isEqualTo("{ \"name\" : \"Bubba\"}");
}
@Test
public void testNotEqualCriteria() {
Criteria c = new Criteria("name").ne("Bubba");
- assertThat(c.getCriteriaObject()).isEqualTo(Document.parse("{ \"name\" : { \"$ne\" : \"Bubba\"}}"));
+ assertThat(c.getCriteriaObject()).isEqualTo("{ \"name\" : { \"$ne\" : \"Bubba\"}}");
}
@Test
@@ -67,7 +68,7 @@ public void buildsIsNullCriteriaCorrectly() {
@Test
public void testChainedCriteria() {
Criteria c = new Criteria("name").is("Bubba").and("age").lt(21);
- assertThat(c.getCriteriaObject()).isEqualTo(Document.parse("{ \"name\" : \"Bubba\" , \"age\" : { \"$lt\" : 21}}"));
+ assertThat(c.getCriteriaObject()).isEqualTo("{ \"name\" : \"Bubba\" , \"age\" : { \"$lt\" : 21}}");
}
@Test(expected = InvalidMongoDbApiUsageException.class)
@@ -153,13 +154,13 @@ public void shouldNegateFollowingSimpleExpression() {
Document co = c.getCriteriaObject();
assertThat(co).isNotNull();
- assertThat(co).isEqualTo(Document.parse("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}"));
+ assertThat(co).isEqualTo("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}");
}
@Test // GH-3726
public void shouldBuildCorrectSampleRateOperation() {
Criteria c = new Criteria().sampleRate(0.4);
- assertThat(c.getCriteriaObject()).isEqualTo(Document.parse("{ \"$sampleRate\" : 0.4 }"));
+ assertThat(c.getCriteriaObject()).isEqualTo("{ \"$sampleRate\" : 0.4 }");
}
@Test // GH-3726
@@ -302,7 +303,7 @@ public void shouldAppendBitsAllClearWithIntBitmaskCorrectly() {
Criteria numericBitmaskCriteria = new Criteria("field").bits().allClear(0b101);
assertThat(numericBitmaskCriteria.getCriteriaObject())
- .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAllClear\" : 5} }"));
+ .isEqualTo("{ \"field\" : { \"$bitsAllClear\" : 5} }");
}
@Test // DATAMONGO-1808
@@ -311,7 +312,7 @@ public void shouldAppendBitsAllClearWithPositionListCorrectly() {
Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().allClear(Arrays.asList(0, 2));
assertThat(bitPositionsBitmaskCriteria.getCriteriaObject())
- .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAllClear\" : [ 0, 2 ]} }"));
+ .isEqualTo("{ \"field\" : { \"$bitsAllClear\" : [ 0, 2 ]} }");
}
@Test // DATAMONGO-1808
@@ -320,7 +321,7 @@ public void shouldAppendBitsAllSetWithIntBitmaskCorrectly() {
Criteria numericBitmaskCriteria = new Criteria("field").bits().allSet(0b101);
assertThat(numericBitmaskCriteria.getCriteriaObject())
- .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAllSet\" : 5} }"));
+ .isEqualTo("{ \"field\" : { \"$bitsAllSet\" : 5} }");
}
@Test // DATAMONGO-1808
@@ -329,7 +330,7 @@ public void shouldAppendBitsAllSetWithPositionListCorrectly() {
Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().allSet(Arrays.asList(0, 2));
assertThat(bitPositionsBitmaskCriteria.getCriteriaObject())
- .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAllSet\" : [ 0, 2 ]} }"));
+ .isEqualTo("{ \"field\" : { \"$bitsAllSet\" : [ 0, 2 ]} }");
}
@Test // DATAMONGO-1808
@@ -338,7 +339,7 @@ public void shouldAppendBitsAnyClearWithIntBitmaskCorrectly() {
Criteria numericBitmaskCriteria = new Criteria("field").bits().anyClear(0b101);
assertThat(numericBitmaskCriteria.getCriteriaObject())
- .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAnyClear\" : 5} }"));
+ .isEqualTo("{ \"field\" : { \"$bitsAnyClear\" : 5} }");
}
@Test // DATAMONGO-1808
@@ -347,7 +348,7 @@ public void shouldAppendBitsAnyClearWithPositionListCorrectly() {
Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().anyClear(Arrays.asList(0, 2));
assertThat(bitPositionsBitmaskCriteria.getCriteriaObject())
- .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAnyClear\" : [ 0, 2 ]} }"));
+ .isEqualTo("{ \"field\" : { \"$bitsAnyClear\" : [ 0, 2 ]} }");
}
@Test // DATAMONGO-1808
@@ -356,7 +357,7 @@ public void shouldAppendBitsAnySetWithIntBitmaskCorrectly() {
Criteria numericBitmaskCriteria = new Criteria("field").bits().anySet(0b101);
assertThat(numericBitmaskCriteria.getCriteriaObject())
- .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAnySet\" : 5} }"));
+ .isEqualTo("{ \"field\" : { \"$bitsAnySet\" : 5} }");
}
@Test // DATAMONGO-1808
@@ -365,7 +366,7 @@ public void shouldAppendBitsAnySetWithPositionListCorrectly() {
Criteria bitPositionsBitmaskCriteria = new Criteria("field").bits().anySet(Arrays.asList(0, 2));
assertThat(bitPositionsBitmaskCriteria.getCriteriaObject())
- .isEqualTo(Document.parse("{ \"field\" : { \"$bitsAnySet\" : [ 0, 2 ]} }"));
+ .isEqualTo("{ \"field\" : { \"$bitsAnySet\" : [ 0, 2 ]} }");
}
@Test // DATAMONGO-2002
diff --git a/src/main/asciidoc/reference/mongo-repositories.adoc b/src/main/asciidoc/reference/mongo-repositories.adoc
index b847174c67..328a547b5a 100644
--- a/src/main/asciidoc/reference/mongo-repositories.adoc
+++ b/src/main/asciidoc/reference/mongo-repositories.adoc
@@ -281,10 +281,6 @@ lower / upper bounds (`$gt` / `$gte` & `$lt` / `$lte`) according to `Range`
| `Exists`
| `findByLocationExists(boolean exists)`
| `{"location" : {"$exists" : exists }}`
-
-| `SampleRate`
-| `sampleRate(double sampleRate)`
-| `{"$sampleRate" : sampleRate }`
|===
NOTE: If the property criterion compares a document, the order of the fields and exact equality in the document matters.
diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc
index f214edba4c..7bf034f461 100644
--- a/src/main/asciidoc/reference/mongodb.adoc
+++ b/src/main/asciidoc/reference/mongodb.adoc
@@ -1219,6 +1219,7 @@ The `Criteria` class provides the following methods, all of which correspond to
* `Criteria` *orOperator* `(Criteria... criteria)` Creates an or query using the `$or` operator for all of the provided criteria
* `Criteria` *orOperator* `(Collection criteria)` Creates an or query using the `$or` operator for all of the provided criteria
* `Criteria` *regex* `(String re)` Creates a criterion using a `$regex`
+* `Criteria` *sampleRate* `(double sampleRate)` Creates a criterion using the `$sampleRate` operator
* `Criteria` *size* `(int s)` Creates a criterion using the `$size` operator
* `Criteria` *type* `(int t)` Creates a criterion using the `$type` operator
* `Criteria` *matchingDocumentStructure* `(MongoJsonSchema schema)` Creates a criterion using the `$jsonSchema` operator for <>. `$jsonSchema` can only be applied on the top level of a query and not property specific. Use the `properties` attribute of the schema to match against nested fields.
From bf86f39b2d5bc2d3c197fbff90551b85b440474a Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Tue, 24 Aug 2021 07:31:25 +0200
Subject: [PATCH 046/920] Fix id field target type conversion for document
references.
This commit fixes an issue where a defined custom target type conversion for the id field was not properly considered when writing a document reference. Previously an eg. String was not being converted into an ObjectId correctly causing lookup queries to return empty results.
Converting the id property value on write solves the issue.
Includes a minor polish in the mapping centralizing pointer creation within the DocumentPointerFactory.
Closes: #3782
Original pull request: #3785.
---
.../core/convert/DocumentPointerFactory.java | 11 ++-
.../core/convert/MappingMongoConverter.java | 23 ++---
.../MongoTemplateDocumentReferenceTests.java | 98 ++++++++++++++++++-
3 files changed, 114 insertions(+), 18 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java
index 09d69e4b27..b30aa957de 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentPointerFactory.java
@@ -83,7 +83,16 @@ DocumentPointer> computePointer(
.getRequiredPersistentEntity(property.getAssociationTargetType());
if (usesDefaultLookup(property)) {
- return () -> persistentEntity.getIdentifierAccessor(value).getIdentifier();
+
+ MongoPersistentProperty idProperty = persistentEntity.getIdProperty();
+ Object idValue = persistentEntity.getIdentifierAccessor(value).getIdentifier();
+
+ if (idProperty.hasExplicitWriteTarget()
+ && conversionService.canConvert(idValue.getClass(), idProperty.getFieldType())) {
+ return () -> conversionService.convert(idValue, idProperty.getFieldType());
+ }
+
+ return () -> idValue;
}
MongoPersistentEntity> valueEntity = mappingContext.getPersistentEntity(value.getClass());
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index 48505559c0..a60c853c33 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -869,15 +869,12 @@ protected List createCollection(Collection> collection, MongoPersisten
if (!property.isDbReference()) {
if (property.isAssociation()) {
- return writeCollectionInternal(collection.stream().map(it -> {
- if (conversionService.canConvert(it.getClass(), DocumentPointer.class)) {
- return conversionService.convert(it, DocumentPointer.class).getPointer();
- } else {
- // just take the id as a reference
- return mappingContext.getPersistentEntity(property.getAssociationTargetType()).getIdentifierAccessor(it)
- .getIdentifier();
- }
- }).collect(Collectors.toList()), ClassTypeInformation.from(DocumentPointer.class), new ArrayList<>());
+
+ List targetCollection = collection.stream().map(it -> {
+ return documentPointerFactory.computePointer(mappingContext, property, it, property.getActualType()).getPointer();
+ }).collect(Collectors.toList());
+
+ return writeCollectionInternal(targetCollection, ClassTypeInformation.from(DocumentPointer.class), new ArrayList<>());
}
if (property.hasExplicitWriteTarget()) {
@@ -930,13 +927,7 @@ protected Bson createMap(Map map, MongoPersistentProperty proper
if (property.isDbReference()) {
document.put(simpleKey, value != null ? createDBRef(value, property) : null);
} else {
- if (conversionService.canConvert(value.getClass(), DocumentPointer.class)) {
- document.put(simpleKey, conversionService.convert(value, DocumentPointer.class).getPointer());
- } else {
- // just take the id as a reference
- document.put(simpleKey, mappingContext.getPersistentEntity(property.getAssociationTargetType())
- .getIdentifierAccessor(value).getIdentifier());
- }
+ document.put(simpleKey, documentPointerFactory.computePointer(mappingContext, property, value, property.getActualType()).getPointer());
}
} else {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
index fa1deb4f1c..d6bcc10e49 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
@@ -32,6 +32,7 @@
import java.util.Map;
import org.bson.Document;
+import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -44,6 +45,8 @@
import org.springframework.data.mongodb.core.mapping.DocumentPointer;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
import org.springframework.data.mongodb.core.mapping.Field;
+import org.springframework.data.mongodb.core.mapping.FieldType;
+import org.springframework.data.mongodb.core.mapping.MongoId;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.test.util.Client;
import org.springframework.data.mongodb.test.util.MongoClientExtension;
@@ -106,6 +109,26 @@ void writeSimpleTypeReference() {
assertThat(target.get("simpleValueRef")).isEqualTo("ref-1");
}
+ @Test // GH-3782
+ void writeTypeReferenceHavingCustomizedIdTargetType() {
+
+ ObjectId expectedIdValue = new ObjectId();
+ String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
+
+ SingleRefRoot source = new SingleRefRoot();
+ source.id = "root-1";
+ source.customIdTargetRef = new ObjectRefHavingCustomizedIdTargetType(expectedIdValue.toString(),
+ "me-the-referenced-object");
+
+ template.save(source);
+
+ Document target = template.execute(db -> {
+ return db.getCollection(rootCollectionName).find(Filters.eq("_id", "root-1")).first();
+ });
+
+ assertThat(target.get("customIdTargetRef")).isEqualTo(expectedIdValue);
+ }
+
@Test // GH-3602
void writeMapTypeReference() {
@@ -126,6 +149,26 @@ void writeMapTypeReference() {
assertThat(target.get("mapValueRef", Map.class)).containsEntry("frodo", "ref-1").containsEntry("bilbo", "ref-2");
}
+ @Test // GH-3782
+ void writeMapOfTypeReferenceHavingCustomizedIdTargetType() {
+
+ ObjectId expectedIdValue = new ObjectId();
+ String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
+
+ CollectionRefRoot source = new CollectionRefRoot();
+ source.id = "root-1";
+ source.customIdTargetRefMap = Collections.singletonMap("frodo",
+ new ObjectRefHavingCustomizedIdTargetType(expectedIdValue.toString(), "me-the-referenced-object"));
+
+ template.save(source);
+
+ Document target = template.execute(db -> {
+ return db.getCollection(rootCollectionName).find(Filters.eq("_id", "root-1")).first();
+ });
+
+ assertThat(target.get("customIdTargetRefMap", Map.class)).containsEntry("frodo", expectedIdValue);
+ }
+
@Test // GH-3602
void writeCollectionOfSimpleTypeReference() {
@@ -145,6 +188,26 @@ void writeCollectionOfSimpleTypeReference() {
assertThat(target.get("simpleValueRef", List.class)).containsExactly("ref-1", "ref-2");
}
+ @Test // GH-3782
+ void writeListOfTypeReferenceHavingCustomizedIdTargetType() {
+
+ ObjectId expectedIdValue = new ObjectId();
+ String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
+
+ CollectionRefRoot source = new CollectionRefRoot();
+ source.id = "root-1";
+ source.customIdTargetRefList = Collections.singletonList(
+ new ObjectRefHavingCustomizedIdTargetType(expectedIdValue.toString(), "me-the-referenced-object"));
+
+ template.save(source);
+
+ Document target = template.execute(db -> {
+ return db.getCollection(rootCollectionName).find(Filters.eq("_id", "root-1")).first();
+ });
+
+ assertThat(target.get("customIdTargetRefList", List.class)).containsExactly(expectedIdValue);
+ }
+
@Test // GH-3602
void writeObjectTypeReference() {
@@ -739,6 +802,26 @@ void updateReferenceWithValue() {
assertThat(target).containsEntry("toB", "b");
}
+ @Test // GH-3782
+ void updateReferenceHavingCustomizedIdTargetType() {
+
+ ObjectId expectedIdValue = new ObjectId();
+ String rootCollectionName = template.getCollectionName(SingleRefRoot.class);
+
+ SingleRefRoot root = new SingleRefRoot();
+ root.id = "root-1";
+ template.save(root);
+
+ template.update(SingleRefRoot.class).apply(new Update().set("customIdTargetRef",
+ new ObjectRefHavingCustomizedIdTargetType(expectedIdValue.toString(), "b"))).first();
+
+ Document target = template.execute(db -> {
+ return db.getCollection(rootCollectionName).find(Filters.eq("_id", "root-1")).first();
+ });
+
+ assertThat(target).containsEntry("customIdTargetRef", expectedIdValue);
+ }
+
@Test // GH-3602
void updateReferenceCollectionWithEntity() {
@@ -998,6 +1081,8 @@ static class SingleRefRoot {
@DocumentReference(lookup = "{ 'refKey1' : '?#{refKey1}', 'refKey2' : '?#{refKey2}' }", lazy = true) //
ObjectRefOnNonIdField lazyObjectValueRefOnNonIdFields;
+
+ @DocumentReference ObjectRefHavingCustomizedIdTargetType customIdTargetRef;
}
@Data
@@ -1027,6 +1112,10 @@ static class CollectionRefRoot {
@DocumentReference(lookup = "{ 'refKey1' : '?#{refKey1}', 'refKey2' : '?#{refKey2}' }") //
List objectValueRefOnNonIdFields;
+
+ @DocumentReference List customIdTargetRefList;
+
+ @DocumentReference Map customIdTargetRefMap;
}
@FunctionalInterface
@@ -1094,6 +1183,14 @@ public Object toReference() {
}
}
+ @Data
+ @AllArgsConstructor
+ static class ObjectRefHavingCustomizedIdTargetType {
+
+ @MongoId(targetType = FieldType.OBJECT_ID) String id;
+ String name;
+ }
+
static class ReferencableConverter implements Converter> {
@Nullable
@@ -1196,5 +1293,4 @@ static class UsingAtReference {
@Reference //
Publisher publisher;
}
-
}
From f24e8e5361bf02484e20dc7799bc2a13b808873e Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 1 Sep 2021 10:39:36 +0200
Subject: [PATCH 047/920] Avoid nested Document conversion to primitive types
for fields with an explicit write target.
We now no longer attempt to convert query Documents into primitive types to avoid e.g. Document to String conversion.
Closes: #3783
Original Pull Request: #3797
---
.../mongodb/core/convert/QueryMapper.java | 3 +-
.../core/convert/QueryMapperUnitTests.java | 31 +++++++++++++++++--
2 files changed, 31 insertions(+), 3 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
index 7a14f07c4c..e7deb38231 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
@@ -778,7 +778,8 @@ protected boolean isKeyword(String candidate) {
@Nullable
private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) {
- if (value == null || documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget()) {
+ if (value == null || documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget()
+ || value instanceof Document || value instanceof DBObject) {
return value;
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
index efd354b866..808263697a 100755
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
@@ -33,8 +33,7 @@
import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.junit.jupiter.MockitoExtension;
+
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
@@ -83,9 +82,12 @@ public class QueryMapperUnitTests {
@BeforeEach
void beforeEach() {
+ MongoCustomConversions conversions = new MongoCustomConversions();
this.context = new MongoMappingContext();
+ this.context.setSimpleTypeHolder(conversions.getSimpleTypeHolder());
this.converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, context);
+ this.converter.setCustomConversions(conversions);
this.converter.afterPropertiesSet();
this.mapper = new QueryMapper(converter);
@@ -1335,6 +1337,25 @@ void mapStringIdFieldProjection() {
assertThat(mappedFields).containsEntry("_id", 1);
}
+ @Test // GH-3783
+ void retainsId$InWithStringArray() {
+
+ org.bson.Document mappedQuery = mapper.getMappedObject(
+ org.bson.Document.parse("{ _id : { $in: [\"5b8bedceb1e0bfc07b008828\"]}}"),
+ context.getPersistentEntity(WithExplicitStringId.class));
+ assertThat(mappedQuery.get("_id")).isEqualTo(org.bson.Document.parse("{ $in: [\"5b8bedceb1e0bfc07b008828\"]}"));
+ }
+
+ @Test // GH-3783
+ void mapsId$InInToObjectIds() {
+
+ org.bson.Document mappedQuery = mapper.getMappedObject(
+ org.bson.Document.parse("{ _id : { $in: [\"5b8bedceb1e0bfc07b008828\"]}}"),
+ context.getPersistentEntity(ClassWithDefaultId.class));
+ assertThat(mappedQuery.get("_id"))
+ .isEqualTo(org.bson.Document.parse("{ $in: [ {$oid: \"5b8bedceb1e0bfc07b008828\" } ]}"));
+ }
+
class WithDeepArrayNesting {
List level0;
@@ -1404,6 +1425,12 @@ class WithStringId {
String name;
}
+ class WithExplicitStringId {
+
+ @MongoId(FieldType.STRING) String id;
+ String name;
+ }
+
class BigIntegerId {
@Id private BigInteger id;
From e71ec874ab69ecc3cfd199be5ce9cda76686913e Mon Sep 17 00:00:00 2001
From: divyajnu08
Date: Sun, 29 Aug 2021 16:41:52 +0530
Subject: [PATCH 048/920] Add support for `$expr` operator.
Also, allow construction of $match with an AggregationExpression.
Closes #3790
---
.../core/aggregation/AddFieldsOperation.java | 1 +
.../mongodb/core/aggregation/Aggregation.java | 12 +-
.../core/aggregation/EvaluationOperators.java | 109 ++++++++++++++++++
.../core/aggregation/MatchOperation.java | 39 ++++++-
.../aggregation/ReplaceRootOperation.java | 1 +
.../core/aggregation/SetOperation.java | 1 +
.../aggregation/MatchOperationUnitTests.java | 26 +++++
.../aggregation/SetOperationUnitTests.java | 1 +
8 files changed, 187 insertions(+), 3 deletions(-)
create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MatchOperationUnitTests.java
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java
index 3f3dd125d1..90cc828591 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AddFieldsOperation.java
@@ -201,4 +201,5 @@ public interface ValueAppender {
AddFieldsOperationBuilder withValueOfExpression(String operation, Object... values);
}
}
+
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
index cecc8f2554..55964bab93 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
@@ -498,7 +498,17 @@ public static MatchOperation match(Criteria criteria) {
public static MatchOperation match(CriteriaDefinition criteria) {
return new MatchOperation(criteria);
}
-
+
+ /**
+ * Creates a new {@link MatchOperation}
+ *
+ * @return new instance of {@link MatchOperation}.
+ * @since 1.10
+ */
+ public static MatchOperation match() {
+ return new MatchOperation();
+ }
+
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
* {@code distanceField} defines output field that contains the calculated distance.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
new file mode 100644
index 0000000000..0fb8e25fab
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
@@ -0,0 +1,109 @@
+package org.springframework.data.mongodb.core.aggregation;
+
+import org.springframework.util.Assert;
+
+public class EvaluationOperators {
+
+ /**
+ * Take the value resulting from the given fieldReference.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link EvaluationOperatorFactory}.
+ */
+ public static EvaluationOperatorFactory valueOf(String fieldReference) {
+ return new EvaluationOperatorFactory(fieldReference);
+ }
+
+ /**
+ * Take the value resulting from the given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link EvaluationOperatorFactory}.
+ */
+ public static EvaluationOperatorFactory valueOf(AggregationExpression expression) {
+ return new EvaluationOperatorFactory(expression);
+ }
+
+ public static class EvaluationOperatorFactory {
+
+ private final String fieldReference;
+ private final AggregationExpression expression;
+
+ /**
+ * Creates new {@link EvaluationOperatorFactory} for given {@literal fieldReference}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ */
+ public EvaluationOperatorFactory(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ this.fieldReference = fieldReference;
+ this.expression = null;
+ }
+
+
+ /**
+ * Creates new {@link EvaluationOperatorFactory} for given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ */
+ public EvaluationOperatorFactory(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ this.fieldReference = null;
+ this.expression = expression;
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that is a valid aggregation expression.
+ *
+ * @return new instance of {@link Expr}.
+ */
+ public Expr expr() {
+ return usesFieldRef() ? Expr.valueOf(fieldReference) : Expr.valueOf(expression);
+ }
+
+
+ public static class Expr extends AbstractAggregationExpression {
+
+ private Expr(Object value) {
+ super(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$expr";
+ }
+
+ /**
+ * Creates new {@link Expr}.
+ *
+ * @param fieldReference must not be {@literal null}.
+ * @return new instance of {@link Expr}.
+ */
+ public static Expr valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new Expr(Fields.field(fieldReference));
+ }
+
+ /**
+ * Creates new {@link Expr}.
+ *
+ * @param expression must not be {@literal null}.
+ * @return new instance of {@link Expr}.
+ */
+ public static Expr valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new Expr(expression);
+ }
+
+ }
+
+ private boolean usesFieldRef() {
+ return fieldReference != null;
+ }
+ }
+
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
index c9d83ae6c8..c2796aaa03 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
@@ -16,6 +16,7 @@
package org.springframework.data.mongodb.core.aggregation;
import org.bson.Document;
+import org.springframework.data.mongodb.core.aggregation.EvaluationOperators.EvaluationOperatorFactory.Expr;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.util.Assert;
@@ -36,7 +37,16 @@
public class MatchOperation implements AggregationOperation {
private final CriteriaDefinition criteriaDefinition;
-
+ private final AggregationExpression expression;
+
+ /**
+ * Creates a new {@link MatchOperation}
+ */
+ public MatchOperation() {
+ this.criteriaDefinition = null;
+ this.expression = null;
+ }
+
/**
* Creates a new {@link MatchOperation} for the given {@link CriteriaDefinition}.
*
@@ -46,14 +56,39 @@ public MatchOperation(CriteriaDefinition criteriaDefinition) {
Assert.notNull(criteriaDefinition, "Criteria must not be null!");
this.criteriaDefinition = criteriaDefinition;
+ this.expression = null;
}
-
+
+ /**
+ * Creates a new {@link MatchOperation} for the given {@link Expression}.
+ *
+ * @param criteriaDefinition must not be {@literal null}.
+ */
+ private MatchOperation(Expr expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ this.criteriaDefinition = null;
+ this.expression = expression;
+ }
+
+ /**
+ * Creates a new {@link MatchOperation} for the given {@link AggregationExpression}.
+ *
+ * @param expression must not be {@literal null}.
+ */
+ public MatchOperation withValueOf(AggregationExpression expression) {
+ Assert.notNull(expression, "Expression must not be null!");
+ return new MatchOperation(EvaluationOperators.valueOf(expression).expr());
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
+ if(expression != null) {
+ return new Document(getOperator(), expression.toDocument());
+ }
return new Document(getOperator(), context.getMappedObject(criteriaDefinition.getCriteriaObject()));
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java
index c452ffb8ea..94f9785595 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperation.java
@@ -21,6 +21,7 @@
import java.util.List;
import org.bson.Document;
+
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.expression.spel.ast.Projection;
import org.springframework.util.Assert;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java
index 731668ed3c..d065f81662 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SetOperation.java
@@ -193,5 +193,6 @@ public interface ValueAppender {
*/
SetOperation withValueOfExpression(String operation, Object... values);
}
+
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MatchOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MatchOperationUnitTests.java
new file mode 100644
index 0000000000..04d3824de1
--- /dev/null
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MatchOperationUnitTests.java
@@ -0,0 +1,26 @@
+package org.springframework.data.mongodb.core.aggregation;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.bson.Document;
+import org.junit.jupiter.api.Test;
+
+class MatchOperationUnitTests {
+
+ @Test // DATAMONGO - 3729
+ public void shouldRenderStdDevPopCorrectly() {
+ MatchOperation operation = Aggregation.match().withValueOf(ArithmeticOperators.valueOf("quiz").stdDevPop());
+ assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).
+ isEqualTo(Document.parse("{ $match: { \"$expr\" : { \"$stdDevPop\" : \"$quiz\" } } } "));
+
+ }
+
+ @Test // DATAMONGO - 3729
+ public void shouldRenderStdDevSampCorrectly() {
+ MatchOperation operation = Aggregation.match().withValueOf(ArithmeticOperators.valueOf("quiz").stdDevSamp());
+ assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).
+ isEqualTo(Document.parse("{ $match: { \"$expr\" : { \"$stdDevSamp\" : \"$quiz\" } } } "));
+
+ }
+
+}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java
index b90b049da1..8fd8bd5526 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SetOperationUnitTests.java
@@ -21,6 +21,7 @@
import org.bson.Document;
import org.junit.jupiter.api.Test;
+
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.convert.QueryMapper;
From 34d66a276ac35a24f076d565543803e4392c5880 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Mon, 6 Sep 2021 15:07:02 +0200
Subject: [PATCH 049/920] Polishing.
Add license headers. Update Javadoc, author, and since tags. Add tests. Add toCriteriaDefinition method.
See #3790
---
.../mongodb/core/aggregation/Aggregation.java | 13 ++--
.../core/aggregation/EvaluationOperators.java | 66 ++++++++++++++++---
.../core/aggregation/MatchOperation.java | 44 +++++--------
.../EvaluationOperatorsUnitTests.java | 35 ++++++++++
.../aggregation/MatchOperationUnitTests.java | 29 ++++----
.../ReplaceRootOperationUnitTests.java | 16 ++---
.../ReplaceWithOperationUnitTests.java | 8 +--
.../core/convert/QueryMapperUnitTests.java | 18 +++++
8 files changed, 156 insertions(+), 73 deletions(-)
create mode 100644 spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperatorsUnitTests.java
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
index 55964bab93..614489692c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java
@@ -498,17 +498,18 @@ public static MatchOperation match(Criteria criteria) {
public static MatchOperation match(CriteriaDefinition criteria) {
return new MatchOperation(criteria);
}
-
+
/**
- * Creates a new {@link MatchOperation}
+ * Creates a new {@link MatchOperation} using the given {@link AggregationExpression}.
*
+ * @param expression must not be {@literal null}.
* @return new instance of {@link MatchOperation}.
- * @since 1.10
+ * @since 3.3
*/
- public static MatchOperation match() {
- return new MatchOperation();
+ public static MatchOperation match(AggregationExpression expression) {
+ return new MatchOperation(expression);
}
-
+
/**
* Creates a new {@link GeoNearOperation} instance from the given {@link NearQuery} and the {@code distanceField}. The
* {@code distanceField} defines output field that contains the calculated distance.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
index 0fb8e25fab..181bab5ef5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperators.java
@@ -1,9 +1,33 @@
+/*
+ * 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 org.bson.Document;
+
+import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.util.Assert;
+/**
+ * Gateway to {@literal evaluation operators} such as {@literal $expr}.
+ *
+ * @author Divya Srivastava
+ * @since 3.3
+ */
public class EvaluationOperators {
-
+
/**
* Take the value resulting from the given fieldReference.
*
@@ -13,7 +37,7 @@ public class EvaluationOperators {
public static EvaluationOperatorFactory valueOf(String fieldReference) {
return new EvaluationOperatorFactory(fieldReference);
}
-
+
/**
* Take the value resulting from the given {@link AggregationExpression}.
*
@@ -23,12 +47,12 @@ public static EvaluationOperatorFactory valueOf(String fieldReference) {
public static EvaluationOperatorFactory valueOf(AggregationExpression expression) {
return new EvaluationOperatorFactory(expression);
}
-
+
public static class EvaluationOperatorFactory {
-
+
private final String fieldReference;
private final AggregationExpression expression;
-
+
/**
* Creates new {@link EvaluationOperatorFactory} for given {@literal fieldReference}.
*
@@ -41,7 +65,6 @@ public EvaluationOperatorFactory(String fieldReference) {
this.expression = null;
}
-
/**
* Creates new {@link EvaluationOperatorFactory} for given {@link AggregationExpression}.
*
@@ -53,7 +76,7 @@ public EvaluationOperatorFactory(AggregationExpression expression) {
this.fieldReference = null;
this.expression = expression;
}
-
+
/**
* Creates new {@link AggregationExpression} that is a valid aggregation expression.
*
@@ -62,8 +85,10 @@ public EvaluationOperatorFactory(AggregationExpression expression) {
public Expr expr() {
return usesFieldRef() ? Expr.valueOf(fieldReference) : Expr.valueOf(expression);
}
-
-
+
+ /**
+ * Allows the use of aggregation expressions within the query language.
+ */
public static class Expr extends AbstractAggregationExpression {
private Expr(Object value) {
@@ -99,8 +124,29 @@ public static Expr valueOf(AggregationExpression expression) {
return new Expr(expression);
}
+ /**
+ * Creates {@code $expr} as {@link CriteriaDefinition}.
+ *
+ * @return the {@link CriteriaDefinition} from this expression.
+ */
+ public CriteriaDefinition toCriteriaDefinition(AggregationOperationContext context) {
+
+ Document criteriaObject = toDocument(context);
+
+ return new CriteriaDefinition() {
+ @Override
+ public Document getCriteriaObject() {
+ return criteriaObject;
+ }
+
+ @Override
+ public String getKey() {
+ return getMongoMethod();
+ }
+ };
+ }
}
-
+
private boolean usesFieldRef() {
return fieldReference != null;
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
index c2796aaa03..c3d1f366ec 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/MatchOperation.java
@@ -16,7 +16,7 @@
package org.springframework.data.mongodb.core.aggregation;
import org.bson.Document;
-import org.springframework.data.mongodb.core.aggregation.EvaluationOperators.EvaluationOperatorFactory.Expr;
+
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.util.Assert;
@@ -30,6 +30,7 @@
* @author Sebastian Herold
* @author Thomas Darimont
* @author Oliver Gierke
+ * @author Divya Srivastava
* @since 1.3
* @see MongoDB Aggregation Framework:
* $match
@@ -38,15 +39,7 @@ public class MatchOperation implements AggregationOperation {
private final CriteriaDefinition criteriaDefinition;
private final AggregationExpression expression;
-
- /**
- * Creates a new {@link MatchOperation}
- */
- public MatchOperation() {
- this.criteriaDefinition = null;
- this.expression = null;
- }
-
+
/**
* Creates a new {@link MatchOperation} for the given {@link CriteriaDefinition}.
*
@@ -55,41 +48,34 @@ public MatchOperation() {
public MatchOperation(CriteriaDefinition criteriaDefinition) {
Assert.notNull(criteriaDefinition, "Criteria must not be null!");
+
this.criteriaDefinition = criteriaDefinition;
this.expression = null;
}
-
- /**
- * Creates a new {@link MatchOperation} for the given {@link Expression}.
- *
- * @param criteriaDefinition must not be {@literal null}.
- */
- private MatchOperation(Expr expression) {
- Assert.notNull(expression, "Expression must not be null!");
- this.criteriaDefinition = null;
- this.expression = expression;
- }
-
+
/**
* Creates a new {@link MatchOperation} for the given {@link AggregationExpression}.
*
* @param expression must not be {@literal null}.
+ * @since 3.3
*/
- public MatchOperation withValueOf(AggregationExpression expression) {
+ public MatchOperation(AggregationExpression expression) {
+
Assert.notNull(expression, "Expression must not be null!");
- return new MatchOperation(EvaluationOperators.valueOf(expression).expr());
+
+ this.criteriaDefinition = null;
+ this.expression = expression;
}
-
+
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public Document toDocument(AggregationOperationContext context) {
- if(expression != null) {
- return new Document(getOperator(), expression.toDocument());
- }
- return new Document(getOperator(), context.getMappedObject(criteriaDefinition.getCriteriaObject()));
+
+ return new Document(getOperator(),
+ context.getMappedObject(expression != null ? expression.toDocument() : criteriaDefinition.getCriteriaObject()));
}
/*
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperatorsUnitTests.java
new file mode 100644
index 0000000000..67f5093b8f
--- /dev/null
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/EvaluationOperatorsUnitTests.java
@@ -0,0 +1,35 @@
+/*
+ * 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.springframework.data.mongodb.test.util.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for {@link EvaluationOperators}.
+ *
+ * @author Mark Paluch
+ */
+class EvaluationOperatorsUnitTests {
+
+ @Test // GH-3790
+ void shouldRenderExprCorrectly() {
+
+ assertThat(EvaluationOperators.valueOf("foo").expr().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $expr: \"$foo\" }");
+ }
+}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MatchOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MatchOperationUnitTests.java
index 04d3824de1..ec3decb7a8 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MatchOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/MatchOperationUnitTests.java
@@ -1,26 +1,23 @@
package org.springframework.data.mongodb.core.aggregation;
-import static org.assertj.core.api.Assertions.*;
-import org.bson.Document;
+import static org.springframework.data.mongodb.test.util.Assertions.*;
+
import org.junit.jupiter.api.Test;
+/**
+ * Unit tests for {@link MatchOperation}.
+ *
+ * @author Divya Srivastava
+ */
class MatchOperationUnitTests {
-
- @Test // DATAMONGO - 3729
- public void shouldRenderStdDevPopCorrectly() {
- MatchOperation operation = Aggregation.match().withValueOf(ArithmeticOperators.valueOf("quiz").stdDevPop());
- assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).
- isEqualTo(Document.parse("{ $match: { \"$expr\" : { \"$stdDevPop\" : \"$quiz\" } } } "));
-
- }
-
- @Test // DATAMONGO - 3729
- public void shouldRenderStdDevSampCorrectly() {
- MatchOperation operation = Aggregation.match().withValueOf(ArithmeticOperators.valueOf("quiz").stdDevSamp());
+
+ @Test // GH-3790
+ void matchShouldRenderCorrectly() {
+
+ MatchOperation operation = Aggregation.match(ArithmeticOperators.valueOf("quiz").stdDevPop());
assertThat(operation.toDocument(Aggregation.DEFAULT_CONTEXT)).
- isEqualTo(Document.parse("{ $match: { \"$expr\" : { \"$stdDevSamp\" : \"$quiz\" } } } "));
-
+ isEqualTo("{ $match: { \"$stdDevPop\" : \"$quiz\" } } ");
}
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java
index e97e1ff018..9fbc36586f 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceRootOperationUnitTests.java
@@ -27,20 +27,20 @@
*
* @author Mark Paluch
*/
-public class ReplaceRootOperationUnitTests {
+class ReplaceRootOperationUnitTests {
@Test // DATAMONGO-1550
- public void rejectsNullField() {
+ void rejectsNullField() {
assertThatIllegalArgumentException().isThrownBy(() -> new ReplaceRootOperation((Field) null));
}
@Test // DATAMONGO-1550
- public void rejectsNullExpression() {
+ void rejectsNullExpression() {
assertThatIllegalArgumentException().isThrownBy(() -> new ReplaceRootOperation((AggregationExpression) null));
}
@Test // DATAMONGO-1550
- public void shouldRenderCorrectly() {
+ void shouldRenderCorrectly() {
ReplaceRootOperation operation = ReplaceRootDocumentOperation.builder()
.withDocument(new Document("hello", "world"));
@@ -50,7 +50,7 @@ public void shouldRenderCorrectly() {
}
@Test // DATAMONGO-1550
- public void shouldRenderExpressionCorrectly() {
+ void shouldRenderExpressionCorrectly() {
ReplaceRootOperation operation = new ReplaceRootOperation(VariableOperators //
.mapItemsOf("array") //
@@ -64,7 +64,7 @@ public void shouldRenderExpressionCorrectly() {
}
@Test // DATAMONGO-1550
- public void shouldComposeDocument() {
+ void shouldComposeDocument() {
ReplaceRootOperation operation = ReplaceRootDocumentOperation.builder().withDocument() //
.andValue("value").as("key") //
@@ -77,7 +77,7 @@ public void shouldComposeDocument() {
}
@Test // DATAMONGO-1550
- public void shouldComposeSubDocument() {
+ void shouldComposeSubDocument() {
Document partialReplacement = new Document("key", "override").append("key2", "value2");
@@ -92,7 +92,7 @@ public void shouldComposeSubDocument() {
}
@Test // DATAMONGO-1550
- public void shouldNotExposeFields() {
+ void shouldNotExposeFields() {
ReplaceRootOperation operation = new ReplaceRootOperation(Fields.field("field"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceWithOperationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceWithOperationUnitTests.java
index 8f8b5c9dd1..d1a21a254c 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceWithOperationUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ReplaceWithOperationUnitTests.java
@@ -25,15 +25,15 @@
*
* @author Christoph Strobl
*/
-public class ReplaceWithOperationUnitTests {
+class ReplaceWithOperationUnitTests {
@Test // DATAMONGO-2331
- public void rejectsNullField() {
+ void rejectsNullField() {
assertThatIllegalArgumentException().isThrownBy(() -> new ReplaceWithOperation(null));
}
@Test // DATAMONGO-2331
- public void shouldRenderValueCorrectly() {
+ void shouldRenderValueCorrectly() {
ReplaceWithOperation operation = ReplaceWithOperation.replaceWithValue(new Document("hello", "world"));
Document dbObject = operation.toDocument(Aggregation.DEFAULT_CONTEXT);
@@ -42,7 +42,7 @@ public void shouldRenderValueCorrectly() {
}
@Test // DATAMONGO-2331
- public void shouldRenderExpressionCorrectly() {
+ void shouldRenderExpressionCorrectly() {
ReplaceWithOperation operation = ReplaceWithOperation.replaceWithValueOf(VariableOperators //
.mapItemsOf("array") //
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
index 808263697a..46db6e7d6a 100755
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
@@ -43,6 +43,9 @@
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.DocumentTestUtils;
import org.springframework.data.mongodb.core.Person;
+import org.springframework.data.mongodb.core.aggregation.ConditionalOperators;
+import org.springframework.data.mongodb.core.aggregation.EvaluationOperators;
+import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.mapping.DBRef;
@@ -1330,6 +1333,21 @@ void allowsUsingFieldPathsForPropertiesHavingCustomConversionRegistered() {
assertThat(mapper.getMappedSort(query.getQueryObject(), context.getPersistentEntity(Customer.class))).isEqualTo(new org.bson.Document("address.street", "1007 Mountain Drive"));
}
+ @Test // GH-3790
+ void shouldAcceptExprAsCriteriaDefinition() {
+
+ EvaluationOperators.EvaluationOperatorFactory.Expr expr = EvaluationOperators
+ .valueOf(ConditionalOperators.ifNull("customizedField").then(true)).expr();
+
+ Query query = query(
+ expr.toCriteriaDefinition(new TypeBasedAggregationOperationContext(EmbeddedClass.class, context, mapper)));
+
+ org.bson.Document mappedQuery = mapper.getMappedObject(query.getQueryObject(),
+ context.getRequiredPersistentEntity(EmbeddedClass.class));
+
+ assertThat(mappedQuery).isEqualTo("{ $expr : { $ifNull : [\"$fancy_custom_name\", true] } }");
+ }
+
@Test // GH-3668
void mapStringIdFieldProjection() {
From ffceed8da96bb2b83206a87440bebc8d30687c10 Mon Sep 17 00:00:00 2001
From: divya srivastava
Date: Sun, 29 Aug 2021 19:07:14 +0530
Subject: [PATCH 050/920] Add support for `$atan`, `$atan2` and `$atanh`
aggregation operators.
Closes #3709
Original pull request: #3794.
---
.../core/aggregation/ArithmeticOperators.java | 258 ++++++++++++++++++
.../core/spel/MethodReferenceNode.java | 3 +
.../data/mongodb/util/RegexFlags.java | 2 +-
.../ArithmeticOperatorsUnitTests.java | 22 ++
.../SpelExpressionTransformerUnitTests.java | 15 +
5 files changed, 299 insertions(+), 1 deletion(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index 8fe3d9120c..bf10488f99 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -790,6 +790,68 @@ public Cosh cosh(AngularUnit unit) {
public Tan tan() {
return tan(AngularUnit.RADIANS);
}
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the inverse tangent of a numeric value.
+ *
+ * @return new instance of {@link ATan}.
+ */
+ public ATan atan() {
+ return usesFieldRef() ? ATan.atanOf(fieldReference) : ATan.atanOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value
+ * divided by the given numeric value in the argument.
+ *
+ * @param the numeric value
+ * @return new instance of {@link ATan2}.
+ */
+ public ATan2 atan2(Number value) {
+
+ Assert.notNull(value, "Value must not be null!");
+ return createATan2().atan2of(value);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value
+ * divided by the given field reference in the argument.
+ *
+ * @param the numeric value
+ * @return new instance of {@link ATan2}.
+ */
+ public ATan2 atan2(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return createATan2().atan2of(fieldReference);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value
+ * divided by the given {@link AggregationExpression} in the argument.
+ *
+ * @param the numeric value
+ * @return new instance of {@link ATan2}.
+ */
+ public ATan2 atan2(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return createATan2().atan2of(expression);
+ }
+
+ private ATan2 createATan2() {
+
+ return usesFieldRef() ? ATan2.valueOf(fieldReference) : ATan2.valueOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a numeric value.
+ *
+ * @return new instance of {@link ATanh}.
+ */
+ public ATanh atanh() {
+ return usesFieldRef() ? ATanh.atanhOf(fieldReference) : ATanh.atanhOf(expression);
+ }
/**
* Creates new {@link AggregationExpression} that calculates the tangent of a numeric value in the given
@@ -2579,6 +2641,148 @@ protected String getMongoMethod() {
return "$tan";
}
}
+
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the inverse tangent of a value.
+ *
+ */
+ public static class ATan extends AbstractAggregationExpression {
+
+ private ATan(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse tangent of a value.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link ATan}.
+ */
+ public static ATan atanOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new ATan(Fields.field(fieldReference));
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse tangent of a value.
+ *
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link ATan}.
+ */
+ public static ATan atanOf(AggregationExpression expression) {
+ return new ATan(expression);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse tangent of a value.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @return new instance of {@link ATan}.
+ */
+ public static ATan atanof(Number value) {
+ return new ATan(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$atan";
+ }
+ }
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the inverse
+ * tangent of y / x, where y and x are the first and second values passed to the
+ * expression respectively.
+ *
+ */
+ public static class ATan2 extends AbstractAggregationExpression {
+
+ private ATan2(List> value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse
+ * tangent of of y / x, where y and x are the first and second values passed to
+ * the expression respectively.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a
+ * numeric value.
+ * @return new instance of {@link ATan2}.
+ */
+ public static ATan2 valueOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new ATan2(asFields(fieldReference));
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse
+ * tangent of of y / x, where y and x are the first and second values passed to
+ * the expression respectively.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves
+ * to a numeric value.
+ * @return new instance of {@link ATan2}.
+ */
+ public static ATan2 valueOf(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new ATan2((Collections.singletonList(expression)));
+ }
+
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse
+ * tangent of of y / x, where y and x are the first and second values passed to
+ * the expression respectively.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression
+ * expression}, ...) that resolves to a numeric value.
+ * @return new instance of {@link ATan2}.
+ */
+ public ATan2 atan2of(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new ATan2(append(Fields.field(fieldReference)));
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
+ * {@link AngularUnit#RADIANS}.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @return new instance of {@link ATan2}.
+ */
+ public ATan2 atan2of(AggregationExpression expression) {
+
+ Assert.notNull(expression, "Expression must not be null!");
+ return new ATan2(append(expression));
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse
+ * tangent of of y / x, where y and x are the first and second values passed to
+ * the expression respectively.
+ *
+ * @param value of type {@link Number}
+ * @return new instance of {@link ATan2}.
+ */
+ public ATan2 atan2of(Number value) {
+
+ return new ATan2(append(value));
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$atan2";
+ }
+ }
/**
* An {@link AggregationExpression expression} that calculates the hyperbolic tangent of a value that is measured in
@@ -2684,6 +2888,60 @@ protected String getMongoMethod() {
return "$tanh";
}
}
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the inverse
+ * hyperbolic tangent of a value
+ *
+ */
+ public static class ATanh extends AbstractAggregationExpression {
+
+ private ATanh(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse
+ * hyperbolic tangent of a value.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a
+ * numeric value.
+ * @return new instance of {@link ATanh}.
+ */
+ public static ATanh atanhOf(String fieldReference) {
+ return new ATanh(Fields.field(fieldReference));
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse
+ * hyperbolic tangent of a value.
+ *
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves
+ * to a numeric value.
+ * @return new instance of {@link ATanh}.
+ */
+ public static ATanh atanhOf(AggregationExpression expression) {
+ return new ATanh(expression);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse
+ * hyperbolic tangent of a value.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression
+ * expression}, ...) that resolves to a numeric value.
+ * @return new instance of {@link ATanh}.
+ */
+ public static ATanh atanhof(Object value) {
+ return new ATanh(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$atanh";
+ }
+ }
/**
* {@link Rand} returns a floating value between 0 and 1.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index 0fbfe51f09..0f27c463e2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -100,6 +100,9 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("tan", singleArgRef().forOperator("$tan"));
map.put("tanh", singleArgRef().forOperator("$tanh"));
map.put("rand", emptyRef().forOperator("$rand"));
+ map.put("atan", singleArgRef().forOperator("$atan"));
+ map.put("atan2", arrayArgRef().forOperator("$atan2"));
+ map.put("atanh", singleArgRef().forOperator("$atanh"));
// STRING OPERATORS
map.put("concat", arrayArgRef().forOperator("$concat"));
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
index dfee94954c..ba6531e93c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/RegexFlags.java
@@ -113,4 +113,4 @@ public static int toRegexFlag(char c) {
return flag;
}
-}
+}
\ No newline at end of file
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index 02f76d5c10..8a52a8a2f5 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -166,6 +166,28 @@ void rendersTanhWithValueInDegrees() {
assertThat(valueOf("angle").tanh(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $tanh : { $degreesToRadians : \"$angle\" } }");
}
+
+ @Test // DATAMONGO - 3709
+ void rendersATan() {
+
+ assertThat(valueOf("field").atan().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $atan : \"$field\" }");
+ }
+
+ @Test // DATAMONGO - 3709
+ void rendersATan2() {
+
+ assertThat(valueOf("field1").atan2("field2").toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $atan2 : [ \"$field1\" , \"$field2\" ] }");
+ }
+
+ @Test // DATAMONGO - 3709
+ void rendersATanh() {
+
+ assertThat(valueOf("field").atanh().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $atanh : \"$field\" }");
+ }
+
@Test // GH-3724
void rendersRand() {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index e92ea38336..c9ba9c12e7 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1098,6 +1098,21 @@ void shouldRenderTan() {
void shouldRenderTanh() {
assertThat(transform("tanh(angle)")).isEqualTo("{ \"$tanh\" : \"$angle\"}");
}
+
+ @Test // DATAMONGO - 3709
+ void shouldRenderATan() {
+ assertThat(transform("atan(number)")).isEqualTo("{ \"$atan\" : \"$number\"}");
+ }
+
+ @Test // DATAMONGO - 3709
+ void shouldRenderATan2() {
+ assertThat(transform("atan2(number1,number2)")).isEqualTo("{ \"$atan2\" : [ \"$number1\" , \"$number2\" ] }");
+ }
+
+ @Test // DATAMONGO - 3709
+ void shouldRenderATanh() {
+ assertThat(transform("atanh(number)")).isEqualTo("{ \"$atanh\" : \"$number\"}");
+ }
@Test // GH-3713
void shouldRenderDateAdd() {
From 8af904b81fb190e6b9663629daff76b11ed8028f Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Mon, 6 Sep 2021 15:46:07 +0200
Subject: [PATCH 051/920] Polishing.
Add author and since tags. Tweak Javadoc format.
See #3709
Original pull request: #3794.
---
.../core/aggregation/ArithmeticOperators.java | 155 +++++++++---------
.../ArithmeticOperatorsUnitTests.java | 46 +++---
.../reference/aggregation-framework.adoc | 2 +-
3 files changed, 93 insertions(+), 110 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index bf10488f99..d21d985882 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -717,7 +717,7 @@ public Sin sin(AngularUnit unit) {
* Creates new {@link AggregationExpression} that calculates the sine of a numeric value given in
* {@link AngularUnit#RADIANS radians}.
*
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Sinh}.
* @since 3.3
*/
public Sinh sinh() {
@@ -728,7 +728,7 @@ public Sinh sinh() {
* Creates new {@link AggregationExpression} that calculates the sine of a numeric value.
*
* @param unit the unit of measure.
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Sinh}.
* @since 3.3
*/
public Sinh sinh(AngularUnit unit) {
@@ -739,7 +739,7 @@ public Sinh sinh(AngularUnit unit) {
* Creates new {@link AggregationExpression} that calculates the cosine of a numeric value given in
* {@link AngularUnit#RADIANS radians}.
*
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Cos}.
* @since 3.3
*/
public Cos cos() {
@@ -751,7 +751,7 @@ public Cos cos() {
* {@link AngularUnit unit}.
*
* @param unit the unit of measure.
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Cos}.
* @since 3.3
*/
public Cos cos(AngularUnit unit) {
@@ -762,7 +762,7 @@ public Cos cos(AngularUnit unit) {
* Creates new {@link AggregationExpression} that calculates the hyperbolic cosine of a numeric value given in
* {@link AngularUnit#RADIANS radians}.
*
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Cosh}.
* @since 3.3
*/
public Cosh cosh() {
@@ -773,7 +773,7 @@ public Cosh cosh() {
* Creates new {@link AggregationExpression} that calculates the hyperbolic cosine of a numeric value.
*
* @param unit the unit of measure.
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Cosh}.
* @since 3.3
*/
public Cosh cosh(AngularUnit unit) {
@@ -784,70 +784,75 @@ public Cosh cosh(AngularUnit unit) {
* Creates new {@link AggregationExpression} that calculates the tangent of a numeric value given in
* {@link AngularUnit#RADIANS radians}.
*
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Tan}.
* @since 3.3
*/
public Tan tan() {
return tan(AngularUnit.RADIANS);
}
-
+
/**
* Creates new {@link AggregationExpression} that calculates the inverse tangent of a numeric value.
*
* @return new instance of {@link ATan}.
+ * @since 3.3
*/
public ATan atan() {
return usesFieldRef() ? ATan.atanOf(fieldReference) : ATan.atanOf(expression);
}
-
+
/**
- * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value
- * divided by the given numeric value in the argument.
+ * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value divided by
+ * the given numeric value in the argument.
*
- * @param the numeric value
+ * @param the numeric value
* @return new instance of {@link ATan2}.
+ * @since 3.3
*/
public ATan2 atan2(Number value) {
-
+
Assert.notNull(value, "Value must not be null!");
return createATan2().atan2of(value);
}
-
+
/**
- * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value
- * divided by the given field reference in the argument.
+ * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value divided by
+ * the given field reference in the argument.
*
- * @param the numeric value
+ * @param the numeric value
* @return new instance of {@link ATan2}.
+ * @since 3.3
*/
public ATan2 atan2(String fieldReference) {
-
+
Assert.notNull(fieldReference, "FieldReference must not be null!");
return createATan2().atan2of(fieldReference);
}
-
+
/**
- * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value
- * divided by the given {@link AggregationExpression} in the argument.
+ * Creates new {@link AggregationExpression} that calculates the inverse tangent of the the numeric value divided by
+ * the given {@link AggregationExpression} in the argument.
*
- * @param the numeric value
+ * @param the numeric value
* @return new instance of {@link ATan2}.
+ * @since 3.3
*/
public ATan2 atan2(AggregationExpression expression) {
-
+
Assert.notNull(expression, "Expression must not be null!");
return createATan2().atan2of(expression);
}
-
+
private ATan2 createATan2() {
-
+
return usesFieldRef() ? ATan2.valueOf(fieldReference) : ATan2.valueOf(expression);
}
-
+
/**
* Creates new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a numeric value.
*
* @return new instance of {@link ATanh}.
+ * @since 3.3
*/
public ATanh atanh() {
return usesFieldRef() ? ATanh.atanhOf(fieldReference) : ATanh.atanhOf(expression);
@@ -858,7 +863,7 @@ public ATanh atanh() {
* {@link AngularUnit unit}.
*
* @param unit the unit of measure.
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Tan}.
* @since 3.3
*/
public Tan tan(AngularUnit unit) {
@@ -869,7 +874,7 @@ public Tan tan(AngularUnit unit) {
* Creates new {@link AggregationExpression} that calculates the hyperbolic tangent of a numeric value given in
* {@link AngularUnit#RADIANS radians}.
*
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Tan}.
* @since 3.3
*/
public Tanh tanh() {
@@ -880,7 +885,7 @@ public Tanh tanh() {
* Creates new {@link AggregationExpression} that calculates the hyperbolic tangent of a numeric value.
*
* @param unit the unit of measure.
- * @return new instance of {@link Sin}.
+ * @return new instance of {@link Tanh}.
* @since 3.3
*/
public Tanh tanh(AngularUnit unit) {
@@ -2357,8 +2362,6 @@ private Cos(Object value) {
* { $cos : { $degreesToRadians : "$angle" } }
*
*
- * .
- *
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link Cos}.
*/
@@ -2470,8 +2473,6 @@ public static Cosh coshOf(String fieldReference) {
* { $cosh : { $degreesToRadians : "$angle" } }
*
*
- * .
- *
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Cosh}.
@@ -2563,8 +2564,6 @@ private Tan(Object value) {
* { $tan : { $degreesToRadians : "$angle" } }
*
*
- * .
- *
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link Tan}.
*/
@@ -2641,11 +2640,12 @@ protected String getMongoMethod() {
return "$tan";
}
}
-
-
+
/**
* An {@link AggregationExpression expression} that calculates the inverse tangent of a value.
*
+ * @author Divya Srivastava
+ * @since 3.3
*/
public static class ATan extends AbstractAggregationExpression {
@@ -2660,14 +2660,13 @@ private ATan(Object value) {
* @return new instance of {@link ATan}.
*/
public static ATan atanOf(String fieldReference) {
-
+
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new ATan(Fields.field(fieldReference));
}
/**
* Creates a new {@link AggregationExpression} that calculates the inverse tangent of a value.
- *
*
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @return new instance of {@link ATan}.
@@ -2683,7 +2682,7 @@ public static ATan atanOf(AggregationExpression expression) {
* numeric value.
* @return new instance of {@link ATan}.
*/
- public static ATan atanof(Number value) {
+ public static ATan atanOf(Number value) {
return new ATan(value);
}
@@ -2692,26 +2691,25 @@ protected String getMongoMethod() {
return "$atan";
}
}
-
+
/**
- * An {@link AggregationExpression expression} that calculates the inverse
- * tangent of y / x, where y and x are the first and second values passed to the
- * expression respectively.
+ * An {@link AggregationExpression expression} that calculates the inverse tangent of y / x, where y and x are the
+ * first and second values passed to the expression respectively.
*
+ * @author Divya Srivastava
+ * @since 3.3
*/
public static class ATan2 extends AbstractAggregationExpression {
-
+
private ATan2(List> value) {
super(value);
}
/**
- * Creates a new {@link AggregationExpression} that calculates the inverse
- * tangent of of y / x, where y and x are the first and second values passed to
- * the expression respectively.
- *
- * @param fieldReference the name of the {@link Field field} that resolves to a
- * numeric value.
+ * Creates a new {@link AggregationExpression} that calculates the inverse tangent of of y / x, where y and x are
+ * the first and second values passed to the expression respectively.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @return new instance of {@link ATan2}.
*/
public static ATan2 valueOf(String fieldReference) {
@@ -2721,12 +2719,10 @@ public static ATan2 valueOf(String fieldReference) {
}
/**
- * Creates a new {@link AggregationExpression} that calculates the inverse
- * tangent of of y / x, where y and x are the first and second values passed to
- * the expression respectively.
- *
- * @param expression the {@link AggregationExpression expression} that resolves
- * to a numeric value.
+ * Creates a new {@link AggregationExpression} that calculates the inverse tangent of of y / x, where y and x are
+ * the first and second values passed to the expression respectively.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @return new instance of {@link ATan2}.
*/
public static ATan2 valueOf(AggregationExpression expression) {
@@ -2737,12 +2733,11 @@ public static ATan2 valueOf(AggregationExpression expression) {
/**
- * Creates a new {@link AggregationExpression} that calculates the inverse
- * tangent of of y / x, where y and x are the first and second values passed to
- * the expression respectively.
- *
- * @param value anything ({@link Field field}, {@link AggregationExpression
- * expression}, ...) that resolves to a numeric value.
+ * Creates a new {@link AggregationExpression} that calculates the inverse tangent of of y / x, where y and x are
+ * the first and second values passed to the expression respectively.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
* @return new instance of {@link ATan2}.
*/
public ATan2 atan2of(String fieldReference) {
@@ -2750,7 +2745,7 @@ public ATan2 atan2of(String fieldReference) {
Assert.notNull(fieldReference, "FieldReference must not be null!");
return new ATan2(append(Fields.field(fieldReference)));
}
-
+
/**
* Creates a new {@link AggregationExpression} that calculates the hyperbolic tangent of a value that is measured in
* {@link AngularUnit#RADIANS}.
@@ -2760,21 +2755,20 @@ public ATan2 atan2of(String fieldReference) {
* @return new instance of {@link ATan2}.
*/
public ATan2 atan2of(AggregationExpression expression) {
-
+
Assert.notNull(expression, "Expression must not be null!");
return new ATan2(append(expression));
}
-
+
/**
- * Creates a new {@link AggregationExpression} that calculates the inverse
- * tangent of of y / x, where y and x are the first and second values passed to
- * the expression respectively.
- *
+ * Creates a new {@link AggregationExpression} that calculates the inverse tangent of of y / x, where y and x are
+ * the first and second values passed to the expression respectively.
+ *
* @param value of type {@link Number}
* @return new instance of {@link ATan2}.
*/
public ATan2 atan2of(Number value) {
-
+
return new ATan2(append(value));
}
@@ -2818,8 +2812,6 @@ public static Tanh tanhOf(String fieldReference) {
* { $tanh : { $degreesToRadians : "$angle" } }
*
*
- * .
- *
* @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
* @param unit the unit of measure used by the value of the given field.
* @return new instance of {@link Tanh}.
@@ -2888,11 +2880,12 @@ protected String getMongoMethod() {
return "$tanh";
}
}
-
+
/**
- * An {@link AggregationExpression expression} that calculates the inverse
- * hyperbolic tangent of a value
+ * An {@link AggregationExpression expression} that calculates the inverse hyperbolic tangent of a value
*
+ * @author Divya Srivastava
+ * @since 3.3
*/
public static class ATanh extends AbstractAggregationExpression {
@@ -2913,12 +2906,10 @@ public static ATanh atanhOf(String fieldReference) {
}
/**
- * Creates a new {@link AggregationExpression} that calculates the inverse
- * hyperbolic tangent of a value.
+ * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic tangent of a value.
*
- *
- * @param expression the {@link AggregationExpression expression} that resolves
- * to a numeric value.
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @return new instance of {@link ATanh}.
*/
public static ATanh atanhOf(AggregationExpression expression) {
@@ -2933,7 +2924,7 @@ public static ATanh atanhOf(AggregationExpression expression) {
* expression}, ...) that resolves to a numeric value.
* @return new instance of {@link ATanh}.
*/
- public static ATanh atanhof(Object value) {
+ public static ATanh atanhOf(Object value) {
return new ATanh(value);
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index 8a52a8a2f5..84d228f75e 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -30,6 +30,7 @@
* @author Christoph Strobl
* @author Mark Paluch
* @author Mushtaq Ahmed
+ * @author Divya Srivastava
*/
class ArithmeticOperatorsUnitTests {
@@ -86,8 +87,7 @@ void rendersIntegralWithUnit() {
@Test // GH-3728
void rendersSin() {
- assertThat(valueOf("angle").sin().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $sin : \"$angle\" }");
+ assertThat(valueOf("angle").sin().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $sin : \"$angle\" }");
}
@Test // GH-3728
@@ -100,8 +100,7 @@ void rendersSinWithValueInDegrees() {
@Test // GH-3728
void rendersSinh() {
- assertThat(valueOf("angle").sinh().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $sinh : \"$angle\" }");
+ assertThat(valueOf("angle").sinh().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $sinh : \"$angle\" }");
}
@Test // GH-3728
@@ -114,8 +113,7 @@ void rendersSinhWithValueInDegrees() {
@Test // GH-3710
void rendersCos() {
- assertThat(valueOf("angle").cos().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $cos : \"$angle\" }");
+ assertThat(valueOf("angle").cos().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $cos : \"$angle\" }");
}
@Test // GH-3710
@@ -128,8 +126,7 @@ void rendersCosWithValueInDegrees() {
@Test // GH-3710
void rendersCosh() {
- assertThat(valueOf("angle").cosh().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $cosh : \"$angle\" }");
+ assertThat(valueOf("angle").cosh().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $cosh : \"$angle\" }");
}
@Test // GH-3710
@@ -142,8 +139,7 @@ void rendersCoshWithValueInDegrees() {
@Test // GH-3730
void rendersTan() {
- assertThat(valueOf("angle").tan().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $tan : \"$angle\" }");
+ assertThat(valueOf("angle").tan().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $tan : \"$angle\" }");
}
@Test // GH-3730
@@ -156,8 +152,7 @@ void rendersTanWithValueInDegrees() {
@Test // GH-3730
void rendersTanh() {
- assertThat(valueOf("angle").tanh().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $tanh : \"$angle\" }");
+ assertThat(valueOf("angle").tanh().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $tanh : \"$angle\" }");
}
@Test // GH-3730
@@ -166,28 +161,25 @@ void rendersTanhWithValueInDegrees() {
assertThat(valueOf("angle").tanh(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $tanh : { $degreesToRadians : \"$angle\" } }");
}
-
- @Test // DATAMONGO - 3709
+
+ @Test // GH-3709
void rendersATan() {
-
- assertThat(valueOf("field").atan().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $atan : \"$field\" }");
+
+ assertThat(valueOf("field").atan().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $atan : \"$field\" }");
}
-
- @Test // DATAMONGO - 3709
+
+ @Test // GH-3709
void rendersATan2() {
-
+
assertThat(valueOf("field1").atan2("field2").toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $atan2 : [ \"$field1\" , \"$field2\" ] }");
+ .isEqualTo("{ $atan2 : [ \"$field1\" , \"$field2\" ] }");
}
-
- @Test // DATAMONGO - 3709
+
+ @Test // GH-3709
void rendersATanh() {
-
- assertThat(valueOf("field").atanh().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $atanh : \"$field\" }");
- }
+ assertThat(valueOf("field").atanh().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $atanh : \"$field\" }");
+ }
@Test // GH-3724
void rendersRand() {
diff --git a/src/main/asciidoc/reference/aggregation-framework.adoc b/src/main/asciidoc/reference/aggregation-framework.adoc
index 75ed415096..387a0acf65 100644
--- a/src/main/asciidoc/reference/aggregation-framework.adoc
+++ b/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`
| Arithmetic Aggregation Operators
-| `abs`, `add` (+++*+++ via `plus`), `ceil`, `cos`, `cosh`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `sin`, `sinh`, `tan`, `tanh`, `trunc`
+| `abs`, `add` (+++*+++ via `plus`), `atan`, `atan2`, `atanh`, `ceil`, `cos`, `cosh`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `sin`, `sinh`, `tan`, `tanh`, `trunc`
| String Aggregation Operators
| `concat`, `substr`, `toLower`, `toUpper`, `strcasecmp`, `indexOfBytes`, `indexOfCP`, `regexFind`, `regexFindAll`, `regexMatch`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`
From 59d0042d13a8af35a84a48f992c190bed00006b0 Mon Sep 17 00:00:00 2001
From: divyajnu08
Date: Wed, 1 Sep 2021 13:02:43 +0530
Subject: [PATCH 052/920] Add support for `$asin` and `$asinh` aggregation
operators.
Closes #3708
Original pull request: #3796.
---
.../core/aggregation/ArithmeticOperators.java | 116 ++++++++++++++++++
.../core/spel/MethodReferenceNode.java | 2 +
.../ArithmeticOperatorsUnitTests.java | 14 +++
.../SpelExpressionTransformerUnitTests.java | 10 ++
4 files changed, 142 insertions(+)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index d21d985882..d865d57a7d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -735,6 +735,24 @@ public Sinh sinh(AngularUnit unit) {
return usesFieldRef() ? Sinh.sinhOf(fieldReference, unit) : Sinh.sinhOf(expression, unit);
}
+ /**
+ * Creates new {@link AggregationExpression} that calculates the inverse sine of a numeric value.
+ *
+ * @return new instance of {@link ASin}.
+ */
+ public ASin asin() {
+ return usesFieldRef() ? ASin.asinOf(fieldReference) : ASin.asinOf(expression);
+ }
+
+ /**
+ * Creates new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a numeric value.
+ *
+ * @return new instance of {@link ASinh}.
+ */
+ public ASinh asinh() {
+ return usesFieldRef() ? ASinh.asinhOf(fieldReference) : ASinh.asinhOf(expression);
+ }
+
/**
* Creates new {@link AggregationExpression} that calculates the cosine of a numeric value given in
* {@link AngularUnit#RADIANS radians}.
@@ -2339,6 +2357,104 @@ protected String getMongoMethod() {
return "$sinh";
}
}
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the inverse sine of a value.
+ *
+ */
+ public static class ASin extends AbstractAggregationExpression {
+
+ private ASin(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link ASin}.
+ */
+ public static ASin asinOf(String fieldReference) {
+
+ Assert.notNull(fieldReference, "FieldReference must not be null!");
+ return new ASin(Fields.field(fieldReference));
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value.
+ *
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link ASin}.
+ */
+ public static ASin asinOf(AggregationExpression expression) {
+ return new ASin(expression);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse sine of a value.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @return new instance of {@link ASin}.
+ */
+ public static ASin asinOf(Number value) {
+ return new ASin(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$asin";
+ }
+ }
+
+ /**
+ * An {@link AggregationExpression expression} that calculates the inverse hyperbolic sine of a value
+ */
+ public static class ASinh extends AbstractAggregationExpression {
+
+ private ASinh(Object value) {
+ super(value);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value.
+ *
+ * @param fieldReference the name of the {@link Field field} that resolves to a numeric value.
+ * @return new instance of {@link ASinh}.
+ */
+ public static ASinh asinhOf(String fieldReference) {
+ return new ASinh(Fields.field(fieldReference));
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value.
+ *
+ *
+ * @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
+ * @return new instance of {@link ASinh}.
+ */
+ public static ASinh asinhOf(AggregationExpression expression) {
+ return new ASinh(expression);
+ }
+
+ /**
+ * Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value.
+ *
+ * @param value anything ({@link Field field}, {@link AggregationExpression expression}, ...) that resolves to a
+ * numeric value.
+ * @return new instance of {@link ASinh}.
+ */
+ public static ASinh asinhOf(Object value) {
+ return new ASinh(value);
+ }
+
+ @Override
+ protected String getMongoMethod() {
+ return "$asinh";
+ }
+ }
+
/**
* An {@link AggregationExpression expression} that calculates the cosine of a value that is measured in radians.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
index 0f27c463e2..dc7a3cc982 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
@@ -95,6 +95,8 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("integral", mapArgRef().forOperator("$integral").mappingParametersTo("input", "unit"));
map.put("sin", singleArgRef().forOperator("$sin"));
map.put("sinh", singleArgRef().forOperator("$sinh"));
+ map.put("asin", singleArgRef().forOperator("$asin"));
+ map.put("asinh", singleArgRef().forOperator("$asinh"));
map.put("cos", singleArgRef().forOperator("$cos"));
map.put("cosh", singleArgRef().forOperator("$cosh"));
map.put("tan", singleArgRef().forOperator("$tan"));
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index 84d228f75e..d0f50d2baf 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -109,6 +109,20 @@ void rendersSinhWithValueInDegrees() {
assertThat(valueOf("angle").sinh(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $sinh : { $degreesToRadians : \"$angle\" } }");
}
+
+ @Test // DATAMONGO - 3708
+ void rendersASin() {
+
+ assertThat(valueOf("field").asin().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $asin : \"$field\" }");
+ }
+
+ @Test // DATAMONGO - 3708
+ void rendersASinh() {
+
+ assertThat(valueOf("field").asinh().toDocument(Aggregation.DEFAULT_CONTEXT))
+ .isEqualTo("{ $asinh : \"$field\" }");
+ }
@Test // GH-3710
void rendersCos() {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index c9ba9c12e7..8077f604e5 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1078,6 +1078,16 @@ void shouldRenderSin() {
void shouldRenderSinh() {
assertThat(transform("sinh(angle)")).isEqualTo("{ \"$sinh\" : \"$angle\"}");
}
+
+ @Test // DATAMONGO-3708
+ void shouldRenderASin() {
+ assertThat(transform("asin(number)")).isEqualTo("{ \"$asin\" : \"$number\"}");
+ }
+
+ @Test // DATAMONGO-3708
+ void shouldRenderASinh() {
+ assertThat(transform("asinh(number)")).isEqualTo("{ \"$asinh\" : \"$number\"}");
+ }
@Test // GH-3710
void shouldRenderCos() {
From dcf184888e88f1ae4a205df15b04b4b7d63a0880 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Tue, 7 Sep 2021 09:56:18 +0200
Subject: [PATCH 053/920] Polishing.
Add since and author tags. Update reference docs. Fix format of ticket references in tests.
See #3708
Original pull request: #3796.
---
.../core/aggregation/ArithmeticOperators.java | 18 ++++++++++++------
.../ArithmeticOperatorsUnitTests.java | 16 ++++++----------
.../SpelExpressionTransformerUnitTests.java | 12 ++++++------
.../reference/aggregation-framework.adoc | 2 +-
4 files changed, 25 insertions(+), 23 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
index d865d57a7d..9c9132e679 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperators.java
@@ -739,20 +739,22 @@ public Sinh sinh(AngularUnit unit) {
* Creates new {@link AggregationExpression} that calculates the inverse sine of a numeric value.
*
* @return new instance of {@link ASin}.
+ * @since 3.3
*/
public ASin asin() {
return usesFieldRef() ? ASin.asinOf(fieldReference) : ASin.asinOf(expression);
}
-
+
/**
* Creates new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a numeric value.
*
* @return new instance of {@link ASinh}.
+ * @since 3.3
*/
public ASinh asinh() {
return usesFieldRef() ? ASinh.asinhOf(fieldReference) : ASinh.asinhOf(expression);
}
-
+
/**
* Creates new {@link AggregationExpression} that calculates the cosine of a numeric value given in
* {@link AngularUnit#RADIANS radians}.
@@ -2357,10 +2359,12 @@ protected String getMongoMethod() {
return "$sinh";
}
}
-
+
/**
* An {@link AggregationExpression expression} that calculates the inverse sine of a value.
*
+ * @author Divya Srivastava
+ * @since 3.3
*/
public static class ASin extends AbstractAggregationExpression {
@@ -2407,9 +2411,12 @@ protected String getMongoMethod() {
return "$asin";
}
}
-
+
/**
* An {@link AggregationExpression expression} that calculates the inverse hyperbolic sine of a value
+ *
+ * @author Divya Srivastava
+ * @since 3.3
*/
public static class ASinh extends AbstractAggregationExpression {
@@ -2430,7 +2437,7 @@ public static ASinh asinhOf(String fieldReference) {
/**
* Creates a new {@link AggregationExpression} that calculates the inverse hyperbolic sine of a value.
*
- *
+ *
* @param expression the {@link AggregationExpression expression} that resolves to a numeric value.
* @return new instance of {@link ASinh}.
*/
@@ -2884,7 +2891,6 @@ public ATan2 atan2of(AggregationExpression expression) {
* @return new instance of {@link ATan2}.
*/
public ATan2 atan2of(Number value) {
-
return new ATan2(append(value));
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
index d0f50d2baf..ab3d1c2400 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArithmeticOperatorsUnitTests.java
@@ -109,19 +109,15 @@ void rendersSinhWithValueInDegrees() {
assertThat(valueOf("angle").sinh(AngularUnit.DEGREES).toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo("{ $sinh : { $degreesToRadians : \"$angle\" } }");
}
-
- @Test // DATAMONGO - 3708
- void rendersASin() {
- assertThat(valueOf("field").asin().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $asin : \"$field\" }");
+ @Test // GH-3708
+ void rendersASin() {
+ assertThat(valueOf("field").asin().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $asin : \"$field\" }");
}
-
- @Test // DATAMONGO - 3708
- void rendersASinh() {
- assertThat(valueOf("field").asinh().toDocument(Aggregation.DEFAULT_CONTEXT))
- .isEqualTo("{ $asinh : \"$field\" }");
+ @Test // GH-3708
+ void rendersASinh() {
+ assertThat(valueOf("field").asinh().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo("{ $asinh : \"$field\" }");
}
@Test // GH-3710
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
index 8077f604e5..899e02a172 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java
@@ -1078,13 +1078,13 @@ void shouldRenderSin() {
void shouldRenderSinh() {
assertThat(transform("sinh(angle)")).isEqualTo("{ \"$sinh\" : \"$angle\"}");
}
-
- @Test // DATAMONGO-3708
+
+ @Test // GH-3708
void shouldRenderASin() {
assertThat(transform("asin(number)")).isEqualTo("{ \"$asin\" : \"$number\"}");
}
- @Test // DATAMONGO-3708
+ @Test // GH-3708
void shouldRenderASinh() {
assertThat(transform("asinh(number)")).isEqualTo("{ \"$asinh\" : \"$number\"}");
}
@@ -1108,17 +1108,17 @@ void shouldRenderTan() {
void shouldRenderTanh() {
assertThat(transform("tanh(angle)")).isEqualTo("{ \"$tanh\" : \"$angle\"}");
}
-
+
@Test // DATAMONGO - 3709
void shouldRenderATan() {
assertThat(transform("atan(number)")).isEqualTo("{ \"$atan\" : \"$number\"}");
}
-
+
@Test // DATAMONGO - 3709
void shouldRenderATan2() {
assertThat(transform("atan2(number1,number2)")).isEqualTo("{ \"$atan2\" : [ \"$number1\" , \"$number2\" ] }");
}
-
+
@Test // DATAMONGO - 3709
void shouldRenderATanh() {
assertThat(transform("atanh(number)")).isEqualTo("{ \"$atanh\" : \"$number\"}");
diff --git a/src/main/asciidoc/reference/aggregation-framework.adoc b/src/main/asciidoc/reference/aggregation-framework.adoc
index 387a0acf65..45315cda36 100644
--- a/src/main/asciidoc/reference/aggregation-framework.adoc
+++ b/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`
| Arithmetic Aggregation Operators
-| `abs`, `add` (+++*+++ via `plus`), `atan`, `atan2`, `atanh`, `ceil`, `cos`, `cosh`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `sin`, `sinh`, `tan`, `tanh`, `trunc`
+| `abs`, `add` (+++*+++ via `plus`), `asin`, `asin`, `atan`, `atan2`, `atanh`, `ceil`, `cos`, `cosh`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `sin`, `sinh`, `tan`, `tanh`, `trunc`
| String Aggregation Operators
| `concat`, `substr`, `toLower`, `toUpper`, `strcasecmp`, `indexOfBytes`, `indexOfCP`, `regexFind`, `regexFindAll`, `regexMatch`, `split`, `strLenBytes`, `strLenCP`, `substrCP`, `trim`, `ltrim`, `rtim`
From c8307d5a39d246a245db2866a55cee813edd888d Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Tue, 7 Sep 2021 11:07:27 +0200
Subject: [PATCH 054/920] Allow one-to-many style lookups with via
`@DocumentReference`.
This commit adds support for relational style One-To-Many references using a combination of ReadonlyProperty and @DocumentReference.
It allows to link types without explicitly storing the linking values within the document itself.
@Document
class Publisher {
@Id
ObjectId id;
// ...
@ReadOnlyProperty
@DocumentReference(lookup="{'publisherId':?#{#self._id} }")
List books;
}
Closes: #3798
Original pull request: #3802.
---
.../convert/DefaultReferenceResolver.java | 2 +-
.../core/convert/DocumentReferenceSource.java | 63 +++++++++++++++++++
.../core/convert/MappingMongoConverter.java | 16 +++--
.../core/convert/ReferenceLookupDelegate.java | 52 +++++++++++----
.../MongoTemplateDocumentReferenceTests.java | 48 ++++++++++++++
.../reference/document-references.adoc | 56 +++++++++++++++++
6 files changed, 218 insertions(+), 19 deletions(-)
create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
index f801b8d990..62e713065f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultReferenceResolver.java
@@ -108,6 +108,6 @@ private Object createLazyLoadingProxy(MongoPersistentProperty property, Object s
ReferenceLookupDelegate referenceLookupDelegate, LookupFunction lookupFunction, MongoEntityReader entityReader) {
return proxyFactory.createLazyLoadingProxy(property, it -> {
return referenceLookupDelegate.readReference(it, source, lookupFunction, entityReader);
- }, source);
+ }, source instanceof DocumentReferenceSource ? ((DocumentReferenceSource)source).getTargetSource() : source);
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
new file mode 100644
index 0000000000..03e5eb0d5d
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
@@ -0,0 +1,63 @@
+/*
+ * 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.convert;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * The source object to resolve document references upon. Encapsulates the actual source and the reference specific
+ * values.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+public class DocumentReferenceSource {
+
+ private final Object self;
+
+ @Nullable private final Object targetSource;
+
+ /**
+ * Create a new instance of {@link DocumentReferenceSource}.
+ *
+ * @param self the entire wrapper object holding references. Must not be {@literal null}.
+ * @param targetSource the reference value source.
+ */
+ DocumentReferenceSource(Object self, @Nullable Object targetSource) {
+
+ this.self = self;
+ this.targetSource = targetSource;
+ }
+
+ /**
+ * Get the outer document.
+ *
+ * @return never {@literal null}.
+ */
+ public Object getSelf() {
+ return self;
+ }
+
+ /**
+ * Get the actual (property specific) reference value.
+ *
+ * @return can be {@literal null}.
+ */
+ @Nullable
+ public Object getTargetSource() {
+ return targetSource;
+ }
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index a60c853c33..5a2c3e952a 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -38,7 +38,6 @@
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
@@ -524,10 +523,6 @@ private void readAssociation(Association association, P
MongoPersistentProperty property = association.getInverse();
Object value = documentAccessor.get(property);
- if (value == null) {
- return;
- }
-
if (property.isDocumentReference()
|| (!property.isDbReference() && property.findAnnotation(Reference.class) != null)) {
@@ -535,17 +530,26 @@ private void readAssociation(Association association, P
if (conversionService.canConvert(DocumentPointer.class, property.getActualType())) {
+ if(value == null) {
+ return;
+ }
+
DocumentPointer> pointer = () -> value;
// collection like special treatment
accessor.setProperty(property, conversionService.convert(pointer, property.getActualType()));
} else {
+
accessor.setProperty(property,
- dbRefResolver.resolveReference(property, value, referenceLookupDelegate, context::convert));
+ dbRefResolver.resolveReference(property, new DocumentReferenceSource(documentAccessor.getDocument(), documentAccessor.get(property)), referenceLookupDelegate, context::convert));
}
return;
}
+ if (value == null) {
+ return;
+ }
+
DBRef dbref = value instanceof DBRef ? (DBRef) value : null;
accessor.setProperty(property, dbRefResolver.resolveDbRef(property, dbref, callback, handler));
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
index 3ca730452f..e16f9024b5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
@@ -87,17 +87,20 @@ public ReferenceLookupDelegate(
* Read the reference expressed by the given property.
*
* @param property the reference defining property. Must not be {@literal null}. THe
- * @param value the source value identifying to the referenced entity. Must not be {@literal null}.
+ * @param source the source value identifying to the referenced entity. Must not be {@literal null}.
* @param lookupFunction to execute a lookup query. Must not be {@literal null}.
* @param entityReader the callback to convert raw source values into actual domain types. Must not be
* {@literal null}.
* @return can be {@literal null}.
*/
@Nullable
- public Object readReference(MongoPersistentProperty property, Object value, LookupFunction lookupFunction,
+ public Object readReference(MongoPersistentProperty property, Object source, LookupFunction lookupFunction,
MongoEntityReader entityReader) {
- DocumentReferenceQuery filter = computeFilter(property, value, spELContext);
+ Object value = source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getTargetSource()
+ : source;
+
+ DocumentReferenceQuery filter = computeFilter(property, source, spELContext);
ReferenceCollection referenceCollection = computeReferenceContext(property, value, spELContext);
Iterable result = lookupFunction.apply(filter, referenceCollection);
@@ -196,8 +199,16 @@ private T parseValueOrGet(String value, ParameterBindingContext bindingConte
ParameterBindingContext bindingContext(MongoPersistentProperty property, Object source, SpELContext spELContext) {
- return new ParameterBindingContext(valueProviderFor(source), spELContext.getParser(),
+ ValueProvider valueProvider;
+ if (source instanceof DocumentReferenceSource) {
+ valueProvider = valueProviderFor(((DocumentReferenceSource) source).getTargetSource());
+ } else {
+ valueProvider = valueProviderFor(source);
+ }
+
+ return new ParameterBindingContext(valueProvider, spELContext.getParser(),
() -> evaluationContextFor(property, source, spELContext));
+
}
ValueProvider valueProviderFor(Object source) {
@@ -212,9 +223,18 @@ ValueProvider valueProviderFor(Object source) {
EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object source, SpELContext spELContext) {
- EvaluationContext ctx = spELContext.getEvaluationContext(source);
- ctx.setVariable("target", source);
- ctx.setVariable(property.getName(), source);
+ Object target = source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getTargetSource()
+ : source;
+
+ if (target == null) {
+ target = new Document();
+ }
+
+ EvaluationContext ctx = spELContext.getEvaluationContext(target);
+ ctx.setVariable("target", target);
+ ctx.setVariable("self",
+ source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getSelf() : source);
+ ctx.setVariable(property.getName(), target);
return ctx;
}
@@ -223,22 +243,30 @@ EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object
* Compute the query to retrieve linked documents.
*
* @param property must not be {@literal null}.
- * @param value must not be {@literal null}.
+ * @param source must not be {@literal null}.
* @param spELContext must not be {@literal null}.
* @return never {@literal null}.
*/
@SuppressWarnings("unchecked")
- DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object value, SpELContext spELContext) {
+ DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object source, SpELContext spELContext) {
DocumentReference documentReference = property.isDocumentReference() ? property.getDocumentReference()
: ReferenceEmulatingDocumentReference.INSTANCE;
String lookup = documentReference.lookup();
- Document sort = parseValueOrGet(documentReference.sort(), bindingContext(property, value, spELContext),
+ Object value = source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getTargetSource()
+ : source;
+
+ Document sort = parseValueOrGet(documentReference.sort(), bindingContext(property, source, spELContext),
() -> new Document());
- if (property.isCollectionLike() && value instanceof Collection) {
+ if (property.isCollectionLike() && (value instanceof Collection || value == null)) {
+
+ if (value == null) {
+ return new ListDocumentReferenceQuery(codec.decode(lookup, bindingContext(property, source, spELContext)),
+ sort);
+ }
List ors = new ArrayList<>();
for (Object entry : (Collection) value) {
@@ -263,7 +291,7 @@ DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object va
return new MapDocumentReferenceQuery(new Document("$or", filterMap.values()), sort, filterMap);
}
- return new SingleDocumentReferenceQuery(codec.decode(lookup, bindingContext(property, value, spELContext)), sort);
+ return new SingleDocumentReferenceQuery(codec.decode(lookup, bindingContext(property, source, spELContext)), sort);
}
enum ReferenceEmulatingDocumentReference implements DocumentReference {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
index d6bcc10e49..06d288d1f5 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
@@ -39,6 +39,7 @@
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id;
+import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.annotation.Reference;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mongodb.core.convert.LazyLoadingTestUtils;
@@ -1049,7 +1050,34 @@ void updateWhenUsingAtReferenceDirectly() {
});
assertThat(target).containsEntry("publisher", "p-1");
+ }
+
+ @Test // GH-3798
+ void allowsOneToMayStyleLookupsUsingSelfVariable() {
+
+ OneToManyStyleBook book1 = new OneToManyStyleBook();
+ book1.id = "id-1";
+ book1.publisherId = "p-100";
+
+ OneToManyStyleBook book2 = new OneToManyStyleBook();
+ book2.id = "id-2";
+ book2.publisherId = "p-200";
+
+ OneToManyStyleBook book3 = new OneToManyStyleBook();
+ book3.id = "id-3";
+ book3.publisherId = "p-100";
+
+ template.save(book1);
+ template.save(book2);
+ template.save(book3);
+ OneToManyStylePublisher publisher = new OneToManyStylePublisher();
+ publisher.id = "p-100";
+
+ template.save(publisher);
+
+ OneToManyStylePublisher target = template.findOne(query(where("id").is(publisher.id)), OneToManyStylePublisher.class);
+ assertThat(target.books).containsExactlyInAnyOrder(book1, book3);
}
@Data
@@ -1293,4 +1321,24 @@ static class UsingAtReference {
@Reference //
Publisher publisher;
}
+
+ @Data
+ static class OneToManyStyleBook {
+
+ @Id
+ String id;
+
+ private String publisherId;
+ }
+
+ @Data
+ static class OneToManyStylePublisher {
+
+ @Id
+ String id;
+
+ @ReadOnlyProperty
+ @DocumentReference(lookup="{'publisherId':?#{#self._id} }")
+ List books;
+ }
}
diff --git a/src/main/asciidoc/reference/document-references.adoc b/src/main/asciidoc/reference/document-references.adoc
index 885d2d6ade..23bc025e80 100644
--- a/src/main/asciidoc/reference/document-references.adoc
+++ b/src/main/asciidoc/reference/document-references.adoc
@@ -262,6 +262,62 @@ class Publisher {
<2> The field value placeholders of the lookup query (like `acc`) is used to form the reference document.
====
+It is also possible to model relational style _One-To-Many_ references using a combination of `@ReadonlyProperty` and `@DocumentReference`.
+This approach allows to link types without explicitly storing the linking values within the document itself as shown in the snipped below.
+
+====
+[source,java]
+----
+@Document
+class Book {
+
+ @Id
+ ObjectId id;
+ String title;
+ List author;
+
+ ObjectId publisherId; <1>
+}
+
+@Document
+class Publisher {
+
+ @Id
+ ObjectId id;
+ String acronym;
+ String name;
+
+ @ReadOnlyProperty <2>
+ @DocumentReference(lookup="{'publisherId':?#{#self._id} }") <3>
+ List books;
+}
+----
+
+.`Book` document
+[source,json]
+----
+{
+ "_id" : 9a48e32,
+ "title" : "The Warded Man",
+ "author" : ["Peter V. Brett"],
+ "publisherId" : 8cfb002
+}
+----
+
+.`Publisher` document
+[source,json]
+----
+{
+ "_id" : 8cfb002,
+ "acronym" : "DR",
+ "name" : "Del Rey"
+}
+----
+<1> Set up the link from `Book` to `Publisher` by storing the `Publisher.id` within the `Book` document.
+<2> Mark the property holding the references to be read only. This prevents storing references to individual ``Book``s with the `Publisher` document.
+<3> Use the `#self` variable to access values within the `Publisher` document and in this retrieve `Books` with matching `publisherId`.
+====
+
With all the above in place it is possible to model all kind of associations between entities.
Have a look at the non-exhaustive list of samples below to get feeling for what is possible.
From 977e5e4c5c877e17a25de14bb47f98f8fa802161 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 8 Sep 2021 13:50:54 +0200
Subject: [PATCH 055/920] Polishing.
Tweak reference documentation wording. Extract self/target source dereferencing into utility methods.
See: #3798
Original pull request: #3802.
---
.../core/convert/DocumentReferenceSource.java | 25 +++++++++++++++++--
.../core/convert/ReferenceLookupDelegate.java | 17 +++----------
.../reference/document-references.adoc | 7 +++---
3 files changed, 31 insertions(+), 18 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
index 03e5eb0d5d..89d7360e4d 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentReferenceSource.java
@@ -28,11 +28,11 @@ public class DocumentReferenceSource {
private final Object self;
- @Nullable private final Object targetSource;
+ private final @Nullable Object targetSource;
/**
* Create a new instance of {@link DocumentReferenceSource}.
- *
+ *
* @param self the entire wrapper object holding references. Must not be {@literal null}.
* @param targetSource the reference value source.
*/
@@ -60,4 +60,25 @@ public Object getSelf() {
public Object getTargetSource() {
return targetSource;
}
+
+ /**
+ * Dereference a {@code targetSource} if it is a {@link DocumentReferenceSource} or return {@code source} otherwise.
+ *
+ * @param source
+ * @return
+ */
+ @Nullable
+ static Object getTargetSource(Object source) {
+ return source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getTargetSource() : source;
+ }
+
+ /**
+ * Dereference a {@code self} object if it is a {@link DocumentReferenceSource} or return {@code self} otherwise.
+ *
+ * @param self
+ * @return
+ */
+ static Object getSelf(Object self) {
+ return self instanceof DocumentReferenceSource ? ((DocumentReferenceSource) self).getSelf() : self;
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
index e16f9024b5..36ccc23a6b 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
@@ -174,7 +174,6 @@ private ReferenceCollection computeReferenceContext(MongoPersistentProperty prop
* @param
* @return can be {@literal null}.
*/
- @Nullable
@SuppressWarnings("unchecked")
private T parseValueOrGet(String value, ParameterBindingContext bindingContext, Supplier defaultValue) {
@@ -199,16 +198,10 @@ private T parseValueOrGet(String value, ParameterBindingContext bindingConte
ParameterBindingContext bindingContext(MongoPersistentProperty property, Object source, SpELContext spELContext) {
- ValueProvider valueProvider;
- if (source instanceof DocumentReferenceSource) {
- valueProvider = valueProviderFor(((DocumentReferenceSource) source).getTargetSource());
- } else {
- valueProvider = valueProviderFor(source);
- }
+ ValueProvider valueProvider = valueProviderFor(DocumentReferenceSource.getTargetSource(source));
return new ParameterBindingContext(valueProvider, spELContext.getParser(),
() -> evaluationContextFor(property, source, spELContext));
-
}
ValueProvider valueProviderFor(Object source) {
@@ -232,8 +225,7 @@ EvaluationContext evaluationContextFor(MongoPersistentProperty property, Object
EvaluationContext ctx = spELContext.getEvaluationContext(target);
ctx.setVariable("target", target);
- ctx.setVariable("self",
- source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getSelf() : source);
+ ctx.setVariable("self", DocumentReferenceSource.getSelf(source));
ctx.setVariable(property.getName(), target);
return ctx;
@@ -255,11 +247,10 @@ DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object so
String lookup = documentReference.lookup();
- Object value = source instanceof DocumentReferenceSource ? ((DocumentReferenceSource) source).getTargetSource()
- : source;
+ Object value = DocumentReferenceSource.getTargetSource(source);
Document sort = parseValueOrGet(documentReference.sort(), bindingContext(property, source, spELContext),
- () -> new Document());
+ Document::new);
if (property.isCollectionLike() && (value instanceof Collection || value == null)) {
diff --git a/src/main/asciidoc/reference/document-references.adoc b/src/main/asciidoc/reference/document-references.adoc
index 23bc025e80..b7d55678a5 100644
--- a/src/main/asciidoc/reference/document-references.adoc
+++ b/src/main/asciidoc/reference/document-references.adoc
@@ -263,7 +263,7 @@ class Publisher {
====
It is also possible to model relational style _One-To-Many_ references using a combination of `@ReadonlyProperty` and `@DocumentReference`.
-This approach allows to link types without explicitly storing the linking values within the document itself as shown in the snipped below.
+This approach allows link types without storing the linking values within the owning document but rather on the referencing document as shown in the example below.
====
[source,java]
@@ -313,8 +313,9 @@ class Publisher {
"name" : "Del Rey"
}
----
-<1> Set up the link from `Book` to `Publisher` by storing the `Publisher.id` within the `Book` document.
-<2> Mark the property holding the references to be read only. This prevents storing references to individual ``Book``s with the `Publisher` document.
+<1> Set up the link from `Book` (reference) to `Publisher` (owner) by storing the `Publisher.id` within the `Book` document.
+<2> Mark the property holding the references to be readonly.
+This prevents storing references to individual ``Book``s with the `Publisher` document.
<3> Use the `#self` variable to access values within the `Publisher` document and in this retrieve `Books` with matching `publisherId`.
====
From ada7e199a4dcbc2cea45d6c0e13d5a9cb8fde7b5 Mon Sep 17 00:00:00 2001
From: Oliver Drotbohm
Date: Tue, 7 Sep 2021 14:54:12 +0200
Subject: [PATCH 056/920] Properly detect all supported identifier annotations
as explicitly annotated.
We now simply delegate to AnnotationBasedPersistentProperty.isIdProperty() for the detection of annotated identifiers. The previous, manual identifier check was preventing additional identifier annotations, supported by ABP, to be considered, too.
Fixes #3803.
---
spring-data-mongodb/pom.xml | 9 +++++++++
.../mapping/BasicMongoPersistentProperty.java | 3 +--
.../BasicMongoPersistentPropertyUnitTests.java | 15 ++++++++++++++-
3 files changed, 24 insertions(+), 3 deletions(-)
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 1f157e75bc..2f73c10eba 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -317,6 +317,15 @@
test
+
+
+
+ org.jmolecules
+ jmolecules-ddd
+ ${jmolecules}
+ test
+
+
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
index 87eb56b732..1315757896 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
@@ -22,7 +22,6 @@
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
@@ -115,7 +114,7 @@ public boolean isIdProperty() {
*/
@Override
public boolean isExplicitIdProperty() {
- return isAnnotationPresent(Id.class);
+ return super.isIdProperty();
}
/**
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
index bbcb8dada0..fffa861914 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
@@ -28,9 +28,9 @@
import org.bson.Document;
import org.bson.types.ObjectId;
+import org.jmolecules.ddd.annotation.Identity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.MappingException;
@@ -241,6 +241,15 @@ void fieldTypeShouldBeDocumentForPropertiesAnnotatedIdWhenAComplexTypeAndFieldTy
assertThat(property.getFieldType()).isEqualTo(Document.class);
}
+ @Test
+ void considersJMoleculesIdentityExplicitlyAnnotatedIdentifier() {
+
+ MongoPersistentProperty property = getPropertyFor(WithJMoleculesIdentity.class, "identifier");
+
+ assertThat(property.isIdProperty()).isTrue();
+ assertThat(property.isExplicitIdProperty()).isTrue();
+ }
+
private MongoPersistentProperty getPropertyFor(Field field) {
return getPropertyFor(entity, field);
}
@@ -369,4 +378,8 @@ static class WithComplexId {
@Id @org.springframework.data.mongodb.core.mapping.Field ComplexId id;
}
+
+ static class WithJMoleculesIdentity {
+ @Identity ObjectId identifier;
+ }
}
From cba7eaba4c442c426c3cba15be5d2c7073ebdb16 Mon Sep 17 00:00:00 2001
From: Oliver Drotbohm
Date: Tue, 7 Sep 2021 14:54:35 +0200
Subject: [PATCH 057/920] Polishing.
Formatting and indentation in parent project's pom.xml.
See #3803
---
pom.xml | 8 ++++----
.../mapping/BasicMongoPersistentPropertyUnitTests.java | 3 +--
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/pom.xml b/pom.xml
index 5d28c8a5c5..4aa47bbf2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -141,11 +141,11 @@
sonatype-libs-snapshothttps://oss.sonatype.org/content/repositories/snapshots
- false
-
+ false
+
- true
-
+ true
+
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
index fffa861914..66ae0199fc 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
@@ -356,8 +356,7 @@ static class DocumentWithComposedAnnotations {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Id
- static @interface ComposedIdAnnotation {
- }
+ static @interface ComposedIdAnnotation {}
static class WithStringMongoId {
From 061c28f84ac8c8a302dffc6e3d7264d3703a0f6d Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 8 Sep 2021 13:58:37 +0200
Subject: [PATCH 058/920] Polishing.
Add ticket reference to tests.
See #3803
---
.../core/mapping/BasicMongoPersistentPropertyUnitTests.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
index 66ae0199fc..d731854a02 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentPropertyUnitTests.java
@@ -241,7 +241,7 @@ void fieldTypeShouldBeDocumentForPropertiesAnnotatedIdWhenAComplexTypeAndFieldTy
assertThat(property.getFieldType()).isEqualTo(Document.class);
}
- @Test
+ @Test // GH-3803
void considersJMoleculesIdentityExplicitlyAnnotatedIdentifier() {
MongoPersistentProperty property = getPropertyFor(WithJMoleculesIdentity.class, "identifier");
From 4e960a968288833f6e8ca6a8ce429eef226972a4 Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Wed, 8 Sep 2021 09:24:15 +0200
Subject: [PATCH 059/920] Fix document reference on empty reference arrays.
This commit fixes an issue caused by empty reference arrays.
Closes #3805
Original pull request: #3807.
---
.../core/convert/ReferenceLookupDelegate.java | 9 ++++-
.../MongoTemplateDocumentReferenceTests.java | 39 +++++++++++++++++++
2 files changed, 47 insertions(+), 1 deletion(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
index 36ccc23a6b..a2726e6338 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
@@ -19,6 +19,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -122,7 +123,9 @@ private ReferenceCollection computeReferenceContext(MongoPersistentProperty prop
// Use the first value as a reference for others in case of collection like
if (value instanceof Iterable) {
- value = ((Iterable>) value).iterator().next();
+
+ Iterator iterator = ((Iterable) value).iterator();
+ value = iterator.hasNext() ? iterator.next() : new Document();
}
// handle DBRef value
@@ -266,6 +269,10 @@ DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object so
ors.add(decoded);
}
+ if(ors.isEmpty()) {
+ return new ListDocumentReferenceQuery(new Document("_id", new Document("$exists", false)), sort);
+ }
+
return new ListDocumentReferenceQuery(new Document("$or", ors), sort);
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
index 06d288d1f5..2b96b3dc22 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
@@ -25,6 +25,7 @@
import lombok.Setter;
import lombok.ToString;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -679,6 +680,41 @@ void loadCollectionReferenceWithMissingRefs() {
assertThat(result.getSimpleValueRef()).containsExactly(new SimpleObjectRef("ref-2", "me-the-2-referenced-object"));
}
+ @Test // GH-3805
+ void loadEmptyCollectionReference() {
+
+ String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
+
+ // an empty reference array.
+ Document source = new Document("_id", "id-1").append("value", "v1").append("simplePreinitializedValueRef",
+ Collections.emptyList());
+
+ template.execute(db -> {
+ db.getCollection(rootCollectionName).insertOne(source);
+ return null;
+ });
+
+ CollectionRefRoot result = template.findOne(query(where("id").is("id-1")), CollectionRefRoot.class);
+ assertThat(result.simplePreinitializedValueRef).isEmpty();
+ }
+
+ @Test // GH-3805
+ void loadNoExistingCollectionReference() {
+
+ String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
+
+ // no reference array at all
+ Document source = new Document("_id", "id-1").append("value", "v1");
+
+ template.execute(db -> {
+ db.getCollection(rootCollectionName).insertOne(source);
+ return null;
+ });
+
+ CollectionRefRoot result = template.findOne(query(where("id").is("id-1")), CollectionRefRoot.class);
+ assertThat(result.simplePreinitializedValueRef).isEmpty();
+ }
+
@Test // GH-3602
void queryForReference() {
@@ -1122,6 +1158,9 @@ static class CollectionRefRoot {
@DocumentReference(lookup = "{ '_id' : '?#{#target}' }") //
List simpleValueRef;
+ @DocumentReference
+ List simplePreinitializedValueRef = new ArrayList<>();
+
@DocumentReference(lookup = "{ '_id' : '?#{#target}' }", sort = "{ '_id' : -1 } ") //
List simpleSortedValueRef;
From 270456ed81ae3a11d08ec6a3a3bffd8eca9b8d77 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 8 Sep 2021 14:18:17 +0200
Subject: [PATCH 060/920] Polishing.
Extract query that yields no hits into constant. Guard Map-typed reference properties against empty $or.
See #3805
Original pull request: #3807.
---
.../core/convert/ReferenceLookupDelegate.java | 25 +++++++++++++------
.../MongoTemplateDocumentReferenceTests.java | 21 ++++++++++++++++
2 files changed, 38 insertions(+), 8 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
index a2726e6338..dbbdbe99eb 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/ReferenceLookupDelegate.java
@@ -62,6 +62,8 @@
*/
public final class ReferenceLookupDelegate {
+ private static final Document NO_RESULTS_PREDICATE = new Document("_id", new Document("$exists", false));
+
private final MappingContext extends MongoPersistentEntity>, MongoPersistentProperty> mappingContext;
private final SpELContext spELContext;
private final ParameterBindingDocumentCodec codec;
@@ -262,15 +264,17 @@ DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object so
sort);
}
- List ors = new ArrayList<>();
- for (Object entry : (Collection) value) {
+ Collection objects = (Collection) value;
- Document decoded = codec.decode(lookup, bindingContext(property, entry, spELContext));
- ors.add(decoded);
+ if (objects.isEmpty()) {
+ return new ListDocumentReferenceQuery(NO_RESULTS_PREDICATE, sort);
}
- if(ors.isEmpty()) {
- return new ListDocumentReferenceQuery(new Document("_id", new Document("$exists", false)), sort);
+ List ors = new ArrayList<>(objects.size());
+ for (Object entry : objects) {
+
+ Document decoded = codec.decode(lookup, bindingContext(property, entry, spELContext));
+ ors.add(decoded);
}
return new ListDocumentReferenceQuery(new Document("$or", ors), sort);
@@ -278,9 +282,14 @@ DocumentReferenceQuery computeFilter(MongoPersistentProperty property, Object so
if (property.isMap() && value instanceof Map) {
- Map filterMap = new LinkedHashMap<>();
+ Set> entries = ((Map) value).entrySet();
+ if (entries.isEmpty()) {
+ return new MapDocumentReferenceQuery(NO_RESULTS_PREDICATE, sort, Collections.emptyMap());
+ }
+
+ Map filterMap = new LinkedHashMap<>(entries.size());
- for (Entry entry : ((Map) value).entrySet()) {
+ for (Entry entry : entries) {
Document decoded = codec.decode(lookup, bindingContext(property, entry.getValue(), spELContext));
filterMap.put(entry.getKey(), decoded);
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
index 2b96b3dc22..c63e7a1115 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
@@ -698,6 +698,24 @@ void loadEmptyCollectionReference() {
assertThat(result.simplePreinitializedValueRef).isEmpty();
}
+ @Test // GH-3805
+ void loadEmptyMapReference() {
+
+ String rootCollectionName = template.getCollectionName(CollectionRefRoot.class);
+
+ // an empty reference array.
+ Document source = new Document("_id", "id-1").append("value", "v1").append("simplePreinitializedMapRef",
+ new Document());
+
+ template.execute(db -> {
+ db.getCollection(rootCollectionName).insertOne(source);
+ return null;
+ });
+
+ CollectionRefRoot result = template.findOne(query(where("id").is("id-1")), CollectionRefRoot.class);
+ assertThat(result.simplePreinitializedMapRef).isEmpty();
+ }
+
@Test // GH-3805
void loadNoExistingCollectionReference() {
@@ -1167,6 +1185,9 @@ static class CollectionRefRoot {
@DocumentReference(lookup = "{ '_id' : '?#{#target}' }") //
Map mapValueRef;
+ @DocumentReference //
+ Map simplePreinitializedMapRef = new LinkedHashMap<>();
+
@Field("simple-value-ref-annotated-field-name") //
@DocumentReference(lookup = "{ '_id' : '?#{#target}' }") //
List simpleValueRefWithAnnotatedFieldName;
From f128e6df152bc559bbae6e07592307d3f3fc402d Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Wed, 8 Sep 2021 10:29:02 +0200
Subject: [PATCH 061/920] Fix `@DocumentReference` resolution for properties
used in constructor.
This commit fixes an issue that prevented referenced entities from being used as constructor arguments.
Closes: #3806
Original pull request: #3810.
---
.../core/convert/MappingMongoConverter.java | 25 +++--
.../MongoTemplateDocumentReferenceTests.java | 106 ++++++++++++++++++
2 files changed, 124 insertions(+), 7 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index 5a2c3e952a..07709df365 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -530,7 +530,7 @@ private void readAssociation(Association association, P
if (conversionService.canConvert(DocumentPointer.class, property.getActualType())) {
- if(value == null) {
+ if (value == null) {
return;
}
@@ -541,7 +541,9 @@ private void readAssociation(Association association, P
} else {
accessor.setProperty(property,
- dbRefResolver.resolveReference(property, new DocumentReferenceSource(documentAccessor.getDocument(), documentAccessor.get(property)), referenceLookupDelegate, context::convert));
+ dbRefResolver.resolveReference(property,
+ new DocumentReferenceSource(documentAccessor.getDocument(), documentAccessor.get(property)),
+ referenceLookupDelegate, context::convert));
}
return;
}
@@ -875,10 +877,12 @@ protected List createCollection(Collection> collection, MongoPersisten
if (property.isAssociation()) {
List targetCollection = collection.stream().map(it -> {
- return documentPointerFactory.computePointer(mappingContext, property, it, property.getActualType()).getPointer();
+ return documentPointerFactory.computePointer(mappingContext, property, it, property.getActualType())
+ .getPointer();
}).collect(Collectors.toList());
- return writeCollectionInternal(targetCollection, ClassTypeInformation.from(DocumentPointer.class), new ArrayList<>());
+ return writeCollectionInternal(targetCollection, ClassTypeInformation.from(DocumentPointer.class),
+ new ArrayList<>());
}
if (property.hasExplicitWriteTarget()) {
@@ -931,7 +935,8 @@ protected Bson createMap(Map map, MongoPersistentProperty proper
if (property.isDbReference()) {
document.put(simpleKey, value != null ? createDBRef(value, property) : null);
} else {
- document.put(simpleKey, documentPointerFactory.computePointer(mappingContext, property, value, property.getActualType()).getPointer());
+ document.put(simpleKey, documentPointerFactory
+ .computePointer(mappingContext, property, value, property.getActualType()).getPointer());
}
} else {
@@ -1814,6 +1819,11 @@ public T getPropertyValue(MongoPersistentProperty property) {
return (T) dbRefResolver.resolveDbRef(property, dbref, callback, dbRefProxyHandler);
}
+ if (property.isDocumentReference()) {
+ return (T) dbRefResolver.resolveReference(property, accessor.get(property), referenceLookupDelegate,
+ context::convert);
+ }
+
return super.getPropertyValue(property);
}
}
@@ -2036,7 +2046,7 @@ public S convert(Object source, TypeInformation extends S>
if (typeHint.isMap()) {
- if(ClassUtils.isAssignable(Document.class, typeHint.getType())) {
+ if (ClassUtils.isAssignable(Document.class, typeHint.getType())) {
return (S) documentConverter.convert(this, BsonUtils.asBson(source), typeHint);
}
@@ -2044,7 +2054,8 @@ public S convert(Object source, TypeInformation extends S>
return (S) mapConverter.convert(this, BsonUtils.asBson(source), typeHint);
}
- throw new IllegalArgumentException(String.format("Expected map like structure but found %s", source.getClass()));
+ throw new IllegalArgumentException(
+ String.format("Expected map like structure but found %s", source.getClass()));
}
if (source instanceof DBRef) {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
index c63e7a1115..3abd3a3add 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateDocumentReferenceTests.java
@@ -733,6 +733,52 @@ void loadNoExistingCollectionReference() {
assertThat(result.simplePreinitializedValueRef).isEmpty();
}
+ @Test // GH-3806
+ void resolveReferenceWhenUsedAsCtorArgument() {
+
+ Publisher publisher = new Publisher();
+ publisher.id = "p-111";
+ publisher.name = "ppp";
+
+ template.save(publisher);
+
+ WithRequiredArgsCtor source = new WithRequiredArgsCtor("id-1", publisher);
+
+ template.save(source);
+
+ WithRequiredArgsCtor target = template.findOne(query(where("id").is(source.id)), WithRequiredArgsCtor.class);
+ assertThat(target.publisher).isNotNull();
+ }
+
+ @Test // GH-3806
+ void resolveLazyReferenceWhenUsedAsCtorArgument() {
+
+ Publisher publisher = new Publisher();
+ publisher.id = "p-111";
+ publisher.name = "ppp";
+
+ template.save(publisher);
+
+ WithLazyRequiredArgsCtor source = new WithLazyRequiredArgsCtor("id-1", publisher);
+
+ template.save(source);
+
+ WithLazyRequiredArgsCtor target = template.findOne(query(where("id").is(source.id)), WithLazyRequiredArgsCtor.class);
+
+ // proxy not yet resolved
+ LazyLoadingTestUtils.assertProxy(target.publisher, (proxy) -> {
+
+ assertThat(proxy.isResolved()).isFalse();
+ assertThat(proxy.currentValue()).isNull();
+ });
+
+ // resolve the proxy by invoking a method on it
+ assertThat(target.getPublisher().getName()).isEqualTo("ppp");
+ LazyLoadingTestUtils.assertProxy(target.publisher, (proxy) -> {
+ assertThat(proxy.isResolved()).isTrue();
+ });
+ }
+
@Test // GH-3602
void queryForReference() {
@@ -1371,6 +1417,30 @@ static class Publisher {
String id;
String acronym;
String name;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getAcronym() {
+ return acronym;
+ }
+
+ public void setAcronym(String acronym) {
+ this.acronym = acronym;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
}
@Data
@@ -1401,4 +1471,40 @@ static class OneToManyStylePublisher {
@DocumentReference(lookup="{'publisherId':?#{#self._id} }")
List books;
}
+
+ static class WithRequiredArgsCtor {
+
+ final String id;
+
+ @DocumentReference
+ final Publisher publisher;
+
+ public WithRequiredArgsCtor(String id, Publisher publisher) {
+
+ this.id = id;
+ this.publisher = publisher;
+ }
+ }
+
+ static class WithLazyRequiredArgsCtor {
+
+ final String id;
+
+ @DocumentReference(lazy = true)
+ final Publisher publisher;
+
+ public WithLazyRequiredArgsCtor(String id, Publisher publisher) {
+
+ this.id = id;
+ this.publisher = publisher;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public Publisher getPublisher() {
+ return publisher;
+ }
+ }
}
From 9014f770d8027c4e1bb35fa91b80d16ac4f6e09e Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Wed, 8 Sep 2021 13:33:46 +0200
Subject: [PATCH 062/920] Fix slice argument in query fields projection.
We now use a Collection instead of an Array to pass on $slice projection values for offset and limit.
Closes: #3811
Original pull request: #3812.
---
.../data/mongodb/core/query/Field.java | 3 ++-
.../data/mongodb/core/MongoTemplateTests.java | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java
index 0561bbdca6..02450505b6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Field.java
@@ -15,6 +15,7 @@
*/
package org.springframework.data.mongodb.core.query;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
@@ -192,7 +193,7 @@ public Field slice(String field, int size) {
*/
public Field slice(String field, int offset, int size) {
- slices.put(field, new Integer[] { offset, size });
+ slices.put(field, Arrays.asList(offset, size));
return this;
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
index 28cdaa4830..33ae0ef994 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
@@ -3768,6 +3768,23 @@ void shouldFindSubdocumentWithNullCorrectly() {
assertThat(loaded).isNotNull();
}
+ @Test // GH-3811
+ public void sliceShouldLimitCollectionValues() {
+
+ DocumentWithCollectionOfSimpleType source = new DocumentWithCollectionOfSimpleType();
+ source.id = "id-1";
+ source.values = Arrays.asList("spring", "data", "mongodb");
+
+ template.save(source);
+
+ Criteria criteria = Criteria.where("id").is(source.id);
+ Query query = Query.query(criteria);
+ query.fields().slice("values", 0, 1);
+ DocumentWithCollectionOfSimpleType target = template.findOne(query, DocumentWithCollectionOfSimpleType.class);
+
+ assertThat(target.values).containsExactly("spring");
+ }
+
private AtomicReference createAfterSaveReference() {
AtomicReference saved = new AtomicReference<>();
From 8fb0e1326b3a33591fca6c7a6ace8fb2088a91ec Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 8 Sep 2021 10:03:45 +0200
Subject: [PATCH 063/920] Introduce `SessionSynchronization.NEVER` to disable
transactional participation.
SessionSynchronization.NEVER bypasses all transactional integration in cases where applications do not want to make use of transactions so that transaction inspection overhead is avoided.
Closes: #3760
Original Pull Request: #3809
---
.../data/mongodb/MongoDatabaseUtils.java | 3 ++-
.../mongodb/ReactiveMongoDatabaseUtils.java | 4 ++++
.../data/mongodb/SessionSynchronization.java | 20 +++++++++++++---
.../mongodb/MongoDatabaseUtilsUnitTests.java | 24 +++++++++++++++++++
.../ReactiveMongoDatabaseUtilsUnitTests.java | 14 +++++++++++
5 files changed, 61 insertions(+), 4 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
index ba8efa536c..c9342ec4f6 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDatabaseUtils.java
@@ -104,7 +104,8 @@ private static MongoDatabase doGetMongoDatabase(@Nullable String dbName, MongoDa
Assert.notNull(factory, "Factory must not be null!");
- if (!TransactionSynchronizationManager.isSynchronizationActive()) {
+ if (sessionSynchronization == SessionSynchronization.NEVER
+ || !TransactionSynchronizationManager.isSynchronizationActive()) {
return StringUtils.hasText(dbName) ? factory.getMongoDatabase(dbName) : factory.getMongoDatabase();
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
index 711947a30d..4699ac56c2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtils.java
@@ -138,6 +138,10 @@ private static Mono doGetMongoDatabase(@Nullable String dbName, R
Assert.notNull(factory, "DatabaseFactory must not be null!");
+ if (sessionSynchronization == SessionSynchronization.NEVER) {
+ return getMongoDatabaseOrDefault(dbName, factory);
+ }
+
return TransactionSynchronizationManager.forCurrentTransaction()
.filter(TransactionSynchronizationManager::isSynchronizationActive) //
.flatMap(synchronizationManager -> {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionSynchronization.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionSynchronization.java
index 2223b82391..144d3d3cb3 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionSynchronization.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/SessionSynchronization.java
@@ -15,13 +15,20 @@
*/
package org.springframework.data.mongodb;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
+
/**
- * {@link SessionSynchronization} is used along with {@link org.springframework.data.mongodb.core.MongoTemplate} to
- * define in which type of transactions to participate if any.
+ * {@link SessionSynchronization} is used along with {@code MongoTemplate} to define in which type of transactions to
+ * participate if any.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 2.1
+ * @see MongoTemplate#setSessionSynchronization(SessionSynchronization)
+ * @see MongoDatabaseUtils#getDatabase(MongoDatabaseFactory, SessionSynchronization)
+ * @see ReactiveMongoTemplate#setSessionSynchronization(SessionSynchronization)
+ * @see ReactiveMongoDatabaseUtils#getDatabase(ReactiveMongoDatabaseFactory, SessionSynchronization)
*/
public enum SessionSynchronization {
@@ -34,5 +41,12 @@ public enum SessionSynchronization {
/**
* Synchronize with native MongoDB transactions initiated via {@link MongoTransactionManager}.
*/
- ON_ACTUAL_TRANSACTION;
+ ON_ACTUAL_TRANSACTION,
+
+ /**
+ * Do not participate in ongoing transactions.
+ *
+ * @since 3.2.5
+ */
+ NEVER;
}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java
index 8cb222f0e6..5b0cd81cc2 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/MongoDatabaseUtilsUnitTests.java
@@ -109,6 +109,30 @@ void shouldNotStartSessionWhenNoTransactionOngoing() {
verify(dbFactory, never()).withSession(any(ClientSession.class));
}
+ @Test // GH-3760
+ void shouldJustReturnDatabaseIfSessionSynchronizationDisabled() throws Exception {
+
+ when(dbFactory.getMongoDatabase()).thenReturn(db);
+
+ JtaTransactionManager txManager = new JtaTransactionManager(userTransaction);
+ TransactionTemplate txTemplate = new TransactionTemplate(txManager);
+
+ txTemplate.execute(new TransactionCallbackWithoutResult() {
+
+ @Override
+ protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
+
+ MongoDatabaseUtils.getDatabase(dbFactory, SessionSynchronization.NEVER);
+
+ assertThat(TransactionSynchronizationManager.hasResource(dbFactory)).isFalse();
+ }
+ });
+
+ verify(userTransaction).getStatus();
+ verifyNoMoreInteractions(userTransaction);
+ verifyNoInteractions(session);
+ }
+
@Test // DATAMONGO-1920
void shouldParticipateInOngoingJtaTransactionWithCommitWhenSessionSychronizationIsAny() throws Exception {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtilsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtilsUnitTests.java
index 60a7ff9a47..a7393a1392 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtilsUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReactiveMongoDatabaseUtilsUnitTests.java
@@ -88,6 +88,20 @@ void isTransactionActiveShouldLookupTxForActiveTransactionSynchronizationViaTxMa
}).as(StepVerifier::create).expectNext(true).verifyComplete();
}
+ @Test // GH-3760
+ void shouldJustReturnDatabaseIfSessionSynchronizationDisabled() {
+
+ when(databaseFactory.getMongoDatabase()).thenReturn(Mono.just(db));
+
+ ReactiveMongoDatabaseUtils.getDatabase(databaseFactory, SessionSynchronization.NEVER) //
+ .as(StepVerifier::create) //
+ .expectNextCount(1) //
+ .verifyComplete();
+
+ verify(databaseFactory, never()).getSession(any());
+ verify(databaseFactory, never()).withSession(any(ClientSession.class));
+ }
+
@Test // DATAMONGO-2265
void shouldNotStartSessionWhenNoTransactionOngoing() {
From a26e78095745ece93ed6711e62f44c1a80ac8a46 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Wed, 8 Sep 2021 10:04:58 +0200
Subject: [PATCH 064/920] Reduce allocations in query and update mapping.
Introduce EmptyDocument and utility methods in BsonUtils. Avoid entrySet and iterator creation for document iterations/inspections.
Relates to: #3760
Original Pull Request: #3809
---
.../data/mongodb/core/MappedDocument.java | 9 ++
.../data/mongodb/core/QueryOperations.java | 2 +-
.../core/convert/DocumentAccessor.java | 2 +-
.../core/convert/MappingMongoConverter.java | 14 +--
.../mongodb/core/convert/MongoConverter.java | 3 +
.../mongodb/core/convert/QueryMapper.java | 53 +++++++----
.../data/mongodb/core/query/Meta.java | 18 +++-
.../data/mongodb/core/query/Query.java | 33 +++++--
.../data/mongodb/core/query/TextQuery.java | 13 +--
.../data/mongodb/core/query/Update.java | 30 +++++-
.../data/mongodb/util/BsonUtils.java | 63 ++++++++++++
.../data/mongodb/util/EmptyDocument.java | 95 +++++++++++++++++++
.../mongodb/core/MongoTemplateUnitTests.java | 7 +-
.../data/mongodb/core/query/QueryTests.java | 16 ++--
.../query/PartTreeMongoQueryUnitTests.java | 7 +-
15 files changed, 298 insertions(+), 67 deletions(-)
create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java
index 340c11bb99..e3c1f3d64c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappedDocument.java
@@ -156,5 +156,14 @@ public Boolean isIsolated() {
public List getArrayFilters() {
return delegate.getArrayFilters();
}
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.query.UpdateDefinition#hasArrayFilters()
+ */
+ @Override
+ public boolean hasArrayFilters() {
+ return delegate.hasArrayFilters();
+ }
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
index 1ec8fc9366..e9431aa3d2 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/QueryOperations.java
@@ -613,7 +613,7 @@ class UpdateContext extends QueryContext {
UpdateContext(MappedDocument update, boolean upsert) {
- super(new BasicQuery(new Document(BsonUtils.asMap(update.getIdFilter()))));
+ super(new BasicQuery(BsonUtils.asDocument(update.getIdFilter())));
this.multi = false;
this.upsert = upsert;
this.mappedDocument = update;
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java
index 9c94487a3e..0b31f75341 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DocumentAccessor.java
@@ -135,7 +135,7 @@ public Object get(MongoPersistentProperty property) {
*/
@Nullable
public Object getRawId(MongoPersistentEntity> entity) {
- return entity.hasIdProperty() ? get(entity.getRequiredIdProperty()) : BsonUtils.asMap(document).get("_id");
+ return entity.hasIdProperty() ? get(entity.getRequiredIdProperty()) : BsonUtils.get(document, "_id");
}
/**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
index 07709df365..302c3dad45 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
@@ -25,7 +25,6 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -1325,21 +1324,22 @@ protected Map readMap(ConversionContext context, Bson bson, Type
return map;
}
- for (Entry entry : sourceMap.entrySet()) {
+ sourceMap.forEach((k, v) -> {
- if (typeMapper.isTypeKey(entry.getKey())) {
- continue;
+ if (typeMapper.isTypeKey(k)) {
+ return;
}
- Object key = potentiallyUnescapeMapKey(entry.getKey());
+ Object key = potentiallyUnescapeMapKey(k);
if (!rawKeyType.isAssignableFrom(key.getClass())) {
key = doConvert(key, rawKeyType);
}
- Object value = entry.getValue();
+ Object value = v;
map.put(key, value == null ? value : context.convert(value, valueType));
- }
+
+ });
return map;
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
index 20499d3173..aff1b8d8e0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java
@@ -140,6 +140,9 @@ default Object convertId(@Nullable Object id, Class> targetType) {
if (ObjectId.isValid(id.toString())) {
return new ObjectId(id.toString());
}
+
+ // avoid ConversionException as convertToMongoType will return String anyways.
+ return id;
}
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
index e7deb38231..356dd89faa 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
@@ -193,12 +193,11 @@ public Document getMappedSort(Document sortObject, @Nullable MongoPersistentEnti
Assert.notNull(sortObject, "SortObject must not be null!");
if (sortObject.isEmpty()) {
- return new Document();
+ return BsonUtils.EMPTY_DOCUMENT;
}
Document mappedSort = mapFieldsToPropertyNames(sortObject, entity);
- mapMetaAttributes(mappedSort, entity, MetaMapping.WHEN_PRESENT);
- return mappedSort;
+ return mapMetaAttributes(mappedSort, entity, MetaMapping.WHEN_PRESENT);
}
/**
@@ -215,42 +214,51 @@ public Document getMappedFields(Document fieldsObject, @Nullable MongoPersistent
Assert.notNull(fieldsObject, "FieldsObject must not be null!");
Document mappedFields = mapFieldsToPropertyNames(fieldsObject, entity);
- mapMetaAttributes(mappedFields, entity, MetaMapping.FORCE);
- return mappedFields;
+ return mapMetaAttributes(mappedFields, entity, MetaMapping.FORCE);
}
private Document mapFieldsToPropertyNames(Document fields, @Nullable MongoPersistentEntity> entity) {
if (fields.isEmpty()) {
- return new Document();
+ return BsonUtils.EMPTY_DOCUMENT;
}
Document target = new Document();
- for (Map.Entry entry : BsonUtils.asMap(filterUnwrappedObjects(fields, entity)).entrySet()) {
- Field field = createPropertyField(entity, entry.getKey(), mappingContext);
+ BsonUtils.asMap(filterUnwrappedObjects(fields, entity)).forEach((k, v) -> {
+
+ Field field = createPropertyField(entity, k, mappingContext);
if (field.getProperty() != null && field.getProperty().isUnwrapped()) {
- continue;
+ return;
}
- target.put(field.getMappedKey(), entry.getValue());
- }
+ target.put(field.getMappedKey(), v);
+ });
+
return target;
}
- private void mapMetaAttributes(Document source, @Nullable MongoPersistentEntity> entity, MetaMapping metaMapping) {
+ private Document mapMetaAttributes(Document source, @Nullable MongoPersistentEntity> entity,
+ MetaMapping metaMapping) {
if (entity == null) {
- return;
+ return source;
}
if (entity.hasTextScoreProperty() && !MetaMapping.IGNORE.equals(metaMapping)) {
+
+ if (source == BsonUtils.EMPTY_DOCUMENT) {
+ source = new Document();
+ }
+
MongoPersistentProperty textScoreProperty = entity.getTextScoreProperty();
if (MetaMapping.FORCE.equals(metaMapping)
|| (MetaMapping.WHEN_PRESENT.equals(metaMapping) && source.containsKey(textScoreProperty.getFieldName()))) {
source.putAll(getMappedTextScoreField(textScoreProperty));
}
}
+
+ return source;
}
private Document filterUnwrappedObjects(Document fieldsObject, @Nullable MongoPersistentEntity> entity) {
@@ -679,7 +687,7 @@ protected final Entry createMapEntry(Field field, @Nullable Obje
private Entry createMapEntry(String key, @Nullable Object value) {
Assert.hasText(key, "Key must not be null or empty!");
- return Collections.singletonMap(key, value).entrySet().iterator().next();
+ return new AbstractMap.SimpleEntry<>(key, value);
}
private Object createReferenceFor(Object source, MongoPersistentProperty property) {
@@ -733,13 +741,13 @@ protected boolean isNestedKeyword(@Nullable Object candidate) {
return false;
}
- Set keys = BsonUtils.asMap((Bson) candidate).keySet();
+ Map map = BsonUtils.asMap((Bson) candidate);
- if (keys.size() != 1) {
+ if (map.size() != 1) {
return false;
}
- return isKeyword(keys.iterator().next());
+ return isKeyword(map.entrySet().iterator().next().getKey());
}
/**
@@ -823,11 +831,14 @@ public Keyword(Bson source, String key) {
public Keyword(Bson bson) {
- Set keys = BsonUtils.asMap(bson).keySet();
- Assert.isTrue(keys.size() == 1, "Can only use a single value Document!");
+ Map map = BsonUtils.asMap(bson);
+ Assert.isTrue(map.size() == 1, "Can only use a single value Document!");
+
+ Set> entries = map.entrySet();
+ Entry entry = entries.iterator().next();
- this.key = keys.iterator().next();
- this.value = BsonUtils.get(bson, key);
+ this.key = entry.getKey();
+ this.value = entry.getValue();
}
/**
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java
index 2bfddfa2cd..d70a21707f 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Meta.java
@@ -49,8 +49,8 @@ private enum MetaKey {
}
}
- private final Map values = new LinkedHashMap<>(2);
- private final Set flags = new LinkedHashSet<>();
+ private Map values = Collections.emptyMap();
+ private Set flags = Collections.emptySet();
private Integer cursorBatchSize;
private Boolean allowDiskUse;
@@ -63,8 +63,9 @@ public Meta() {}
* @param source
*/
Meta(Meta source) {
- this.values.putAll(source.values);
- this.flags.addAll(source.flags);
+
+ this.values = new LinkedHashMap<>(source.values);
+ this.flags = new LinkedHashSet<>(source.flags);
this.cursorBatchSize = source.cursorBatchSize;
this.allowDiskUse = source.allowDiskUse;
}
@@ -158,6 +159,11 @@ public void setCursorBatchSize(int cursorBatchSize) {
public boolean addFlag(CursorOption option) {
Assert.notNull(option, "CursorOption must not be null!");
+
+ if (this.flags == Collections.EMPTY_SET) {
+ this.flags = new LinkedHashSet<>(2);
+ }
+
return this.flags.add(option);
}
@@ -220,6 +226,10 @@ void setValue(String key, @Nullable Object value) {
Assert.hasText(key, "Meta key must not be 'null' or blank.");
+ if (values == Collections.EMPTY_MAP) {
+ values = new LinkedHashMap<>(2);
+ }
+
if (value == null || (value instanceof String && !StringUtils.hasText((String) value))) {
this.values.remove(key);
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
index 1f54e7049d..ce60798bf5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java
@@ -21,6 +21,7 @@
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -30,6 +31,7 @@
import java.util.concurrent.TimeUnit;
import org.bson.Document;
+
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
@@ -52,7 +54,7 @@ public class Query {
private static final String RESTRICTED_TYPES_KEY = "_$RESTRICTED_TYPES";
- private final Set> restrictedTypes = new HashSet<>();
+ private Set> restrictedTypes = Collections.emptySet();
private final Map criteria = new LinkedHashMap<>();
private @Nullable Field fieldSpec = null;
private Sort sort = Sort.unsorted();
@@ -235,8 +237,15 @@ public Query restrict(Class> type, Class>... additionalTypes) {
Assert.notNull(type, "Type must not be null!");
Assert.notNull(additionalTypes, "AdditionalTypes must not be null");
+ if (restrictedTypes == Collections.EMPTY_SET) {
+ restrictedTypes = new HashSet<>(1 + additionalTypes.length);
+ }
+
restrictedTypes.add(type);
- restrictedTypes.addAll(Arrays.asList(additionalTypes));
+
+ if (additionalTypes.length > 0) {
+ restrictedTypes.addAll(Arrays.asList(additionalTypes));
+ }
return this;
}
@@ -246,6 +255,17 @@ public Query restrict(Class> type, Class>... additionalTypes) {
*/
public Document getQueryObject() {
+ if (criteria.isEmpty() && restrictedTypes.isEmpty()) {
+ return BsonUtils.EMPTY_DOCUMENT;
+ }
+
+ if (criteria.size() == 1 && restrictedTypes.isEmpty()) {
+
+ for (CriteriaDefinition definition : criteria.values()) {
+ return definition.getCriteriaObject();
+ }
+ }
+
Document document = new Document();
for (CriteriaDefinition definition : criteria.values()) {
@@ -263,7 +283,7 @@ public Document getQueryObject() {
* @return the field {@link Document}.
*/
public Document getFieldsObject() {
- return this.fieldSpec == null ? new Document() : fieldSpec.getFieldsObject();
+ return this.fieldSpec == null ? BsonUtils.EMPTY_DOCUMENT : fieldSpec.getFieldsObject();
}
/**
@@ -272,13 +292,12 @@ public Document getFieldsObject() {
public Document getSortObject() {
if (this.sort.isUnsorted()) {
- return new Document();
+ return BsonUtils.EMPTY_DOCUMENT;
}
Document document = new Document();
- this.sort.stream()//
- .forEach(order -> document.put(order.getProperty(), order.isAscending() ? 1 : -1));
+ this.sort.forEach(order -> document.put(order.getProperty(), order.isAscending() ? 1 : -1));
return document;
}
@@ -557,7 +576,7 @@ public boolean isSorted() {
target.limit = source.getLimit();
target.hint = source.getHint();
target.collation = source.getCollation();
- target.restrictedTypes.addAll(source.getRestrictedTypes());
+ target.restrictedTypes = new HashSet<>(source.getRestrictedTypes());
if (source.getMeta().hasValues()) {
target.setMeta(new Meta(source.getMeta()));
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java
index 9a72b3ffc0..84a5b9d47e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/TextQuery.java
@@ -18,6 +18,8 @@
import java.util.Locale;
import org.bson.Document;
+
+import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.lang.Nullable;
/**
@@ -157,7 +159,7 @@ public Document getFieldsObject() {
return super.getFieldsObject();
}
- Document fields = super.getFieldsObject();
+ Document fields = BsonUtils.asMutableDocument(super.getFieldsObject());
fields.put(getScoreFieldName(), META_TEXT_SCORE);
return fields;
@@ -170,15 +172,14 @@ public Document getFieldsObject() {
@Override
public Document getSortObject() {
- Document sort = new Document();
-
if (this.sortByScore) {
+ Document sort = new Document();
sort.put(getScoreFieldName(), META_TEXT_SCORE);
+ sort.putAll(super.getSortObject());
+ return sort;
}
- sort.putAll(super.getSortObject());
-
- return sort;
+ return super.getSortObject();
}
/*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java
index 34cab18c31..bdea768d31 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java
@@ -56,10 +56,10 @@ public enum Position {
}
private boolean isolated = false;
- private Set keysToUpdate = new HashSet<>();
- private Map modifierOps = new LinkedHashMap<>();
- private Map pushCommandBuilders = new LinkedHashMap<>(1);
- private List arrayFilters = new ArrayList<>();
+ private final Set keysToUpdate = new HashSet<>();
+ private final Map modifierOps = new LinkedHashMap<>();
+ private Map pushCommandBuilders = Collections.emptyMap();
+ private List arrayFilters = Collections.emptyList();
/**
* Static factory method to create an Update using the provided key
@@ -193,6 +193,11 @@ public Update push(String key, @Nullable Object value) {
public PushOperatorBuilder push(String key) {
if (!pushCommandBuilders.containsKey(key)) {
+
+ if (pushCommandBuilders == Collections.EMPTY_MAP) {
+ pushCommandBuilders = new LinkedHashMap<>(1);
+ }
+
pushCommandBuilders.put(key, new PushOperatorBuilder(key));
}
return pushCommandBuilders.get(key);
@@ -412,6 +417,10 @@ public Update isolated() {
*/
public Update filterArray(CriteriaDefinition criteria) {
+ if (arrayFilters == Collections.EMPTY_LIST) {
+ this.arrayFilters = new ArrayList<>();
+ }
+
this.arrayFilters.add(criteria::getCriteriaObject);
return this;
}
@@ -427,6 +436,10 @@ public Update filterArray(CriteriaDefinition criteria) {
*/
public Update filterArray(String identifier, Object expression) {
+ if (arrayFilters == Collections.EMPTY_LIST) {
+ this.arrayFilters = new ArrayList<>();
+ }
+
this.arrayFilters.add(() -> new Document(identifier, expression));
return this;
}
@@ -455,6 +468,15 @@ public List getArrayFilters() {
return Collections.unmodifiableList(this.arrayFilters);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.mongodb.core.query.UpdateDefinition#hasArrayFilters()
+ */
+ @Override
+ public boolean hasArrayFilters() {
+ return !this.arrayFilters.isEmpty();
+ }
+
/**
* This method is not called anymore rather override {@link #addMultiFieldOperation(String, String, Object)}.
*
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
index d452ad662f..c540a14603 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/BsonUtils.java
@@ -60,12 +60,26 @@
*/
public class BsonUtils {
+ /**
+ * The empty document (immutable). This document is serializable.
+ *
+ * @since 3.2.5
+ */
+ public static final Document EMPTY_DOCUMENT = new EmptyDocument();
+
@SuppressWarnings("unchecked")
@Nullable
public static T get(Bson bson, String key) {
return (T) asMap(bson).get(key);
}
+ /**
+ * Return the {@link Bson} object as {@link Map}. Depending on the input type, the return value can be either a casted
+ * version of {@code bson} or a converted (detached from the original value).
+ *
+ * @param bson
+ * @return
+ */
public static Map asMap(Bson bson) {
if (bson instanceof Document) {
@@ -81,6 +95,55 @@ public static Map asMap(Bson bson) {
return (Map) bson.toBsonDocument(Document.class, MongoClientSettings.getDefaultCodecRegistry());
}
+ /**
+ * Return the {@link Bson} object as {@link Document}. Depending on the input type, the return value can be either a
+ * casted version of {@code bson} or a converted (detached from the original value).
+ *
+ * @param bson
+ * @return
+ * @since 3.2.5
+ */
+ public static Document asDocument(Bson bson) {
+
+ if (bson instanceof Document) {
+ return (Document) bson;
+ }
+
+ Map map = asMap(bson);
+
+ if (map instanceof Document) {
+ return (Document) map;
+ }
+
+ return new Document(map);
+ }
+
+ /**
+ * Return the {@link Bson} object as mutable {@link Document} containing all entries from {@link Bson}.
+ *
+ * @param bson
+ * @return a mutable {@link Document} containing all entries from {@link Bson}.
+ * @since 3.2.5
+ */
+ public static Document asMutableDocument(Bson bson) {
+
+ if (bson instanceof EmptyDocument) {
+ bson = new Document(asDocument(bson));
+ }
+
+ if (bson instanceof Document) {
+ return (Document) bson;
+ }
+
+ Map map = asMap(bson);
+
+ if (map instanceof Document) {
+ return (Document) map;
+ }
+
+ return new Document(map);
+ }
+
public static void addToMap(Bson bson, String key, @Nullable Object value) {
if (bson instanceof Document) {
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java
new file mode 100644
index 0000000000..83c95c82e5
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/EmptyDocument.java
@@ -0,0 +1,95 @@
+/*
+ * 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.util;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+
+import org.bson.Document;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Empty variant of {@link Document}.
+ *
+ * @author Mark Paluch
+ */
+class EmptyDocument extends Document {
+
+ @Override
+ public Document append(String key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object put(String key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void putAll(Map extends String, ?> map) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void replaceAll(BiFunction super String, ? super Object, ?> function) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean remove(Object key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean replace(String key, Object oldValue, Object newValue) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nullable
+ @Override
+ public Object replace(String key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set> entrySet() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Collection values() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Set keySet() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
index 147d2e49c3..b1d3d6a839 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateUnitTests.java
@@ -101,6 +101,7 @@
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.mongodb.core.timeseries.Granularity;
+import org.springframework.data.mongodb.util.BsonUtils;
import org.springframework.lang.Nullable;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.CollectionUtils;
@@ -1071,7 +1072,7 @@ void doesNotApplyFieldsWhenInterfaceProjectionIsOpen() {
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonSpELProjection.class,
CursorPreparer.NO_OP_PREPARER);
- verify(findIterable).projection(eq(new Document()));
+ verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
}
@Test // DATAMONGO-1733, DATAMONGO-2041
@@ -1098,7 +1099,7 @@ void doesNotApplyFieldsWhenTargetIsNotAProjection() {
template.doFind("star-wars", new Document(), new Document(), Person.class, Person.class,
CursorPreparer.NO_OP_PREPARER);
- verify(findIterable).projection(eq(new Document()));
+ verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
}
@Test // DATAMONGO-1733
@@ -1107,7 +1108,7 @@ void doesNotApplyFieldsWhenTargetExtendsDomainType() {
template.doFind("star-wars", new Document(), new Document(), Person.class, PersonExtended.class,
CursorPreparer.NO_OP_PREPARER);
- verify(findIterable).projection(eq(new Document()));
+ verify(findIterable).projection(eq(BsonUtils.EMPTY_DOCUMENT));
}
@Test // DATAMONGO-1348, DATAMONGO-2264
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryTests.java
index 01dddcd084..69da412073 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/QueryTests.java
@@ -237,11 +237,8 @@ void clonedQueryShouldNotDependOnCriteriaFromSource() {
source.addCriteria(where("From one make ten").is("and two let be."));
Query target = Query.of(source);
- compareQueries(target, source);
- source.addCriteria(where("Make even three").is("then rich you'll be."));
-
- assertThat(target.getQueryObject()).isEqualTo(new Document("From one make ten", "and two let be."))
- .isNotEqualTo(source.getQueryObject());
+ assertThat(target.getQueryObject()).containsAllEntriesOf(new Document("From one make ten", "and two let be."))
+ .isNotSameAs(source.getQueryObject());
}
@Test // DATAMONGO-1783
@@ -353,9 +350,12 @@ void queryOfShouldWorkOnProxiedObjects() {
private void compareQueries(Query actual, Query expected) {
assertThat(actual.getCollation()).isEqualTo(expected.getCollation());
- assertThat(actual.getSortObject()).isEqualTo(expected.getSortObject());
- assertThat(actual.getFieldsObject()).isEqualTo(expected.getFieldsObject());
- assertThat(actual.getQueryObject()).isEqualTo(expected.getQueryObject());
+ assertThat(actual.getSortObject()).hasSameSizeAs(expected.getSortObject())
+ .containsAllEntriesOf(expected.getSortObject());
+ assertThat(actual.getFieldsObject()).hasSameSizeAs(expected.getFieldsObject())
+ .containsAllEntriesOf(expected.getFieldsObject());
+ assertThat(actual.getQueryObject()).hasSameSizeAs(expected.getQueryObject())
+ .containsAllEntriesOf(expected.getQueryObject());
assertThat(actual.getHint()).isEqualTo(expected.getHint());
assertThat(actual.getLimit()).isEqualTo(expected.getLimit());
assertThat(actual.getSkip()).isEqualTo(expected.getSkip());
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
index c6c1b140cd..9d8400995a 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/PartTreeMongoQueryUnitTests.java
@@ -31,11 +31,8 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.data.mongodb.MongoDatabaseFactory;
import org.springframework.data.mongodb.core.ExecutableFindOperation.ExecutableFind;
import org.springframework.data.mongodb.core.MongoOperations;
-import org.springframework.data.mongodb.core.convert.DbRefResolver;
-import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
@@ -128,7 +125,7 @@ void propagatesRootExceptionForInvalidQuery() {
@Test // DATAMONGO-1345, DATAMONGO-1735
void doesNotDeriveFieldSpecForNormalDomainType() {
- assertThat(deriveQueryFromMethod("findPersonBy", new Object[0]).getFieldsObject()).isEqualTo(new Document());
+ assertThat(deriveQueryFromMethod("findPersonBy", new Object[0]).getFieldsObject()).isEmpty();
}
@Test // DATAMONGO-1345
@@ -173,7 +170,7 @@ void doesNotCreateFieldsObjectForOpenProjection() {
org.springframework.data.mongodb.core.query.Query query = deriveQueryFromMethod("findAllBy");
- assertThat(query.getFieldsObject()).isEqualTo(new Document());
+ assertThat(query.getFieldsObject()).isEmpty();
}
@Test // DATAMONGO-1865
From d70e459ffe821c4c3b44783ed162d6461732a05c Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Fri, 10 Sep 2021 10:48:22 +0200
Subject: [PATCH 065/920] Upgrade to MongoDB Java Drivers 4.3.2
Closes: #3816
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 4aa47bbf2b..7cb1d10f85 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,7 +27,7 @@
multispring-data-mongodb2.6.0-SNAPSHOT
- 4.3.1
+ 4.3.2${mongo}1.19
From 7d6b5ae5fee0ec48dc07f2abce3ab6d342076635 Mon Sep 17 00:00:00 2001
From: Mark Paluch
Date: Fri, 10 Sep 2021 15:37:59 +0200
Subject: [PATCH 066/920] Upgrade to Maven Wrapper 3.8.2.
See #3818
---
.mvn/wrapper/maven-wrapper.properties | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
index 00d32aab1d..39700a5c4b 100755
--- a/.mvn/wrapper/maven-wrapper.properties
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -1 +1,2 @@
-distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip
\ No newline at end of file
+#Fri Sep 10 15:37:59 CEST 2021
+distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.2/apache-maven-3.8.2-bin.zip
From e7150f525ed39ef565c56c45d9c1d965ce728c96 Mon Sep 17 00:00:00 2001
From: divyajnu08
Date: Fri, 10 Sep 2021 10:37:30 +0530
Subject: [PATCH 067/920] Fix update mapping using nested integer keys on map
structures.
Closes: #3775
Original Pull Request: #3815
---
.../mongodb/core/convert/QueryMapper.java | 5 ++--
.../core/convert/QueryMapperUnitTests.java | 25 +++++++++++++++++++
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
index 356dd89faa..df53f2c21c 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
@@ -70,6 +70,7 @@
* @author Christoph Strobl
* @author Mark Paluch
* @author David Julia
+ * @author Divya Srivastava
*/
public class QueryMapper {
@@ -1032,8 +1033,8 @@ public TypeInformation> getTypeHint() {
*/
protected static class MetadataBackedField extends Field {
- private static final Pattern POSITIONAL_PARAMETER_PATTERN = Pattern.compile("\\.\\$(\\[.*?\\])?|\\.\\d+");
- private static final Pattern DOT_POSITIONAL_PATTERN = Pattern.compile("\\.\\d+");
+ private static final Pattern POSITIONAL_PARAMETER_PATTERN = Pattern.compile("\\.\\$(\\[.*?\\])?");
+ private static final Pattern DOT_POSITIONAL_PATTERN = Pattern.compile("\\.\\d+(?!$)");
private static final String INVALID_ASSOCIATION_REFERENCE = "Invalid path reference %s! Associations can only be pointed to directly or via their id property!";
private final MongoPersistentEntity> entity;
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
index 46db6e7d6a..a54e80fa39 100755
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
@@ -61,6 +61,7 @@
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.TextQuery;
+import org.springframework.data.mongodb.core.query.Update;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClientSettings;
@@ -1354,6 +1355,25 @@ void mapStringIdFieldProjection() {
org.bson.Document mappedFields = mapper.getMappedFields(new org.bson.Document("id", 1), context.getPersistentEntity(WithStringId.class));
assertThat(mappedFields).containsEntry("_id", 1);
}
+
+ @Test
+ void mapNestedStringFieldCorrectly() {
+ Update update = new Update();
+ update.set("levelOne.a.b.d", "e");
+ org.bson.Document document = mapper.getMappedObject(update.getUpdateObject(),
+ context.getPersistentEntity(EntityWithNestedMap.class));
+ assertThat(document).isEqualTo(new org.bson.Document("$set",new org.bson.Document("levelOne.a.b.d","e")));
+ }
+
+ @Test
+ void mapNestedIntegerFieldCorrectly() {
+ Update update = new Update();
+ update.set("levelOne.0.1.3", "4");
+ org.bson.Document document = mapper.getMappedObject(update.getUpdateObject(),
+ context.getPersistentEntity(EntityWithNestedMap.class));
+ assertThat(document).isEqualTo(new org.bson.Document("$set",new org.bson.Document("levelOne.0.1.3","4")));
+ }
+
@Test // GH-3783
void retainsId$InWithStringArray() {
@@ -1542,6 +1562,11 @@ static class EntityWithIntKeyedMapOfMap{
static class EntityWithComplexValueTypeList {
List list;
}
+
+ static class EntityWithNestedMap {
+ Map>> levelOne;
+ }
+
static class WithExplicitTargetTypes {
From eda1c793157b8883441a43c7bcc629926a7e7206 Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Mon, 13 Sep 2021 14:25:17 +0200
Subject: [PATCH 068/920] Move and add tests to UpdateMapper.
Also update author information.
Original Pull Request: #3815
---
.../core/convert/QueryMapperUnitTests.java | 24 --------
.../core/convert/UpdateMapperUnitTests.java | 55 +++++++++++++++++++
2 files changed, 55 insertions(+), 24 deletions(-)
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
index a54e80fa39..11ea78fd4d 100755
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
@@ -1355,25 +1355,6 @@ void mapStringIdFieldProjection() {
org.bson.Document mappedFields = mapper.getMappedFields(new org.bson.Document("id", 1), context.getPersistentEntity(WithStringId.class));
assertThat(mappedFields).containsEntry("_id", 1);
}
-
- @Test
- void mapNestedStringFieldCorrectly() {
- Update update = new Update();
- update.set("levelOne.a.b.d", "e");
- org.bson.Document document = mapper.getMappedObject(update.getUpdateObject(),
- context.getPersistentEntity(EntityWithNestedMap.class));
- assertThat(document).isEqualTo(new org.bson.Document("$set",new org.bson.Document("levelOne.a.b.d","e")));
- }
-
- @Test
- void mapNestedIntegerFieldCorrectly() {
- Update update = new Update();
- update.set("levelOne.0.1.3", "4");
- org.bson.Document document = mapper.getMappedObject(update.getUpdateObject(),
- context.getPersistentEntity(EntityWithNestedMap.class));
- assertThat(document).isEqualTo(new org.bson.Document("$set",new org.bson.Document("levelOne.0.1.3","4")));
- }
-
@Test // GH-3783
void retainsId$InWithStringArray() {
@@ -1562,11 +1543,6 @@ static class EntityWithIntKeyedMapOfMap{
static class EntityWithComplexValueTypeList {
List list;
}
-
- static class EntityWithNestedMap {
- Map>> levelOne;
- }
-
static class WithExplicitTargetTypes {
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java
index bba9811e56..44712fa8d1 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java
@@ -68,6 +68,7 @@
* @author Mark Paluch
* @author Pavel Vodrazka
* @author David Julia
+ * @author Divya Srivastava
*/
@ExtendWith(MockitoExtension.class)
class UpdateMapperUnitTests {
@@ -1200,6 +1201,56 @@ void mapsObjectClassPropertyFieldInMapValueTypeAsKey() {
assertThat(mappedUpdate).isEqualTo("{\"$set\": {\"map.class\": \"value\"}}");
}
+ @Test // GH-3775
+ void mapNestedStringFieldCorrectly() {
+
+ Update update = new Update().set("levelOne.a.b.d", "e");
+ Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
+ context.getPersistentEntity(EntityWithNestedMap.class));
+
+ assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("levelOne.a.b.d","e")));
+ }
+
+ @Test // GH-3775
+ void mapNestedIntegerFieldCorrectly() {
+
+ Update update = new Update().set("levelOne.0.1.3", "4");
+ Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
+ context.getPersistentEntity(EntityWithNestedMap.class));
+
+ assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("levelOne.0.1.3","4")));
+ }
+
+ @Test // GH-3775
+ void mapNestedMixedStringIntegerFieldCorrectly() {
+
+ Update update = new Update().set("levelOne.0.1.c", "4");
+ Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
+ context.getPersistentEntity(EntityWithNestedMap.class));
+
+ assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("levelOne.0.1.c","4")));
+ }
+
+ @Test // GH-3775
+ void mapNestedMixedStringIntegerWithStartNumberFieldCorrectly() {
+
+ Update update = new Update().set("levelOne.0a.1b.3c", "4");
+ Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
+ context.getPersistentEntity(EntityWithNestedMap.class));
+
+ assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("levelOne.0a.1b.3c","4")));
+ }
+
+ @Test // GH-3688
+ void multipleKeysStartingWithANumberInNestedPath() {
+
+ Update update = new Update().set("intKeyedMap.1a.map.0b", "testing");
+ Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
+ context.getPersistentEntity(EntityWithIntKeyedMap.class));
+
+ assertThat(mappedUpdate).isEqualTo("{\"$set\": {\"intKeyedMap.1a.map.0b\": \"testing\"}}");
+ }
+
static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes {
ListModelWrapper concreteTypeWithListAttributeOfInterfaceType;
}
@@ -1566,4 +1617,8 @@ static class UnwrappableType {
String transientValue;
}
+ static class EntityWithNestedMap {
+ Map>> levelOne;
+ }
+
}
From 99203b397a27f9cd595eebb028161b9c054dfd68 Mon Sep 17 00:00:00 2001
From: Christoph Strobl
Date: Tue, 24 Aug 2021 07:06:17 +0200
Subject: [PATCH 069/920] Add support for deriving json schema for encrypted
properties.
This commit introduces support for creating a MongoJsonSchema containing encrypted fields for a given type based on mapping metadata.
Using the Encrypted annotation allows to derive required encryptMetadata and encrypt properties within a given (mapping)context.
@Document
@Encrypted(keyId = "...")
static class Patient {
// ...
@Encrypted(algorithm = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic")
private Integer ssn;
}
MongoJsonSchemaCreator schemaCreator = MongoJsonSchemaCreator.create(mappingContext);
MongoJsonSchema patientSchema = schemaCreator
.filter(MongoJsonSchemaCreator.encryptedOnly())
.createSchemaFor(Patient.class);
Closes: #3800
Original pull request: #3801.
---
.../mongodb/core/EncryptionAlgorithms.java | 29 ++
.../core/MappingMongoJsonSchemaCreator.java | 109 ++++++-
.../mongodb/core/MongoJsonSchemaCreator.java | 136 +++++++++
.../mapping/BasicMongoPersistentEntity.java | 38 +++
.../mapping/BasicMongoPersistentProperty.java | 48 ++++
.../data/mongodb/core/mapping/Encrypted.java | 112 ++++++++
.../core/mapping/MongoMappingContext.java | 6 +
.../core/mapping/MongoPersistentEntity.java | 9 +
.../core/mapping/MongoPersistentProperty.java | 9 +
.../UnwrappedMongoPersistentEntity.java | 6 +
.../UnwrappedMongoPersistentProperty.java | 6 +
.../core/schema/DefaultMongoJsonSchema.java | 36 ++-
.../core/schema/DocumentJsonSchema.java | 6 +-
.../IdentifiableJsonSchemaProperty.java | 16 +-
.../mongodb/core/schema/MongoJsonSchema.java | 32 ++-
.../core/schema/TypedJsonSchemaObject.java | 4 +
.../util/encryption/EncryptionUtils.java | 67 +++++
.../mongodb/util/spel/ExpressionUtils.java | 52 ++++
...appingMongoJsonSchemaCreatorUnitTests.java | 272 +++++++++++++++++-
.../asciidoc/reference/mongo-json-schema.adoc | 103 +++++++
20 files changed, 1074 insertions(+), 22 deletions(-)
create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EncryptionAlgorithms.java
create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Encrypted.java
create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/encryption/EncryptionUtils.java
create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/util/spel/ExpressionUtils.java
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EncryptionAlgorithms.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EncryptionAlgorithms.java
new file mode 100644
index 0000000000..0ed7340aa1
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EncryptionAlgorithms.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Encryption algorithms supported by MongoDB Client Side Field Level Encryption.
+ *
+ * @author Christoph Strobl
+ * @since 3.3
+ */
+public final class EncryptionAlgorithms {
+
+ public static final String AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic = "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic";
+ public static final String AEAD_AES_256_CBC_HMAC_SHA_512_Random = "AEAD_AES_256_CBC_HMAC_SHA_512-Random";
+
+}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
index ecbf8a4f07..a53ff8f5a5 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MappingMongoJsonSchemaCreator.java
@@ -20,13 +20,19 @@
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import org.bson.Document;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.convert.MongoConverter;
+import org.springframework.data.mongodb.core.mapping.BasicMongoPersistentEntity;
+import org.springframework.data.mongodb.core.mapping.Encrypted;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
+import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.EncryptedJsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.IdentifiableJsonSchemaProperty.ObjectJsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.JsonSchemaObject;
import org.springframework.data.mongodb.core.schema.JsonSchemaObject.Type;
@@ -34,10 +40,12 @@
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema.MongoJsonSchemaBuilder;
import org.springframework.data.mongodb.core.schema.TypedJsonSchemaObject;
+import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
/**
* {@link MongoJsonSchemaCreator} implementation using both {@link MongoConverter} and {@link MappingContext} to obtain
@@ -52,6 +60,7 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
private final MongoConverter converter;
private final MappingContext, MongoPersistentProperty> mappingContext;
+ private final Predicate filter;
/**
* Create a new instance of {@link MappingMongoJsonSchemaCreator}.
@@ -61,10 +70,24 @@ class MappingMongoJsonSchemaCreator implements MongoJsonSchemaCreator {
@SuppressWarnings("unchecked")
MappingMongoJsonSchemaCreator(MongoConverter converter) {
+ this(converter, (MappingContext, MongoPersistentProperty>) converter.getMappingContext(),
+ (property) -> true);
+ }
+
+ @SuppressWarnings("unchecked")
+ MappingMongoJsonSchemaCreator(MongoConverter converter,
+ MappingContext, MongoPersistentProperty> mappingContext,
+ Predicate filter) {
+
Assert.notNull(converter, "Converter must not be null!");
this.converter = converter;
- this.mappingContext = (MappingContext, MongoPersistentProperty>) converter
- .getMappingContext();
+ this.mappingContext = mappingContext;
+ this.filter = filter;
+ }
+
+ @Override
+ public MongoJsonSchemaCreator filter(Predicate filter) {
+ return new MappingMongoJsonSchemaCreator(converter, mappingContext, filter);
}
/*
@@ -77,11 +100,29 @@ public MongoJsonSchema createSchemaFor(Class> type) {
MongoPersistentEntity> entity = mappingContext.getRequiredPersistentEntity(type);
MongoJsonSchemaBuilder schemaBuilder = MongoJsonSchema.builder();
+ {
+ Encrypted encrypted = entity.findAnnotation(Encrypted.class);
+ if (encrypted != null) {
+
+ Document encryptionMetadata = new Document();
+
+ Collection encryptionKeyIds = entity.getEncryptionKeyIds();
+ if (!CollectionUtils.isEmpty(encryptionKeyIds)) {
+ encryptionMetadata.append("keyId", encryptionKeyIds);
+ }
+
+ if (StringUtils.hasText(encrypted.algorithm())) {
+ encryptionMetadata.append("algorithm", encrypted.algorithm());
+ }
+
+ schemaBuilder.encryptionMetadata(encryptionMetadata);
+ }
+ }
+
List schemaProperties = computePropertiesForEntity(Collections.emptyList(), entity);
schemaBuilder.properties(schemaProperties.toArray(new JsonSchemaProperty[0]));
return schemaBuilder.build();
-
}
private List computePropertiesForEntity(List path,
@@ -93,6 +134,11 @@ private List computePropertiesForEntity(List currentPath = new ArrayList<>(path);
+ if (!filter.test(new PropertyContext(
+ currentPath.stream().map(PersistentProperty::getName).collect(Collectors.joining(".")), nested))) {
+ continue;
+ }
+
if (path.contains(nested)) { // cycle guard
schemaProperties.add(createSchemaProperty(computePropertyFieldName(CollectionUtils.lastElement(currentPath)),
Object.class, false));
@@ -120,15 +166,38 @@ private JsonSchemaProperty computeSchemaForProperty(List path,
@@ -207,4 +276,30 @@ static JsonSchemaProperty createPotentiallyRequiredSchemaProperty(JsonSchemaProp
return JsonSchemaProperty.required(property);
}
+
+ class PropertyContext implements JsonSchemaPropertyContext {
+
+ private String path;
+ private MongoPersistentProperty property;
+
+ public PropertyContext(String path, MongoPersistentProperty property) {
+ this.path = path;
+ this.property = property;
+ }
+
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public MongoPersistentProperty getProperty() {
+ return property;
+ }
+
+ @Override
+ public MongoPersistentEntity resolveEntity(MongoPersistentProperty property) {
+ return (MongoPersistentEntity) mappingContext.getPersistentEntity(property);
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java
index f3c0dcd624..5e5bc50644 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoJsonSchemaCreator.java
@@ -15,7 +15,23 @@
*/
package org.springframework.data.mongodb.core;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.springframework.data.mapping.PersistentProperty;
+import org.springframework.data.mapping.context.MappingContext;
+import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConverter;
+import org.springframework.data.mongodb.core.convert.MongoCustomConversions;
+import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
+import org.springframework.data.mongodb.core.mapping.Encrypted;
+import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
+import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
+import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
+import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
+import org.springframework.data.mongodb.core.mapping.Unwrapped.Nullable;
+import org.springframework.data.mongodb.core.schema.JsonSchemaProperty;
import org.springframework.data.mongodb.core.schema.MongoJsonSchema;
import org.springframework.util.Assert;
@@ -46,6 +62,7 @@
* {@link org.bson.types.ObjectId} like {@link String} will be mapped to {@code type : 'object'} unless there is more
* specific information available via the {@link org.springframework.data.mongodb.core.mapping.MongoId} annotation.
*
+ * {@link Encrypted} properties will contain {@literal encrypt} information.
*
* @author Christoph Strobl
* @since 2.2
@@ -60,6 +77,88 @@ public interface MongoJsonSchemaCreator {
*/
MongoJsonSchema createSchemaFor(Class> type);
+ /**
+ * Filter matching {@link JsonSchemaProperty properties}.
+ *
+ * @param filter the {@link Predicate} to evaluate for inclusion. Must not be {@literal null}.
+ * @return new instance of {@link MongoJsonSchemaCreator}.
+ * @since 3.3
+ */
+ MongoJsonSchemaCreator filter(Predicate filter);
+
+ /**
+ * The context in which a specific {@link #getProperty()} is encountered during schema creation.
+ *
+ * @since 3.3
+ */
+ interface JsonSchemaPropertyContext {
+
+ /**
+ * The path to a given field/property in dot notation.
+ *
+ * @return never {@literal null}.
+ */
+ String getPath();
+
+ /**
+ * The current property.
+ *
+ * @return never {@literal null}.
+ */
+ MongoPersistentProperty getProperty();
+
+ /**
+ * Obtain the {@link MongoPersistentEntity} for a given property.
+ *
+ * @param property must not be {@literal null}.
+ * @param
+ * @return {@literal null} if the property is not an entity. It is nevertheless recommend to check
+ * {@link PersistentProperty#isEntity()} first.
+ */
+ @Nullable
+ MongoPersistentEntity resolveEntity(MongoPersistentProperty property);
+
+ }
+
+ /**
+ * A filter {@link Predicate} that matches {@link Encrypted encrypted properties} and those having nested ones.
+ *
+ * @return new instance of {@link Predicate}.
+ * @since 3.3
+ */
+ static Predicate encryptedOnly() {
+
+ return new Predicate() {
+
+ // cycle guard
+ private final Set seen = new HashSet<>();
+
+ @Override
+ public boolean test(JsonSchemaPropertyContext context) {
+ return extracted(context.getProperty(), context);
+ }
+
+ private boolean extracted(MongoPersistentProperty property, JsonSchemaPropertyContext context) {
+ if (property.isAnnotationPresent(Encrypted.class)) {
+ return true;
+ }
+
+ if (!property.isEntity() || seen.contains(property)) {
+ return false;
+ }
+
+ seen.add(property);
+
+ for (MongoPersistentProperty nested : context.resolveEntity(property)) {
+ if (extracted(nested, context)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+
/**
* Creates a new {@link MongoJsonSchemaCreator} that is aware of conversions applied by the given
* {@link MongoConverter}.
@@ -72,4 +171,41 @@ static MongoJsonSchemaCreator create(MongoConverter mongoConverter) {
Assert.notNull(mongoConverter, "MongoConverter must not be null!");
return new MappingMongoJsonSchemaCreator(mongoConverter);
}
+
+ /**
+ * Creates a new {@link MongoJsonSchemaCreator} that is aware of type mappings and potential
+ * {@link org.springframework.data.spel.spi.EvaluationContextExtension extensions}.
+ *
+ * @param mappingContext must not be {@literal null}.
+ * @return new instance of {@link MongoJsonSchemaCreator}.
+ * @since 3.3
+ */
+ static MongoJsonSchemaCreator create(MappingContext mappingContext) {
+
+ MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
+ converter.setCustomConversions(MongoCustomConversions.create(config -> {}));
+ converter.afterPropertiesSet();
+
+ return create(converter);
+ }
+
+ /**
+ * Creates a new {@link MongoJsonSchemaCreator} that does not consider potential extensions - suitable for testing. We
+ * recommend to use {@link #create(MappingContext)}.
+ *
+ * @return new instance of {@link MongoJsonSchemaCreator}.
+ * @since 3.3
+ */
+ static MongoJsonSchemaCreator create() {
+
+ MongoMappingContext mappingContext = new MongoMappingContext();
+ mappingContext.setSimpleTypeHolder(MongoSimpleTypes.HOLDER);
+ mappingContext.afterPropertiesSet();
+
+ MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
+ converter.setCustomConversions(MongoCustomConversions.create(config -> {}));
+ converter.afterPropertiesSet();
+
+ return create(converter);
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
index 7bf8214aeb..6840fce5bf 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentEntity.java
@@ -17,8 +17,12 @@
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.springframework.data.annotation.Id;
@@ -28,6 +32,9 @@
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.mongodb.MongoCollectionUtils;
+import org.springframework.data.mongodb.util.encryption.EncryptionUtils;
+import org.springframework.data.spel.ExpressionDependencies;
+import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
@@ -212,6 +219,11 @@ public EvaluationContext getEvaluationContext(Object rootObject) {
return super.getEvaluationContext(rootObject);
}
+ @Override
+ public EvaluationContext getEvaluationContext(Object rootObject, ExpressionDependencies dependencies) {
+ return super.getEvaluationContext(rootObject, dependencies);
+ }
+
private void verifyFieldUniqueness() {
AssertFieldNameUniquenessHandler handler = new AssertFieldNameUniquenessHandler();
@@ -360,6 +372,32 @@ private void assertUniqueness(MongoPersistentProperty property) {
}
}
+ @Override
+ public Collection getEncryptionKeyIds() {
+
+ Encrypted encrypted = findAnnotation(Encrypted.class);
+ if (encrypted == null) {
+ return null;
+ }
+
+ if (ObjectUtils.isEmpty(encrypted.keyId())) {
+ return Collections.emptySet();
+ }
+
+ Lazy evaluationContext = Lazy.of(() -> {
+
+ EvaluationContext ctx = getEvaluationContext(null);
+ ctx.setVariable("target", getType().getSimpleName());
+ return ctx;
+ });
+
+ List target = new ArrayList<>();
+ for (String keyId : encrypted.keyId()) {
+ target.add(EncryptionUtils.resolveKeyId(keyId, evaluationContext));
+ }
+ return target;
+ }
+
/**
* @author Christoph Strobl
* @since 1.6
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
index 1315757896..cf74d696a8 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/BasicMongoPersistentProperty.java
@@ -16,7 +16,11 @@
package org.springframework.data.mongodb.core.mapping;
import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import org.bson.types.ObjectId;
@@ -29,7 +33,12 @@
import org.springframework.data.mapping.model.Property;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
import org.springframework.data.mapping.model.SimpleTypeHolder;
+import org.springframework.data.mongodb.util.encryption.EncryptionUtils;
+import org.springframework.data.util.Lazy;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
+import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
@@ -299,4 +308,43 @@ public boolean isTextScoreProperty() {
return isAnnotationPresent(TextScore.class);
}
+ /**
+ * Obtain the {@link EvaluationContext} for a specific root object.
+ *
+ * @param rootObject can be {@literal null}.
+ * @return never {@literal null}.
+ * @since 3.3
+ */
+ public EvaluationContext getEvaluationContext(@Nullable Object rootObject) {
+
+ if (getOwner() instanceof BasicMongoPersistentEntity) {
+ return ((BasicMongoPersistentEntity) getOwner()).getEvaluationContext(rootObject);
+ }
+ return rootObject != null ? new StandardEvaluationContext(rootObject) : new StandardEvaluationContext();
+ }
+
+ @Override
+ public Collection getEncryptionKeyIds() {
+
+ Encrypted encrypted = findAnnotation(Encrypted.class);
+ if (encrypted == null) {
+ return null;
+ }
+
+ if (ObjectUtils.isEmpty(encrypted.keyId())) {
+ return Collections.emptySet();
+ }
+
+ Lazy evaluationContext = Lazy.of(() -> {
+ EvaluationContext ctx = getEvaluationContext(null);
+ ctx.setVariable("target", getOwner().getType().getSimpleName() + "." + getName());
+ return ctx;
+ });
+
+ List target = new ArrayList<>();
+ for (String keyId : encrypted.keyId()) {
+ target.add(EncryptionUtils.resolveKeyId(keyId, evaluationContext));
+ }
+ return target;
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Encrypted.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Encrypted.java
new file mode 100644
index 0000000000..8bd0f99c41
--- /dev/null
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/mapping/Encrypted.java
@@ -0,0 +1,112 @@
+/*
+ * 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.mapping;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * {@link Encrypted} provides data required for MongoDB Client Side Field Level Encryption that is applied during schema
+ * resolution. It can be applied on top level (typically those types annotated with {@link Document} to provide the
+ * {@literal encryptMetadata}.
+ *
+ *