MongoDB的聚合项目检查,如果数组包含

MongoDB的聚合项目检查,如果数组包含

问题描述:

我有以下文件:MongoDB的聚合项目检查,如果数组包含

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

    ] 
} 

现在我想要得到的用户名,文本和注释采用聚集和项目。另外,如果comments数组包含用户名“User1”,我也想要一个布尔值。我有这个,但它不起作用。

db.posttest.aggregate(
    [ 
    { 
     $project: 
      { 
      username: 1, 
      text: 1, 
      comments : 1, 
      hasComment: { $eq: [ "comments.$.username", "User1" ] }, 
      _id: 0 
      } 
    } 
    ] 
) 

要做到这一点,你需要先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 } } 
]) 

解释如下。

首先,我们需要摆脱一个数组(comments)。要做到这一点,我们打开记录;它给了我们四条记录:

{ 
    "_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" 
    } 
} 

现在我们可以将所有记录组合成一个应用函数给每个字段。首先,我们需要给出标准,'分组'字段(或一组字段)。在我们的例子中,它仅仅是id:_id: "$_id"

然后,对于每个字段,我们需要决定如何将其包含到结果记录中。我们有几个字段:username,textcomments。对于每四条记录,用户名和文本都是相同的,因此我们可以轻松选取其中的任何一条,即$first$last

comments然而,是不同的。我们希望保留所有这些,以便我们每个人都退回$push

hasComments这里有点棘手:我们需要检查是否至少有一个comment.username包含用户名。我们可以在这里使用$eq: [...],它会给我们一些数组,例如[true, false, false, false][false, false, true, false]。我们需要选择哪个值进入结果记录。在这种情况下,我们既不能使用$first也不能使用$last。但是,$max会给我们一个合适的结果。

+2

谢谢,很好的回答! – user6586661

你有几个可供选择的选项,你可以尝试。

第一个选项$filter小号comments阵列usernameUser1随后$size计数没有出现和true如果尺寸GT大于0别的false项目布尔值的。

db.posttest.aggregate(
    [{ 
     $project: { 
      username: 1, 
      text: 1, 
      comments: 1, 
      hasComment: { 
       $gt: [{ 
        $size: { 
         $filter: { 
          input: "$comments", 
          as: "comment", 
          cond: { 
           $eq: ["$$comment.username", "User1"] 
          } 
         } 
        } 
       }, 0] 
      }, 
      _id: 0 
     } 
    }] 
) 

第二个选择是使用$setIsSubset集合运算符来检查是否阵列usernameUser1username列数组值的子集在comments阵列。

db.posttest.aggregate(
    [{ 
     $project: { 
      username: 1, 
      text: 1, 
      comments: 1, 
      hasComment: { 
       $setIsSubset: [ 
        ["User1"], "$comments.username" 
       ] 
      }, 
      _id: 0 
     } 
    }] 
)