【问题标题】:Get the first 2 elements of an array in an aggregation query in MongoDB 3.0在 MongoDB 3.0 的聚合查询中获取数组的前 2 个元素
【发布时间】:2016-06-17 21:22:32
【问题描述】:

就在有人提议我在聚合查询中使用$slice 之前,我正在使用 mongolab 作为我的后端引擎,它不支持 mongo 3.2,它只支持最高 3.0,所以我不能使用$slice

我正在尝试从评论者数组中获取前两个评论者的姓名。

完成一些聚合魔术后,数据集如下所示:

{ "_id" : "e", "commentor" : [ "John", "Bethany", "Mary", "Peter", "Mike", "Simon" ]}
{ "_id" : "f", "commentor" : [ "Sam", "Jan", "George", "Fred", "Greg", "Paul", "Ben" ]}

在每个帖子中,我都有一个评论者列表。

这个想法是只提取前 2 个评论者,然后对数组执行 $size,这样我就可以生成仅包含相关信息的活动提要。例如,对于帖子 id = e,我希望活动提要说:

John, Bethany and 4 other people commented on post e.

我可以很容易地对 $first 评论者进行聚合查询,方法是:

group2 = {
    "$group" : {
        "_id" : "$_id",
        "commentor" : {
            "$first" : "$commentor"
        }
    }
}

在这种情况下,我的活动提要会显示:

John and 5 other people commented on post e.

但我希望我的结果是这样的:

John, Bethany and 4 other people commented on post e.

在 mongo 3.0 上有没有办法做到这一点?

【问题讨论】:

  • 我认为 3.0 中的聚合框架没有任何方法可以做到这一点。如果这是管道中的最后一个阶段,那么您可以使用客户端处理来检索数组中的前两个元素。
  • 我知道你可以做到,但现在你的帖子上有 1000 个 cmets,下载文档需要太长时间,并且客户端会耗尽内存。
  • 您可以$unwind 数组和$limit 结果,但这也会很昂贵,因为$unwind 的工作方式。但是我可以准确地告诉这里最好的选择是什么,因为我无法访问您的管道。
  • 如果我的社交网络中有多个帖子,我认为 $unwind 和 $limit 不会起作用。我需要分别将帖子 e 限制为 2,将帖子 f 限制为 2,并且我不希望将限制应用于所有帖子。
  • 这就是为什么我一开始没有把它作为答案发布。

标签: javascript mongodb aggregation-framework


【解决方案1】:

使用聚合管道中的 $match、$project、$unwind、$limit 和 $group 我们可以实现它。

下面是具有魔力的查询。

db.stackoverflow1.aggregate([
   {$match : { "_id": "e"  }},  // Pass the value of post you want to retrieve, in this query we are passing "e" 
   {$project: {commentor: 1, numberOfCommentors: { $size: "$commentor" }}},
   {$unwind: "$commentor"}, 
   {$limit : 2 },
   {$group : {"_id":{_id:"$_id", numberOfCommentors:"$numberOfCommentors"}, 
    myArr:{$push: {commentor :"$commentor"}} 
    }}   
 ])

工作原理 管道中的第一个查询是 $match,这有助于我们减少结果

 db.stackoverflow1.aggregate([ {$match : { "_id": "e"  }} ])

本节将为我们提供帮助

{ "_id" : "e", "commentor" : [ "John", "Bethany", "Mary", "Peter", "Mike", "Simon" ] }

现在我们有了想要的记录,但我们必须努力显示 2 个评论者的姓名和评论者的数量

$project 是我们聚合管道中的下一个部分,我们将评论者及其数量预测到下一阶段。

我们对 $project 的查询

db.stackoverflow1.aggregate([ {$match : { "_id": "e"  }},
{$project: {commentor: 1, numberOfCommentors: { $size: "$commentor" }}} ])

执行此查询后,我们的结果集将是

{ "_id" : "e", "commentor" : [ "John", "Bethany", "Mary", "Peter", "Mike", "Simon" ], "numberOfCommentors" : 6 }

我们只是在上一个查询中将评论者的数量添加到我们想要的结果中,并将其投影到管道的下一个阶段。

现在我们需要展开我们的评论者数组以只接收两个评论者

我们使用 $unwind 的查询

 db.stackoverflow1.aggregate([
  {$match : { "_id": "e"  }},
  {$project: {commentor: 1, numberOfCommentors: { $size: "$commentor" }}},
  {$unwind: "$commentor"}
 ])

现在我们执行查询 $unwind 后的结果如下所示

{ "_id" : "e", "commentor" : "John", "numberOfCommentors" : 6 }
{ "_id" : "e", "commentor" : "Bethany", "numberOfCommentors" : 6 }
{ "_id" : "e", "commentor" : "Mary", "numberOfCommentors" : 6 }
{ "_id" : "e", "commentor" : "Peter", "numberOfCommentors" : 6 }
{ "_id" : "e", "commentor" : "Mike", "numberOfCommentors" : 6 }
{ "_id" : "e", "commentor" : "Simon", "numberOfCommentors" : 6 }

我们展开了commentors数组,也带入了commentors的数量,剩下的就是将数量限制为2并分组,即我们的最终查询,执行我们最终查询后的结果是

{ "_id" : { "_id" : "e", "numberOfCommentors" : 6 }, "myArr" : [ { "commentor" : "John" }, { "commentor" : "Bethany" } ] }

【讨论】:

  • 感谢您的详细回答,但我认为如果像我的示例中那样有超过 1 个帖子,我认为它不会真正起作用,我有帖子 e 和帖子 f。您将帖子 e 的计数限制为 2,但如果您同时拥有帖子 e 和帖子 f,这将不起作用。
  • 我们将帖子 e 的评论人数限制为 2,但不限制帖子。如果我们想要处理更多帖子,请修改 $match 部分。上面发布的查询将带来所需的结果:“John、Bethany 和其他 4 人评论了帖子 e”。要获得计数,您应该从 numberOfCommentors 中减去 2
  • @Simon - 添加了对查询的评论。
猜你喜欢
  • 2019-02-02
  • 2017-02-16
  • 2021-11-17
  • 2019-05-26
  • 2021-06-18
  • 2017-03-07
  • 1970-01-01
  • 2017-02-15
  • 2017-03-12
相关资源
最近更新 更多