选哪个
| 方案 | 特点 |
|---|---|
原生 driver(mongodb) |
灵活、贴近 MongoDB API |
| mongoose(ODM) | Schema / 校验 / 中间件,类似 ORM |
新项目两边都行。简单 CRUD 用原生、复杂数据模型用 mongoose。
原生 driver
npm install mongodb
import { MongoClient } from 'mongodb';
const client = new MongoClient(process.env.MONGODB_URI);
await client.connect();
const db = client.db('myapp');
const users = db.collection('users');
// 插入
const r = await users.insertOne({ name: 'Alice', age: 30 });
console.log(r.insertedId);
// 查找
const user = await users.findOne({ name: 'Alice' });
const list = await users.find({ age: { $gte: 18 } }).limit(20).toArray();
// 更新
await users.updateOne({ name: 'Alice' }, { $set: { age: 31 } });
await users.updateMany({}, { $inc: { age: 1 } });
// 删除
await users.deleteOne({ name: 'Bob' });
// 索引
await users.createIndex({ email: 1 }, { unique: true });
// 聚合
const stats = await users.aggregate([
{ $match: { age: { $gte: 18 } } },
{ $group: { _id: '$role', count: { $sum: 1 } } },
{ $sort: { count: -1 } },
]).toArray();
await client.close();
mongoose:带 Schema 的 ODM
npm install mongoose
import mongoose from 'mongoose';
await mongoose.connect(process.env.MONGODB_URI);
const userSchema = new mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true, unique: true, lowercase: true },
age: { type: Number, min: 0, max: 150 },
tags: [String],
createdAt: { type: Date, default: Date.now },
});
userSchema.index({ name: 1, email: 1 });
userSchema.virtual('isAdult').get(function () {
return this.age >= 18;
});
userSchema.methods.greet = function () {
return `Hi, I'm ${this.name}`;
};
const User = mongoose.model('User', userSchema);
// 操作
const u = await User.create({ name: 'Alice', email: 'a@x.com', age: 30 });
const found = await User.findOne({ email: 'a@x.com' });
console.log(found.greet());
await User.updateMany({}, { $inc: { age: 1 } });
await User.deleteOne({ _id: u._id });
常用查询操作符
{ age: { $gt: 18 } } // 大于
{ age: { $gte: 18 } } // 大于等于
{ age: { $lt: 65 } } // 小于
{ age: { $in: [10, 20, 30] } } // 在列表
{ age: { $nin: [...] } } // 不在列表
{ name: { $regex: /^A/ } } // 正则
{ $or: [{ age: 10 }, { age: 20 }] }
{ $and: [...] }
{ 'address.city': 'Beijing' } // 嵌套字段
更新操作符
{ $set: { name: 'Bob' } } // 设
{ $inc: { count: 1 } } // 加
{ $unset: { field: '' } } // 删字段
{ $push: { tags: 'new' } } // 数组加
{ $pull: { tags: 'old' } } // 数组删
{ $addToSet: { tags: 'a' } } // 数组加(去重)
索引
// 单字段
db.users.createIndex({ email: 1 });
// 复合
db.users.createIndex({ name: 1, age: -1 });
// 唯一
db.users.createIndex({ email: 1 }, { unique: true });
// 部分索引(只索引匹配的文档)
db.users.createIndex(
{ email: 1 },
{ unique: true, partialFilterExpression: { email: { $exists: true } } }
);
// TTL(自动过期文档)
db.sessions.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 3600 }
);
// 看现有索引
db.users.getIndexes();
聚合管道
const result = await db.collection('orders').aggregate([
{ $match: { status: 'completed' } },
{ $group: {
_id: { $dateToString: { format: '%Y-%m', date: '$createdAt' } },
total: { $sum: '$amount' },
count: { $sum: 1 },
}},
{ $sort: { _id: 1 } },
]).toArray();
事务(4.0+ 副本集才支持)
const session = client.startSession();
try {
await session.withTransaction(async () => {
await accounts.updateOne(
{ _id: from }, { $inc: { balance: -amount } }, { session }
);
await accounts.updateOne(
{ _id: to }, { $inc: { balance: amount } }, { session }
);
});
} finally {
await session.endSession();
}
流式遍历大集合
const cursor = users.find({});
for await (const user of cursor) {
process(user);
}
关联(MongoDB 不强类型关系)
// $lookup(类 JOIN)
db.orders.aggregate([
{ $lookup: {
from: 'users',
localField: 'userId',
foreignField: '_id',
as: 'user',
}},
{ $unwind: '$user' },
]);
// mongoose populate
const order = await Order.findById(id).populate('userId');
坑
- MongoDB 没有内置外键——关联完整性自己保证
_id默认 ObjectId(不是 string)—— 查询时注意类型- 索引要建,否则大集合 = 全表扫描
findOne不存在返回null,不抛错- 事务在单实例不工作——必须 replica set
下一篇:Redis。