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

时间:2018-09-12 06:16:33

标签: mongodb mongodb-query aggregation-framework pymongo

我有一个嵌套的嵌入式文档,看起来像这样。每个帖子都有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"
            }
         }
    ]
}

我想通过使用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"
            ]
        }
    ]
}

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:1)

您可以先计划要做的事情。例如。您可以尝试以下方法:

计划

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

实施

  1. Unwind所有评论

因此阶段将是:

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

这导致将文档复制(或更确切地说,就是将其复制)成与注释相同的数量。

  1. 项目

现在,project根据需要添加评论者姓名/ ID:

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

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

  1. 将它们重新分组。

现在,您可以group返回您的评论,并按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的代码来完成此操作。)

答案 2 :(得分:0)

unwind注释,然后project和PostId group将所有注释推入数组。

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"}