MongoDB聚合项目检查数组是否包含

3

我有以下文件:

{
    _id : 21353456,
    username : "xy",
    text : "asdf",
    comments : [
        {
            username : "User1",
            text : "hi",
        },
        {
            username : "User2",
            text : "hi1",
        },
        {
            username : "User3",
            text : "hi2",
        },
        {
            username : "User4",
            text : "hi3",
        }

    ]
}

现在,我想使用聚合和项目获取用户名、文本和评论。此外,我还想知道评论数组中是否包含一个带有“User1”用户名的布尔值。我有以下代码,但它不起作用。
db.posttest.aggregate(
   [
     {
       $project:
          {
            username: 1,
            text: 1,
            comments : 1,
            hasComment: { $eq: [ "comments.$.username", "User1" ] },
            _id: 0
          }
     }
   ]
)
2个回答

8
为了实现这个目的,你首先需要 unwind 这些评论,然后使用一个小技巧来进行 group。如果你想省略 _id,那么还需要进行简单的投影操作。以下是完整的聚合管道:
db.posttest.aggregate([
  { $unwind : "$comments" },
  { $group : {
    _id : "$_id",
    username : { $first : "$username" },
    text : { $first : "$text" },
    comments : { $push : "$comments" },
    hasComments : { $max : { $eq : [ "$comments.username", "User1" ] } }
  }},
  { $project : { _id : false } }
])

以下是解释。

首先,我们需要摆脱一个数组(评论)。为此,我们展开记录;这给我们四个记录:

{
  "_id" : 21353456,
  "username" : "xy",
  "text" : "asdf",
  "comments" : {
    "username" : "User1",
    "text" : "hi"
  }
},
{
  "_id" : 21353456,
  "username" : "xy",
  "text" : "asdf",
  "comments" : {
    "username" : "User2",
    "text" : "hi1"
  }
},
{
  "_id" : 21353456,
  "username" : "xy",
  "text" : "asdf",
  "comments" : {
    "username" : "User3",
    "text" : "hi2"
  }
},
{
  "_id" : 21353456,
  "username" : "xy",
  "text" : "asdf",
  "comments" : {
    "username" : "User4",
    "text" : "hi3"
  }
}

现在,我们可以将所有记录分组到一个应用程序中,并对每个字段应用一个函数。首先,我们需要提供标准,即“group by”字段(或一组字段)。在我们的例子中,它只是id:_id:“$ _id”
然后,对于每个字段,我们需要决定如何将其包含在结果记录中。我们有几个字段:usernametextcomments。对于每四个记录,用户名和文本都相同,因此我们可以轻松地选择它们中的任何一个,即$first$last
然而,comments是不同的。我们想要保留它们中的所有内容,以便我们$push每一个回来。
这里的hasComments有点棘手:我们需要检查至少一个comment.username是否包含用户名。我们可以在这里使用$eq:[...],它会给我们一些数组,例如[true,false,false,false][false,false,true,false]。我们需要选择哪个值进入结果记录。在这种情况下,我们既不能使用$first也不能使用$last。但是,$max将为我们提供适当的结果。

2
谢谢,非常好的答案! - user6586661

1

我知道这是一个老问题,也有一个被接受的答案,但有一个比使用$unwind$group更简单的方法,只需使用$project,像这样:

这里的技巧是比较comments数组和期望的值(在这种情况下是User1)之间的交集。如果交集大于0(即存在该值),那么字段existsUser将为true,否则为false

{
  "$project": {
    "comments": 1,
    "existsUser": {
      "$cond": {
        "if": {
          "$gt": [
            {
              "$size": {
                "$setIntersection": [
                  "$comments.username",
                  [
                    "User1"
                  ]
                ]
              }
            },
            0
          ]
        },
        "then": true,
        "else": false
      }
    }
  }
}

示例 这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接