Skip to content

Commit 4cf9180

Browse files
christophstroblmp911de
authored andcommitted
Accept expression as input for filter aggregation operator.
Closes #4394 Original pull request: #4395
1 parent 5f9b0f0 commit 4cf9180

File tree

2 files changed

+69
-15
lines changed

2 files changed

+69
-15
lines changed

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

+54-11
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public static class ArrayOperatorFactory {
7878

7979
private final @Nullable String fieldReference;
8080
private final @Nullable AggregationExpression expression;
81-
private final @Nullable Collection values;
81+
private final @Nullable Collection<?> values;
8282

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

216+
if (usesExpression()) {
217+
return Filter.filter(expression);
218+
}
219+
216220
Assert.state(values != null, "Values must not be null!");
217221
return Filter.filter(new ArrayList<>(values));
218222
}
@@ -363,7 +367,7 @@ public ArrayToObject toObject() {
363367

364368
return usesExpression() ? ArrayToObject.arrayValueOfToObject(expression) : ArrayToObject.arrayToObject(values);
365369
}
366-
370+
367371
/**
368372
* Creates new {@link AggregationExpression} that return the first element in the associated array.
369373
* <strong>NOTE:</strong> Requires MongoDB 4.4 or later.
@@ -379,10 +383,10 @@ public First first() {
379383

380384
return usesExpression() ? First.firstOf(expression) : First.first(values);
381385
}
382-
386+
383387
/**
384-
* Creates new {@link AggregationExpression} that return the last element in the given array.
385-
* <strong>NOTE:</strong> Requires MongoDB 4.4 or later.
388+
* Creates new {@link AggregationExpression} that return the last element in the given array. <strong>NOTE:</strong>
389+
* Requires MongoDB 4.4 or later.
386390
*
387391
* @return new instance of {@link Last}.
388392
* @since 3.4
@@ -633,6 +637,19 @@ public static AsBuilder filter(Field field) {
633637
return new FilterExpressionBuilder().filter(field);
634638
}
635639

640+
/**
641+
* Set the {@link AggregationExpression} resolving to an arry to apply the {@code $filter} to.
642+
*
643+
* @param expression must not be {@literal null}.
644+
* @return never {@literal null}.
645+
* @since 4.2
646+
*/
647+
public static AsBuilder filter(AggregationExpression expression) {
648+
649+
Assert.notNull(expression, "Field must not be null");
650+
return new FilterExpressionBuilder().filter(expression);
651+
}
652+
636653
/**
637654
* Set the {@literal values} to apply the {@code $filter} to.
638655
*
@@ -669,7 +686,16 @@ private Document toFilter(ExposedFields exposedFields, AggregationOperationConte
669686
}
670687

671688
private Object getMappedInput(AggregationOperationContext context) {
672-
return input instanceof Field ? context.getReference((Field) input).toString() : input;
689+
690+
if (input instanceof Field) {
691+
return context.getReference((Field) input).toString();
692+
}
693+
694+
if (input instanceof AggregationExpression) {
695+
return ((AggregationExpression) input).toDocument(context);
696+
}
697+
698+
return input;
673699
}
674700

675701
private Object getMappedCondition(AggregationOperationContext context) {
@@ -703,6 +729,15 @@ public interface InputBuilder {
703729
* @return
704730
*/
705731
AsBuilder filter(Field field);
732+
733+
/**
734+
* Set the {@link AggregationExpression} resolving to an array to apply the {@code $filter} to.
735+
*
736+
* @param expression must not be {@literal null}.
737+
* @return
738+
* @since 3.4.13
739+
*/
740+
AsBuilder filter(AggregationExpression expression);
706741
}
707742

708743
/**
@@ -793,6 +828,14 @@ public AsBuilder filter(Field field) {
793828
return this;
794829
}
795830

831+
@Override
832+
public AsBuilder filter(AggregationExpression expression) {
833+
834+
Assert.notNull(expression, "Expression must not be null");
835+
filter.input = expression;
836+
return this;
837+
}
838+
796839
/*
797840
* (non-Javadoc)
798841
* @see org.springframework.data.mongodb.core.aggregation.ArrayOperators.Filter.AsBuilder#as(java.lang.String)
@@ -1348,7 +1391,7 @@ public Reduce reduce(PropertyExpression... expressions) {
13481391
Assert.notNull(expressions, "PropertyExpressions must not be null");
13491392

13501393
return new Reduce(Fields.field(fieldReference), initialValue,
1351-
Arrays.<AggregationExpression>asList(expressions));
1394+
Arrays.<AggregationExpression> asList(expressions));
13521395
}
13531396
};
13541397
}
@@ -1708,7 +1751,7 @@ public Zip zip(Object... arrays) {
17081751
* @author Christoph Strobl
17091752
* @author Shashank Sharma
17101753
* @see <a href=
1711-
* "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/</a>
1754+
* "https://docs.mongodb.com/manual/reference/operator/aggregation/in/">https://docs.mongodb.com/manual/reference/operator/aggregation/in/</a>
17121755
* @since 2.2
17131756
*/
17141757
public static class In extends AbstractAggregationExpression {
@@ -1797,7 +1840,7 @@ public interface InBuilder {
17971840
*
17981841
* @author Christoph Strobl
17991842
* @see <a href=
1800-
* "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/</a>
1843+
* "https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/">https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/</a>
18011844
* @since 2.1
18021845
*/
18031846
public static class ArrayToObject extends AbstractAggregationExpression {
@@ -1845,7 +1888,7 @@ protected String getMongoMethod() {
18451888
return "$arrayToObject";
18461889
}
18471890
}
1848-
1891+
18491892
/**
18501893
* {@link AggregationExpression} for {@code $first} that returns the first element in an array. <br />
18511894
* <strong>NOTE:</strong> Requires MongoDB 4.4 or later.
@@ -1899,7 +1942,7 @@ protected String getMongoMethod() {
18991942
return "$first";
19001943
}
19011944
}
1902-
1945+
19031946
/**
19041947
* {@link AggregationExpression} for {@code $last} that returns the last element in an array. <br />
19051948
* <strong>NOTE:</strong> Requires MongoDB 4.4 or later.

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

+15-4
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,8 @@
2525
import org.junit.jupiter.api.BeforeEach;
2626
import org.junit.jupiter.api.Test;
2727
import org.junit.jupiter.api.extension.ExtendWith;
28-
import org.mockito.Mock;
2928
import org.mockito.junit.jupiter.MockitoExtension;
30-
31-
import org.springframework.data.mongodb.MongoDatabaseFactory;
3229
import org.springframework.data.mongodb.core.DocumentTestUtils;
33-
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
3430
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
3531
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
3632
import org.springframework.data.mongodb.core.convert.QueryMapper;
@@ -121,6 +117,21 @@ void shouldConstructFilterExpressionCorrectlyWhenConditionContainsFieldReference
121117
assertThat($filter).isEqualTo(new Document(expected));
122118
}
123119

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("{ $filter : {" + //
127+
"input: { $objectToArray: \"$data.metadata\" }," + //
128+
"as: \"item\"," + //
129+
"cond: { $gt: [ \"$$item.price\", \"$field-1\" ] }" + //
130+
"}}");
131+
132+
assertThat($filter).isEqualTo(expected);
133+
}
134+
124135
private Document extractFilterOperatorFromDocument(Document source) {
125136

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

0 commit comments

Comments
 (0)