From 714b23e0ce4e7ebad53bcb817ba55db6e080c97f Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 26 Sep 2022 09:22:59 +0200 Subject: [PATCH] Add support for $sortArray aggregation operator. See #4139 Original pull request: #4182. --- .../core/aggregation/ArrayOperators.java | 78 +++++++++++++++++++ .../core/spel/MethodReferenceNode.java | 1 + .../aggregation/ArrayOperatorsUnitTests.java | 8 ++ .../SpelExpressionTransformerUnitTests.java | 6 ++ 4 files changed, 93 insertions(+) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java index 4babbf6eb7..1ea699852f 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ArrayOperators.java @@ -23,6 +23,7 @@ import org.bson.Document; import org.springframework.data.domain.Range; +import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.AsBuilder; import org.springframework.data.mongodb.core.aggregation.ArrayOperators.Reduce.PropertyExpression; import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; @@ -315,6 +316,21 @@ public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(PropertyExpression. .withInitialValue(initialValue).reduce(expressions); } + /** + * Creates new {@link AggregationExpression} that takes the associated array and sorts it by the given {@link Sort order}. + * + * @return new instance of {@link SortArray}. + * @since 4.0 + */ + public SortArray sort(Sort sort) { + + if (usesFieldRef()) { + return SortArray.sortArrayOf(fieldReference).by(sort); + } + + return (usesExpression() ? SortArray.sortArrayOf(expression) : SortArray.sortArray(values)).by(sort); + } + /** * Creates new {@link AggregationExpression} that transposes an array of input arrays so that the first element of * the output array would be an array containing, the first element of the first input array, the first element of @@ -1915,4 +1931,66 @@ protected String getMongoMethod() { return "$last"; } } + + /** + * {@link AggregationExpression} for {@code $sortArray} that sorts elements in an array.
+ * + * @author Christoph Strobl + * @since 4.0 + */ + public static class SortArray extends AbstractAggregationExpression { + + private SortArray(Object value) { + super(value); + } + + /** + * Returns the given array. + * + * @param array must not be {@literal null}. + * @return new instance of {@link SortArray}. + */ + public static SortArray sortArray(Object array) { + return new SortArray(Collections.singletonMap("input", array)); + } + + /** + * Sorts the elements in the array pointed to by the given {@link Field field reference}. + * + * @param fieldReference must not be {@literal null}. + * @return new instance of {@link SortArray}. + */ + public static SortArray sortArrayOf(String fieldReference) { + return sortArray(Fields.field(fieldReference)); + } + + /** + * Sorts the elements of the array computed buy the given {@link AggregationExpression expression}. + * + * @param expression must not be {@literal null}. + * @return new instance of {@link SortArray}. + */ + public static SortArray sortArrayOf(AggregationExpression expression) { + return sortArray(expression); + } + + /** + * Set the order to put elements in. + * + * @param sort must not be {@literal null}. + * @return new instance of {@link SortArray}. + */ + public SortArray by(Sort sort) { + return new SortArray(append("sortBy", sort)); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.aggregation.AbstractAggregationExpression#getMongoMethod() + */ + @Override + protected String getMongoMethod() { + return "$sortArray"; + } + } } 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 b1641f5f64..4347adba15 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 @@ -142,6 +142,7 @@ public class MethodReferenceNode extends ExpressionNode { map.put("last", singleArgRef().forOperator("$last")); map.put("size", singleArgRef().forOperator("$size")); map.put("slice", arrayArgRef().forOperator("$slice")); + map.put("sortArray", mapArgRef().forOperator("$sortArray").mappingParametersTo("input", "sortBy")); map.put("reverseArray", singleArgRef().forOperator("$reverseArray")); map.put("reduce", mapArgRef().forOperator("$reduce").mappingParametersTo("input", "initialValue", "in")); map.put("zip", mapArgRef().forOperator("$zip").mappingParametersTo("inputs", "useLongestLength", "defaults")); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArrayOperatorsUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArrayOperatorsUnitTests.java index 63165c7641..a542de5bea 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArrayOperatorsUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ArrayOperatorsUnitTests.java @@ -23,6 +23,7 @@ import org.bson.Document; import org.junit.jupiter.api.Test; +import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.aggregation.ArrayOperators.ArrayToObject; /** @@ -171,4 +172,11 @@ public void lastWithFieldReference() { assertThat(ArrayOperators.arrayOf("field").last().toDocument(Aggregation.DEFAULT_CONTEXT)) .isEqualTo("{ $last : \"$field\" }"); } + + @Test // GH-4139 + void sortByWithFieldRef() { + + assertThat(ArrayOperators.arrayOf("team").sort(Sort.by("name")).toDocument(Aggregation.DEFAULT_CONTEXT)) + .isEqualTo("{ $sortArray: { input: \"$team\", sortBy: { name: 1 } } }"); + } } 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 e5dca5f363..ebf944020b 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 @@ -1235,6 +1235,12 @@ void shouldRenderSetField() { assertThat(transform("setField(\"score\", 100, source)")).isEqualTo("{ $setField : { field : \"score\", value : 100, input : \"$source\" }}"); } + @Test // GH-4139 + void shouldRenderSortArray() { + assertThat(transform( + "sortArray(team, new org.bson.Document(\"name\" , 1))")).isEqualTo("{ $sortArray : { input : \"$team\", sortBy : {\"name\" : 1 } }}"); + } + private Document transform(String expression, Object... params) { return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params); }