Search code examples
mongodb-querylodashnode-mongodb-native

Group mongodb collection and output the result as a single object


Is there a way to group a collection which looks like

[
  {_id: "5bd258a7877e74059b6b65b2", year: 2017, title: "One"},
  {_id: "5bd258a7877e74059b6b65b3", year: 2017, title: "Two"},
  {_id: "5bd258a7877e74059b6b65b4", year: 2018, title: "Three"},
  {_id: "5bd258a7877e74059b6b65b5", year: 2018, title: "Four"}
]

and output the result as

{
  2017: [
    0: {_id: "5bd258a7877e74059b6b65b2", title: "One", …}
    1: {_id: "5bd258a7877e74059b6b65b3", title: "Two", …}
  ],
  2018: [
    0: {_id: "5bd258a7877e74059b6b65b4", title: "Three", …}
    1: {_id: "5bd258a7877e74059b6b65b5", title: "Four", …}
  ]
}

using mongodb aggregations? Similar to how lodash groupBy works


Solution

  • You can do that with the following mongoDB aggregation:

    db.collection.aggregate([{
        $group: {
          _id: "$year",
          docs: {
            $addToSet: "$$CURRENT"
          }
        }
      },
      {
        "$group": {
          "_id": null,
          "data": {
            "$push": {
              "k": {
                $toString: "$_id"
              },
              "v": "$docs"
            }
          }
        }
      },
      {
        "$replaceRoot": {
          "newRoot": {
            "$arrayToObject": "$data"
          }
        }
      }
    ])
    

    You can see the result here

    The idea is to group the data in a way where we can utilize $arrayToObject. First group gives you the grouped by year where the second is just prep for $arrayToObject which requires key, value object. Last thing is the $replaceRoot.

    This requires MongoDB 3.6 and up due to $arrayToObject being introduced in that version. Before that you had to use $push etc.