【问题标题】:Project nested embedded document in mongodb aggregatemongodb聚合中的项目嵌套嵌入文档
【发布时间】:2019-02-16 16:57:17
【问题描述】:

我有一个看起来像这样的嵌套嵌入文档。每个帖子都有 n-cmets,每个评论都有一个用户详细信息,包括姓名和电子邮件 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"
            }
         }
    ]
}

我想通过使用 mongodb 的聚合管道转换成类似这样的东西。

{
    "PostId":"Post001"
    "Comments":[
         {"_id": "001",
          "CommentedBy":"UserName001",
         },
         {"_id": "002",
           "CommentedBy": "UserName002"
         },
         {"_id": "003",
          "CommentedBy": "UserName003"
         }
    ]
}

使用 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"
            ]
        }
    ]
}

【问题讨论】:

    标签: mongodb mongodb-query aggregation-framework pymongo


    【解决方案1】:

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

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

    【讨论】:

      【解决方案2】:

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

      计划

      • 放松cmets
      • 投影你想要的字段
      • 重新组合起来
      • (可选)清理

      实施

      1. Unwind所有cmets

      所以阶段将是:

      const unwind = {
          $unwind: '$Comments',
      };
      

      这会导致将您的文档复制(或者更确切地说,乘以)您拥有的 cmets 数量。

      1. 项目

      现在,project 需要的评论者姓名/ID:

      const project = {
          $project: {
              PostId: 1,
              CommentId: '$Comments._id',
              CommentedBy: '$Comments.commentedBy.Name',
          },
      }
      

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

      1. 再次将它们分组。

      现在您可以group 回复您的 cmets,将它们按PostId 分组:

      const group = {
          $group: {
              _id: '$PostId',
              PostId: '$PostId',
              Comments: {
                  $push: {
                      _id: '$CommentId',
                      CommentedBy: '$CommentedBy',
                  },
              },
          },
      };
      

      您现在将获得如下文件:

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

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

      const cleanup = { $project: { _id: 0, ... } };
      

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

       db.getCollection('posts')
           .aggregate([
              unwind,
              project,
              group,
              cleanup,
           ]);
      

      我已经省略了一些样板文件,并且我在这里输入的内容没有 MongoDB,因此您可能需要双重和三次检查代码。 (不过,您可能还是想使用 internetz 的代码来做到这一点。)

      【讨论】:

        【解决方案3】:

        unwind cmets 然后 project it 和 group it by PostId 通过将所有 cmets 推送到数组。

        db.getCollection('test').aggregate([
        {"$unwind" : "$Comments"},
        {$project:{
            "PostId":"$PostId",
            "Comments.CommentedBy":"$Comments.CommentedBy.Name", 
            "Comments._id" : "$Comments.CommentedBy._id"
          }
        },
        {"$group" : {
            _id: "$PostId",
            //Comments: { "$push" : {"_id" : "$Comments._id", "CommentedBy" : "$Comments.CommentedBy"}} If you want to push specifi field.
            Comments: { "$push" : "$Comments"} // Only push whole Comments object will also work.
          }
        }
        ])
        

        输出:

        {
            "_id" : "Post001",
            "Comments" : [ 
                {
                    "_id" : "User001",
                    "CommentedBy" : "UserName001"
                }, 
                {
                    "_id" : "User002",
                    "CommentedBy" : "UserName002"
                }, 
                {
                    "_id" : "User003",
                    "CommentedBy" : "UserName003"
                }
            ]
        }
        


        注意:

        如果要推送特定字段。

        Comments: { "$push" : {"_id" : "$Comments._id", "CommentedBy" : "$Comments.CommentedBy"}}
        

        或整个对象:

        Comments: { "$push" : "$Comments"}
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-09-18
          • 1970-01-01
          • 2020-03-08
          • 2021-07-27
          • 2017-07-31
          • 2018-12-31
          • 2014-03-04
          • 1970-01-01
          相关资源
          最近更新 更多