在 Mongoose 中,Populate 允许您引用其他集合中的文档。其类似于 SQL 中的左外部连接,但区别在于 Populate 发生在 Node.js 应用程序中,而不是在数据库服务器上。Mongoose 在引擎下执行单独的查询以加载引用的文档。
假设你有两个 Mongoose 模型:Movie
和 Person
。Movie
文档有一个 director
和一系列 actors
。
const Person = mongoose.model(
'Person',
new mongoose.Schema({
name: String
})
)
// ref 告诉 Mongoose Populate 要查询的模型
const Movie = mongoose.model(
'Movie',
new mongoose.Schema({
title: String,
director: {
type: mongoose.ObjectId,
ref: 'Person'
},
actors: [
{
type: mongoose.ObjectId,
ref: 'Person'
}
]
})
)
Mongoose 查询有一个 populate()
方法,允许您在一行中加载电影及其相应的 director
和 actors
:
const people = await Person.create([
{ name: 'James Cameron' },
{ name: 'Arnold Schwarzenegger' },
{ name: 'Linda Hamilton' }
])
await Movie.create({
title: 'Terminator 2',
director: people[0]._id,
actors: [people[1]._id, people[2]._id]
})
// 只加载电影导演
let movie = await Movie.findOne().populate('director')
console.log(movie.director.name) // 'James Cameron'
console.log(movie.actors[0].name) // undefined
// 加载导演和演员
movie = await Movie.findOne().populate(['director', 'actors'])
console.log(movie.director.name) // 'James Cameron'
console.log(movie.actors[0].name) // 'Arnold Schwarzenegger'
console.log(movie.actors[1].name) // 'Linda Hamilton'
注意:Mongoose 文档上也有一个
populate()
方法,详细可以查看文档。这里需要注意的是,6.x 已移除文档上document.execPopulate()
。populate
现在返回一个 Promise,其不再是可链接。
所以,以下方法将不在适用:
movie = await Movie.findOne().populate('director').populate('actors')
您应将旧的链接写法进行替换:
await doc.populate('path1').populate('path2').execPopulate()
// 替换为
await doc.populate(['path1', 'path2'])
await doc
.populate('path1', 'select1')
.populate('path2', 'select2')
.execPopulate()
// 替换为
await doc.populate([
{ path: 'path1', select: 'select1' },
{ path: 'path2', select: 'select2' }
])
如果您正在 populate
单个文档,而引用的文档不存在,Mongoose 会将 populate 的属性设置为 null
。
await Person.deleteOne({ name: 'James Cameron' })
const movie = await Movie.findOne().populate('director')
console.log(movie.director) // null
如果您正在 populate
一个数组,而其中一个引用的文档不存在,Mongoose 将在默认情况下从数组中过滤该值,并返回一个较短的数组。您可以使用 retainNullValues
选项覆盖此选项。
await Person.deleteOne({ name: 'Arnold Schwarzenegger' })
let movie = await Movie.findOne().populate('actors')
console.log(movie.actors.length) // 1
console.log(movie.actors[0].name) // 'Linda Hamilton'
// 设置 retainNullValues 选项,为数组中缺少的文档插入 null
movie = await Movie.findOne().populate({
path: 'actors',
options: { retainNullValues: true }
})
console.log(movie.actors.length) // 2
console.log(movie.actors[0]) // null
console.log(movie.actors[1].name) // 'Linda Hamilton'