skip to Main Content

I have a MongoDB collection named Venue with elements of type:

{
    venue: "Grand Hall",
    sections: [{
        name: "Lobby",
        drinks: [{
            name: "Vodka",
            quantity: 3
        }, {
            name: "Red Wine",
            quantity: 1
        }]
    }, {
        name: "Ballroom",
        drinks: [{
            name: "Vodka",
            quantity: 22
        }, {
            name: "Red Wine",
            quantity: 50
        }]
    }]
}

I want to calculate the total amounts of each drink for the party. So I want my result to be something like that:

{
    venue: "Grand Hall",
    sections: 2,
    drinks: [{
        name: "Vodka",
        quantity: 25
    }, {
        name: "Red Wine",
        quantity: 51
    }]
}

2

Answers


    1. $unwind – Deconstruct sections array into multiple documents.

    2. $unwind – Deconstruct sections.drinks array into multiple documents.

    3. $group – Group by venue and sections.drinks.name. Perform sum for quantity.

    4. $group – Group by venue. Perform count for grouped result in previous stage. And add the document into drinks array.

    db.collection.aggregate([
      {
        $unwind: "$sections"
      },
      {
        $unwind: "$sections.drinks"
      },
      {
        $group: {
          _id: {
            venue: "$venue",
            drink_name: "$sections.drinks.name"
          },
          quantity: {
            $sum: "$sections.drinks.quantity"
          }
        }
      },
      {
        $group: {
          _id: "$_id.venue",
          section: {
            $sum: 1
          },
          drinks: {
            $push: {
              name: "$_id.drink_name",
              quantity: "$quantity"
            }
          }
        }
      }
    ])
    

    Demo @ Mongo Playground

    Login or Signup to reply.
  1. There are lots of ways to do this. Here’s another one using "$reduce" and "$map", etc.

    db.Venue.aggregate({
      "$match": {
        "venue": "Grand Hall"
      }
    },
    {
      "$set": {
        "sections": {"$size": "$sections"},
        "drinks": {
          "$reduce": {
            "input": {  // flatten array of drink objects
              "$reduce": {
                "input": "$sections.drinks",
                "initialValue": [],
                "in": {"$concatArrays": ["$$value", "$$this"]}
              }
            },
            "initialValue": [],
            "in": {
              "$let": {
                "vars": {  // position of drink in $$value, or -1 if not found
                  "idx": {"$indexOfArray": ["$$value.name", "$$this.name"]}
                },
                "in": {
                  "$cond": [
                    {"$eq": ["$$idx", -1]},  // not found?
                    {"$concatArrays": ["$$value", ["$$this"]]},  // not found, add object
                    {  // found, so update object in $$value by summing quantities
                      "$map": {
                        "input": "$$value",
                        "as": "val",
                        "in": {
                          "$cond": [
                            {"$eq": ["$$val.name", "$$this.name"]},  // right object?
                            {  // yes, update quantity
                              "name": "$$val.name",
                              "quantity": {"$sum": ["$$val.quantity", "$$this.quantity"]}
                            },
                            "$$val"  // wrong object for update, so just keep it
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    })
    

    Try it on mongoplayground.net.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search