Mongoose - MongoDB对象建模工具
1. 什么是Mongoose?
Mongoose是一个优雅的MongoDB对象建模工具,用于在Node.js环境中工作。它提供了一个直接的、基于模式的解决方案,用于对应用程序数据进行建模,包括内置的数据验证、查询构建、中间件支持和业务逻辑钩子。
1.1 核心特性
- 模式定义:通过Schema定义数据结构和验证规则
- 中间件支持:支持文档生命周期的钩子函数
- 查询构建器:提供链式调用的查询API
- 数据验证:内置验证规则,确保数据完整性
- 虚拟属性:支持计算属性
- 聚合管道:支持MongoDB的聚合操作
2. 快速开始
2.1 安装
npm install mongoose2.2 基本用法
const mongoose = require('mongoose');
// 连接数据库
mongoose.connect('mongodb://localhost:27017/test', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// 定义模式
const userSchema = new mongoose.Schema({
name: String,
email: {
type: String,
required: true,
unique: true
},
age: Number,
createdAt: {
type: Date,
default: Date.now
}
});
// 创建模型
const User = mongoose.model('User', userSchema);
// 创建文档
const newUser = new User({
name: '张三',
email: 'zhangsan@example.com',
age: 30
});
// 保存文档
newUser.save()
.then(user => console.log('用户创建成功:', user))
.catch(err => console.error('创建用户时出错:', err));3. 模式定义
3.1 基本类型
Mongoose支持以下基本数据类型:
- String
- Number
- Date
- Buffer
- Boolean
- Mixed
- ObjectId
- Array
3.2 模式选项
const schema = new mongoose.Schema({
// 字段定义
field1: String,
field2: {
type: Number,
required: true,
min: 0,
max: 100
},
field3: {
type: Date,
default: Date.now
}
}, {
// 模式选项
timestamps: true, // 自动添加createdAt和updatedAt字段
collection: 'custom_collection_name' // 自定义集合名
});4. 查询操作
4.1 基本查询
// 查找所有文档
User.find({})
.then(users => console.log('所有用户:', users));
// 按条件查找
User.find({ age: { $gt: 25 } })
.then(users => console.log('年龄大于25的用户:', users));
// 查找单个文档
User.findOne({ email: 'zhangsan@example.com' })
.then(user => console.log('找到的用户:', user));
// 按ID查找
User.findById('60d7f8e4b4e4a0001c8e4b4a')
.then(user => console.log('按ID找到的用户:', user));4.2 高级查询
// 排序
User.find({})
.sort({ age: -1 }) // 降序
.then(users => console.log('按年龄排序的用户:', users));
// 分页
User.find({})
.skip(10) // 跳过前10个
.limit(5) // 限制返回5个
.then(users => console.log('分页结果:', users));
// 字段选择
User.find({})
.select('name email') // 只返回name和email字段
.then(users => console.log('只包含指定字段的用户:', users));5. 更新操作
5.1 基本更新
// 更新单个文档
User.updateOne({ email: 'zhangsan@example.com' }, {
$set: { age: 31 }
})
.then(result => console.log('更新结果:', result));
// 更新多个文档
User.updateMany({ age: { $lt: 18 } }, {
$set: { status: 'minor' }
})
.then(result => console.log('更新结果:', result));
// 按ID更新
User.findByIdAndUpdate('60d7f8e4b4e4a0001c8e4b4a', {
$set: { name: '李四' }
}, { new: true }) // 返回更新后的文档
.then(user => console.log('更新后的用户:', user));6. 删除操作
// 删除单个文档
User.deleteOne({ email: 'zhangsan@example.com' })
.then(result => console.log('删除结果:', result));
// 删除多个文档
User.deleteMany({ age: { $gt: 60 } })
.then(result => console.log('删除结果:', result));
// 按ID删除
User.findByIdAndDelete('60d7f8e4b4e4a0001c8e4b4a')
.then(user => console.log('删除的用户:', user));7. 中间件
7.1 文档中间件
// 保存前的钩子
userSchema.pre('save', function(next) {
// 在保存前执行操作
this.updatedAt = new Date();
next();
});
// 保存后的钩子
userSchema.post('save', function(doc, next) {
// 在保存后执行操作
console.log('文档已保存:', doc._id);
next();
});7.2 查询中间件
// 查询前的钩子
userSchema.pre('find', function(next) {
// 在查询前执行操作
this.where({ active: true });
next();
});8. 数据验证
8.1 内置验证器
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, '姓名是必填的'],
minlength: [2, '姓名至少需要2个字符'],
maxlength: [50, '姓名最多50个字符']
},
email: {
type: String,
required: true,
unique: true,
validate: {
validator: function(v) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v);
},
message: props => `${props.value} 不是有效的邮箱地址!`
}
},
age: {
type: Number,
min: [0, '年龄不能为负数'],
max: [120, '年龄不能超过120岁']
}
});9. 关联关系
9.1 引用关系
const postSchema = new mongoose.Schema({
title: String,
content: String,
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
});
const Post = mongoose.model('Post', postSchema);
// 填充关联数据
Post.find({})
.populate('author') // 填充author字段
.then(posts => console.log('带作者信息的帖子:', posts));10. 实际应用示例
10.1 用户认证系统
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
}
});
// 密码加密中间件
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
// 验证密码方法
userSchema.methods.validatePassword = async function(password) {
return await bcrypt.compare(password, this.password);
};
const User = mongoose.model('User', userSchema);10.2 博客系统
const commentSchema = new mongoose.Schema({
content: {
type: String,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
},
createdAt: {
type: Date,
default: Date.now
}
});
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
tags: [String],
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}],
createdAt: {
type: Date,
default: Date.now
},
updatedAt: {
type: Date,
default: Date.now
}
});
// 更新时间中间件
postSchema.pre('save', function(next) {
this.updatedAt = new Date();
next();
});
const User = mongoose.model('User', userSchema);
const Post = mongoose.model('Post', postSchema);
const Comment = mongoose.model('Comment', commentSchema);11. 性能优化
11.1 索引
// 在模式中定义索引
const userSchema = new mongoose.Schema({
email: {
type: String,
unique: true, // 创建唯一索引
index: true // 创建普通索引
},
age: {
type: Number,
index: true // 创建普通索引
}
});
// 创建复合索引
userSchema.index({ email: 1, age: -1 });11.2 批量操作
// 批量插入
User.insertMany([
{ name: '张三', email: 'zhangsan@example.com' },
{ name: '李四', email: 'lisi@example.com' },
{ name: '王五', email: 'wangwu@example.com' }
])
.then(users => console.log('批量插入成功:', users.length))
.catch(err => console.error('批量插入失败:', err));12. 最佳实践
12.1 连接管理
// 连接数据库
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false
});
console.log('MongoDB连接成功');
} catch (error) {
console.error('MongoDB连接失败:', error.message);
process.exit(1);
}
};
connectDB();
// 监听连接事件
mongoose.connection.on('error', err => {
console.error('MongoDB连接错误:', err);
});
mongoose.connection.on('disconnected', () => {
console.log('MongoDB连接断开');
});12.2 错误处理
// 全局错误处理中间件
app.use((err, req, res, next) => {
if (err.name === 'ValidationError') {
// 验证错误
const errors = Object.values(err.errors).map(val => val.message);
return res.status(400).json({ errors });
}
if (err.code === 11000) {
// 唯一索引错误
return res.status(400).json({ error: '该值已存在' });
}
// 其他错误
console.error(err);
res.status(500).json({ error: '服务器内部错误' });
});13. 总结
Mongoose是一个功能强大的MongoDB对象建模工具,它大大简化了Node.js应用程序与MongoDB的交互。通过模式定义、数据验证、中间件支持和丰富的查询API,Mongoose使得MongoDB的使用更加直观和高效。
13.1 核心优势
- 模式化数据:通过Schema定义数据结构,提高代码可维护性
- 内置验证:确保数据完整性和一致性
- 丰富的查询API:支持复杂的查询操作
- 中间件系统:灵活的钩子函数,用于处理文档生命周期事件
- 关联关系:支持文档之间的引用和填充
13.2 适用场景
- Web应用后端:处理用户数据、内容管理等
- API服务:构建RESTful或GraphQL API
- 实时应用:与Socket.io等结合使用
- 数据处理:批量数据处理和分析
通过本教程的学习,您应该已经掌握了Mongoose的基本用法和高级特性,可以开始在实际项目中应用它来构建强大的MongoDB驱动的应用程序了。