Docs 菜单
Docs 主页
/ / /
Node.js 驱动程序
/

教程:开始使用Mongoose

Mongoose是一个适用于MongoDB的对象数据建模 (ODM) 库。您可以使用Mongoose来帮助进行数据建模、模式实施、模型验证和一般数据操作。由于MongoDB具有灵活数据模型,允许您将来更改和更新数据库,因此不存在严格的模式。但是, Mongoose从一开始就强制执行半刚性模式,因此您必须定义模式和模型。

模式定义集合文档的结构。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集合。

在本教程中,您将执行以下操作:

  • 设置环境以使用Mongoose

  • 连接至 MongoDB

  • 创建Mongoose模式和模型

  • 插入、更新、查找和删除数据

  • 项目文档字段

  • 验证数据

  • 使用多个模式和中间件

1

在开始本教程之前,请确保您已准备好以下组件:

  • 已配置集群的MongoDB Atlas帐户。要查看说明,请参阅 Atlas入门指南

  • Node.js v16.20.1或更高版本。

2

本教程使用nodemon在本地运行代码。在终端中运行以下命令以初始化项目并安装必要的依赖项:

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 时,每次保存文件时都会运行代码。

3

在项目的根级别中,创建一个名为 index.js 的文件,并将以下代码添加到文件顶部:

import mongoose from 'mongoose';
mongoose.connect("<connection string>")

<connection string> 占位符替换为MongoDB Atlas连接字符串。 有关如何查找连接字符串的更多信息,请参阅Atlas文档中的连接到集群教程。

4

在开始在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;

此模式启用 timestamps 选项,将 mongoose 管理的 createdAtupdatedAt 字段添加到自动更新的模式中。有关更多信息,请参阅Mongoose文档中的时间戳页面。

5

您现在已经设立了第一个模型和模式,可以开始向数据库插入数据。以下部分向您展示如何使用Mongoose执行增删改查操作。

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
}

注意

默认下, Mongoose查询返回 thenables,它们是具有JavaScript Promise 某些属性的对象。您可以将 exec() 方法附加到查询中以接收真正的JavaScript Promise。要学习;了解有关在Mongoose中使用 Promise 的更多信息,请参阅Mongoose文档中的Promise指南

您可以使用Mongoose仅项目您需要的字段。以下代码指定仅项目titleslugcontent 字段。

将以下代码添加到 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 }
6

在前面步骤中插入的项目不包含 authordatescomments 字段,即使这些字段包含在模式中。这是因为,尽管您定义了文档的结构,但尚未定义哪些字段是必填字段。任何未定义为必填字段都可以省略。

在Mongoose中,当您对字段进行验证时,必须传递一个对象作为其值。

注意

验证器会自动对 create()save() 方法运行。您可以通过将 runValidators 选项设置为 true 来指定它们在更新方法(例如 update()updateOne())上运行。有关验证器的更多信息,请参阅Mongoose文档中的验证页面。

您可以在Mongoose中使用多种验证方法。示例,您可以在任何您想要要求的字段上将 required设立为 true。您还可以验证类型和格式。在以下代码中,slug字段被定义为 minLength4string。这意味着提供少于 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: Stringvalue: {type: String} 相同。

有关更多信息,请参阅Mongoose文档中的验证页面。

7

接下来,您可以通过创建另一个模式并将其嵌套在博客模式中来增加 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

要将文章的 nameemail 字段的值添加到 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
}

有关详细信息,请参阅 Document.populate() Mongoose API文档页面。

8

在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

如果更正电子邮件解决,则可以看到未引发错误。

除了 pre() 中间件函数外, Mongoose还提供 post() 中间件函数。有关中间件的更多信息,请参阅Mongoose文档中的中间件页面。

您现在有一个示例项目,它使用Mongoose对MongoDB集合执行增删改查操作。 在这里,您可以选择使用更复杂的查询或文档验证来构建此项目。以下部分包括您可能会在应用程序中使用的Mongoose功能的一些示例。

自定义 setter 会在保存数据时修改数据,并且可以类似于验证器来实现。Mongoose提供以下自定义 setter:

  • lowercase

  • uppercase

  • trim

以下示例将 blog.slug字段中的字符小写:

const blogSchema = new Schema({
...
slug: {
type: String,
required: true,
minLength: 4,
lowercase: true,
},
...
});

有关更多信息,请参阅 Mongoose API 文档中的 SchemaStringOptions 页面。

Mongoose包含多个从常规MongoDB方法抽象而来的辅助方法。在本节中,您可以找到其中一些方法的示例。本教程中并未专门使用这些方法,但在开始使用Mongoose时作为参考很有帮助。

exists() 方法返回 null 或与所提供的查询匹配的第一个文档。以下是根据 author字段匹配文章的示例:

const blog = await Blog.exists({ author: 'Jess Garcia' });
console.log(blog);

有关更多信息,请参阅 Model.exists()Mongoose API文档部分。

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开发者社区页面。

后退

SOCKS 代理

在此页面上