Mongoose 与 MongoDB 数据库教程
介绍
Mongoose 是 Node.js 中最流行的 MongoDB 对象建模工具,它为 MongoDB 提供了 schema-based 的解决方案,包括数据验证、查询构建、业务逻辑钩子等。
前置条件
- Node.js 安装(建议 v14 或更高版本)
- MongoDB 数据库(本地或 Atlas 云服务)
- 基本的 JavaScript/TypeScript 知识
安装与设置
- 创建新项目并初始化
bash
mkdir mongoose-tutorial
cd mongoose-tutorial
npm init -y
npm install mongoose
- 安装开发依赖(可选)
bash
npm install typescript ts-node @types/node @types/mongoose --save-dev
连接 MongoDB
- 创建连接文件
db.js
:
javascript
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect('mongodb://localhost:27017/mongoose_tutorial', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
console.log('MongoDB Connected...');
} catch (err) {
console.error('Connection error:', err.message);
process.exit(1);
}
};
module.exports = connectDB;
- 使用 Atlas 云服务的连接字符串:
javascript
mongoose.connect('mongodb+srv://<username>:<password>@cluster0.mongodb.net/mongoose_tutorial?retryWrites=true&w=majority');
定义 Schema 和 Model
- 创建用户模型
models/User.js
:
javascript
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('User', UserSchema);
- 创建博客文章模型
models/Post.js
:
javascript
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
published: {
type: Boolean,
default: false
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Post', PostSchema);
基本 CRUD 操作
创建记录
javascript
const User = require('./models/User');
const createUser = async () => {
try {
const user = new User({
name: 'John Doe',
email: 'john@example.com',
password: 'securepassword123'
});
await user.save();
console.log('User created:', user);
} catch (err) {
console.error('Error creating user:', err.message);
}
};
查询记录
javascript
// 查找所有用户
const users = await User.find();
// 带条件的查询
const activeUsers = await User.find({ active: true });
// 单条查询
const user = await User.findOne({ email: 'john@example.com' });
// 按ID查询
const userById = await User.findById('507f1f77bcf86cd799439011');
更新记录
javascript
// 更新单个文档
const updatedUser = await User.findOneAndUpdate(
{ email: 'john@example.com' },
{ name: 'John Smith' },
{ new: true } // 返回更新后的文档
);
// 更新多个文档
const result = await User.updateMany(
{ active: false },
{ active: true }
);
删除记录
javascript
// 删除单个文档
const deletedUser = await User.findOneAndDelete({ email: 'john@example.com' });
// 按ID删除
await User.findByIdAndDelete('507f1f77bcf86cd799439011');
// 删除多个文档
await User.deleteMany({ active: false });
高级功能
数据验证
javascript
const ProductSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Product name is required'],
minlength: [3, 'Name must be at least 3 characters'],
maxlength: [100, 'Name cannot exceed 100 characters']
},
price: {
type: Number,
required: true,
min: [0, 'Price cannot be negative']
},
category: {
type: String,
enum: ['electronics', 'clothing', 'books', 'other']
}
});
中间件(钩子)
javascript
UserSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (err) {
next(err);
}
});
UserSchema.post('save', function(doc, next) {
console.log('New user was created:', doc);
next();
});
虚拟属性
javascript
UserSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
查询构建
javascript
const results = await User.find()
.where('age').gt(18)
.where('active').equals(true)
.sort('-createdAt')
.limit(10)
.select('name email')
.exec();
聚合管道
javascript
const stats = await User.aggregate([
{ $match: { active: true } },
{ $group: {
_id: '$department',
total: { $sum: 1 },
avgAge: { $avg: '$age' }
}},
{ $sort: { total: -1 } }
]);
填充引用
javascript
const posts = await Post.find()
.populate('author', 'name email')
.exec();
索引
javascript
UserSchema.index({ email: 1 }, { unique: true });
UserSchema.index({ name: 'text' }); // 文本索引
错误处理
javascript
try {
const user = await User.create({ email: 'existing@email.com' });
} catch (err) {
if (err.code === 11000) {
console.log('Duplicate key error');
} else {
console.log('Other error:', err.message);
}
}
使用 Mongoose 与 TypeScript
- 定义接口:
typescript
import { Document, Schema, model } from 'mongoose';
interface IUser extends Document {
name: string;
email: string;
password: string;
createdAt: Date;
}
const UserSchema = new Schema<IUser>({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
const User = model<IUser>('User', UserSchema);
最佳实践
-
连接管理:
- 在应用启动时连接数据库
- 使用连接池
- 处理连接错误和断开
-
Schema 设计:
- 合理使用引用和嵌入
- 考虑读写比例
- 避免大文档
-
性能优化:
- 使用适当的索引
- 限制返回字段
- 使用分页
-
安全:
- 不要直接暴露 Mongoose 文档
- 使用 schema 验证
- 清理用户输入
示例应用结构
text
project/
├── models/
│ ├── User.js
│ ├── Post.js
│ └── index.js
├── db.js
├── services/
│ ├── userService.js
│ └── postService.js
├── controllers/
│ ├── userController.js
│ └── postController.js
└── app.js
总结
Mongoose 为 MongoDB 提供了强大的抽象层,使得在 Node.js 中处理数据更加方便和安全。通过定义 Schema 和 Model,你可以获得数据验证、类型安全、中间件钩子等特性。结合 MongoDB 的灵活性和性能,Mongoose 是构建 Node.js 应用的优秀选择。