在mongodb聚合中项目嵌套嵌入文档

Bha*_*avi 7 mongodb pymongo mongodb-query aggregation-framework

我有一个嵌套的嵌入式文档,看起来像这样。每个帖子都有 n 条评论,每条评论都有一个包含姓名和电子邮件 ID 的用户详细信息。

我只想将评论用户的姓名投影到列表中

{
    "PostId":"Post001",
    "Comments":[
         {"_id": "001",
          "CommentedBy":{
            "_id":"User001",
            "Name":"UserName001",
            "email":"user001@eg.com"
            }
         },
         {"_id": "002",
           "CommentedBy":{
            "_id":"User002",
            "Name":"UserName002",
            "email":"user001@eg.com"
            }
         },
         {"_id": "003",
          "CommentedBy":{
            "_id":"User003",
            "Name":"UserName003",
            "email":"user001@eg.com"
            }
         }
    ]
}
Run Code Online (Sandbox Code Playgroud)

我想通过使用 mongodb 的聚合管道转换成看起来像这样的东西。

{
    "PostId":"Post001"
    "Comments":[
         {"_id": "001",
          "CommentedBy":"UserName001",
         },
         {"_id": "002",
           "CommentedBy": "UserName002"
         },
         {"_id": "003",
          "CommentedBy": "UserName003"
         }
    ]
}
Run Code Online (Sandbox Code Playgroud)

使用 mongo 的投影查询提供了一个CommentedBy包含所有名称的列表。我如何使用 mongo 的聚合查询来实现这一点。有没有办法不使用$unwind.

我试过的查询和我得到的结果。

db.getCollection('post').aggregate([
{$project:{"Comments.CommentedBy":"$Comments.CommentedBy.Name"}}
])

{
    "_id" : ObjectId("5b98b4cc3bb8c65aeacabd78"),
    "Comments" : [ 
        {
            "CommentedBy" : [ 
                "UserName001", 
                "UserName002", 
                "UserName003"
            ]
        }, 
        {
            "CommentedBy" : [ 
                "UserName001", 
                "UserName002", 
                "UserName003"
            ]
        }, 
        {
            "CommentedBy" : [ 
                "UserName001", 
                "UserName002", 
                "UserName003"
            ]
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

Zla*_*tko 8

你可以从计划你想做的事情开始。例如你可以尝试这个:

计划

  • 放松评论
  • 投影您想要的字段
  • 将其重新组合在一起
  • (可选)清理

执行

  1. 展开全部评论

所以阶段是:

const unwind = {
    $unwind: '$Comments',
};
Run Code Online (Sandbox Code Playgroud)

这会导致您的文档重复(或者更确切地说,倍增),数量与您的评论一样多。

  1. 项目

现在,根据需要投影评论者姓名/ID:

const project = {
    $project: {
        PostId: 1,
        CommentId: '$Comments._id',
        CommentedBy: '$Comments.commentedBy.Name',
    },
}
Run Code Online (Sandbox Code Playgroud)

现在,对于每条评论,您都有一个文档:{ PostId, CommentId, CommentedBy }

  1. 将他们再次分组。

现在您可以将您的评论重新分组,按以下方式分组PostId

const group = {
    $group: {
        _id: '$PostId',
        PostId: '$PostId',
        Comments: {
            $push: {
                _id: '$CommentId',
                CommentedBy: '$CommentedBy',
            },
        },
    },
};
Run Code Online (Sandbox Code Playgroud)

您现在将获得如下文件:

{
    _id: '<PostID>',
    PostId: '<PostID>',
    Comments: [
      { _id: '<CommentId>', CommentedBy: '<username>' },
    ],
}
Run Code Online (Sandbox Code Playgroud)
  1. (可选)清理

你会注意到那里有一个额外的顶层_id,你可以在另一个阶段摆脱它$project

const cleanup = { $project: { _id: 0, ... } };
Run Code Online (Sandbox Code Playgroud)

所以你的整个管道现在很简单:

 db.getCollection('posts')
     .aggregate([
        unwind,
        project,
        group,
        cleanup,
     ]);
Run Code Online (Sandbox Code Playgroud)

我省略了一些样板文件,并且我在这里没有使用 MongoDB 进行输入,因此您可能需要对代码进行双重和三次检查。(无论如何,您可能想使用来自 internetz 的代码来做到这一点。)


Ash*_*shh 8

您可以尝试使用$map聚合,并可以通过循环遍历Comments数组来更改内部的键。

db.collection.aggregate([
  { "$project": {
    "PostId": 1,
    "Comments": {
      "$map": {
        "input": "$Comments",
        "as": "comment",
        "in": {
          "_id": "$$comment._id",
          "CommentedBy": "$$comment.CommentedBy.Name"
        }
      }
    }
  }}
])
Run Code Online (Sandbox Code Playgroud)