View Javadoc
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 &lt;major&gt;.&lt;minor&gt;.&lt;bugfix&gt;) 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 &lt;major&gt;.&lt;minor&gt;.&lt;bugfix&gt;)
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 }