如何使用MongoDB聚合框架对嵌套帖子进行分组?

时间:2016-01-13 09:03:34

标签: mongodb mongoose aggregation-framework

我有以下帖子文件:

{
   "_id" : ObjectId("56960cd909b0d8801d145543"),
   "title" : "Post title",
   "body" : "Post body"
}

{
   "_id" : ObjectId("56960cd909b0d8801d145544"),
   "_post": ObjectId("56960cd909b0d8801d145543"),
   "body" : "Comment one"
}

{
   "_id" : ObjectId("56960cd909b0d8801d145544"),
   "_post": ObjectId("56960cd909b0d8801d145543"),
   "body" : "Comment Two"
}

从上面的文档中可以看出,这是我的帖子和评论实施的平面列表(如SO)。如果帖子有_post字段,那么就是评论,但如果没有帖子本身。

当我查询问题56960cd909b0d8801d145543时,我需要在以下视图中从mongoDB获得响应:

// query
Post.aggregate({_id: ObjectId("56960cd909b0d8801d145543")});

// result 
{
   "_id" : ObjectId("56960cd909b0d8801d145543"),
   "title" : "Post title",
   "body" : "Post body",
   "comments" [{
      "_id" : ObjectId("56960cd909b0d8801d145544"),
      "_post": ObjectId("56960cd909b0d8801d145543"),
      "body" : "Comment one"
   },
   {
      "_id" : ObjectId("56960cd909b0d8801d145544"),
      "_post": ObjectId("56960cd909b0d8801d145543"),
      "body" : "Comment Two"
   }]
}

如何构建聚合pipilenes以获得上面的结果?

1 个答案:

答案 0 :(得分:1)

以下管道适用于您:

post_id

管道的结构使得您的第一步 $project 运算符阶段是将字段comments投影为按键用作组在下一个管道阶段。由于您的架构是分层的,因此您需要此字段用于父/根文档。 $ifNull 运算符将充当coalesce运算符,如果文档中不存在该字段,则返回替换值。

下一个管道步骤, $group 管道阶段会尝试对数据进行分组以处理它们。 $group 管道运算符类似于SQL的GROUP BY子句。在SQL中,除非我们使用任何聚合函数,否则我们不能使用GROUP BY。同样,我们也必须在MongoDB中使用聚合函数。在这种情况下,您需要 $push 运算符来创建comments数组。然后使用 $first 运算符累积其他字段。

最后一步涉及使用comments数组,以便删除包含帖子详细信息的文档,这绝对不是注释类型。这可以通过 $setDifference $map 运算符实现。 $map 运算符本质上创建了一个新的数组字段,该字段通过子表达式中的计算逻辑将数值保存到数组的每个元素。 $setDifference 运算符然后返回一个集合,其中的元素出现在第一个集合中但不出现在第二个集合中;即执行第二组相对于第一组的相对补码。在这种情况下,它将通过_id属性返回包含与父文档无关的元素的最终INNER JOIN数组。