Overview
Mongoose是一个适用于MongoDB的对象数据建模 (ODM) 库。您可以使用Mongoose来帮助进行数据建模、模式实施、模型验证和一般数据操作。由于MongoDB具有灵活数据模型,允许您将来更改和更新数据库,因此不存在严格的模式。但是, Mongoose从一开始就强制执行半刚性模式,因此您必须定义模式和模型。
Schemas
模式定义集合文档的结构。Mongoose模式直接映射到MongoDB集合。
以下示例创建了一个名为 blog
的新模式:
const blog = new Schema({ title: String, slug: String, published: Boolean, author: String, content: String, tags: [String], comments: [{ user: String, content: String, votes: Number }] }, { timestamps: true });
创建模式时,必须定义每个字段及其数据类型。允许使用以下类型:
字符串
数值
Date
Buffer
布尔
混合
ObjectId
阵列
Int32
Decimal128
double
Map
模型
模型采用您的模式并将其应用其集合中的每个文档。模型负责所有文档交互,例如创建、读取、更新和删除(增删改查) 操作。
提示
您传递给模型的第一个参数是集合名称的单数形式。Mongoose会自动将其更改为复数形式,转换为小写形式,并将其用作数据库集合名称。
以下示例展示如何声明使用 blog
模式的名为 Blog
的模型:
const Blog = mongoose.model('Blog', blog);
在前面的示例中,Blog
转换为MongoDB中的 blogs
集合。
Tutorial
在本教程中,您将执行以下操作:
设置环境以使用Mongoose
连接至 MongoDB
创建Mongoose模式和模型
插入、更新、查找和删除数据
项目文档字段
验证数据
使用多个模式和中间件
设置您的环境
This tutorial uses nodemon to run the code locally. Run the following commands in your terminal to initialize your project and install the necessary dependencies:
mkdir mongodb-mongoose cd mongodb-mongoose npm init -y npm i mongoose npm i -D nodemon
在首选代码编辑器中打开项目。本教程使用 ES 模块而不是 CommonJS。必须添加 module
类型才能使用 ES 模块。Gopackage.json
文件并添加以下代码:
... "scripts": { "dev": "nodemon index.js" }, "type": "module", ...
通过运行以下命令,使用 nodemon
启动应用程序:
npm run dev
注意
使用 nodemon 时,每次保存文件时都会运行代码。
连接至 MongoDB
在项目的根级别中,创建一个名为 index.js
的文件,并将以下代码添加到文件顶部:
import mongoose from 'mongoose'; mongoose.connect("<connection string>")
将 <connection string>
占位符替换为MongoDB Atlas连接字符串。 有关如何查找连接字符串的更多信息,请参阅Atlas文档中的连接到集群教程。
创建模式和模型
在开始在MongoDB中添加和更新数据之前,您必须创建模式和模型。
使用Mongoose ,您可以为所需的每个模式创建一个模式模型文件。首先,创建一个名为 model
的文件夹来放置所有模式文件,然后创建您的第一个模式文件并将其命名为 Blog.js
。打开此文件并添加以下代码:
import mongoose from 'mongoose'; const { Schema, model } = mongoose; const blogSchema = new Schema({ title: String, slug: String, published: Boolean, author: String, content: String, tags: [String], comments: [{ user: String, content: String, votes: Number }] }, { timestamps: true }); const Blog = model('Blog', blogSchema); export default Blog;
This schema enables a timestamps
option, which adds mongoose-manged createdAt
and updatedAt
fields to the schema that are updated automatically. For more information, see the Timestamps page in the Mongoose documentation.
执行 CRUD 操作
您现在已经设立了第一个模型和模式,可以开始向数据库插入数据。以下部分向您展示如何使用Mongoose执行增删改查操作。
Insert Data
Goindex.js
并将以下导入声明添加到文件顶部:
import Blog from './model/Blog.js';
在包含连接字符串的行下方添加以下代码:
// Creates a new blog post and inserts it into database const article = await Blog.create({ title: 'Awesome Post!', slug: 'awesome-post', published: true, content: 'This is the best post ever', tags: ['featured', 'announcement'], }); console.log('Created article:', article);
前面的代码使用Mongoose create()
方法实例化表示博客文章的对象,然后将其保存到数据库中。返回的文档记录到控制台,包括其 _id
。请记下此 _id
,以便在后续步骤中使用。
更新数据
要更新数据,您可以使用Mongoose直接编辑本地对象。然后,您可以使用 save()
方法将更新写入数据库。
添加以下代码以更新在上一节中插入的文章:
// Updates the title of the article article.title = "The Most Awesomest Post!!"; await article.save(); console.log('Updated Article:', article);
Updated Article: { title: 'The Most Awesomest Post!!', slug: 'awesome-post', published: true, content: 'This is the best post ever', tags: [ 'featured', 'announcement' ], _id: new ObjectId('...'), comments: [], __v: 0 }
查找数据
要查找特定文档,可以使用Mongoose findById()
方法通过指定文档的 _id
来查询文档。
将以下代码添加到 index.js
文件,以通过 _id
查找特定文章:
// Finds the article by its ID. Replace <object id> with the objectId of the article. const articleFound = await Blog.findById("<object id>").exec(); console.log('Found Article by ID:', articleFound);
Found Article by ID: { _id: new ObjectId('68261c9dae39e390341c367c'), title: 'The Most Awesomest Post!!', slug: 'awesome-post', published: true, content: 'This is the best post ever', tags: [ 'featured', 'announcement' ], comments: [], __v: 0 }
注意
By default, Mongoose queries return thenables, which are objects with some properties of a JavaScript Promise. You can append the exec()
method to a query to receive a true JavaScript Promise. To learn more about working with promises in Mongoose, see the Promises guide in the Mongoose documentation.
指定文档字段
您可以使用Mongoose仅项目您需要的字段。以下代码指定仅项目title
、slug
和 content
字段。
将以下代码添加到 index.js
文件中,将 <object
id>
占位符替换为之前插入的文档的 ObjectId
值:
// Finds the article by its ID and projects only the title, slug, and content fields. // Replace <object id> with the objectId of the article. const articleProject = await Blog.findById("<object id>", "title slug content").exec(); console.log('Projected Article:', articleProject);
Projected Article: { _id: new ObjectId('...'), title: 'The Most Awesomest Post!!', slug: 'awesome-post', content: 'This is the best post ever' }
删除数据
Mongoose使用 deleteOne()
和 deleteMany()
方法从集合中删除数据。您可以指定要删除的文档字段,并将该字段传递给您选择的方法。
将以下代码添加到索引.js文件中,以从数据库中删除一篇文章:
// Deletes one article with the slug "awesome-post". const blogOne = await Blog.deleteOne({ slug: "awesome-post" }); console.log('Deleted One Blog:', blogOne);
Deleted One Blog: { acknowledged: true, deletedCount: 1 }
要删除多篇文章,可以添加以下代码:
// Deletes all articles with the slug "awesome-post". const blogMany = await Blog.deleteMany({ slug: "awesome-post" }); console.log('Deleted Many Blogs:', blogMany);
Deleted Many Blogs: { acknowledged: true, deletedCount: 4 }
验证数据
在前面步骤中插入的项目不包含 author
、dates
或 comments
字段,即使这些字段包含在模式中。这是因为,尽管您定义了文档的结构,但尚未定义哪些字段是必填字段。任何未定义为必填字段都可以省略。
在Mongoose中,当您对字段进行验证时,必须传递一个对象作为其值。
注意
Validators automatically run on the create()
and save()
methods. You can specify them to run them on update methods, such as update()
and updateOne()
by setting the runValidators
option to true
. For more information about validators, see the Validation page in the Mongoose documentation.
您可以在Mongoose中使用多种验证方法。示例,您可以在任何您想要要求的字段上将 required
设立为 true。您还可以验证类型和格式。在以下代码中,slug
字段被定义为 minLength
为 4
的 string
。这意味着提供少于 4 个字符的 slug
将得到 ValidationError
。
要添加数据验证并定义这些要求,更新Blog.js
中的模式,如以下示例所示:
const blogSchema = new Schema({ title: { type: String, required: true, }, slug: { type: String, required: true, minLength: 4, }, published: { type: Boolean, default: false, }, author: { type: String, required: true, }, content: String, tags: [String], comments: [{ user: String, content: String, votes: Number }] }, { timestamps: true });
添加此验证后,您的应用程序将崩溃。将 author
字段添加到 index.js
文件中的 create()
调用,以满足新的验证要求:
// Creates a new blog post and inserts it into database const article = await Blog.create({ title: 'Awesome Post!', slug: 'awesome-post', published: true, author: 'A.B. Cee', content: 'This is the best post ever', tags: ['featured', 'announcement'], });
提示
当您将模式与Mongoose一起使用时,value: String
与 value: {type: String}
相同。
For more information, see the Validation page in the Mongoose documentation.
引入多种模式
接下来,您可以通过创建另一个模式并将其嵌套在博客模式中来增加 author
字段的复杂性。
在 model
文件夹中,创建一个名为 User.js
的新文件。将以下代码添加到此文件:
import mongoose from 'mongoose'; const {Schema, model} = mongoose; const userSchema = new Schema({ name: { type: String, required: true, }, email: { type: String, minLength: 10, required: true, lowercase: true }, }); const User = model('User', userSchema); export default User;
要使用新的 User 模型定义博客模式中的 author
字段,请使用以下更改更新Blog.js
文件:
将
SchemaTypes
添加到从Mongoose库提取的属性列表中。将
author
字段type
更改为SchemaTypes.ObjectId
,并添加对'User'
模型的引用 (ref
)。
以下代码显示了这些更新:
const { Schema, SchemaTypes, model } = mongoose; ... // Inside Schema block: author: { type: SchemaTypes.ObjectId, ref: 'User', required: true, }, ...
您可以通过更改 blogSchema
定义,为 comment.user
字段重复使用相同的 User
模型:
... // Inside Schema block: comments: [{ user: { type: SchemaTypes.ObjectId, ref: 'User', required: true, }, ...
要在应用程序中使用新用户模型,Goindex.js
文件。 将以下代码添加到文件顶部以导入用户模型:
import User from './model/User.js';
由于您在博客模式中添加了字段验证,因此之前用于插入、更新和删除博客以及指定要项目的字段的代码将无法通过验证,应用程序将出错。
通过添加以下 create()
调用来创建新用户:
// Create a new user const user = await User.create({ name: 'Jess Garica', email: 'jgarcia@email.com', });
使用以下代码更新现有的 create()
调用,以创建一篇将新用户作为作者的新文章,如以下代码所示:
// Creates a new blog post that references the user as the author const articleAuthor = await Blog.create({ title: 'Awesome Post!', slug: 'Awesome-Post', author: user._id, content: 'This is the best post ever', tags: ['featured', 'announcement'], }); console.log('Article with Author:', articleAuthor);
Article with Author: { title: 'Awesome Post!', slug: 'awesome-post', published: false, author: new ObjectId('...'), content: 'This is the best post ever', tags: [ 'featured', 'announcement' ], _id: new ObjectId('...'), createdAt: 2025-05-15T18:05:23.780Z, comments: [], __v: 0 }
前面的代码在MongoDB 数据库中添加一个带有 blogs
集合的 users
集合。此代码添加必填的 author
字段并将其值设置为 user._id
。
要将文章的 name
和 email
字段的值添加到 author
字段,您可以将Mongoose populate()
方法附加到博客查询中。populate()
方法将对 user._id
引用的用户文档执行第二次查询。
将以下代码添加到 index.js
以填充此数据:
// Populates the author field with user data const articlePopulate = await Blog.findOne({ title: "Awesome Post!" }).populate("author"); console.log('Article Populated:', articlePopulate);
Article Populated: { _id: new ObjectId('...'), title: 'Awesome Post!', slug: 'awesome-post', published: false, author: { _id: new ObjectId('...'), name: 'Jess Garica', email: 'jgarcia@email.com', __v: 0 }, content: 'This is the best post ever', tags: [ 'featured', 'announcement' ], createdAt: 2025-05-15T18:04:28.590Z, comments: [], __v: 0 }
For more information, see the Document.populate() page of the Mongoose API documentation.
添加中间件
在Mongoose中,中间件是在模式级别的异步函数执行之前或期间运行的函数。
中间件的一个示例是在保存或更新之前验证 User
实例的 email
字段的函数。
此示例使用 validator
包。您可以通过运行以下命令来安装 validator
包:
npm install validator
要添加此中间件函数,请将以下代码添加到 User.js
文件的 userSchema
声明中:
import validator from 'validator';
userSchema.pre('save', function (next) { const user = this; // Normalize email if (user.email) { user.email = user.email.trim().toLowerCase(); } // Validate email format if (!validator.isEmail(user.email)) { return next(new Error('Invalid email format')); } next(); });
要查看此函数的效果,请修改 index.js
文件中的 user.email
字段,使其成为无效的电子邮件解决:
const user = await User.create({ name: 'Jess Garica', email: 'jgarciaemail.com', });
Error: Invalid email format
如果更正电子邮件解决,则可以看到未引发错误。
Besides the pre()
middleware function, Mongoose also offers a post()
middleware function. For more information about middleware, see the Middleware page in the Mongoose documentation.
后续步骤
您现在有一个示例项目,它使用Mongoose对MongoDB集合执行增删改查操作。 在这里,您可以选择使用更复杂的查询或文档验证来构建此项目。以下部分包括您可能会在应用程序中使用的Mongoose功能的一些示例。
Mongoose自定义 Setter
自定义 setter 会在保存数据时修改数据,并且可以类似于验证器来实现。Mongoose提供以下自定义 setter:
lowercase
uppercase
trim
以下示例将 blog.slug
字段中的字符小写:
const blogSchema = new Schema({ ... slug: { type: String, required: true, minLength: 4, lowercase: true, }, ... });
For more information, see the SchemaStringOptions page in the Mongoose API documentation.
辅助方法
Mongoose包含多个从常规MongoDB方法抽象而来的辅助方法。在本节中,您可以找到其中一些方法的示例。本教程中并未专门使用这些方法,但在开始使用Mongoose时作为参考很有帮助。
exists()
exists()
方法返回 null
或与所提供的查询匹配的第一个文档。以下是根据 author
字段匹配文章的示例:
const blog = await Blog.exists({ author: 'Jess Garcia' }); console.log(blog);
For more information, see the Model.exists() section of the Mongoose API documentation.
where()
where()
方法允许您链接和构建复杂的查询。
以下是使用带有普通查询对象的findOne()
以及带有 where()
方法的等效查询来执行查找操作的示例:
const blogFind = await Blog.findOne({ author: "Jess Garcia" }); console.log(blogFind); const blogWhere = await Blog.findOne().where("author").equals("Jess Garcia"); console.log(blogWhere);
在此实施中,where()
实施从 findOne()
开始, 告诉Mongoose将其视为 findOne()
查询。这一点很重要,因为如果您单独使用 where()
(Blog.where(...)
), Mongoose会将其隐式视为 find()
操作。
通常,where()
方法用于涉及动态查询构建或多个比较器的复杂查询,或者使用此方法提高可读性时。使用 where()
方法与普通对象查询在性能上没有差异。
要在使用 where()
方法时包含投影,请在查询之后链接 select()
方法,如以下示例所示:
const blog = await Blog.findOne().where("author").equals("Jess Garcia").select("title author"); console.log(blog);
有关更多信息,请参阅Mongoose API文档的以下部分:
其他资源
要学习;了解有关将Mongoose与MongoDB结合使用的更多信息,请参阅Mongoose文档。
要寻求支持或为MongoDB Community做出贡献,请参阅MongoDB开发者社区页面。