Pedro T. Lima
Pedro T. Lima

Reputation: 1

MongoDB - Aggregate fields inside and object

I have the following dataset in MongoDB:

{
  _id: 574718ec2bc91f565db33897,
  topic: {
    T69: 0.9566255761668587
  }
},
{
  _id: 574718ec2bc91f565db33899,
  topic: {
    T257: 0.046038051058499445,
    T2: 1.8206715756325407,
    T31: 0.08838710118945285
  }
},
{
  _id: 574718ec2bc91f565db33889,
  topic: {
    T95: 0.37718859499517865,
    T40: 0.2620479937270479,
    T2: 0.3594989449758472,
    T1: 1.9161288780994465
  }
}

I've been trying to create an aggregation query which returns the sum of all topics, Tn, over the set of all such documents. Can anyone give me a pointer in the right direction? Since I'm new to MongoDB I couldn't find an answer to this problem (though this seemed related $unwind an object in aggregation framework).

Upvotes: 0

Views: 707

Answers (2)

Dario
Dario

Reputation: 4035

I think you can't do it with the mongoDB aggregation framework (that works better with collections/array of subdocs), but is pretty simple with a map/reduce. For example you can try with:

db.YOURCOLLECTION.mapReduce(
  function () {
    var topic = this.topic;
    Object.keys(topic).forEach(function(k) {
      emit(k, topic[k]);
    });
  },
  function (key, values) {
     return Array.sum(values);
  }
);

Upvotes: 0

Sede
Sede

Reputation: 61273

Our best bet here is mapReduce. In our map function all we need is to iterate over the "topic" property and emit the value. To get the total sum in the collection we need to "emit" with null as key value.

In the reduce function we simply use the Array.sum method to return the sum.

db.coll.mapReduce(function() { 
    for (var key in this.topic) { 
        if (Object.prototype.hasOwnProperty.call(this.topic, key)) {
            emit(null, this.topic[key]) 
        }
    }}, 
    function(key, value) { 
        return Array.sum(value);
    }, 
    { "out": { "inline": 1 } }
)

which produces:

{
    "results" : [
        {
            "_id" : null,
            "value" : 5.826586715844872
        }
    ],
    "timeMillis" : 26,
    "counts" : {
        "input" : 3,
        "emit" : 8,
        "reduce" : 1,
        "output" : 1
    },
    "ok" : 1
}

If you want the "sum" for each document, simply call emit(this._id, this.topic[key]) in your map function instead of emit(null, this.topic[key])

Upvotes: 0

Related Questions