$group inner array values without $unwind

↘锁芯ラ 提交于 2021-01-27 08:11:08

问题


I want to group objects in the array by same value for specified field and produce a count.

I have the following mongodb document (non-relevant fields are not present).

{
  arrayField: [ 
    { fieldA: value1, ...otherFields }, 
    { fieldA: value2, ...otherFields },
    { fieldA: value2, ...otherFields } 
  ],
  ...otherFields
}

The following is what I want.

{
  arrayField: [ 
    { fieldA: value1, ...otherFields }, 
    { fieldA: value2, ...otherFields },
    { fieldA: value2, ...otherFields } 
  ],
  newArrayField: [ 
    { fieldA: value1, count: 1 }, 
    { fieldA: value2, count: 2 },
  ],
  ...otherFields
}

Here I grouped embedded documents by fieldA.

I know how to do it with unwind and 2 group stages the following way. (irrelevant stages are ommited)

Concrete example

// document structure
{
  _id: ObjectId(...),
  type: "test",
  results: [ 
    { choice: "a" }, 
    { choice: "b" },
    { choice: "a" } 
  ]
}
db.test.aggregate([
{ $match: {} },
{
  $unwind: {
    path: "$results",
    preserveNullAndEmptyArrays: true
  }
},
{
  $group: {
    _id: {
      _id: "$_id",
      type: "$type",
      choice: "$results.choice",
    },
    count: { $sum: 1 }
  }
},
{
  $group: {
    _id: {
      _id: "$_id._id",
      type: "$_id.type",
      result: "$results.choice",
    },
    groupedResults: { $push: { count: "$count", choice: "$_id.choice" } }
  }
}
])

回答1:


You can use below aggregation

db.test.aggregate([
  { "$addFields": {
    "newArrayField": {
      "$map": {
        "input": { "$setUnion": ["$arrayField.fieldA"] },
        "as": "m",
        "in": {
          "fieldA": "$$m",
          "count": {
            "$size": {
              "$filter": {
                "input": "$arrayField",
                "as": "d",
                "cond": { "$eq": ["$$d.fieldA", "$$m"] }
              }
            }
          }
        }
      }
    }
  }}
])



回答2:


The below adds a new array field, which is generated by:

  1. Using $setUnion to get unique set of array items, with inner $map to extract only the choice field
  2. Using $map on the unique set of items, with inner $reduce on the original array, to sum all items where choice matches

Pipeline:

db.test.aggregate([{
  $addFields: {
    newArrayField: {
      $map: {
        input: {
          $setUnion: [{
              $map: {
                input: "$results",
                in: { choice: "$$this.choice" }
              }
            }
          ]
        },
        as: "i",
        in: {
          choice: '$$i.choice',
          count: {
            $reduce: {
              input: "$results",
              initialValue: 0,
              in: { 
                $sum: ["$$value", { $cond: [ { $eq: [ "$$this.choice", "$$i.choice" ] }, 1, 0 ] }]
              }
            }
          }
        }
      }
    }
  }
}])

The $reduce will iterate over the results array n times, where n is the number of unique values of choice, so the performance will depend on that.



来源:https://stackoverflow.com/questions/55726116/group-inner-array-values-without-unwind

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!