fdomig
fdomig

Reputation: 4457

MongoDB: Aggregate values of all fields of a specific subdocument

I am trying to use MongoDB's aggregate-framework to generate documents from a collection which has the following structure:

{
  "_id" : ObjectId("5510eb56f877bbef153d236d"),
  "attributes" : {
    "brand" : "Mercedes",
    "price" : 289,
    "family" : "GARDEN"
  },
  "name" : "Bigger Fix Soft Crisps"
}
{
  "_id" : ObjectId("5510eb56f877bbef153d236e"),
  "attributes" : {
    "brand" : "Adelholzener",
    "price" : 683,
    "family" : "COMPUTER"
  },
  "name" : "Soft Stockhome Chips"
}
{
  "_id" : ObjectId("5510eb56f877bbef153d236f"),
  "attributes" : {
    "brand" : "Pepsi",
    "price" : 789,
    "family" : "CAR"
  },
  "name" : "Light Yearsailfresh Meat"
}

I want to to get the aggregation of all fields from the attributes subdocument with their possible values. Since the fields in the subdocument are not known I cannot simply use the fields given in this example and therefore has to be dynamic. The result should look similar to this:

{
  "_id" : "brand",
  "values" : [
    "Mercedes", "Adelholzener", "Pepsi"
  ]
},
{
  "_id" : "price",
  "values" : [
    289, 683, 789
  ]
},
{
  "_id" : "family",
  "values" : [
    "GARDEN", "COMPUTER", "CAR"
  ]
}

I haven't found a solution so far for this particularly problem. I tried with $project and $unwind (which does obviously only work for arrays).

Upvotes: 3

Views: 174

Answers (1)

while
while

Reputation: 3772

Tried to figure out how to do it w aggregate but i'm not sure it's possible as you can't get the keys in an object in any nice way.

Here is a MapReduce job that does the thing:

db.runCommand({
  "mapreduce" : "test_collection", // Change collection here
  "map" : function() {
    for (var key in this.attributes) {
      // Emit objects w array as we cannot reduce arrays directly
      emit(key, {x: [this.attributes[key]]});
    }
  },
  "reduce" : function(key, values) { 
    var res = [];
    values.forEach(function (d) {
      Array.prototype.push.apply(res, d.x);         
    });
    return {x: res};
  }, 
  "finalize": function (key, reducedVal) {
    return reducedVal.x;
  },
  "out": { inline: 1 }
});

Upvotes: 1

Related Questions