Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

unwind and group for the nested array objects in mongodb

In mongodb, I have the data structure like this:

{
    "data" : [ 
        {
            "car" : [ 
                {
                    "model" : "aaa",
                }, 
                {
                    "model" : "bbb",
                }, 
                {
                    "model" : "ccc",
                }
            ],
            "user" : {
                "id" : "123"
            }
        }
    ],
}

And I want to group and count the car model field. So first I tried the unwind method like this:

.aggregate([
  { $project: {"data.car.model":1, "data.user.id":1} },
  { $unwind: {
      path: "$data.car",
      preserveNullAndEmptyArrays: true
      } },
  { $group: {
      _id: "$data.car.model",
      count: { $sum: 1 }
      } }
])

But the result is:

{
    "_id" : [ ["aaa", "bbb", "ccc"] ],
    "count" : 1.0
}

which is not what I wanted since the model didn't unwind. The result I'm hoping is:

[{_id:"aaa",count:1}, {_id:"bbb",count:1}, {_id:"ccc", count:1}]

Then by looked at mongodb unwind array nested inside an array of documents, I tried the $project the nested array:

.aggregate([
  { $project: {"car":"$data.car.model", "user":"$data.user.id"} },
])

But the result is:

{
    "car" : [ ["aaa", "bbb", "ccc"] ],
    "user" : ["123"]
}

As you can see, the car and user was embedded in an array, which I still can't apply the $unwind method. However, I hope it should be like:

{
    "car" : ["aaa", "bbb", "ccc"],
    "user" : "123"
}

The mongo I'm using is 3.2, I searched a lot on the web but didn't find the answer. Is there possible to do so?

like image 386
popo Avatar asked May 31 '26 20:05

popo


1 Answers

You need to $unwind "each" array independently:

.aggregate([
  { "$unwind": { "path": "$data", "preserveNullAndEmptyArrays": true } },
  { "$unwind": { "path": "$data.car", "preserveNullAndEmptyArrays": true } },
  { "$group": {
    "_id": "$data.car.model",
    "count": { "$sum": 1 }
  }}
])

You cannot actually "reach into" and $unwind as you attempted. So you first "un-wrap" the outer array, and then the "inner" one. Then the documents are flattened for grouping.

An alternate approach may be to use $reduce to "flatten the array" before "unwinding":

.aggregate([
  { "$project": {
    "data": {
      "$reduce": {
        "input": "$data.car",
        "initialValue": [],
        "in": {
          "$concatArrays": [ "$$value", "$$this" ]
        }
      }
    }
  }},
  { "$unwind": "$data" },
  { "$group": {
    "_id": "$data.model",
    "count": { "$sum": 1 }
  }}
])

But it would not really be recommended since the "projection" more likely involves a higher cost than that incurred by using $unwind for each level of nesting in the array.

like image 114
Neil Lunn Avatar answered Jun 02 '26 11:06

Neil Lunn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!