如何在 $project 中使用带有过滤器内部数组的聚合

how to use aggregate with filter inner array inside $project

目前我正在使用 mongodb 来存储有关我的表单和回复的数据,当我在我的集​​合中使用查找时,以下数据是 returned:

[
    {
        "_id": 1,
        "forms": [
            {
                "current_version": 0,
                "history": [
                    {
                        "content": [{"label": "vrau"}],
                        "responses": [
                            {"client_id": 1, "response": [{"field": "vrau1"}]},
                            {"client_id": 1, "response": [{"field": "vrau1"}]},
                            {"client_id": 2, "response": [{"field": "vrau2"}]},
                            {"client_id": 2, "response": [{"field": "vrau2"}]},
                        ],
                    }
                ],
            }
        ],
        "name": "Testing",
    },
    {
        "_id": 2,
        "forms": [
            {
                "current_version": 1,
                "history": [
                    {
                        "content": [{"label": "vrau"}],
                        "responses": [
                            {"client_id": 1, "response": [{"field": "vrau11"}]},
                            {"client_id": 1, "response": [{"field": "vrau11"}]},
                            {"client_id": 2, "response": [{"field": "vrau22"}]},
                            {"client_id": 2, "response": [{"field": "vrau22"}]},
                        ],
                    }
                ],
            }
        ],
        "name": "Testing2",
    },
]

我想做的是 return 只有来自第一个文档的响应:_id = 1 其中 client_id 等于 1。因此,以下输出是我想要的。

[{
        "_id": 1,
        "forms": [
            {
                "history": [
                    {
                        "responses": [
                            {"client_id": 1, "response": [{"field": "vrau1"}]},
                            {"client_id": 1, "response": [{"field": "vrau1"}]},
                        ],
                    }
                ],
            }
        ]
    }]

但我的查询是 return 将表单字段清空,这就是我想要做的:

collection.aggregate([
        {
           "$match" : {
               "_id" : 1,
           }
        },
        {
          "$project": {
             "forms": {
                "$filter": {
                   "input": "$forms",
                   "as": "form",
                   "cond": { "$and":[
                       {"$eq": [ "$$form.history.responses.client_id", 1 ]}
                       ]
                   }
                }
             }
          }
        }
    ])

上面的代码有什么问题?

一种方法是使用 $reduce 将您的多级数组“扁平化”为一个响应数组。然后,剩下的就是 $filter 您需要的回复:

db.collection.aggregate([
  {$match: {_id: 1}},
  {
    $project: {
      responses: {
        $reduce: {
          input: "$forms",
          initialValue: [],
          in: {
            $concatArrays: [
              {
                $reduce: {
                  input: "$$this.history",
                  initialValue: [],
                  in: {$concatArrays: ["$$value", "$$this.responses"]}
                }
              },
              "$$value"
            ]
          }
        }
      }
    }
  },
  {
    $project: {
      responses: {
        $filter: {
          input: "$responses", as: "item", cond: {$eq: ["$$item.client_id", 1]}
        }
      }
    }
  }
])

Playground example