How to sum the value of a key across all documents in a MongoDB collection
I have collection in MongoDB:
{ "_id" : ObjectId("4d2407265ff08824e3000001"), "subida" : 3.95 }
{ "_id" : ObjectId("4d2551b4ae9fa739640df821"), "subida" : 6.03 }
{ "_id" : ObjectId("4d255b115ff08821c2000001"), "subida" : 5.53 }
{ "_id" : ObjectId("4d25e8d55ff08814f8000001"), "subida" : 1.96 }
How I can sum the value of a key, e.g., "subida"
, across all documents? With the documents above, I should receive something along the lines of:
{ "subida" : 17.47 }
Solution 1:
In this case, aggregation is much simpler and much more efficient than mapReduce:
db.collection.aggregate({
$group: {
_id: '',
subida: { $sum: '$subida' }
}
}, {
$project: {
_id: 0,
subida: '$subida'
}
})
- use $group with $sum to calculate sum
- use projection's $project operator to remove id key required by $group operator
Solution 2:
I'd personally perform a mapreduce on the collection :
map is a simple function emitting the "subida" field. The key should the same if you need a single sum; the result after reduce will yield the single object {<key>: <sum>}
, with <key>
being whatever value you supplied in the emit.
map = function() { emit(<key>, this.subida); }
reduce is also a simple function summing them :
red = function(k, v) {
var i, sum = 0;
for (i in v) {
sum += v[i];
}
return sum;
}
You can then call mapreduce on your collection <mycollection>
:
res = db.<mycollection>.mapReduce(map, red);
Which will create a temporary new collection you can manipulate like any other collection. The value returned by mapReduce holds several values regarding the mapReduce such as the time taken, status..., as well as the temp. collection name created in the "result" field. To get the values you need, you have to query that collection :
db[res.result].find()
Which should give you the object {<key>: <sum>}
.
If you run MongoDB 1.7.4 or higher, you can save you a bit of hassle by asking MongoDB to return the result directly without creating a collection :
db.<mycollection>.mapReduce(map, red, {out : {inline: 1}});
Solution 3:
Use this simplest query to get the sum of the result
db.collection.aggregate({
$group: {
_id: '',
subida: { $sum: '$subida' }
}
}
)
Solution 4:
Option 0: Use the MongoDB aggregation pipeline
[NB: this option was added a long time after this question was asked but is the right approach now]
Option 1: Query all the records, return only the subida field from Mongo and add them up by iterating over the Mongo cursor client side.
Option 2: Write a map reduce command that emits only the subdia field (same key for all) and then a reduce command that totals them.
Option 3: Use db.eval to execute javascript on the server: http://www.mongodb.org/display/DOCS/Server-side+Code+Execution
Option 4: Accumulate 'subida' values as you insert values into your collection so that you have an up-to-date total at hand whenever you need it. You can store the total in a different document and use atomic "update if current" operations to update it: http://www.mongodb.org/display/DOCS/Atomic+Operations