如何在 mongoDB 中取消嵌套和分组集合

Posted

技术标签:

【中文标题】如何在 mongoDB 中取消嵌套和分组集合【英文标题】:How to un-nest and group collections in mongoDB 【发布时间】:2021-06-18 12:32:54 【问题描述】:

我不明白如何在 mongoDB 中展开然后嵌套集合。基本上我有两个结构如下的集合:

问题文档:


    "_id" : 1,
    "questions" : [
        
            "_id" : 1,
            "body" : "What fabric is the top made of?",
            "date_written" : "2018-01-04",
            "asker_name" : "yankeelover",
            "asker_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 2
        ,
        
            "_id" : 2,
            "body" : "HEY THIS IS A WEIRD QUESTION!!!!?",
            "date_written" : "2019-04-28",
            "asker_name" : "jbilas",
            "asker_email" : "first.last@gmail.com",
            "reported" : 1,
            "helpful" : 4
        ,
        
            "_id" : 4,
            "body" : "How long does it last?",
            "date_written" : "2019-07-06",
            "asker_name" : "funnygirl",
            "asker_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 6
        ,

答案文档:


    "_id" : 1,
    "answers" : [
        
            "_id" : 8,
            "body" : "DONT BUY IT! It's bad for the environment",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 8
        ,
        
            "_id" : 7,
            "body" : "Its the best! Seriously magic fabric",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 7
        ,
        
            "_id" : 5,
            "body" : "Something pretty soft but I can't be sure",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 5,
            "photos" : [
                
                    "_id" : 1,
                    "url" : "https://images.unsplash.com/photo-1530519729491-aea5b51d1ee1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1651&q=80"
                ,

答案文档中的 _id 字段与作为答案的问题的 _id 字段相匹配。

最终目标是获得如下所示的数据:


    "_id": "17762",
    "questions": [
        
            "question_id": 152829,
            "question_body": "Why Does it look like this?",
            "question_date": "2021-03-06T00:00:00.000Z",
            "asker_name": "garethTheGreato",
            "question_helpfulness": 60,
            "reported": false,
            "answers": 
                "1443770": 
                    "id": 1443770,
                    "body": "This question was really helpful! Thank you.",
                    "date": "2021-03-09T00:00:00.000Z",
                    "answerer_name": "SatisfiedCustomer",
                    "helpfulness": 3,
                    "photos": []
                ,
                "1443807": 
                    "id": 1443807,
                    "body": "mimk",
                    "date": "2021-03-09T00:00:00.000Z",
                    "answerer_name": "jij",
                    "helpfulness": 3,
                    "photos": [
                        "blob:http://localhost:3000/8f6375b3-0795-4210-bef7-f112feed8244"
                    ]
                ,
                "1443834": 
                    "id": 1443834,
                    "body": "10/10 would recomend.",
                    "date": "2021-03-09T00:00:00.000Z",
                    "answerer_name": "Krista",
                    "helpfulness": 2,
                    "photos": []
                ,
                "1443845": 
                    "id": 1443845,
                    "body": "Thank you so much for playing my game!",
                    "date": "2021-03-10T00:00:00.000Z",
                    "answerer_name": "itsameemario",
                    "helpfulness": 1,
                    "photos": []
                ,
                "1443880": 
                    "id": 1443880,
                    "body": "Tree",
                    "date": "2021-03-10T00:00:00.000Z",
                    "answerer_name": "Tree",
                    "helpfulness": 0,
                    "photos": [
                        "blob:http://localhost:3000/123051b6-4dfb-410a-a96f-d4a5128e3056"
                    ]
                
            
        ,
        
            "question_id": 152702,
            "question_body": "Please write your question here",
            "question_date": "2021-03-05T00:00:00.000Z",
            "asker_name": "Your nickname",
            "question_helpfulness": 32,
            "reported": false,
            "answers": 
        ,

我遇到的问题是,当我运行查找时,我得到了一个与问题集合相关的答案数组,但由于答案深深嵌套,我不确定如何获得他们特定问题的每组答案.

这是我目前所拥有的:(暂时忽略切片和排序,这些是我稍后作为项目的另一部分需要用到的参数)

  db.prodquests.aggregate([
     $match:  _id: 5  ,
     $unwind: '$questions' ,
     $match:  'questions.reported':  $lt: 1   ,
     $sort:  'questions.helpful': -1  ,
     $group:  _id: '$_id', questions:  $push: '$questions'   ,
     $project:  _id: 1, questions:  $slice: ['$questions', 0, 1]   ,
     $unwind: '$questions' ,
    
      $lookup: 
        from: 'groupansphotos',
        localField: 'questions._id',
        foreignField: '_id',
        as: 'answers',
      ,
    ,
  ])

这条语句的返回如下:


    "_id" : 5,
    "questions" : 
        "_id" : 37,
        "body" : "Why is this product cheaper here than other sites?",
        "date_written" : "2018-10-18",
        "asker_name" : "willsmith",
        "asker_email" : "first.last@gmail.com",
        "reported" : 0,
        "helpful" : 4
    ,
    "answers" : [
        
            "_id" : 37,
            "answers" : [
                
                    "_id" : 68,
                    "body" : "We are selling it here without any markup from the middleman!",
                    "date_written" : "2018-08-18",
                    "answerer_name" : "Seller",
                    "answerer_email" : "null",
                    "reported" : 0,
                    "helpful" : 4
                
            ]
        
    ]

本质上,我只想将答案数组分组到 _id 字段匹配的相应问题下。

提前谢谢你!

【问题讨论】:

【参考方案1】:

更新基于 cmets:

更新查询:

db.questions.aggregate([
     $match:  _id: 5  ,
     $unwind: '$questions' ,
     $match:  'questions.reported':  $lt: 1   ,
     $sort:  'questions.helpful': -1  ,
    
        $lookup: 
            from: "answers",
            let:  question_id: "$questions._id" ,
            pipeline: [
                
                    $match: 
                        $expr:  $eq: ["$_id", "$$question_id"] 
                    
                ,
                 $unwind: "$answers" ,
                
                    $project: 
                        _id: 0,
                        k:  $toString: "$answers._id" ,
                        v: "$$ROOT.answers"
                    
                
            ],
            as: "answers"
        
    ,
    
        $group: 
            _id: "$_id",
            questions: 
                $push: 
                    question_id: "$questions._id",
                    question_body: "$questions.body",
                    question_date: "$questions.date_written",
                    asker_name: "$questions.asker_name",
                    question_helpfulness: "$questions.helpful",
                    reported: "$questions.reported",
                    answers:  $arrayToObject: "$answers" 
                
            
        
    
]);

旧查询:

注意:请修正集合名称和/或字段名称。试试这个查询:

db.questions.aggregate([
     $match:  _id: 5  ,
     $unwind: '$questions' ,
     $match:  'questions.reported':  $lt: 1   ,
     $sort:  'questions.helpful': -1  ,
    
        $lookup: 
            from: "answers",
            let:  question_id: "$questions._id" ,
            pipeline: [
                
                    $match: 
                        $expr:  $eq: ["$_id", "$$question_id"] 
                    
                ,
                 $unwind: "$answers" ,
                
                    $project: 
                        _id: 0,
                        k:  $toString: "$answers._id" ,
                        v: "$$ROOT.answers"
                    
                
            ],
            as: "answers"
        
    ,
    
        $match: 
            $expr: 
                $gt: [ $size: "$answers" , 0]
            
        
    ,
    
        $group: 
            _id: "$_id",
            questions: 
                $push: 
                    question_id: "$questions._id",
                    question_body: "$questions.body",
                    question_date: "$questions.date_written",
                    asker_name: "$questions.asker_name",
                    question_helpfulness: "$questions.helpful",
                    reported: "$questions.reported",
                    answers:  $arrayToObject: "$answers" 
                
            
        
    
]);

输出:


    "_id" : 5,
    "questions" : [
        
            "question_id" : 2,
            "question_body" : "HEY THIS IS A WEIRD QUESTION!!!!?",
            "question_date" : "2019-04-28",
            "asker_name" : "jbilas",
            "question_helpfulness" : 4,
            "reported" : 0,
            "answers" : 
                "14" : 
                    "_id" : 14,
                    "body" : "DONT BUY IT! It's bad for the environment",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "first.last@gmail.com",
                    "reported" : 0,
                    "helpful" : 8
                ,
                "15" : 
                    "_id" : 15,
                    "body" : "Its the best! Seriously magic fabric",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "first.last@gmail.com",
                    "reported" : 0,
                    "helpful" : 7
                ,
                "16" : 
                    "_id" : 16,
                    "body" : "Something pretty soft but I can't be sure",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "first.last@gmail.com",
                    "reported" : 0,
                    "helpful" : 5
                
            
        ,
        
            "question_id" : 1,
            "question_body" : "What fabric is the top made of?",
            "question_date" : "2018-01-04",
            "asker_name" : "yankeelover",
            "question_helpfulness" : 2,
            "reported" : 0,
            "answers" : 
                "11" : 
                    "_id" : 11,
                    "body" : "DONT BUY IT! It's bad for the environment",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "first.last@gmail.com",
                    "reported" : 0,
                    "helpful" : 8
                ,
                "12" : 
                    "_id" : 12,
                    "body" : "Its the best! Seriously magic fabric",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "first.last@gmail.com",
                    "reported" : 0,
                    "helpful" : 7
                ,
                "13" : 
                    "_id" : 13,
                    "body" : "Something pretty soft but I can't be sure",
                    "date_written" : "2018-01-04",
                    "answerer_name" : "metslover",
                    "answerer_email" : "first.last@gmail.com",
                    "reported" : 0,
                    "helpful" : 5
                
            
        
    ]

测试数据:

questions收藏


    "_id" : 5,
    "questions" : [
        
            "_id" : 1,
            "body" : "What fabric is the top made of?",
            "date_written" : "2018-01-04",
            "asker_name" : "yankeelover",
            "asker_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 2
        ,
        
            "_id" : 2,
            "body" : "HEY THIS IS A WEIRD QUESTION!!!!?",
            "date_written" : "2019-04-28",
            "asker_name" : "jbilas",
            "asker_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 4
        ,
        
            "_id" : 4,
            "body" : "How long does it last?",
            "date_written" : "2019-07-06",
            "asker_name" : "funnygirl",
            "asker_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 6
        
    ]

answers收藏:

/* 1 */

    "_id" : 1,
    "answers" : [
        
            "_id" : 11,
            "body" : "DONT BUY IT! It's bad for the environment",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 8
        ,
        
            "_id" : 12,
            "body" : "Its the best! Seriously magic fabric",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 7
        ,
        
            "_id" : 13,
            "body" : "Something pretty soft but I can't be sure",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 5
        
    ]
,

/* 2 */

    "_id" : 2,
    "answers" : [
        
            "_id" : 14,
            "body" : "DONT BUY IT! It's bad for the environment",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 8
        ,
        
            "_id" : 15,
            "body" : "Its the best! Seriously magic fabric",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 7
        ,
        
            "_id" : 16,
            "body" : "Something pretty soft but I can't be sure",
            "date_written" : "2018-01-04",
            "answerer_name" : "metslover",
            "answerer_email" : "first.last@gmail.com",
            "reported" : 0,
            "helpful" : 5
        
    ]

【讨论】:

这正是我想要的,非常感谢!在查找阶段拥有管道似乎非常强大,我希望像你一样掌握它。 我们不能从$lookup 阶段内部执行此操作。从外部,我们可以在连接操作产生的字段上使用$unwind,以从外部/本地集合中排除文档,但在您的情况下,我们不能使用$unwind,因为我们需要该数组将其转换为对象。 对不起,我应该更清楚。现在,当我对所选问题执行联接时,如果问题没有匹配的答案文档,则将从最终结果中删除。我想包括在查找之前从原始匹配阶段返回的所有问题,如果它们存在的话,则与他们的答案一起加入 我已经更新了答案。检查它是否有帮助。刚刚删除了$group 之前的$match 阶段。 哇,这是一个非常简单的修复。我现在可以看到那肯定是看错了问题。非常感谢您的帮助!!!

以上是关于如何在 mongoDB 中取消嵌套和分组集合的主要内容,如果未能解决你的问题,请参考以下文章

如何在查询中取消嵌套嵌套表的集合?

如何根据从 objectId 中提取的创建日期对 MongoDB 集合进行分组?

如何根据从 objectId 中提取的创建日期对 MongoDB 集合进行分组?

了解 MongoDB 中的多对多关系以及如何取消引用集合

如何在 MongoDB 中按日期分组

如何使用nodejs从我的数据模型中的嵌套对象填充mongodb中的数据集合?