Node.js MongoDB
官方驱动、文档操作、聚合管道、以及 ObjectId 与索引。
Key PointsMongo 存的是文档不是行;`_id` 默认是 ObjectId,查询时要转成它而不是字符串。
连接
Bash
npm i mongodbJavaScript
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://127.0.0.1:27017');
await client.connect();
const db = client.db('demo');
const users = db.collection('users');
console.log('已连接', db.databaseName);Output
已连接 demo增删改查
JavaScript
// 插入
const { insertedId } = await users.insertOne({ name: '张三', age: 28, tags: ['vip'] });
console.log(insertedId);
await users.insertMany([
{ name: '李四', age: 32 },
{ name: '王五', age: 25 },
]);
// 查询
const one = await users.findOne({ name: '张三' });
const list = await users.find({ age: { $gte: 26 } }).sort({ age: -1 }).limit(10).toArray();
// 更新
await users.updateOne({ _id: insertedId }, { $set: { age: 29 }, $push: { tags: 'new' } });
// 删除
await users.deleteOne({ name: '王五' });Output
new ObjectId('6723a1f4c2e8b9d0a1b2c3d4')查询操作符
| Operator | Meaning |
|---|---|
$eq / $ne | 等于 / 不等于 |
$gt $gte $lt $lte | 比较 |
$in / $nin | 在 / 不在数组里 |
$exists | 字段是否存在 |
$regex | 正则匹配 |
$and $or $not | 逻辑组合 |
$elemMatch | 数组元素匹配多个条件 |
聚合管道
JavaScript
const stats = await users.aggregate([
{ $match: { age: { $gte: 18 } } },
{ $group: { _id: '$city', count: { $sum: 1 }, avgAge: { $avg: '$age' } } },
{ $sort: { count: -1 } },
{ $limit: 5 },
{ $project: { _id: 0, city: '$_id', count: 1, avgAge: { $round: ['$avgAge', 1] } } },
]).toArray();
console.log(stats);Output
[
{ count: 42, avgAge: 29.4, city: '北京' },
{ count: 31, avgAge: 27.8, city: '上海' }
]Pipelines are ordered: putting $match first lets an index filter early; putting it later means a full scan then filter. This is rule one of Mongo performance.
ObjectId 的坑
JavaScript
const { ObjectId } = require('mongodb');
// 错:字符串和 ObjectId 不相等
await users.findOne({ _id: '6723a1f4c2e8b9d0a1b2c3d4' }); // null
// 对:先转换
await users.findOne({ _id: new ObjectId('6723a1f4c2e8b9d0a1b2c3d4') });
// 校验,避免非法 id 抛异常
if (ObjectId.isValid(id)) { /* ... */ }ObjectId.isValid also returns true for 12-byte strings (like '123456789012') — looser than you expect. In production, also test /^[0-9a-f]{24}$/i.索引
JavaScript
await users.createIndex({ email: 1 }, { unique: true });
await users.createIndex({ age: -1, city: 1 });
// 看执行计划,确认走了索引
const plan = await users.find({ email: 'a@b.com' }).explain();
console.log(plan.queryPlanner.winningPlan);连接
Bash
npm i mongodbJavaScript
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://127.0.0.1:27017');
await client.connect();
const db = client.db('demo');
const users = db.collection('users');
console.log('已连接', db.databaseName);Output
已连接 demo增删改查
JavaScript
// 插入
const { insertedId } = await users.insertOne({ name: '张三', age: 28, tags: ['vip'] });
console.log(insertedId);
await users.insertMany([
{ name: '李四', age: 32 },
{ name: '王五', age: 25 },
]);
// 查询
const one = await users.findOne({ name: '张三' });
const list = await users.find({ age: { $gte: 26 } }).sort({ age: -1 }).limit(10).toArray();
// 更新
await users.updateOne({ _id: insertedId }, { $set: { age: 29 }, $push: { tags: 'new' } });
// 删除
await users.deleteOne({ name: '王五' });Output
new ObjectId('6723a1f4c2e8b9d0a1b2c3d4')查询操作符
| 操作符 | 含义 |
|---|---|
$eq / $ne | 等于 / 不等于 |
$gt $gte $lt $lte | 比较 |
$in / $nin | 在 / 不在数组里 |
$exists | 字段是否存在 |
$regex | 正则匹配 |
$and $or $not | 逻辑组合 |
$elemMatch | 数组元素匹配多个条件 |
聚合管道
JavaScript
const stats = await users.aggregate([
{ $match: { age: { $gte: 18 } } },
{ $group: { _id: '$city', count: { $sum: 1 }, avgAge: { $avg: '$age' } } },
{ $sort: { count: -1 } },
{ $limit: 5 },
{ $project: { _id: 0, city: '$_id', count: 1, avgAge: { $round: ['$avgAge', 1] } } },
]).toArray();
console.log(stats);Output
[
{ count: 42, avgAge: 29.4, city: '北京' },
{ count: 31, avgAge: 27.8, city: '上海' }
]管道是有序的:$match 放最前面能先用索引过滤,放后面就变成全表扫完再筛。这是 Mongo 性能优化第一条。
ObjectId 的坑
JavaScript
const { ObjectId } = require('mongodb');
// 错:字符串和 ObjectId 不相等
await users.findOne({ _id: '6723a1f4c2e8b9d0a1b2c3d4' }); // null
// 对:先转换
await users.findOne({ _id: new ObjectId('6723a1f4c2e8b9d0a1b2c3d4') });
// 校验,避免非法 id 抛异常
if (ObjectId.isValid(id)) { /* ... */ }ObjectId.isValid 对 12 字节字符串也返回 true(比如 '123456789012'),它比你想的宽松。生产代码里最好用 /^[0-9a-f]{24}$/i 再校验一遍。索引
JavaScript
await users.createIndex({ email: 1 }, { unique: true });
await users.createIndex({ age: -1, city: 1 });
// 看执行计划,确认走了索引
const plan = await users.find({ email: 'a@b.com' }).explain();
console.log(plan.queryPlanner.winningPlan);