thavan
thavan

Reputation: 2429

MongoDB array aggregation

I have two documents like this

{ "_id" : ObjectId("552cd26276b783ed66031cc4"), "vals" : [  2,  3,  4,  5 ] }
{ "_id" : ObjectId("552cd26e76b783ed66031cc5"), "vals" : [  1,  2,  3,  4 ] }

I need aggregated sum of this two lists

expected output:

[3, 5, 7, 9]

i.e [2+1, 3+2, 4+3, 5+4]

I'm not even sure if this is possible in MongoDB. Any light on this?

Upvotes: 9

Views: 2258

Answers (3)

Sarath Nair
Sarath Nair

Reputation: 2868

Just for an update. This is possible in aggregation framework in the upcoming release of mongoDB 3.2. They have added a new feature to get the unwind index of array in $unwind stage of mongoDB aggregation called includeArrayIndex.

Using this feature you can use aggregate query of the form:

db.collection.aggregate(
  {$unwind: { path: "$vals", includeArrayIndex: "arrayIndex" }},
  {$group: { '_id': '$arrayIndex', sum : {$sum: '$vals'}}},
  {$sort: { '_id': 1 }},
  {$group : { '_id': null, valSum: {$push: '$sum'}}}
)

Explanation:

  • The unwind operation unwinds the vals array and also projects the array index of unwind. So the idea is to group similar indexes so that we can sum them
  • We then group based on the arrayIndex while summing the vals element. In this stage we have perfectly summed the corresponding index elements
  • Next we sort based on arrayIndex so as to prepare for final result array
  • Next we group all documents and push the sums to an array called valSum

Output:

{ '_id': null, valSum: [3, 5, 7, 9] }

The main advantage of this approach is that, you need not have arrays of same length. Also it can take any number of input documents. Thanks to the includeArrayIndex feature.

Cheers!

Upvotes: 2

David Mabodo
David Mabodo

Reputation: 755

Aggregation framework is not just the aggregation pipeline.

Of course you can also use map reduce with really simple functions:

var map_function = function(){
    for(var i=0;i<this.vals.length;i++){
        emit(i,this.vals[i]);
    }
}
var reduce_function = function(key, values){
    return Array.sum(values)
}

The output is a list of documents where the _id is the index and the value is the sum. Like this:

[{u'_id': 0.0, u'value': 3.0},
 {u'_id': 1.0, u'value': 5.0},
 {u'_id': 2.0, u'value': 7.0},
 {u'_id': 3.0, u'value': 9.0}]

Upvotes: -1

Trewaters
Trewaters

Reputation: 1069

Sorry I didn't answer the question correctly. I was thinking of how to sum each object's array. Not adding the arrays between the two objects.

(edit above)

I am new to this but I think you want to use the aggregate operator $unwind, which will explode the array then $sum the "vals". It should look like this

db.Test.aggregate([{$unwind:"$vals"},{$group:{_id:"$_id","TotalVals":{$sum:"$vals"}}}])

refer to the operators here http://docs.mongodb.org/manual/reference/operator/aggregation-group/

This ($unwind) will create one document for each value in the array so watch your memory for bigger collections. This ($unwind) is done in Memory I believe.

Upvotes: 0

Related Questions