Skip to content

Commit 5c5c616

Browse files
mp911dechristophstrobl
authored andcommitted
DATAMONGO-1552 - Add $bucket aggregation stage.
Original Pull Request: #426
1 parent 0bd98d6 commit 5c5c616

File tree

5 files changed

+1250
-1
lines changed

5 files changed

+1250
-1
lines changed

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

+21-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public static ProjectionOperation project(String... fields) {
200200
}
201201

202202
/**
203-
* Creates a new {@link ProjectionOperation} includeing the given {@link Fields}.
203+
* Creates a new {@link ProjectionOperation} including the given {@link Fields}.
204204
*
205205
* @param fields must not be {@literal null}.
206206
* @return
@@ -416,6 +416,26 @@ public static OutOperation out(String outCollectionName) {
416416
return new OutOperation(outCollectionName);
417417
}
418418

419+
/**
420+
* Creates a new {@link BucketOperation} using given {@literal groupByField}.
421+
*
422+
* @param groupByField must not be {@literal null} or empty.
423+
* @return
424+
*/
425+
public static BucketOperation bucket(String groupByField) {
426+
return new BucketOperation(field(groupByField));
427+
}
428+
429+
/**
430+
* Creates a new {@link BucketOperation} using given {@link AggregationExpression group-by expression}.
431+
*
432+
* @param groupByExpression must not be {@literal null}.
433+
* @return
434+
*/
435+
public static BucketOperation bucket(AggregationExpression groupByExpression) {
436+
return new BucketOperation(groupByExpression);
437+
}
438+
419439
/**
420440
* Creates a new {@link LookupOperation}.
421441
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
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.Arrays;
20+
import java.util.Collections;
21+
import java.util.List;
22+
23+
import org.springframework.data.mongodb.core.aggregation.BucketOperation.BucketOperationOutputBuilder;
24+
import org.springframework.util.Assert;
25+
26+
import org.bson.Document;
27+
28+
/**
29+
* Encapsulates the aggregation framework {@code $bucket}-operation.
30+
* <p>
31+
* Bucket stage is typically used with {@link Aggregation} and {@code $facet}. Categorizes incoming documents into
32+
* groups, called buckets, based on a specified expression and bucket boundaries.
33+
* <p>
34+
* We recommend to use the static factory method {@link Aggregation#bucket(String)} instead of creating instances of
35+
* this class directly.
36+
*
37+
* @see http://docs.mongodb.org/manual/reference/aggregation/bucket/
38+
* @see BucketOperationSupport
39+
* @author Mark Paluch
40+
* @since 1.10
41+
*/
42+
public class BucketOperation extends BucketOperationSupport<BucketOperation, BucketOperationOutputBuilder>
43+
implements FieldsExposingAggregationOperation {
44+
45+
private final List<Object> boundaries;
46+
private final Object defaultBucket;
47+
48+
/**
49+
* Creates a new {@link BucketOperation} given a {@link Field group-by field}.
50+
*
51+
* @param groupByField must not be {@literal null}.
52+
*/
53+
public BucketOperation(Field groupByField) {
54+
55+
super(groupByField);
56+
57+
this.boundaries = Collections.emptyList();
58+
this.defaultBucket = null;
59+
}
60+
61+
/**
62+
* Creates a new {@link BucketOperation} given a {@link AggregationExpression group-by expression}.
63+
*
64+
* @param groupByExpression must not be {@literal null}.
65+
*/
66+
public BucketOperation(AggregationExpression groupByExpression) {
67+
68+
super(groupByExpression);
69+
70+
this.boundaries = Collections.emptyList();
71+
this.defaultBucket = null;
72+
}
73+
74+
private BucketOperation(BucketOperation bucketOperation, Outputs outputs) {
75+
76+
super(bucketOperation, outputs);
77+
78+
this.boundaries = bucketOperation.boundaries;
79+
this.defaultBucket = bucketOperation.defaultBucket;
80+
}
81+
82+
private BucketOperation(BucketOperation bucketOperation, List<Object> boundaries, Object defaultBucket) {
83+
84+
super(bucketOperation);
85+
86+
this.boundaries = new ArrayList<Object>(boundaries);
87+
this.defaultBucket = defaultBucket;
88+
}
89+
90+
/* (non-Javadoc)
91+
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
92+
*/
93+
@Override
94+
public Document toDocument(AggregationOperationContext context) {
95+
96+
Document options = new Document();
97+
98+
options.put("boundaries", context.getMappedObject(new Document("$set", boundaries)).get("$set"));
99+
100+
if (defaultBucket != null) {
101+
options.put("default", context.getMappedObject(new Document("$set", defaultBucket)).get("$set"));
102+
}
103+
104+
options.putAll(super.toDocument(context));
105+
106+
return new Document("$bucket", options);
107+
}
108+
109+
/**
110+
* Configures a default bucket {@literal literal} and return a new {@link BucketOperation}.
111+
*
112+
* @param literal must not be {@literal null}.
113+
* @return
114+
*/
115+
public BucketOperation withDefaultBucket(Object literal) {
116+
117+
Assert.notNull(literal, "Default bucket literal must not be null!");
118+
return new BucketOperation(this, boundaries, literal);
119+
}
120+
121+
/**
122+
* Configures {@literal boundaries} and return a new {@link BucketOperation}. Existing {@literal boundaries} are
123+
* preserved and the new {@literal boundaries} are appended.
124+
*
125+
* @param boundaries must not be {@literal null}.
126+
* @return
127+
*/
128+
public BucketOperation withBoundaries(Object... boundaries) {
129+
130+
Assert.notNull(boundaries, "Boundaries must not be null!");
131+
132+
List<Object> newBoundaries = new ArrayList<Object>(this.boundaries.size() + boundaries.length);
133+
newBoundaries.addAll(this.boundaries);
134+
newBoundaries.addAll(Arrays.asList(boundaries));
135+
136+
return new BucketOperation(this, newBoundaries, defaultBucket);
137+
}
138+
139+
/* (non-Javadoc)
140+
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#newBucketOperation(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.Outputs)
141+
*/
142+
@Override
143+
protected BucketOperation newBucketOperation(Outputs outputs) {
144+
return new BucketOperation(this, outputs);
145+
}
146+
147+
/* (non-Javadoc)
148+
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutputExpression(java.lang.String, java.lang.Object[])
149+
*/
150+
@Override
151+
public ExpressionBucketOperationBuilder andOutputExpression(String expression, Object... params) {
152+
return new ExpressionBucketOperationBuilder(expression, this, params);
153+
}
154+
155+
/* (non-Javadoc)
156+
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutput(org.springframework.data.mongodb.core.aggregation.AggregationExpression)
157+
*/
158+
@Override
159+
public BucketOperationOutputBuilder andOutput(AggregationExpression expression) {
160+
return new BucketOperationOutputBuilder(expression, this);
161+
}
162+
163+
/* (non-Javadoc)
164+
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport#andOutput(java.lang.String)
165+
*/
166+
@Override
167+
public BucketOperationOutputBuilder andOutput(String fieldName) {
168+
return new BucketOperationOutputBuilder(Fields.field(fieldName), this);
169+
}
170+
171+
/**
172+
* {@link OutputBuilder} implementation for {@link BucketOperation}.
173+
*/
174+
public static class BucketOperationOutputBuilder
175+
extends BucketOperationSupport.OutputBuilder<BucketOperationOutputBuilder, BucketOperation> {
176+
177+
/**
178+
* Creates a new {@link BucketOperationOutputBuilder} fot the given value and {@link BucketOperation}.
179+
*
180+
* @param value must not be {@literal null}.
181+
* @param operation must not be {@literal null}.
182+
*/
183+
protected BucketOperationOutputBuilder(Object value, BucketOperation operation) {
184+
super(value, operation);
185+
}
186+
187+
/* (non-Javadoc)
188+
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder#apply(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OperationOutput)
189+
*/
190+
@Override
191+
protected BucketOperationOutputBuilder apply(OperationOutput operationOutput) {
192+
return new BucketOperationOutputBuilder(operationOutput, this.operation);
193+
}
194+
}
195+
196+
/**
197+
* {@link ExpressionBucketOperationBuilderSupport} implementation for {@link BucketOperation} using SpEL expression
198+
* based {@link Output}.
199+
*
200+
* @author Mark Paluch
201+
*/
202+
public static class ExpressionBucketOperationBuilder
203+
extends ExpressionBucketOperationBuilderSupport<BucketOperationOutputBuilder, BucketOperation> {
204+
205+
/**
206+
* Creates a new {@link ExpressionBucketOperationBuilderSupport} for the given value, {@link BucketOperation}
207+
* and parameters.
208+
*
209+
* @param expression must not be {@literal null}.
210+
* @param operation must not be {@literal null}.
211+
* @param parameters
212+
*/
213+
protected ExpressionBucketOperationBuilder(String expression, BucketOperation operation, Object[] parameters) {
214+
super(expression, operation, parameters);
215+
}
216+
217+
/* (non-Javadoc)
218+
* @see org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OutputBuilder#apply(org.springframework.data.mongodb.core.aggregation.BucketOperationSupport.OperationOutput)
219+
*/
220+
@Override
221+
protected BucketOperationOutputBuilder apply(OperationOutput operationOutput) {
222+
return new BucketOperationOutputBuilder(operationOutput, this.operation);
223+
}
224+
}
225+
}

0 commit comments

Comments
 (0)