Mongoose详解
什么是Mongoose?
Mongoose是MongoDB的一个ODM(Object Data Modeling)库,为Node.js环境提供了对MongoDB的对象建模工具。它允许开发者定义模式、验证数据、管理数据库关系,以及使用中间件来处理文档的生命周期事件。
安装与配置
安装
npm install mongoose
基本配置
const mongoose = require('mongoose');
// 连接到MongoDB数据库
mongoose.connect('mongodb://localhost:27017/mydatabase', {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => console.log('MongoDB连接成功'))
.catch(err => console.error('MongoDB连接失败:', err));
核心概念
1. Schema(模式)
Schema定义了文档的结构,包括字段名称、类型和验证规则。
const { Schema } = mongoose;
const userSchema = new Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
validate: {
validator: function(v) {
return /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/.test(v);
},
message: props => `${props.value} 不是有效的电子邮件地址!`
}
},
age: {
type: Number,
min: 18,
max: 120
},
createdAt: {
type: Date,
default: Date.now
}
});
2. Model(模型)
Model是基于Schema的可实例化构造函数,用于与数据库交互。
const User = mongoose.model('User', userSchema);
3. Document(文档)
Document是Model的实例,代表数据库中的一条记录。
const newUser = new User({
name: '张三',
email: 'zhangsan@example.com',
age: 30
});
// 保存文档
try {
await newUser.save();
console.log('用户保存成功');
} catch (err) {
console.error('保存失败:', err.message);
}
查询操作
Mongoose提供了丰富的查询API:
// 查找所有用户
const users = await User.find();
// 按条件查找
const user = await User.findOne({ name: '张三' });
// 按ID查找
const user = await User.findById('60d21b4667d0d8992e610c85');
// 高级查询
const users = await User
.find({ age: { $gte: 18, $lte: 30 } })
.sort({ createdAt: -1 })
.limit(10)
.skip(20);
更新操作
// 更新单个文档
await User.findByIdAndUpdate('60d21b4667d0d8992e610c85', {
name: '李四',
age: 31
}, { new: true }); // new: true 返回更新后的文档
// 更新多个文档
await User.updateMany(
{ age: { $lt: 18 } },
{ $set: { status: '未成年' } }
);
删除操作
// 删除单个文档
await User.findByIdAndDelete('60d21b4667d0d8992e610c85');
// 删除多个文档
await User.deleteMany({ status: '非活跃' });
中间件
Mongoose提供了四种类型的中间件:文档中间件、模型中间件、聚合中间件和查询中间件。
// 文档中间件 - save操作前执行
schema.pre('save', function(next) {
// 加密密码
if (this.isModified('password')) {
this.password = encryptPassword(this.password);
}
next();
});
// 文档中间件 - save操作后执行
schema.post('save', function(doc, next) {
console.log('文档已保存:', doc._id);
next();
});
虚拟属性
虚拟属性是计算属性,不会存储在数据库中。
schema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
// 使用虚拟属性
const user = new User({ firstName: '张', lastName: '三' });
console.log(user.fullName); // 输出: 张三
关联关系
Mongoose支持通过引用或嵌入方式处理关联关系:
// 引用关系
const postSchema = new Schema({
title: String,
content: String,
author: {
type: Schema.Types.ObjectId,
ref: 'User'
}
});
// 填充引用数据
const posts = await Post.find().populate('author', 'name email');
// 嵌入关系
const commentSchema = new Schema({
text: String,
createdAt: { type: Date, default: Date.now }
});
const postSchemaWithEmbeddedComments = new Schema({
title: String,
content: String,
comments: [commentSchema]
});
Mongoose的优缺点
优点
- 模式验证:提供强大的数据验证功能
- 中间件支持:可以在文档生命周期的各个阶段执行自定义逻辑
- 丰富的查询API:提供了便捷的链式查询语法
- 关联关系处理:支持引用和嵌入两种关联方式
- 生态系统成熟:社区活跃,文档完善
缺点
- 性能开销:相对于原生MongoDB驱动,有额外的性能开销
- 灵活性降低:严格的模式限制了MongoDB的灵活性
- 学习曲线:需要学习Mongoose特有的概念和API
最佳实践
- 使用索引提升查询性能
- 合理使用中间件,避免过度使用
- 对复杂查询使用聚合管道
- 定期监控和优化查询性能
- 注意虚拟属性不会被序列化,如需序列化需要明确配置
- 合理设计Schema,平衡范式和反范式
与其他ODM工具对比
相比Prisma、TypeORM等其他工具,Mongoose更加专注于MongoDB,提供了更丰富的MongoDB特有功能支持。而Prisma等工具则提供了更好的类型安全性和跨数据库支持。选择哪种工具应根据项目需求、数据库类型和团队熟悉度来决定。