Skip to content

Commit 2985b4c

Browse files
christophstroblmp911de
authored andcommitted
DATAMONGO-1491 - Add support for $filter (aggregation).
We new support $filter in aggregation pipeline. Aggregation.newAggregation(Sales.class, Aggregation.project() .and(filter("items").as("item").by(GTE.of(field("item.price"), 100))) .as("items")) Original pull request: #412.
1 parent 578441e commit 2985b4c

13 files changed

+706
-23
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.data.domain.Sort.Direction;
2626
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
2727
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
28+
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
2829
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
2930
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
3031
import org.springframework.data.mongodb.core.query.Criteria;
@@ -557,7 +558,7 @@ public DBObject getMappedObject(DBObject dbObject) {
557558
*/
558559
@Override
559560
public FieldReference getReference(Field field) {
560-
return new FieldReference(new ExposedField(field, true));
561+
return new DirectFieldReference(new ExposedField(field, true));
561562
}
562563

563564
/*
@@ -566,7 +567,7 @@ public FieldReference getReference(Field field) {
566567
*/
567568
@Override
568569
public FieldReference getReference(String name) {
569-
return new FieldReference(new ExposedField(new AggregationField(name), true));
570+
return new DirectFieldReference(new ExposedField(new AggregationField(name), true));
570571
}
571572
}
572573

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
/*
2+
* Copyright 2016. the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.aggregation;
17+
18+
import java.util.ArrayList;
19+
import java.util.List;
20+
21+
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
22+
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
23+
import org.springframework.util.Assert;
24+
25+
import com.mongodb.BasicDBObject;
26+
import com.mongodb.DBObject;
27+
28+
/**
29+
* @author Christoph Strobl
30+
* @since 1.10
31+
*/
32+
public interface AggregationExpressions {
33+
34+
/**
35+
* {@code $filter} {@link AggregationExpression} allows to select a subset of the array to return based on the
36+
* specified condition.
37+
*
38+
* @author Christoph Strobl
39+
* @since 1.10
40+
*/
41+
class Filter implements AggregationExpression {
42+
43+
private Object input;
44+
private ExposedField as;
45+
private Object condition;
46+
47+
private Filter() {
48+
// used by builder
49+
}
50+
51+
/**
52+
* Set the {@literal field} to apply the {@code $filter} to.
53+
*
54+
* @param field must not be {@literal null}.
55+
* @return never {@literal null}.
56+
*/
57+
public static AsBuilder filter(String field) {
58+
59+
Assert.notNull(field, "Field must not be null!");
60+
return filter(Fields.field(field));
61+
}
62+
63+
/**
64+
* Set the {@literal field} to apply the {@code $filter} to.
65+
*
66+
* @param field must not be {@literal null}.
67+
* @return never {@literal null}.
68+
*/
69+
public static AsBuilder filter(Field field) {
70+
71+
Assert.notNull(field, "Field must not be null!");
72+
return new FilterExpressionBuilder().filter(field);
73+
}
74+
75+
/**
76+
* Set the {@literal values} to apply the {@code $filter} to.
77+
*
78+
* @param values must not be {@literal null}.
79+
* @return
80+
*/
81+
public static AsBuilder filter(List<?> values) {
82+
83+
Assert.notNull(values, "Values must not be null!");
84+
return new FilterExpressionBuilder().filter(values);
85+
}
86+
87+
/*
88+
* (non-Javadoc)
89+
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
90+
*/
91+
@Override
92+
public DBObject toDbObject(final AggregationOperationContext context) {
93+
94+
return toFilter(new ExposedFieldsAggregationOperationContext(ExposedFields.from(as), context) {
95+
96+
@Override
97+
public FieldReference getReference(Field field) {
98+
99+
FieldReference ref = null;
100+
try {
101+
ref = context.getReference(field);
102+
} catch (Exception e) {
103+
// just ignore that one.
104+
}
105+
return ref != null ? ref : super.getReference(field);
106+
}
107+
});
108+
}
109+
110+
private DBObject toFilter(AggregationOperationContext context) {
111+
112+
DBObject filterExpression = new BasicDBObject();
113+
114+
filterExpression.putAll(context.getMappedObject(new BasicDBObject("input", getMappedInput(context))));
115+
filterExpression.put("as", as.getTarget());
116+
117+
filterExpression.putAll(context.getMappedObject(new BasicDBObject("cond", getMappedCondition(context))));
118+
119+
return new BasicDBObject("$filter", filterExpression);
120+
}
121+
122+
private Object getMappedInput(AggregationOperationContext context) {
123+
return input instanceof Field ? context.getReference((Field) input).toString() : input;
124+
}
125+
126+
private Object getMappedCondition(AggregationOperationContext context) {
127+
128+
if (!(condition instanceof AggregationExpression)) {
129+
return condition;
130+
}
131+
132+
NestedDelegatingExpressionAggregationOperationContext nea = new NestedDelegatingExpressionAggregationOperationContext(context);
133+
DBObject mappedCondition = ((AggregationExpression) condition).toDbObject(nea);
134+
return mappedCondition;
135+
}
136+
137+
/**
138+
* @author Christoph Strobl
139+
*/
140+
public interface InputBuilder {
141+
142+
/**
143+
* Set the {@literal values} to apply the {@code $filter} to.
144+
*
145+
* @param array must not be {@literal null}.
146+
* @return
147+
*/
148+
AsBuilder filter(List<?> array);
149+
150+
/**
151+
* Set the {@literal field} holding an array to apply the {@code $filter} to.
152+
*
153+
* @param field must not be {@literal null}.
154+
* @return
155+
*/
156+
AsBuilder filter(Field field);
157+
}
158+
159+
/**
160+
* @author Christoph Strobl
161+
*/
162+
public interface AsBuilder {
163+
164+
/**
165+
* Set the {@literal variableName} for the elements in the input array.
166+
*
167+
* @param variableName must not be {@literal null}.
168+
* @return
169+
*/
170+
ConditionBuilder as(String variableName);
171+
}
172+
173+
/**
174+
* @author Christoph Strobl
175+
*/
176+
public interface ConditionBuilder {
177+
178+
/**
179+
* Set the {@link AggregationExpression} that determines whether to include the element in the resulting array.
180+
*
181+
* @param expression must not be {@literal null}.
182+
* @return
183+
*/
184+
Filter by(AggregationExpression expression);
185+
186+
/**
187+
* Set the {@literal expression} that determines whether to include the element in the resulting array.
188+
*
189+
* @param expression must not be {@literal null}.
190+
* @return
191+
*/
192+
Filter by(String expression);
193+
194+
/**
195+
* Set the {@literal expression} that determines whether to include the element in the resulting array.
196+
*
197+
* @param expression must not be {@literal null}.
198+
* @return
199+
*/
200+
Filter by(DBObject expression);
201+
}
202+
203+
/**
204+
* @author Christoph Strobl
205+
*/
206+
static final class FilterExpressionBuilder implements InputBuilder, AsBuilder, ConditionBuilder {
207+
208+
private final Filter filter;
209+
210+
FilterExpressionBuilder() {
211+
this.filter = new Filter();
212+
}
213+
214+
public static InputBuilder newBuilder() {
215+
return new FilterExpressionBuilder();
216+
}
217+
218+
/*
219+
* (non-Javadoc)
220+
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.InputBuilder#filter(java.util.List)
221+
*/
222+
@Override
223+
public AsBuilder filter(List<?> array) {
224+
225+
Assert.notNull(array, "Array must not be null!");
226+
filter.input = new ArrayList(array);
227+
return this;
228+
}
229+
230+
/*
231+
* (non-Javadoc)
232+
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.InputBuilder#filter(org.springframework.data.mongodb.core.aggregation.Field)
233+
*/
234+
@Override
235+
public AsBuilder filter(Field field) {
236+
237+
Assert.notNull(field, "Field must not be null!");
238+
filter.input = field;
239+
return this;
240+
}
241+
242+
/*
243+
* (non-Javadoc)
244+
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.AsBuilder#as(java.lang.String)
245+
*/
246+
@Override
247+
public ConditionBuilder as(String variableName) {
248+
249+
Assert.notNull(variableName, "Variable name must not be null!");
250+
filter.as = new ExposedField(variableName, true);
251+
return this;
252+
}
253+
254+
/*
255+
* (non-Javadoc)
256+
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
257+
*/
258+
@Override
259+
public Filter by(AggregationExpression condition) {
260+
261+
Assert.notNull(condition, "Condition must not be null!");
262+
filter.condition = condition;
263+
return filter;
264+
}
265+
266+
/*
267+
* (non-Javadoc)
268+
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(java.lang.String)
269+
*/
270+
@Override
271+
public Filter by(String expression) {
272+
273+
Assert.notNull(expression, "Expression must not be null!");
274+
filter.condition = expression;
275+
return filter;
276+
}
277+
278+
/*
279+
* (non-Javadoc)
280+
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Filter.ConditionBuilder#by(com.mongodb.DBObject)
281+
*/
282+
@Override
283+
public Filter by(DBObject expression) {
284+
285+
Assert.notNull(expression, "Expression must not be null!");
286+
filter.condition = expression;
287+
return filter;
288+
}
289+
}
290+
291+
}
292+
293+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
*/
3434
public enum AggregationFunctionExpressions {
3535

36-
SIZE;
36+
SIZE, GTE;
3737

3838
/**
3939
* Returns an {@link AggregationExpression} build from the current {@link Enum} name and the given parameters.

0 commit comments

Comments
 (0)