Search code examples
node.jsmongodbmongooseaggregation-frameworkmongodb-aggregation

Node with mongoose aggregate and sum nested fields


When I do this query against my collection...

models.Project.find(function(err, result) {
    //result = doc below
}).populate('media')

... I get this result:

{ 
  _id: 57f36baa6cf34d079c8474a0,
  code: 'ZMIA',
  __v: 0,
  media:[
    { 
      _id: 57f36bb26cf34d079c847766,
      project_code: 'ZMIA',
      amount: 228,
      __v: 0 
    },
    { 
      _id: 57f36bb26cf34d079c84775c,
      project_code: 'ZMIA',
      amount: 250,
      __v: 0 
    } 
  ]
},
{ 
  _id: 57f36baa6cf34d079c8474a1,
  code: 'ZMJU',
  __v: 0,
  media: [] 
}

media is a ref field. How can I aggregate the nested media objects (if present) to $sum the amount field and group the result by project_code ?


Solution

  • You can use the aggregation framework where you run an aggregation pipeline that consists of an initial $unwind pipeline that will denormalize the media field since it is an array and then a $lookup operator to do a left join to the collection that has the media refs. A further $unwind operator is needed to flatten the array field produced as a result of the join and then do a $group operator pipeline on the flattened documents to produce the desired result.

    Running the following pipeline should work for you:

    models.Project.aggregate([
        { "$unwind": "$media" },
        {
            "$lookup": {
                "from": "media", // <-- collection to join
                "localField": "media",
                "foreignField": "_id",
                "as": "media_joined"
            }
        },
        { "$unwind": "$media_joined" },
        {
            "$group": {
                "_id": "$media_joined.project_code",
                "total": { "$sum": "$media_joined.amount" }
            }
        }
    ], function(err, result){
        console.log(result);
    })