Skip to content

Commit 048af85

Browse files
christophstroblmp911de
authored andcommitted
Accept expression as input for filter aggregation operator.
Closes #4394 Original pull request: #4395
1 parent 110877e commit 048af85

File tree

2 files changed

+70
-9
lines changed

2 files changed

+70
-9
lines changed

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

+53-9
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public static class ArrayOperatorFactory {
7979

8080
private final @Nullable String fieldReference;
8181
private final @Nullable AggregationExpression expression;
82-
private final @Nullable Collection values;
82+
private final @Nullable Collection<?> values;
8383

8484
/**
8585
* Creates new {@link ArrayOperatorFactory} for given {@literal fieldReference}.
@@ -214,6 +214,10 @@ public AsBuilder filter() {
214214
return Filter.filter(fieldReference);
215215
}
216216

217+
if (usesExpression()) {
218+
return Filter.filter(expression);
219+
}
220+
217221
Assert.state(values != null, "Values must not be null");
218222
return Filter.filter(new ArrayList<>(values));
219223
}
@@ -317,7 +321,8 @@ public ArrayOperatorFactory.ReduceInitialValueBuilder reduce(PropertyExpression.
317321
}
318322

319323
/**
320-
* Creates new {@link AggregationExpression} that takes the associated array and sorts it by the given {@link Sort order}.
324+
* Creates new {@link AggregationExpression} that takes the associated array and sorts it by the given {@link Sort
325+
* order}.
321326
*
322327
* @return new instance of {@link SortArray}.
323328
* @since 4.0
@@ -397,8 +402,8 @@ public First first() {
397402
}
398403

399404
/**
400-
* Creates new {@link AggregationExpression} that return the last element in the given array.
401-
* <strong>NOTE:</strong> Requires MongoDB 4.4 or later.
405+
* Creates new {@link AggregationExpression} that return the last element in the given array. <strong>NOTE:</strong>
406+
* Requires MongoDB 4.4 or later.
402407
*
403408
* @return new instance of {@link Last}.
404409
* @since 3.4
@@ -649,6 +654,19 @@ public static AsBuilder filter(Field field) {
649654
return new FilterExpressionBuilder().filter(field);
650655
}
651656

657+
/**
658+
* Set the {@link AggregationExpression} resolving to an arry to apply the {@code $filter} to.
659+
*
660+
* @param expression must not be {@literal null}.
661+
* @return never {@literal null}.
662+
* @since 4.2
663+
*/
664+
public static AsBuilder filter(AggregationExpression expression) {
665+
666+
Assert.notNull(expression, "Field must not be null");
667+
return new FilterExpressionBuilder().filter(expression);
668+
}
669+
652670
/**
653671
* Set the {@literal values} to apply the {@code $filter} to.
654672
*
@@ -681,7 +699,16 @@ private Document toFilter(ExposedFields exposedFields, AggregationOperationConte
681699
}
682700

683701
private Object getMappedInput(AggregationOperationContext context) {
684-
return input instanceof Field field ? context.getReference(field).toString() : input;
702+
703+
if (input instanceof Field field) {
704+
return context.getReference(field).toString();
705+
}
706+
707+
if (input instanceof AggregationExpression expression) {
708+
return expression.toDocument(context);
709+
}
710+
711+
return input;
685712
}
686713

687714
private Object getMappedCondition(AggregationOperationContext context) {
@@ -715,6 +742,15 @@ public interface InputBuilder {
715742
* @return
716743
*/
717744
AsBuilder filter(Field field);
745+
746+
/**
747+
* Set the {@link AggregationExpression} resolving to an array to apply the {@code $filter} to.
748+
*
749+
* @param expression must not be {@literal null}.
750+
* @return
751+
* @since 4.1.1
752+
*/
753+
AsBuilder filter(AggregationExpression expression);
718754
}
719755

720756
/**
@@ -797,6 +833,14 @@ public AsBuilder filter(Field field) {
797833
return this;
798834
}
799835

836+
@Override
837+
public AsBuilder filter(AggregationExpression expression) {
838+
839+
Assert.notNull(expression, "Expression must not be null");
840+
filter.input = expression;
841+
return this;
842+
}
843+
800844
@Override
801845
public ConditionBuilder as(String variableName) {
802846

@@ -1333,7 +1377,7 @@ public Reduce reduce(PropertyExpression... expressions) {
13331377
Assert.notNull(expressions, "PropertyExpressions must not be null");
13341378

13351379
return new Reduce(Fields.field(fieldReference), initialValue,
1336-
Arrays.<AggregationExpression>asList(expressions));
1380+
Arrays.<AggregationExpression> asList(expressions));
13371381
}
13381382
};
13391383
}
@@ -1690,7 +1734,7 @@ public Zip zip(Object... arrays) {
16901734
* @author Christoph Strobl
16911735
* @author Shashank Sharma
16921736
* @see <a href=
1693-
* "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/</a>
1737+
* "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/</a>
16941738
* @since 2.2
16951739
*/
16961740
public static class In extends AbstractAggregationExpression {
@@ -1779,7 +1823,7 @@ public interface InBuilder {
17791823
*
17801824
* @author Christoph Strobl
17811825
* @see <a href=
1782-
* "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/</a>
1826+
* "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/</a>
17831827
* @since 2.1
17841828
*/
17851829
public static class ArrayToObject extends AbstractAggregationExpression {
@@ -1976,7 +2020,7 @@ public static SortArray sortArrayOf(AggregationExpression expression) {
19762020

19772021
/**
19782022
* Set the order to put elements in.
1979-
*
2023+
*
19802024
* @param sort must not be {@literal null}.
19812025
* @return new instance of {@link SortArray}.
19822026
*/

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

+17
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,23 @@ void shouldConstructFilterExpressionCorrectlyWhenConditionContainsFieldReference
117117
assertThat($filter).isEqualTo(new Document(expected));
118118
}
119119

120+
@Test // GH-4394
121+
void filterShouldAcceptExpression() {
122+
123+
Document $filter = ArrayOperators.arrayOf(ObjectOperators.valueOf("data.metadata").toArray()).filter().as("item")
124+
.by(ComparisonOperators.valueOf("item.price").greaterThan("field-1")).toDocument(Aggregation.DEFAULT_CONTEXT);
125+
126+
Document expected = Document.parse("""
127+
{ $filter : {
128+
input: { $objectToArray: "$data.metadata" },
129+
as: "item",
130+
cond: { $gt: [ "$$item.price", "$field-1" ] }
131+
}}
132+
""");
133+
134+
assertThat($filter).isEqualTo(expected);
135+
}
136+
120137
private Document extractFilterOperatorFromDocument(Document source) {
121138

122139
List<Object> pipeline = DocumentTestUtils.getAsDBList(source, "pipeline");

0 commit comments

Comments
 (0)