【问题标题】:How to group nested posts with MongoDB aggregation framework?如何使用 MongoDB 聚合框架对嵌套帖子进行分组?
【发布时间】:2016-04-18 04:05:57
【问题描述】:

我有以下邮寄文件:

{
   "_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 以获得上述结果?

【问题讨论】:

    标签: mongodb mongoose aggregation-framework


    【解决方案1】:

    以下管道应该适合您:

    var pipeline = [
        {
            "$project": {
                "title": 1, "body": 1, 
                "post_id": { "$ifNull": [ "$_post", "$_id" ] }
            }
        },  
        {
            "$group": {
                "_id": "$post_id",
                "title": { "$first": "$title" },
                "body": { "$first": "$body" },
                "comments": {
                    "$push": {
                        "_id": "$_id",
                        "_post": "$post_id",
                        "body": "$body"
                    }
                }
            }
        },
        {
            "$project": {
                "title": 1, "body": 1,
                "comments": {
                    "$setDifference": [
                        {
                            "$map": {
                                "input": "$comments",
                                "as": "el",
                                "in": {
                                    "$cond": [
                                        { "$ne": [ "$$el._id", "$$el._post" ] },
                                        "$$el",
                                        false
                                    ]
                                }
                            }
                        },
                        [false]
                    ]
                }
            }
        }
    ];
    
    Post.aggregate(pipeline, function (err, result) {
        if (err) { /* handle error */ };
        console.log(result);
    });
    

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

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

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

    【讨论】:

    • 我见过的最佳答案:))谢谢!
    • @Erik 别担心,乐于助人:)
    猜你喜欢
    • 2020-11-16
    • 2021-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 2018-01-16
    • 2012-10-25
    相关资源
    最近更新 更多