是否可以在 MongoDB $match 聚合阶段按字段过滤嵌套数组对象?

Is it possible to filter nested array objects by their fields in MongoDB $match aggregation stage?

我在 MongoDB 中有一个用户评论集合,用于存储用户对产品的评论。此外,我还有一个用户回复集合,其中包含对用户评论的回复。

用户评论具有以下文档结构:

{
    username: 'John',
    content: 'I liked the product',
}

用户回复具有以下文档结构:

{
    username: 'John',
    content: 'I liked the product',
    userReviewId: ObjectID('blabla...'),
    status: 'REJECTED' // or can be 'APPROVED'
}

我想进行聚合,以查找所有回复状态为 APPROVED 的用户评论。我在 MongoDB shell:

中尝试了以下聚合
db.getCollection('UserReview').aggregate(
[
  {
    "$lookup": {
      "from": "UserReply",
      "localField": "_id",
      "foreignField": "userReview",
      "as": "replies"
    }
  },
  {
    "$match": {
      "replies": {
        "$elemMatch": {"status":"REJECTED"}
      }
    }
  }
]
)

如果某个用户评论有被批准和拒绝的回复,上述查询将获取具有两种类型回复的用户评论,如下所示:

{
    username: 'John',
    content: 'I liked the product',
    replies: [
        {
            content: 'reply1'
            userReviewId: ObjectID('blabla...'),
            status: 'REJECTED'
        },
        {
            content: 'reply1'
            userReviewId: ObjectID('blabla...'),
            status: 'APPROVED'
        }
    ]
    
}

但是我希望结果如下,即仅在 replies 用户评论字段中包含批准的回复:

{
    username: 'John',
    content: 'I liked the product',
    replies: [
        {
            content: 'reply1'
            userReviewId: ObjectID('blabla...'),
            status: 'APPROVED' // or can be 'APPROVED'
        }
    ]
    
}

我发现实际只获得批准回复的唯一方法是在投影阶段使用 $filter 操作:

$addFields: {
  replies: {
    $filter: {
      input: '$replies',
      as: 'reply',
      cond: { $in: ['$$reply.status', ['APPROVED']] }
    }
  }
}

是否可以在 $match 阶段执行回复过滤,或者这只能在 projections/addFields 阶段执行?

您可以使用lookup with aggregation pipeline,

  • 让 localField 与管道中的 foreignField 匹配,
  • $expr匹配id,匹配status$and条件下被批准
db.UserReview.aggregate([
  {
    "$lookup": {
      "from": "UserReply",
      "let": { id: "$_id" },
      "pipeline": [
        {
          $match: {
            $and: [
              { $expr: { $eq: ["$$id", "$userReviewId"] } },
              { status: "APPROVED" }
            ]
          }
        }
      ],
      "as": "replies"
    }
  }
])

Playground