Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reshape MongoDB aggregation result to field value documents

I have a data collection containing data in the following shape:

[ {_id: "1",
   aa1: 45, aa2: 56,
   bb1: 90, bb2: 78,
   cc1: 34, cc2: 98 },
  {_id: "2",
   aa1: 76, aa2: 56,
   bb1: 45, bb2: 67,
   cc1: 75, cc2: 87 } ]

on this data I perform a MongoDB aggregation pipeline:

db.colleciton.aggregate([
 { $project: {
             "_id": "$_id",
             "aaa": { $avg: ["$aa1","$aa2"] },
             "bbb": { $avg: ["$bb1","$bb2"] },
             "ccc": { $avg: ["$cc1","$cc2"] } 
            }
 }, 
 { $group: {
             "_id": "AvgCalc",
             "aaa": { $avg: "$aaa" },
             "bbb": { $avg: "$bbb" },
             "ccc": { $avg: "$ccc" } 
            }     
 }
])

that gives my result in the format:

[ {_id: "AvgCalc",
   aaa: 67,
   bbb: 55,
   ccc: 90} ]

I would like to convert this in the aggregation to give my result as:

[ {field: "aaa", value: 67}, 
  {field: "bbb", value: 55},
  {field: "ccc", value: 90} ]

Is there a way to achieve this with aggregation? I would like to add this as a final stage in the pipeline. I tried adding a $project stage but with no success.

like image 513
Gerrie van Wyk Avatar asked Aug 08 '16 09:08

Gerrie van Wyk


People also ask

Which aggregation pipeline stage writes the resulting document of the aggregation pipeline to a new collection?

To use the $merge stage, it must be the last stage in the pipeline. New in version 4.2. Writes the resulting documents of the aggregation pipeline to a collection. To use the $out stage, it must be the last stage in the pipeline.

What does MongoDB aggregation return?

aggregate() method returns a cursor to the documents produced by the final stage of the aggregation pipeline operation, or if you include the explain option, the document that provides details on the processing of the aggregation operation.

What are the differences between using aggregate () and find () in MongoDB?

With aggregate + $match, you get a big monolithic BSON containing all matching documents. With find, you get a cursor to all matching documents. Then you can get each document one by one.

How can you group by a particular value in MongoDB?

We can group by single as well as multiple field from the collection, we can use $group operator in MongoDB to group fields from the collection and returns the new document as result. We are using $avg, $sum, $max, $min, $push, $last, $first and $addToSet operator with group by in MongoDB.


1 Answers

This pipeline should give you the desired result

db.getCollection('yourColl').aggregate([
 { 
    $project: {
        "_id": "$_id",
        "aaa": { $avg: ["$aa1","$aa2"] },
        "bbb": { $avg: ["$bb1","$bb2"] },
        "ccc": { $avg: ["$cc1","$cc2"] } 
    }
 }, 
 { 
    $group: {
        "_id": "AvgCalc",
        "aaa": { $avg: "$aaa" },
        "bbb": { $avg: "$bbb" },
        "ccc": { $avg: "$ccc" } 
    }     
 },
 {
    $project: {
        "items": [ 
            {name: {$literal: "aaa"}, value: "$aaa"},
            {name: {$literal: "bbb"}, value: "$bbb"},
            {name: {$literal: "ccc"}, value: "$ccc"}
        ]
    }
 },
 {
     $unwind: {
         path: "$items"
     }
 },
 {
     $project: {
         _id: 0,
         field: "$items.name",
         value: "$items.value"
     }
 }
])

For your sample data the previous output was

/* 1 */
{
    "_id" : "AvgCalc",
    "aaa" : 58.25,
    "bbb" : 70.0,
    "ccc" : 73.5
}

and with the new pipeline the output is like this

/* 1 */
{
    "field" : "aaa",
    "value" : 58.25
}

/* 2 */
{
    "field" : "bbb",
    "value" : 70.0
}

/* 3 */
{
    "field" : "ccc",
    "value" : 73.5
}
like image 170
DAXaholic Avatar answered Nov 03 '22 01:11

DAXaholic