When performing operations on large data sets, it is often useful to group the results based on the distinct values of one or more fields in a document. You could do this in code after retrieving the documents, but it is much more efficient to have MongoDB do it for you as part of a single request that is already iterating though the documents.
To group the results of a query together, you can use the group()
method on the Collection
object. The group()
request first collects all the documents that match a query
and then adds a group
object to an array, based on distinct values of a set of keys
, performs operations on the group
objects, and returns the array of group
objects. The syntax for the group()
methods is shown below:
group(keys, query, initial, reduce, finalize, command, [options], callback)
The parameters of the group()
method are described in the following list:
keys: This can be an object, an array, or a function that expresses the keys to group by. The simplest method is to specify the key(s) in an object such as {field1:true, field2:true}
or an array such as ['first', 'last'].
query: The query
object defines which documents to include in the initial set. See Table 15.1 for a list of query
object options.
initial: Specifies an initial group
object to use when aggregating data while grouping. An initial group
object is created for each distinct set of keys. The most common use is a counter that tracks the number of items that match the keys. For example:
{"count":0}
reduce: This function has two parameters, obj
and prev
. This function is executed on each document that matches the query. The obj
parameter is the current document, and prev
is the object that was created by the initial parameter. You can then use the obj
object to update the prev
object with new values such as counts or sums. For example, to increment the count value, you use:
function(obj, prev) { prev.count++; }
finalize: This function accepts one parameter, obj
, which is the final object resulting from the initial parameter and is updated as prev
in the reduce
function. This function is called on the resulting object for each distinct key before returning the array in the response.
command: This is a Boolean that, when true
, indicates that the command runs using the internal group
command instead of eval()
. The default is true
.
options: This object allows you to define the readPreference
option.
callback: This option accepts an error as the first parameter and an array of the results
objects as the second.
Listing 15.8 shows how to implement grouping of words based on various key sets. Lines 10–18 implement a basic grouping of words by first and last letter. The query in line 11 limits the words to those that begin with o and end with a vowel. The initial object for each has a count
property only, which is updated for each matching document in the function on line 13.
Lines 19–28 sum the total vowels in the documents and group them together by incrementing prev.totalVowels
with the obj.stats.vowels
value in line 23. Then lines 29–40 use a finalize
function that adds a new obj.total
property to the group
object that is a sum of the obj.vowels
and obj.consonants
properties of the object. Figure 15.8 shows the output for the code in Listing 15.8.
01 var MongoClient = require('mongodb').MongoClient;
02 MongoClient.connect("mongodb://localhost/", function(err, db) {
03 var myDB = db.db("words");
04 myDB.collection("word_stats", groupItems);
05 setTimeout(function(){
06 db.close();
07 }, 3000);
08 });
09 function groupItems(err, words){
10 words.group(['first','last'],
11 {first:'o',last:{$in:['a','e','i','o','u']}},
12 {"count":0},
13 function (obj, prev) { prev.count++; }, true,
14 function(err, results){
15 console.log("
'O' words grouped by first and last" +
16 " letter that end with a vowel: ");
17 console.log(results);
18 });
19 words.group(['first'],
20 {size:{$gt:13}},
21 {"count":0, "totalVowels":0},
22 function (obj, prev) {
23 prev.count++; prev.totalVowels += obj.stats.vowels;
24 }, {}, true,
25 function(err, results){
26 console.log("
Words grouped by first letter larger than 13: ");
27 console.log(results);
28 });
29 words.group(['first'],{}, {"count":0, "vowels":0, "consonants":0},
30 function (obj, prev) {
31 prev.count++;
32 prev.vowels += obj.stats.vowels;
33 prev.consonants += obj.stats.consonants;
34 },function(obj){
35 obj.total = obj.vowels + obj.consonants;
36 }, true,
37 function(err, results){
38 console.log("
Words grouped by first letter with totals: ");
39 console.log(results);
40 });
41 }