🦸♂️ 高级 lookup
信息
额外活动:如果您有额外的时间或在家跟着做,可以尝试,导师不会在动手实验中进行讲解
我们收到这样的需求:写一个$lookup
,以获取每个书籍文档中作者的信息,包括name
和bio
。为了完成这个任务,我们需要回顾几件事情:
- 每本书可以有多个作者。这种多对多的关系(因为一个作者也可以写多本书)是通过两个不同的数组来建模的:
authors
集合中的一个books
数组和books
集合中的一个authors
数组。 - 所以我们需要为每本有多个作者的书籍获取一个单独的文档。如果一本书有三个作者,我们将使用
$unwind
来获取三个文档,这些文档的数据相同,除了作者字段的内容不同。
你可以使 用这个聚合管道来尝试:
db.books.aggregate([
// 因为一本书可以有多个作者,我们为每个书籍的作者获取一个文档
{$unwind: "$authors"},
// 删除一些干扰字段
{$project: {attributes: 0, reviews: 0}}
])
- 现在,我们需要获取作者的信息。为此,我们将使用
$lookup
,将authors
集合中的_id
与我们在每本书的authors
数组中的_id
进行链接。但正如我们在这里看到的,这些类型不同:我们数组中的那些是字符串,而authors
集合中的_id
是ObjectId
。
authors: {
_id: '64cc2db4830ba29148da64a2',
name: 'Timothy Findley'
},
所以我们需要将String
转换为ObjectId
。我们可以使用$toObjectId
来做到这一点。这将添加一个新字段authorId
,将其转换为ObjectId
:
db.books.aggregate([
// 因为一本书可以有多个作者,我们为每个书籍的作者获取一个文档
{$unwind: "$authors"},
// 将其转换为ObjectId
{"$set":{"authorId":{"$toObjectId":"$authors._id"}}},
// 删除一些干扰字段
{$project: {attributes: 0, reviews: 0}}
])
- 现在我们准备进行
$lookup
:我们希望获取所有authors
中_id
与我们刚刚创建的authorId
相同的文档。我 们使用一个pipeline
来仅获取authors
的name
和bio
。
db.books.aggregate([
// 因为一本书可以有多个作者,我们为每个书籍的作者获取一个文档
{$unwind: "$authors"},
// 将其转换为ObjectId
{"$set":{"authorId":{"$toObjectId":"$authors._id"}}},
{$lookup: {
from: "authors",
localField: "authorId",
foreignField: "_id",
pipeline: [
{$project: {name: 1, bio: 1}},
],
as: "bookAuthorDetails"
}
},
// 删除一些干扰字段
{$project: {attributes: 0, reviews: 0}}
])