MongoDB中的$lookup嵌套文档查询

28

我对MongoDB很陌生,以下问题一直困扰着我。我有两个集合的结构如下。但是我无论如何都无法在学校集合上执行$lookup操作。通过阅读其他帖子,我确定将ObjectId用作参考以及外键字段。

以下是我的结构:

校友:

{
    "_id": "john",
    "items": [
        {
            "name": "John",
            "items": [
                {
                    "school": ObjectId("56de35ab520fc05b2fa3d5e4"),
                    "grad": true
                },
                {
                    "school": ObjectId("56de35ab520fc05b2fa00000"),
                    "grad": false
                }
            ]
        },
        {
            "name": "Johnny"
            // notice no nested items, this doc should still be included in result
        },
        {
            "name": "Jon",
            "items": [
                {
                    "school": ObjectId("56de35ab520fc05b2fa11111"),
                    "grad": false
                }
            ]
        }
     ]
}

学校

{
    _id: ObjectId("56de35ab520fc05b2fa3d5e4"),
    name: "Some University",
    street: "ABC Boulevard"
}

我想要得到的:

{
    "_id": "john",
    "items": [
        {
            "name": "John",
            "items": [
                {
                    "school": ObjectId("56de35ab520fc05b2fa3d5e4"),
                    "grad": true,
                    "schoolInfo":     {
                        _id: ObjectId("56de35ab520fc05b2fa3d5e4"),
                        name: "Some University",
                        street: "ABC Boulevard"
                    }
                },
                {
                    "school": ObjectId("56de35ab520fc05b2fa00000"),
                    "grad": true,
                    "schoolInfo":     {
                        _id: ObjectId("56de35ab520fc05b2fa00000"),
                        name: "Another University",
                        street: "123 Boulevard"
                    }
                }
            ]
        },
        {
            name: "Johnny"
        },
        {
            "name": "Jon",
            "items": [
                {
                    "school": ObjectId("56de35ab520fc05b2fa11111"),
                    "grad": true,
                    "schoolInfo":     {
                        _id: ObjectId("56de35ab520fc05b2fa11111"),
                        name: "Some University",
                        street: "ABC Boulevard"
                    }
                }
            ]
         }
     ]
}

我尝试过但无法实现的查询:

db.alumni.aggregate([
      {$match: {_id: 'john'}}, 
      {$lookup: {
                from: 'schools', 
                localField: 'items.items.school', 
                foreignField: '_id', 
                as: 'schoolInfo'}}
 ])

非常感谢任何帮助!


1
可能是$lookup在ObjectId数组中的重复使用 - Blakes Seven
2个回答

44

在这种情况下,需要在聚合框架中巧妙地使用$unwind和$project。

请参见下文:

db.alumni.aggregate([
    {$match: {_id: 'john'}},
    {$unwind:"$items"},
    {$unwind:"$items.items"},
    {$lookup: {
        from: 'schools', 
        localField: 'items.items.school', 
        foreignField: '_id', 
        as: 'schoolInfo'}},
    {$unwind:"$schoolInfo"},
    {$project:{
        "_id":1,
        "items":[{
            "name":"$items.name",
            "items":[{
            "school":"$schoolInfo._id"    ,
            "grad":"$items.items.grad"    ,
            "schoolInfo":"$schoolInfo"
            }]
        }]            
    }}
]).pretty()

如果想了解它的工作原理,请尝试从查询中删除聚合阶段并检查文档结构。


3
非常有帮助,非常感谢!通过按照你的答案一步一步地进行操作,我学到了很多东西。但是,我更新了我的问题以澄清$items.items可以在该数组中包含多个学校。我尝试了你的方法,如果$items.items有多个文档,则似乎会单独返回文档。我假设需要对它们进行某种分组。此外,我注意到如果一个$items没有嵌套的$items.items,那么整个外部文档都会被跳过。非常感谢你的帮助。我刚开始使用MongoDB,这种情况教会了我很多东西!谢谢。 - Imran
有关此事有任何更新吗?我遇到了你在评论中描述的相同问题。 - Philipp Jahoda
1
有点晚了,但是不要直接使用 unwind,而是使用额外的子键 preserveNullAndEmptyArrays 来防止空元素折叠。$unwind: { path: "$schoolInfo", preserveNullAndEmptyArrays : true } - Andy Lorenz

7

这样处理能更好地处理$items.items数组中的多个学校吗?

db.alumni.aggregate([
    {$match: {_id: 'john'}},
    {$unwind:"$items"},
    {$unwind:"$items.items"},
    {$lookup: {
        from: 'schools', 
        localField: 'items.items.school', 
        foreignField: '_id', 
        as: 'schoolInfo'}},
    {$unwind:"$schoolInfo"},
    {$group:{
        _id: {
            _id: '$_id',
            name: '$items.name',
        },
        items: {
            $push: {
                'grad': '$items.items.grad',
                'school': '$schoolInfo._id'
                'schoolInfo': '$schoolInfo'
            }
        }
    }},
    {$group:{
        _id: '$_id._id',
        items: {
            $push: {
                'name': '$_id.name',
                'items': '$items'
            }
        }
    }}
]).pretty()

我没有解决缺少 $items.items 的情况,但您可以查看$unwind空数组。 此外,当没有条目时最好留下一个空数组,而不是什么都没有,例如:

{
    name: "Johnny",
    items: [],
},

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接