Mongodb Is it possible to aggregate an object?

2020-07-24 04:17发布

问题:

I am trying to aggregate the total sum of packets in this document.

{
    "_id" : ObjectId("51a6cd102769c63e65061bda"),
    "capture" : "1369885967",
    "packets" : {
        "0" : "595",
        "1" : "596",
        "2" : "595",
        "3" : "595",
        ...
    }
}

The closest I can get is about

db.collection.aggregate({ $match: { capture : "1369885967" } }, {$group: { _id:null, sum: {$sum:"$packets"}}});

However it returns sum 0, which is obviously wrong.

{ "result" : [ { "_id" : null, "sum" : 0 } ], "ok" : 1 }

How do I get the sum of all the packets?

回答1:

Since you have the values in an object instead of an array, you'll need to use mapReduce.

// Emit the values as integers

var mapFunction = 
  function() {
    for (key in this.packets) {
      emit(null, parseInt(this.packets[key]));
    }
  }

// Reduce to a simple sum

var reduceFunction = 
  function(key, values) {
    return Array.sum(values);
  }

> db.collection.mapReduce(mapFunction, reduceFunction, {out: {inline:1}})
{
    "results" : [
        {
            "_id" : null,
            "value" : 2381
        }
    ],
    "ok" : 1,
}

If at all possible, you should emit the values as an array of a numeric type instead since that gives you more options (ie aggregation) and (unless the data set is large) probably performance benefits.



回答2:

If you don't know how many keys are in the packet subdocument and since you also seem to be storing counts as strings (why???) you will have to use mapReduce.

Something like:

m=function() { 
    for (f in "this.packets") {
      emit(null, +this.packets[f]);
};
r=function(k, vals) { 
     int sum=0;
     vals.forEach(function(v) { sum+=v; } );
     return sum;
}

db.collection.mapreduce(m, r, {out:{inline:1}, query:{your query condition here}});


标签: mongodb