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 a5b0913c3..23ed32ec6 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 @@ -78,7 +78,7 @@ public class ArrayOperators { private final @Nullable String fieldReference; private final @Nullable AggregationExpression expression; - private final @Nullable Collection values; + private final @Nullable Collection values; /** * Creates new {@link ArrayOperatorFactory} for given {@literal fieldReference}. @@ -213,6 +213,10 @@ public class ArrayOperators { return Filter.filter(fieldReference); } + if (usesExpression()) { + return Filter.filter(expression); + } + Assert.state(values != null, "Values must not be null!"); return Filter.filter(new ArrayList<>(values)); } @@ -363,7 +367,7 @@ public class ArrayOperators { return usesExpression() ? ArrayToObject.arrayValueOfToObject(expression) : ArrayToObject.arrayToObject(values); } - + /** * Creates new {@link AggregationExpression} that return the first element in the associated array. * NOTE: Requires MongoDB 4.4 or later. @@ -379,10 +383,10 @@ public class ArrayOperators { return usesExpression() ? First.firstOf(expression) : First.first(values); } - + /** - * Creates new {@link AggregationExpression} that return the last element in the given array. - * NOTE: Requires MongoDB 4.4 or later. + * Creates new {@link AggregationExpression} that return the last element in the given array. NOTE: + * Requires MongoDB 4.4 or later. * * @return new instance of {@link Last}. * @since 3.4 @@ -633,6 +637,19 @@ public class ArrayOperators { return new FilterExpressionBuilder().filter(field); } + /** + * Set the {@link AggregationExpression} resolving to an arry to apply the {@code $filter} to. + * + * @param expression must not be {@literal null}. + * @return never {@literal null}. + * @since 4.2 + */ + public static AsBuilder filter(AggregationExpression expression) { + + Assert.notNull(expression, "Field must not be null"); + return new FilterExpressionBuilder().filter(expression); + } + /** * Set the {@literal values} to apply the {@code $filter} to. * @@ -669,7 +686,16 @@ public class ArrayOperators { } private Object getMappedInput(AggregationOperationContext context) { - return input instanceof Field ? context.getReference((Field) input).toString() : input; + + if (input instanceof Field) { + return context.getReference((Field) input).toString(); + } + + if (input instanceof AggregationExpression) { + return ((AggregationExpression) input).toDocument(context); + } + + return input; } private Object getMappedCondition(AggregationOperationContext context) { @@ -703,6 +729,15 @@ public class ArrayOperators { * @return */ AsBuilder filter(Field field); + + /** + * Set the {@link AggregationExpression} resolving to an array to apply the {@code $filter} to. + * + * @param expression must not be {@literal null}. + * @return + * @since 3.4.13 + */ + AsBuilder filter(AggregationExpression expression); } /** @@ -793,6 +828,14 @@ public class ArrayOperators { return this; } + @Override + public AsBuilder filter(AggregationExpression expression) { + + Assert.notNull(expression, "Expression must not be null"); + filter.input = expression; + return this; + } + /* * (non-Javadoc) * @see org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.AsBuilder#as(java.lang.String) @@ -1348,7 +1391,7 @@ public class ArrayOperators { Assert.notNull(expressions, "PropertyExpressions must not be null"); return new Reduce(Fields.field(fieldReference), initialValue, - Arrays.asList(expressions)); + Arrays. asList(expressions)); } }; } @@ -1708,7 +1751,7 @@ public class ArrayOperators { * @author Christoph Strobl * @author Shashank Sharma * @see https://docs.mongodb.com/manual/reference/operator/aggregation/in/ + * "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/ * @since 2.2 */ public static class In extends AbstractAggregationExpression { @@ -1797,7 +1840,7 @@ public class ArrayOperators { * * @author Christoph Strobl * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/ + * "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/ * @since 2.1 */ public static class ArrayToObject extends AbstractAggregationExpression { @@ -1845,7 +1888,7 @@ public class ArrayOperators { return "$arrayToObject"; } } - + /** * {@link AggregationExpression} for {@code $first} that returns the first element in an array.
* NOTE: Requires MongoDB 4.4 or later. @@ -1899,7 +1942,7 @@ public class ArrayOperators { return "$first"; } } - + /** * {@link AggregationExpression} for {@code $last} that returns the last element in an array.
* NOTE: Requires MongoDB 4.4 or later. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java index 4cee9bcdb..a4b8d9da8 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/FilterExpressionUnitTests.java @@ -25,12 +25,8 @@ import org.bson.Document; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.DocumentTestUtils; -import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; import org.springframework.data.mongodb.core.convert.MappingMongoConverter; import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; import org.springframework.data.mongodb.core.convert.QueryMapper; @@ -121,6 +117,21 @@ class FilterExpressionUnitTests { assertThat($filter).isEqualTo(new Document(expected)); } + @Test // GH-4394 + void filterShouldAcceptExpression() { + + Document $filter = ArrayOperators.arrayOf(ObjectOperators.valueOf("data.metadata").toArray()).filter().as("item") + .by(ComparisonOperators.valueOf("item.price").greaterThan("field-1")).toDocument(Aggregation.DEFAULT_CONTEXT); + + Document expected = Document.parse("{ $filter : {" + // + "input: { $objectToArray: \"$data.metadata\" }," + // + "as: \"item\"," + // + "cond: { $gt: [ \"$$item.price\", \"$field-1\" ] }" + // + "}}"); + + assertThat($filter).isEqualTo(expected); + } + private Document extractFilterOperatorFromDocument(Document source) { List pipeline = DocumentTestUtils.getAsDBList(source, "pipeline");