在项目阶段将数组分组为子数组

group an array into subarrays in a project stage

我想根据组值拆分以下数组。我知道我可以使用 $unwind$group 来做到这一点。有什么办法可以在单个 $project 阶段做到这一点吗?

输入

{
    "_id": 1,
    "some_field": "some_value",
    "array": [
        {
            "group": "a",
            "subgroup": "aa",
            "value": 1
        },
        {
            "group": "b",
            "subgroup": "bb",
            "value": 2
        },
        {
            "group": "a",
            "subgroup": "ab",
            "value": 2
        }
    ]
}

期望的输出:

{
    "_id": 1,
    "some_field": "some_value",
    "array": [
        {
            "group": "a",
            "values": [
                {
                    "subgroup": "aa",
                    "value": 1
                },
                {
                    "subgroup": "ab",
                    "value": 2
                }
            ]
        },
        {
            "group": "b",
            "values": [
                {
                    "subgroup": "bb",
                    "value": 2
                }
            ]
        }
    ]
}

可以,绝对不干净不性感。 我的方法是使用 $reduce$mergeObjects,我们将遍历数组并不断重构结果。

困扰此方法的主要问题是 this 功能不允许 $concatArrays 表达式,因此我们必须使用一些非常丑陋的解决方法。

无论如何,这是实现此目标的方法:

db.collection.aggregate([
  {
    $project: {
      _id: 1,
      some_field: 1,
      array: {
        $map: {
          input: {
            "$objectToArray": {
              $reduce: {
                input: "$array",
                initialValue: {},
                in: {
                  "$mergeObjects": [
                    "$$value",
                    {
                      "$arrayToObject": [
                        [
                          {
                            k: "$$this.group",
                            v: {
                              $map: {
                                input: {
                                  "$concatArrays": [
                                    [
                                      "$$this"
                                    ],
                                    {
                                      $map: {
                                        input: {
                                          $filter: {
                                            input: {
                                              "$objectToArray": "$$value"
                                            },
                                            as: "filterItem",
                                            cond: {
                                              $eq: [
                                                "$$filterItem.k",
                                                "$$this.group"
                                              ]
                                            }
                                          }
                                        },
                                        as: "mapItem",
                                        in: "$$mapItem.v"
                                      }
                                    },
                                    
                                  ]
                                },
                                as: "map2Item",
                                in: {
                                  $cond: [
                                    {
                                      "$isArray": "$$map2Item"
                                    },
                                    {
                                      $arrayElemAt: [
                                        "$$map2Item",
                                        0
                                      ]
                                    },
                                    "$$map2Item"
                                  ]
                                }
                              }
                            }
                          }
                        ]
                      ]
                    }
                  ]
                }
              }
            }
          },
          as: "item",
          in: {
            group: "$$item.k",
            values: "$$item.v"
          }
        }
      }
    }
  }
])

Mongo Playground

这远不是一个 project 阶段,但它确实从给定的输入中产生了所需的输出。

db.collection.aggregate([
{'$match': {'_id': 1}},
 {'$unwind': '$array'},
 {'$project': {'array': {'group': '$array.group', 'values': '$array'},
   'some_field': 1,
   'my_id': '$_id'}},
 {'$unset': 'array.values.group'},
 {'$group': {'_id': '$array.group',
   'values': {'$push': '$array.values'},
   'some_field': {'$first': '$some_field'},
   'my_id': {'$first': '$my_id'}}},
 {'$set': {'array': {'group': '$_id', 'values': '$values'}}},
 {'$unset': 'values'},
 {'$group': {'_id': '$my_id',
   'array': {'$push': '$array'},
   'some_field': {'$first': '$some_field'}}}
])

mongoplayground.net 上试用。

试试这个:https://mongoplayground.net/p/pFn3tLtAG4D

$set: {
    _id: "$_id",
    some_field: "$some_field",
    array: {
      $map: {
        input: {
          $setUnion: [
            "$array.group"
          ]
        },
        in: {
          group: "$$this",
          values: {
            $map: {
              input: {
                $filter: {
                  input: "$array",
                  as: "elem",
                  cond: {
                    $eq: [
                      "$$elem.group",
                      "$$this"
                    ]
                  }
                }
              },
              as: "vals",
              in: {
                subgroup: "$$vals.subgroup",
                value: "$$vals.value"
              }
            }
          }
        }
      }
    }
  }