1

I have a collection data in MongoDB which looks like this -

document 1-

{
    metadata:[
        {
            "title": "High",
            "val": 12
        },
        {
            "title": "Medium",
            "val": 15
        },
        {
            "title": "Low",
            "val": 2
        }
    ]
}

document2 -

{
    metadata:[
        {
            "title": "High",
            "val": 10
        },
        {
            "title": "Medium",
            "val": 12
        },
        {
            "title": "Low",
            "val": 20
        }
    ]
}

& so on..

I wish to aggregate the val field based on the title value. The output should look like -

{
    "High": 22,
    "Medium": 27,
    "Low": 22
}

How do I achieve this? Thanks in advance.

2 Answers 2

2
  • $unwind deconstruct metadata array
  • $grpup by title and make sum of val
  • $group by null, convert array to object from key and value by $arrayToObject, and merge objects using $mergeObjects
db.collection.aggregate([
  { $unwind: "$metadata" },
  {
    $group: {
      _id: "$metadata.title",
      sum: { $sum: "$metadata.val" }
    }
  },
  {
    $group: {
      _id: null,
      metadata: {
        $mergeObjects: {
          $arrayToObject: [
            [{ k: "$_id", v: "$sum" }]
          ]
        }
      }
    }
  }
])

Playground

Sign up to request clarification or add additional context in comments.

3 Comments

I get this error when I try to run this query on actual data - $arrayToObject requires an object with keys 'k' and 'v', where the value of 'k' must be of type string. Found type: null
there would be some null or empty value in title or val fields. can you make sure where is it?
figured out. error was from my end. thanks!! :)
1

You can first $unwind the metadata array. Then use $group and $cond to $sum your values.

db.collection.aggregate([
  {
    "$unwind": "$metadata"
  },
  {
    $group: {
      _id: null,
      High: {
        $sum: {
          $cond: [
            {
              $eq: [
                "$metadata.title",
                "High"
              ]
            },
            "$metadata.val",
            0
          ]
        }
      },
      Medium: {
        $sum: {
          $cond: [
            {
              $eq: [
                "$metadata.title",
                "Medium"
              ]
            },
            "$metadata.val",
            0
          ]
        }
      },
      Low: {
        $sum: {
          $cond: [
            {
              $eq: [
                "$metadata.title",
                "Low"
              ]
            },
            "$metadata.val",
            0
          ]
        }
      }
    }
  }
])

Here is the mongo playground for your reference.

3 Comments

thanks!! is there a way to achieve this when we do not know the field values High, Medium, Low by segregating based on unique title values?
@Ray that was too old post, for the latest version it is possible, i have just answered there take a look
@turivishal yes you are right, your answer should be the accepted one :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.