一、前言
在信息化时代,数据无疑已成为企业最宝贵的资产之一。随着数据量的激增和数据类型的多样化,传统关系型数据库在处理某些类型的数据时显得捉襟见肘。正是在这样的背景下,非关系型数据库(NoSQL)应运而生,以其灵活的数据模型、卓越的扩展性和强大的性能处理能力,迅速崛起成为现代应用开发的新宠。MongoDB,作为 NoSQL 数据库的佼佼者,以其文档导向的存储方式和强大的查询语言,为开发者提供了一个高效、易用的数据处理平台。而 Mongoose,作为 Node.js 环境下 MongoDB 的 ODM(对象数据建模)库,进一步简化了数据库操作,让开发者能够更加专注于业务逻辑的实现。本指南旨在提供一个全面的 MongoDB 和 Mongoose 使用入门教程。文章分为上下两篇,当前为"下篇"内容:
二、Mongoose 使用
上篇主要介绍了 MondoDB 云端托管的安装方式、Mongoose 的基础知识以及 Mongoose 中所有内容起点"模型定义"。在完成这些准备工作后,我们就要开始真正运用 Mongoose 进行数据库操作了。
注意点及说明:
- 尽量避免使用箭头函数,因为使用箭头时 this 将无法访问到正确内容!
- 下文仅包含一些较为常用内容,如需更多完整内容请详见 👉 官方文档
2.1 增删改查
新增
javascript
await MyModel.create(docs, options);
// 新增一条:await MyModel.create({ name: 'Tim' });
// 新增多条:await MyModel.create([{ name: 'Tim' }, { name: 'Tom' }]);
await MyModel.create(docs, options);
// 新增多条:await MyModel.insertMany([{ name: 'Tim' }, { name: 'Tom' }]);
删除
javascript
await MyModel.deleteOne(conditions, options); // returns {deletedCount: 1}
await MyModel.deleteMany(conditions, options); // returns {deletedCount: x}
await MyModel.findByIdAndDelete(id, options);
await MyModel.findOneAndDelete(conditions, options)
更新
javascript
await MyModel.updateMany(conditions, update, options);
await MyModel.updateOne(conditions, update, options);
await MyModel.findByIdAndUpdate(id, update, options);
await MyModel.findOneAndReplace(conditions, replacement, options)
await MyModel.findOneAndUpdate(conditions, update, options)
查找
javascript
await MyModel.find(conditions, projection); // 第二个参数指定返回字段,支持多种类型传入,例如字符串形式 await MyModel.find({ name: /john/i }, 'name friends')
await MyModel.findOne(conditions, projection, options);
await MyModel.findById(id, projection, options);
2.2 进阶查询
2.2.1 查询条件
- 查询对象:
Model.find({ field: value })
- 正则表达式:
Model.find({ name: /regexpattern/i })
- 链式查询:
Model.find().where('age').gte(18).lt(65)
- 操作符:
Model.find({ age: { $gt: 18, $lt: 65 } })
,Mongoose 支持 MongoDB 的查询操作符,常用的查询操作符有:

2.2.2 排序&分页
对查询结果进行排序和限制返回的文档数量。
javascript
Model.find().sort({ field: -1 }).limit(10);
用于分页,跳过一定数量的文档,并限制返回的文档数量。
javascript
Model.find().skip(10).limit(10).exec(callback); // skip = (页码-1)*每页数
2.2.3 查询聚合
Mongoose 聚合框架提供了一组丰富的操作符,允许你在 MongoDB 中执行复杂的数据处理和聚合操作。这些操作符基于 MongoDB 的聚合管道,可以将数据转换和处理成几乎任何形式,您可以使用聚合操作来:
- 将多个文档中的值组合在一起。
- 对分组数据执行操作,返回单一结果。
- 分析一段时间内的数据变化。
更多内容查看 👉 Mongoose 文档和MongoDB 文档,示例如下:
javascript
// Find the max balance of all accounts
const res = await Users.aggregate([
{ $group: { _id: null, maxBalance: { $max: '$balance' }}},
{ $project: { _id: 0, maxBalance: 1 }}
]);
console.log(res); // [ { maxBalance: 98000 } ]
// Or use the aggregation pipeline builder.
const res = await Users.aggregate().
group({ _id: null, maxBalance: { $max: '$balance' } }).
project('-id maxBalance').
exec();
console.log(res); // [ { maxBalance: 98 } ]
2.3 中间件
Mongoose 中间件有 4 种类型:文档中间件、模型中间件、聚合中间件和查询中间件,通过这些中间件我们可以文档保存、验证等生命周期中插入自定义的逻辑。所有中间件类型都支持前置(pre)和后置(post)钩子,并且要注意你必须在调用 mongoose.model() 之前添加所有中间件。
javascript
schema.pre('save', function(next) {
// 执行一些操作
next();
});
schema.pre('save', function() {
// 返回一个 Promise 或使用 async/await
return asyncFunc().then(() => {});
});
schema.post('save', function(doc) {
console.log('文档已保存', doc._id);
});
schema.post('save', function(doc, next) {
setTimeout(function() {
console.log('post1');
next();
}, 10);
});
2.3.1 文档中间件
文档中间件中的this指向当前 document,支持以下文档操作:
- init:初始化钩子。
- validate:验证钩子。
- save:保存钩子。
- remove:删除钩子。
javascript
tourSchema.pre("save", function (next) {
this.slug = slugify(this.name, { lower: true });
next();
});
2.3.2 模型中间件
文档中间件中的this指向当前 model,支持以下模型操作:
- bulkWrite
- createCollection
- insertMany
注意,不要将模型中间件(model middleware)和文档中间件(document middleware)混淆。模型中间件与模型类的静态函数相关联,不需要创建类实例就可以调用。文档中间件与模型中间件不同,文档中间件与模型类的方法相关联,需要通过类实例来调用。
2.3.2 聚合中间件
聚合中间件适用于 MyModel.aggregate()。当聚合对象调用 exec() 时,聚合中间件就会执行。在聚合中间件中this指的是聚合对象。
javascript
tourSchema.pre("aggregate", function (next) {
this.pipeline().unshift({ $match: { secretTour: { $ne: true } } });
next();
});
2.3.2 查询中间件
在查询中间件函数中,this指的是当前查询 query,查询中间件支持以下查询操作:
- count
- countDocuments
- deleteMany
- deleteOne
- estimatedDocumentCount
- find
- findOne
- findOneAndDelete
- findOneAndReplace
- findOneAndUpdate
- replaceOne
- updateOne
- updateMany
- validate
javascript
tourSchema.pre(/^find/, function (next) {
this.find({ secretTour: { $ne: true } });
this.start = Date.now();
next();
});
tourSchema.post(/^find/, function (docs, next) {
console.log(`Query took ${Date.now() - this.start} milliseconds!`);
next();
});
三、总结
文本源码 👉 nodejs-learn-natours
本文是指南的下篇-"实践篇",主要介绍了 Mongoose 的基础增删改查、进阶查询和中间件的内容。如想要了解前期准备工作,如MondoDB 云端托管的安装方式、Mongoose 的基础知识以及 Mongoose 中所有内容起点"模型定义"等,请跳转阅读《前端玩数据库 👏 MongoDB/Mongoose 入门指南(上)》。希望本文能够作为您使用 Mongoose 的起点,助您在 Node.js 和 MongoDB 的世界中更进一步,另外请不要忘记官方文档一定是最好的资料,多多阅读。