Node.js 教程
Mongoose ODM
本教程共 76 篇 · 第 52 篇 · 更新于 2026-07-25 · 约 3 分钟阅读
Node.jsMongooseODMSchemaMongoDB
52. Mongoose ODM
本节目标:Mongoose 的 Schema、模型、校验和关联查询。
MongoDB 原生驱动很自由,但自由过了头就是混乱。Mongoose 是 MongoDB 的 ODM(Object Document Mapper),给文档加上 Schema 约束,提供模型层方法,让代码更有结构。
安装与连接
npm install mongoose
import mongoose from 'mongoose';
await mongoose.connect('mongodb://localhost:27017/shop');
console.log('MongoDB connected');
Mongoose 内部会维护连接池,默认最多 100 个连接,一般不用调。
定义 Schema
Schema 是对文档结构的描述,类似关系型数据库的建表语句:
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
age: { type: Number, min: 0, max: 150 },
tags: [String],
isVip: { type: Boolean, default: false },
createdAt: { type: Date, default: Date.now }
});
常用类型:
| 类型 | 说明 |
|---|---|
String | 字符串 |
Number | 数字 |
Boolean | 布尔 |
Date | 日期 |
Array | 数组,如 [String] |
ObjectId | MongoDB 的对象 ID |
Mixed | 任意类型(尽量少用) |
创建 Model
Schema 本身不能操作数据库,要编译成 Model:
const User = mongoose.model('User', userSchema);
第一个参数 'User' 是模型名,Mongoose 会自动把它复数化变成集合名 users。
CRUD 操作
创建
const user = new User({ name: 'Alice', email: 'alice@example.com', age: 28 });
await user.save();
// 或者一步到位
await User.create({ name: 'Bob', email: 'bob@example.com' });
查询
// 查单条
const user = await User.findOne({ email: 'alice@example.com' });
// 查多条
const users = await User.find({ isVip: true });
// 按 ID 查
const byId = await User.findById('60c...');
// 条件链式查询
const result = await User.find({ age: { $gte: 18 } })
.sort({ createdAt: -1 })
.limit(10)
.select('name email'); // 只返回 name 和 email
更新
// 查出来再改
const user = await User.findOne({ email: 'alice@example.com' });
user.age = 29;
await user.save();
// 直接更新(不查出来)
await User.updateOne(
{ email: 'alice@example.com' },
{ $set: { age: 29 } }
);
删除
await User.deleteOne({ email: 'bob@example.com' });
校验与中间件
Schema 会自动校验数据类型:
try {
await User.create({ name: 'X', email: 'not-an-email' });
} catch (err) {
console.log(err.errors.email.message); // Validator failed for path `email`
}
还可以自定义校验器:
const userSchema = new mongoose.Schema({
email: {
type: String,
validate: {
validator: (v) => /^\S+@\S+\.\S+$/.test(v),
message: (props) => `${props.value} 不是有效邮箱`
}
}
});
中间件(Hooks)在保存前后执行逻辑:
userSchema.pre('save', function (next) {
// this 指向当前文档
if (this.isModified('password')) {
this.password = hashPassword(this.password);
}
next();
});
Tip箭头函数别用在
pre('save')里,因为箭头函数没有自己的this,而 Mongoose 中间件依赖this指向文档实例。
关联查询
Mongoose 可以用 ref 建立文档间的引用:
const postSchema = new mongoose.Schema({
title: String,
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
});
const Post = mongoose.model('Post', postSchema);
// 查询时填充关联文档
const posts = await Post.find().populate('author', 'name email').exec();
// 结果中 author 字段会变成完整的 User 对象,只包含 name 和 email
populate 类似 SQL 的 JOIN,但底层是额外查询,不是真正的数据库级关联。数据量特别大时,考虑反范式设计,把常用字段直接嵌入文档。