如何聚合 ObjectId 对数组及其相关集合

Posted

技术标签:

【中文标题】如何聚合 ObjectId 对数组及其相关集合【英文标题】:How to aggregate array of ObjectId pairs with their relevant collection 【发布时间】:2019-09-18 18:22:59 【问题描述】:

我有一个课程集,我在其中为该课程的每个科目分配教师。分配被保存为 JSON 数组,请查看下面的参考文档。


   "_id" : ObjectId("5cc7d72d8e165005cbef939e"),
   "isAssigned" : true,
   "name" : "11",
   "section" : "A",
   "allotment" : [
       
           "subject" : ObjectId("5cc3f7cc88e95a0c8e8ccd7d"),
           "teacher" : ObjectId("5cbee0e37a3c852868ec9797")
       ,
       
           "subject" : ObjectId("5cc3f80e88e95a0c8e8ccd7e"),
           "teacher" : ObjectId("5cbee10c7a3c852868ec9798")
       
   ]

我正在尝试从两个不同的集合中匹配主题和教师字段以及他们的文档。我可以将它们放在两个不同的数组中,但无法按照我预期的输出结构得到它们

教师收藏中的文档


 _id: ObjectId("5cbee0e37a3c852868ec9797"),
 name: "Alister"

主题文档


 _id: ObjectId("5cc3f7cc88e95a0c8e8ccd7d"),
 name: "English",
 code: "EN"

我试过的查询

Course.aggregate([
               "$match": matchQuery,
               "$lookup": 
                   "from": "subjects",
                   "localField": "allotment.subject",
                   "foreignField": "_id",
                   "as": "subjectInfo"
                   
               ,
               "$lookup": 
                   "from": "teachers",
                   "localField": "allotment.teacher",
                   "foreignField": "_id",
                   "as": "teacherInfo"
               ,
               ])

查询的输出


 isAssigned: true
 name: "11"
 section: "A"
 subjectInfo:[
  _id: "5cc3f7cc88e95a0c8e8ccd7d", name:"English", code:"EN"
  _id: "5cc3f80e88e95a0c8e8ccd7e", name: "Science", code:"SC"
 ]
 teacherInfo:[
  _id: ObjectId("5cbee0e37a3c852868ec9797"),name: "Alister",
   _id: ObjectId("5cbee10c7a3c852868ec9798"),name: "Frank"
 ]

扩展输出


   "_id" : ObjectId("5cc7d72d8e165005cbef939e"),
   "isAssigned" : true,
   "name" : "11",
   "section" : "A",
   "allotment" : [
       
           "subject" : 
             _id: ObjectId("5cc3f7cc88e95a0c8e8ccd7d"),
             name: "English",
             code: "EN"
           
           "teacher" : 
              _id: ObjectId("5cbee0e37a3c852868ec9797"),
              name: "Alister"
           
       ,
       
           "subject" : 
             _id: ObjectId("5cc3f80e88e95a0c8e8ccd7e"),
             name: "Science",
             code: "SC"
           
           "teacher" : 
              _id: ObjectId("5cbee10c7a3c852868ec9798"),
              name: "Frank"
           
       
   ]

【问题讨论】:

使用猫鼬填充选项,这个链接可能对你有用***.com/questions/19222520/… 【参考方案1】:

在查找之前展开数组:

Course.aggregate([
               "$match": matchQuery,
               "$unwind: "$allotment"
               "$lookup": 
                   "from": "subjects",
                   "localField": "allotment.subject",
                   "foreignField": "_id",
                   "as": "subjectInfo"
                   
               ,
               "$lookup": 
                   "from": "teachers",
                   "localField": "allotment.teacher",
                   "foreignField": "_id",
                   "as": "teacherInfo"
               ,
               ])

如果您想在那之后重新分组以恢复预期的格式,您可以添加:

 $group :  
         _id: "$_id",
         name: $first: "$name",
         section: $first: "$section,
         isAssigned: $first: "$isAssigned,
         allotment: $push: teacher: "$teacherInfo.0", subject: "$subjectInfo.0"

我假设teacherInfo 和subjectInfo 永远不会为空,如果不是这种情况,您应该添加一个$match 来过滤空的。

【讨论】:

【参考方案2】:

看看$lookup 聚合阶段,它可以让您加入 集合。文档中有大量的用法示例。

编辑:这是应该提供预期结果的完整管道:

courses.aggregate(
    [
         
            "$unwind" : 
                "path" : "$allotment"
            
        , 
         
            "$lookup" : 
                "from" : "subjects", 
                "localField" : "allotment.subject", 
                "foreignField" : "_id", 
                "as" : "allotment.subject"
            
        , 
         
            "$lookup" : 
                "from" : "teachers", 
                "localField" : "allotment.teacher", 
                "foreignField" : "_id", 
                "as" : "allotment.teacher"
            
        , 
         
            "$addFields" : 
                "allotment.subject" : 
                    "$arrayElemAt" : [
                        "$allotment.subject", 
                        0.0
                    ]
                , 
                "allotment.teacher" : 
                    "$arrayElemAt" : [
                        "$allotment.teacher", 
                        0.0
                    ]
                
            
        , 
         
            "$group" : 
                "_id" : "$_id", 
                "isAssigned" : 
                    "$first" : "$isAssigned"
                , 
                "name" : 
                    "$first" : "$name"
                , 
                "section" : 
                    "$first" : "$section"
                , 
                "allotment" : 
                    "$addToSet" : "$allotment"
                
            
        
    ]
)

【讨论】:

好的,理想情况下,提供每个集合的示例文档和预期结果。然后,我们可以尝试提供解决方案。 已编辑我的问题并提供了详细的见解,请查看它【参考方案3】:

首先你必须$unwind allotment 数组,然后为subject 应用$lookup,然后对teachers 重复相同的操作,最后应用$group 将其组合回数组中。请参阅下面已经尝试过的聚合查询,它对我有用。

    Course.aggregate([
        "$match": matchQuery,
        
        $unwind: '$allotment'  
        ,
        
            $lookup:
            "from": "subjects",
            "localField": "allotment.subject",
            "foreignField": "_id",
            "as": "allotment.subject"
            
        ,
        
        $unwind: '$allotment.subject'  
        ,
        
            "$lookup": 
            "from": "teachers",
            "localField": "allotment.teacher",
            "foreignField": "_id",
            "as": "allotment.teacher"
            
        ,
        
        $unwind: '$allotment.teacher'  
        ,
         
                "$group" : 
                    "_id" : "$_id", 
                    "isAssigned" : 
                        "$first" : "$isAssigned"
                    , 
                    "name" : 
                        "$first" : "$name"
                    , 
                    "section" : 
                        "$first" : "$section"
                    , 
                    "allotment" : 
                        "$addToSet" : "$allotment"
                    
                
            
    ])

【讨论】:

以上是关于如何聚合 ObjectId 对数组及其相关集合的主要内容,如果未能解决你的问题,请参考以下文章

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

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

MongoDB(猫鼬)聚合计数集合中特定 ObjectID 的实例

两个集合的聚合数组 mongodb

使用聚合更改集合中记录的值

Mongodb 聚合管道限制 $lookup 字段