Mongoose Populate
Mongoose 是一个流行的面向对象文档模型 (ODM) 库,用于 Node.js,它为模型应用数据提供了一种直观的基于模式的解决方案。它包括内置的类型转换、验证、查询构建和业务逻辑钩子,非常适合许多 Node.js 项目。Mongoose 的一个关键特性是它对文档之间的“引用”提供支持。这使得您可以将一个集合中的文档与另一个集合中的文档进行关联,并且可以通过单个查询轻松地检索相关文档。这类似于 SQL 数据库中的“连接”概念。
先决条件:
- 已安装 MongoDB
- 已安装 Node.js
我们首先将 Mongoose 连接到我们的应用程序:
const mongoose = require("mongoose");
mongoose.connect("mongodb://127.0.0.1:27017/gfg");
在Mongoose中填充引用:
创建引用:
填充被用于“查找”其他集合中的文档并将其合并到当前文档中。这对于在不同集合中创建文档之间的关系特别有用。要在Mongoose中使用填充功能,您首先需要使用模式中的“ref”属性定义文档之间的关系。例如:
const UserSchema = new mongoose.Schema({
name: String,
age: Number,
posts: [{ type: mongoose.Types.ObjectId, ref: "Post" }],
});
const PostSchema = new mongoose.Schema({
title: String,
author: { type: mongoose.Types.ObjectId, ref: "User" },
});
const Post = mongoose.model("Post", PostSchema);
const User = mongoose.model("User", UserSchema);
上面的代码是双向引用的一个示例。在这个示例中,我们定义了一个“User”模型,它有一个包含“Post”对象的数组,和一个“Post”模型,它有一个单独的“User”对象作为其“author”。
填充引用:
要填充引用,可以在查询链上使用.populate()方法。例如:
Post.findOne({ title: "This is my first post" })
.populate("author")
.exec((err, post) => {
// Will have post.author populated
if (!err) console.log(post);
process.exit(0);
});
这里我们通过文章标题来检索一个单独的文章,然后使用.populate()来完全填充“作者”字段,然后再使用另一个调用.populate()来完全填充每个评论中的“作者”字段。以上代码的输出:
由于我们存储了用户的ObjectIds,我们可以在作者的文档中填充帖子。
User.findById("63b1332c8a41f608100eeffd")
.populate("posts")
.exec((err, user) => {
// Will have post.author populated
if (!err) console.log(user);
process.exit(0);
});
自定义填充引用:
Mongoose中的populate()方法允许您指定一些选项来自定义填充过程。您可以使用的一些选项包括:
- path :包含外键引用的字段路径。
- model :用于填充的模型。如果路径引用的模型不在当前文件中,则必须指定。
- select :选择要从填充的文档中选择的字段的以空格分隔的列表。
- match :填充的文档所满足的查询条件。只有满足条件的文档才会被包含在填充结果中。
- options :在执行填充查询时传递给MongoDB驱动程序的find()函数的选项集。可以使用这些选项来指定排序、限制和跳过等选项。
1. 使用populate()方法中的 match 选项来指定填充过程的查询条件。match选项接受一个Mongoose查询对象,允许您指定被填充文档必须满足的条件。
User.findById("63b1332c8a41f608100eeffd")
.populate({
path: "posts", match: {
title: /^T/i
}, select: "title"
})
.exec((err, user) => {
// Will have post.author populated
if (!err) console.log(user);
process.exit(0);
});
在这里,通过用户名检索单个用户,然后使用populate()方法来检索该用户撰写的所有帖子。然而,使用match选项来指定只有标题以“T”开头的帖子应该包含在人口中。还使用select选项来指定只有帖子的“标题”字段应该包含在结果文档中。
2. 在populate()方法中的 options 字段指定了执行population查询时传递给MongoDB驱动程序的find()函数的一组选项。在这种情况下,sort选项用于按照升序排列的“title”字段对帖子进行排序,limit选项用于限制帖子的数量为2。
User.findById("63b1332c8a41f608100eeffd")
.populate({
path: "posts", options: {
sort: { title: 1 }, limit: 2
}
})
.exec((err, user) => {
// Will have post.author populated
if (!err) console.log(user);
process.exit(0);
});
3. select 选项可用于包含或排除填充引用的任何字段。
User.findById("63b1332c8a41f608100eeffd")
.populate({ path: "posts", select: "title -_id" }) // Include title, exclude _id
.exec((err, user) => {
if (!err) console.log(user);
process.exit(0);
});
添加 – 会将 _id 字段从查询中排除。
填充多个级别:
在Mongoose中填充多个级别的相关文档,你可以在查询链中链接多个调用populate()方法。
例如,假设你有以下模式:
const userSchema = new mongoose.Schema({
name: String,
posts: [{ type: mongoose.Types.ObjectId, ref: 'Post' }]
});
const postSchema = new mongoose.Schema({
title: String,
content: String,
author: { type: mongoose.Types.ObjectId, ref: 'User' },
comments: [{ type: mongoose.Types.ObjectId, ref: 'Comment' }]
});
const commentSchema = new mongoose.Schema({
text: String,
author: { type: mongoose.Types.ObjectId, ref: 'User' }
});
此架构定义了一个具有“帖子”对象数组的“用户”模型,一个具有“作者”字段的“帖子”模型,该字段是一个单一的“用户”对象,以及一个具有“作者”字段的“评论”模型,该字段是一个单一的“用户”对象。
要填充这些文档之间的引用,您可以在查询链中多次使用populate()方法。例如:
User.findOne({ name: 'John' }).populate({
path: 'posts',
populate: {
path: 'comments',
model: 'Comment',
populate: {
path: 'author',
model: 'User'
}
}
}).exec((err, user) => {
console.log(user);
});
我们使用另一个调用populate()来完整填充每个评论的“author”字段与相应的“User”文档。