Node.js MongoDB

官方驱动、文档操作、聚合管道、以及 ObjectId 与索引。

Intermediate
Key PointsMongo 存的是文档不是行;`_id` 默认是 ObjectId,查询时要转成它而不是字符串。

连接

Bash
npm i mongodb
JavaScript
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')

查询操作符

OperatorMeaning
$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);

Friends & Partners