Skip to main content

🦸‍♂️ 高级 lookup

info

额外活动:如果您有额外的时间或在家跟着做,可以尝试,导师不会在动手实验中进行讲解

我们收到这样的需求:写一个$lookup,以获取每个书籍文档中作者的信息,包括namebio。为了完成这个任务,我们需要回顾几件事情:

  • 每本书可以有多个作者。这种多对多的关系(因为一个作者也可以写多本书)是通过两个不同的数组来建模的:authors集合中的一个books数组和books集合中的一个authors数组。
  • 所以我们需要为每本有多个作者的书籍获取一个单独的文档。如果一本书有三个作者,我们将使用$unwind来获取三个文档,这些文档的数据相同,除了作者字段的内容不同。

你可以使用这个聚合管道来尝试:

db.books.aggregate([
// 因为一本书可以有多个作者,我们为每个书籍的作者获取一个文档
{$unwind: "$authors"},
// 删除一些干扰字段
{$project: {attributes: 0, reviews: 0}}
])
  • 现在,我们需要获取作者的信息。为此,我们将使用$lookup,将authors集合中的_id与我们在每本书的authors数组中的_id进行链接。但正如我们在这里看到的,这些类型不同:我们数组中的那些是字符串,而authors集合中的_idObjectId
  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来仅获取authorsnamebio
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}}
])