首页 / Node.js 教程 / MongoDB 增删改查与进阶

Node.js 教程

MongoDB 增删改查与进阶

本教程共 76 篇 · 第 51 篇 · 更新于 2026-07-25 · 约 3 分钟阅读

Node.jsMongoDBCRUD聚合NoSQL

51. MongoDB 增删改查与进阶

本节目标:MongoDB 文档操作、查询条件和聚合管道。

MongoDB 的查询风格和 SQL 完全不同。没有 SELECT * FROM,取而代之的是 find() 和灵活的条件对象。本章把 CRUD 过一遍,再看排序、聚合等进阶操作。

前置准备

Docker 起 MongoDB:

docker run -d -p 27017:27017 --name mongo mongo:7

连接代码(沿用第 48 章的封装):

import { MongoClient } from 'mongodb';

const client = new MongoClient('mongodb://localhost:27017');
await client.connect();
const db = client.db('shop');
const products = db.collection('products');

Create:插入文档

MongoDB 里一条记录叫「文档」,类似 JSON 对象。

// 插入单条
await products.insertOne({
  name: '无线鼠标',
  price: 99,
  tags: ['数码', '办公'],
  stock: 100,
  createdAt: new Date()
});

// 插入多条
await products.insertMany([
  { name: '机械键盘', price: 299, tags: ['数码'], stock: 50 },
  { name: '显示器支架', price: 159, tags: ['办公'], stock: 30 }
]);
Note

MongoDB 会自动给每个文档生成 _id 字段(ObjectId 类型),除非你自己传。这个字段全局唯一,可以当作主键用。

Read:查询文档

基础查询

// 等值匹配
await products.findOne({ name: '无线鼠标' });

// 查多条
const list = await products.find({ tags: '数码' }).toArray();

比较操作符

await products.find({ price: { $gte: 100, $lte: 300 } }).toArray(); // 100~300
await products.find({ stock: { $gt: 0 } }).toArray();               // 有库存
await products.find({ tags: { $in: ['数码', '游戏'] } }).toArray();  // 标签在列表中

常见操作符:

操作符含义
$eq等于
$ne不等于
$gt / $gte大于 / 大于等于
$lt / $lte小于 / 小于等于
$in / $nin在 / 不在数组中

逻辑组合

// AND(默认就是 AND)
await products.find({ price: { $gte: 100 }, stock: { $gt: 0 } }).toArray();

// OR
await products.find({
  $or: [{ price: { $lt: 50 } }, { stock: { $gt: 200 } }]
}).toArray();

Update:更新文档

// 局部更新($set 只改指定字段,其他保留)
await products.updateOne(
  { name: '无线鼠标' },
  { $set: { price: 89, stock: 95 } }
);

// 不存在则插入(upsert)
await products.updateOne(
  { name: '新款耳机' },
  { $set: { price: 199, stock: 20 } },
  { upsert: true }
);

// 数字字段原子增减
await products.updateOne(
  { name: '无线鼠标' },
  { $inc: { stock: -1 } }  // 库存减 1
);
Warning

updateOne 默认只改匹配的第一条。如果想像 SQL 的 UPDATE ... WHERE 那样改多条,用 updateMany

Delete:删除文档

// 删单条
await products.deleteOne({ name: '显示器支架' });

// 删多条
await products.deleteMany({ stock: 0 });

和 MySQL 一样,生产环境建议软删除:

await products.updateOne(
  { _id: someId },
  { $set: { deletedAt: new Date() } }
);

查询时统一加上 deletedAt: { $exists: false } 过滤掉已删除的。

排序与分页

// 按价格升序,再按库存降序
const list = await products
  .find({ stock: { $gt: 0 } })
  .sort({ price: 1, stock: -1 })
  .skip(20)      // 跳过前 20 条
  .limit(10)     // 取 10 条
  .toArray();

skip + limit 是 MongoDB 的分页方式。数据量大时 skip 会变慢,可以考虑用 _id 范围查询替代:

const lastId = '...';  // 上一页最后一条的 _id
await products.find({ _id: { $gt: lastId } }).limit(10).toArray();

聚合管道

聚合管道(Aggregation Pipeline)是 MongoDB 的强大武器,类似 Linux 的管道,把数据一步步加工。

统计每个标签下的商品数量和平均价格:

const result = await products.aggregate([
  { $unwind: '$tags' },                          // 把 tags 数组拆成多条
  { $group: {
      _id: '$tags',
      count: { $sum: 1 },
      avgPrice: { $avg: '$price' }
  }},
  { $sort: { count: -1 } }
]).toArray();

常用阶段:

阶段作用
$match过滤文档(类似 WHERE)
$group分组聚合
$sort排序
$limit / $skip分页
$project选择字段、计算新字段
$lookup左外连接(类似 JOIN)