首页 / Node.js 教程 / Mongoose ODM

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]
ObjectIdMongoDB 的对象 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,但底层是额外查询,不是真正的数据库级关联。数据量特别大时,考虑反范式设计,把常用字段直接嵌入文档。