1 /*
2 * #%L
3 * GroupBy.java - mongodb-async-driver - Allanbank Consulting, Inc.
4 * %%
5 * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
6 * %%
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * #L%
19 */
20
21 package com.allanbank.mongodb.builder;
22
23 import static com.allanbank.mongodb.util.Assertions.assertThat;
24
25 import java.util.Collections;
26 import java.util.HashSet;
27 import java.util.Set;
28 import java.util.concurrent.TimeUnit;
29
30 import com.allanbank.mongodb.MongoCollection;
31 import com.allanbank.mongodb.ReadPreference;
32 import com.allanbank.mongodb.Version;
33 import com.allanbank.mongodb.bson.Document;
34 import com.allanbank.mongodb.bson.DocumentAssignable;
35
36 /**
37 * Group provides a container for all of the options to a <tt>group</tt>
38 * command. A {@link Builder} is provided to assist in creating a
39 * {@link GroupBy}.
40 *
41 * @api.yes This class is part of the driver's API. Public and protected members
42 * will be deprecated for at least 1 non-bugfix release (version
43 * numbers are <major>.<minor>.<bugfix>) before being
44 * removed or modified.
45 * @copyright 2012-2013, Allanbank Consulting, Inc., All Rights Reserved
46 */
47 public class GroupBy {
48 /**
49 * The first version of MongoDB to support the {@code group} command with
50 * the ability to limit the execution time on the server.
51 */
52 public static final Version MAX_TIMEOUT_VERSION = Find.MAX_TIMEOUT_VERSION;
53
54 /**
55 * Creates a new builder for a {@link GroupBy}.
56 *
57 * @return The builder to construct a {@link GroupBy}.
58 */
59 public static Builder builder() {
60 return new Builder();
61 }
62
63 /** The finalizer function to run for each group. */
64 private final String myFinalizeFunction;
65
66 /** The initial value for each group. */
67 private final Document myInitialValue;
68
69 /**
70 * Function to return the key for a document. Used instead of the
71 * {@link #getKeys} to dynamically determine the group for each document.
72 */
73 private final String myKeyFunction;
74
75 /** The fields to group by. */
76 private final Set<String> myKeys;
77
78 /** The maximum amount of time to allow the command to run. */
79 private final long myMaximumTimeMilliseconds;
80
81 /** The query to select the documents to run the group against. */
82 private final Document myQuery;
83
84 /** The read preference to use. */
85 private final ReadPreference myReadPreference;
86
87 /**
88 * The reduce function taking the previous value and the current value and
89 * returning the new reduced value.
90 */
91 private final String myReduceFunction;
92
93 /**
94 * Creates a new GroupBy.
95 *
96 * @param builder
97 * The builder to copy the state from.
98 * @throws IllegalArgumentException
99 * If neither the {@link #getKeys() keys} nor
100 * {@link #getKeyFunction() key function} have been set.
101 */
102 protected GroupBy(final Builder builder) throws IllegalArgumentException {
103 assertThat(
104 !builder.myKeys.isEmpty() || (builder.myKeyFunction != null),
105 "Must specify either a set of keys for the groupBy or a key function.");
106
107 myKeys = Collections
108 .unmodifiableSet(new HashSet<String>(builder.myKeys));
109 myReduceFunction = builder.myReduceFunction;
110 myInitialValue = builder.myInitialValue;
111 myKeyFunction = builder.myKeyFunction;
112 myQuery = builder.myQuery;
113 myFinalizeFunction = builder.myFinalizeFunction;
114 myReadPreference = builder.myReadPreference;
115 myMaximumTimeMilliseconds = builder.myMaximumTimeMilliseconds;
116 }
117
118 /**
119 * Returns the finalizer function to run for each group.
120 *
121 * @return The finalizer function to run for each group.
122 */
123 public String getFinalizeFunction() {
124 return myFinalizeFunction;
125 }
126
127 /**
128 * Returns the initial value for each group.
129 *
130 * @return The initial value for each group.
131 */
132 public Document getInitialValue() {
133 return myInitialValue;
134 }
135
136 /**
137 * Returns the function to return the key for a document. Used instead of
138 * the {@link #getKeys} to dynamically determine the group for each
139 * document.
140 *
141 * @return The function to return the key for a document. Used instead of
142 * the {@link #getKeys} to dynamically determine the group for each
143 * document.
144 */
145 public String getKeyFunction() {
146 return myKeyFunction;
147 }
148
149 /**
150 * Returns the fields to group by.
151 *
152 * @return The fields to group by.
153 */
154 public Set<String> getKeys() {
155 return myKeys;
156 }
157
158 /**
159 * Returns the maximum amount of time to allow the command to run on the
160 * Server before it is aborted.
161 *
162 * @return The maximum amount of time to allow the command to run on the
163 * Server before it is aborted.
164 *
165 * @since MongoDB 2.6
166 */
167 public long getMaximumTimeMilliseconds() {
168 return myMaximumTimeMilliseconds;
169 }
170
171 /**
172 * Returns the query to select the documents to run the group against.
173 *
174 * @return The query to select the documents to run the group against.
175 */
176 public Document getQuery() {
177 return myQuery;
178 }
179
180 /**
181 * Returns the {@link ReadPreference} specifying which servers may be used
182 * to execute the {@link GroupBy} command.
183 * <p>
184 * If <code>null</code> then the {@link MongoCollection} instance's
185 * {@link ReadPreference} will be used.
186 * </p>
187 *
188 * @return The read preference to use.
189 *
190 * @see MongoCollection#getReadPreference()
191 */
192 public ReadPreference getReadPreference() {
193 return myReadPreference;
194 }
195
196 /**
197 * Returns the reduce function taking the previous value and the current
198 * value and returning the new reduced value.
199 *
200 * @return The reduce function taking the previous value and the current
201 * value and returning the new reduced value.
202 */
203 public String getReduceFunction() {
204 return myReduceFunction;
205 }
206
207 /**
208 * Builder provides a builder for Group commands.
209 *
210 * @api.yes This class is part of the driver's API. Public and protected
211 * members will be deprecated for at least 1 non-bugfix release
212 * (version numbers are <major>.<minor>.<bugfix>)
213 * before being removed or modified.
214 * @copyright 2012-2013, Allanbank Consulting, Inc., All Rights Reserved
215 */
216 public static class Builder {
217
218 /** The finalizer function to run for each group. */
219 protected String myFinalizeFunction;
220
221 /** The initial value for the group. */
222 protected Document myInitialValue;
223
224 /**
225 * Function to return the key for a document. Used instead of the
226 * {@link #setKeys} to dynamically determine the group for each
227 * document.
228 */
229 protected String myKeyFunction;
230
231 /** The fields to group by. */
232 protected final Set<String> myKeys;
233
234 /** The maximum amount of time to allow the command to run. */
235 protected long myMaximumTimeMilliseconds;
236
237 /** The query to select the documents to run the group against. */
238 protected Document myQuery;
239
240 /** The read preference to use. */
241 protected ReadPreference myReadPreference;
242
243 /**
244 * The reduce function taking the previous value and the current value
245 * and returning the new reduced value.
246 */
247 protected String myReduceFunction;
248
249 /**
250 * Creates a new Builder.
251 */
252 public Builder() {
253 myKeys = new HashSet<String>();
254
255 reset();
256 }
257
258 /**
259 * Creates a new {@link GroupBy} based on the current state of the
260 * builder.
261 *
262 * @return A new {@link GroupBy} based on the current state of the
263 * builder.
264 * @throws IllegalArgumentException
265 * If neither the {@link #getKeys() keys} nor
266 * {@link #getKeyFunction() key function} have been set.
267 */
268 public GroupBy build() throws IllegalArgumentException {
269 return new GroupBy(this);
270 }
271
272 /**
273 * Sets the value of the finalizer function to run for each group.
274 * <p>
275 * This method delegates to {@link #setFinalizeFunction(String)}.
276 * </p>
277 *
278 * @param finalizeFunction
279 * The new value for the finalizer function to run for each
280 * group.
281 * @return This {@link Builder} for method call chaining.
282 */
283 public Builder finalize(final String finalizeFunction) {
284 return setFinalizeFunction(finalizeFunction);
285 }
286
287 /**
288 * Sets the value of the initial value for the group.
289 * <p>
290 * This method delegates to {@link #setInitialValue(DocumentAssignable)}
291 * .
292 * </p>
293 *
294 * @param initialValue
295 * The new value for the initial value for the group.
296 * @return This {@link Builder} for method call chaining.
297 */
298 public Builder initialValue(final DocumentAssignable initialValue) {
299 return setInitialValue(initialValue);
300 }
301
302 /**
303 * Sets the value of the function to return the key for a document. Used
304 * instead of the {@link #setKeys} to dynamically determine the group
305 * for each document.
306 * <p>
307 * This method delegates to {@link #setKeyFunction(String)}.
308 * </p>
309 *
310 * @param keyFunction
311 * The new value for the function to return the key for a
312 * document. Used instead of the {@link #setKeys} to
313 * dynamically determine the group for each document.
314 * @return This {@link Builder} for method call chaining.
315 */
316 public Builder key(final String keyFunction) {
317 return setKeyFunction(keyFunction);
318 }
319
320 /**
321 * Sets the fields to group by
322 * <p>
323 * This method delegates to {@link #setKeys(Set)}.
324 * </p>
325 *
326 * @param keys
327 * The new fields to group by
328 * @return This {@link Builder} for method call chaining.
329 */
330 public Builder keys(final Set<String> keys) {
331 return setKeys(keys);
332 }
333
334 /**
335 * Sets the maximum number of milliseconds to allow the command to run
336 * before aborting the request on the server.
337 * <p>
338 * This method equivalent to {@link #setMaximumTimeMilliseconds(long)
339 * setMaximumTimeMilliseconds(timeLimitUnits.toMillis(timeLimit)}.
340 * </p>
341 *
342 * @param timeLimit
343 * The new maximum amount of time to allow the command to
344 * run.
345 * @param timeLimitUnits
346 * The units for the maximum amount of time to allow the
347 * command to run.
348 *
349 * @return This {@link Builder} for method call chaining.
350 *
351 * @since MongoDB 2.6
352 */
353 public Builder maximumTime(final long timeLimit,
354 final TimeUnit timeLimitUnits) {
355 return setMaximumTimeMilliseconds(timeLimitUnits
356 .toMillis(timeLimit));
357 }
358
359 /**
360 * Sets the value of the query to select the documents to run the group
361 * against.
362 * <p>
363 * This method delegates to {@link #setQuery(DocumentAssignable)}.
364 * </p>
365 *
366 * @param query
367 * The new value for the query to select the documents to run
368 * the group against.
369 * @return This {@link Builder} for method call chaining.
370 */
371 public Builder query(final DocumentAssignable query) {
372 return setQuery(query);
373 }
374
375 /**
376 * Sets the {@link ReadPreference} specifying which servers may be used
377 * to execute the {@link GroupBy} command.
378 * <p>
379 * If not set or set to <code>null</code> then the
380 * {@link MongoCollection} instance's {@link ReadPreference} will be
381 * used.
382 * </p>
383 * <p>
384 * This method delegates to {@link #setReadPreference(ReadPreference)}.
385 * </p>
386 *
387 * @param readPreference
388 * The read preferences specifying which servers may be used.
389 * @return This builder for chaining method calls.
390 *
391 * @see MongoCollection#getReadPreference()
392 */
393 public Builder readPreference(final ReadPreference readPreference) {
394 return setReadPreference(readPreference);
395 }
396
397 /**
398 * Sets the value of the reduce function taking the previous value and
399 * the current value and returning the new reduced value.
400 * <p>
401 * This method delegates to {@link #setReduceFunction(String)}.
402 * </p>
403 *
404 * @param reduceFunction
405 * The new value for the reduce function taking the previous
406 * value and the current value and returning the new reduced
407 * value.
408 * @return This {@link Builder} for method call chaining.
409 */
410 public Builder reduce(final String reduceFunction) {
411 return setReduceFunction(reduceFunction);
412 }
413
414 /**
415 * Resets the builder back to its initial state.
416 *
417 * @return This {@link Builder} for method call chaining.
418 */
419 public Builder reset() {
420 myFinalizeFunction = null;
421 myInitialValue = null;
422 myKeyFunction = null;
423 myKeys.clear();
424 myQuery = null;
425 myReadPreference = null;
426 myReduceFunction = null;
427 myMaximumTimeMilliseconds = 0;
428
429 return this;
430 }
431
432 /**
433 * Sets the value of the finalizer function to run for each group.
434 *
435 * @param finalizeFunction
436 * The new value for the finalizer function to run for each
437 * group.
438 * @return This {@link Builder} for method call chaining.
439 */
440 public Builder setFinalizeFunction(final String finalizeFunction) {
441 myFinalizeFunction = finalizeFunction;
442 return this;
443 }
444
445 /**
446 * Sets the value of the initial value for the group.
447 *
448 * @param initialValue
449 * The new value for the initial value for the group.
450 * @return This {@link Builder} for method call chaining.
451 */
452 public Builder setInitialValue(final DocumentAssignable initialValue) {
453 myInitialValue = initialValue.asDocument();
454 return this;
455 }
456
457 /**
458 * Sets the value of the function to return the key for a document. Used
459 * instead of the {@link #setKeys} to dynamically determine the group
460 * for each document.
461 *
462 * @param keyFunction
463 * The new value for the function to return the key for a
464 * document. Used instead of the {@link #setKeys} to
465 * dynamically determine the group for each document.
466 * @return This {@link Builder} for method call chaining.
467 */
468 public Builder setKeyFunction(final String keyFunction) {
469 myKeyFunction = keyFunction;
470 return this;
471 }
472
473 /**
474 * Sets the fields to group by
475 *
476 * @param keys
477 * The new fields to group by
478 * @return This {@link Builder} for method call chaining.
479 */
480 public Builder setKeys(final Set<String> keys) {
481 myKeys.clear();
482 if (keys != null) {
483 myKeys.addAll(keys);
484 }
485 return this;
486 }
487
488 /**
489 * Sets the maximum number of milliseconds to allow the command to run
490 * before aborting the request on the server.
491 *
492 * @param maximumTimeMilliseconds
493 * The new maximum number of milliseconds to allow the
494 * command to run.
495 * @return This {@link Builder} for method call chaining.
496 *
497 * @since MongoDB 2.6
498 */
499 public Builder setMaximumTimeMilliseconds(
500 final long maximumTimeMilliseconds) {
501 myMaximumTimeMilliseconds = maximumTimeMilliseconds;
502 return this;
503 }
504
505 /**
506 * Sets the value of the query to select the documents to run the group
507 * against.
508 *
509 * @param query
510 * The new value for the query to select the documents to run
511 * the group against.
512 * @return This {@link Builder} for method call chaining.
513 */
514 public Builder setQuery(final DocumentAssignable query) {
515 myQuery = query.asDocument();
516 return this;
517 }
518
519 /**
520 * Sets the {@link ReadPreference} specifying which servers may be used
521 * to execute the {@link GroupBy} command.
522 * <p>
523 * If not set or set to <code>null</code> then the
524 * {@link MongoCollection} instance's {@link ReadPreference} will be
525 * used.
526 * </p>
527 *
528 * @param readPreference
529 * The read preferences specifying which servers may be used.
530 * @return This builder for chaining method calls.
531 *
532 * @see MongoCollection#getReadPreference()
533 */
534 public Builder setReadPreference(final ReadPreference readPreference) {
535 myReadPreference = readPreference;
536 return this;
537 }
538
539 /**
540 * Sets the value of the reduce function taking the previous value and
541 * the current value and returning the new reduced value.
542 *
543 * @param reduceFunction
544 * The new value for the reduce function taking the previous
545 * value and the current value and returning the new reduced
546 * value.
547 * @return This {@link Builder} for method call chaining.
548 */
549 public Builder setReduceFunction(final String reduceFunction) {
550 myReduceFunction = reduceFunction;
551 return this;
552 }
553 }
554 }