CRUD 与聚合管道

查询操作符

类别操作符说明
比较$eq $gt $gte $lt $in $ne条件匹配
逻辑$and $or $not $nor组合条件
元素$exists $type字段存在性/类型
数组$all $elemMatch $size数组匹配
正则$regex模式匹配
// 等值与范围
db.orders.find({ status: "paid", amount: { $gte: 100 } })

// 逻辑组合
db.orders.find({ $or: [{ status: "paid" }, { status: "shipped" }] })

// 嵌套字段用点号
db.users.find({ "address.city": "北京" })

// 数组:包含全部 / 满足任一元素条件
db.articles.find({ tags: { $all: ["go", "db"] } })
db.orders.find({ items: { $elemMatch: { qty: { $gt: 5 }, price: { $lt: 100 } } } })

// 投影与排序分页
db.orders.find({ status: "paid" }, { amount: 1, createdAt: 1 })
         .sort({ createdAt: -1 }).skip(0).limit(20)

// 计数与去重
db.orders.countDocuments({ status: "paid" })
db.orders.distinct("user_id", { status: "paid" })

更新操作

// 更新或插入(upsert)
db.inventory.updateOne(
  { sku: "A001" },
  {
    $inc: { qty: -1 },                 // 原子自增/自减
    $set: { updatedAt: new Date() },
    $push: { logs: { at: new Date(), op: "dec" } }
  },
  { upsert: true }
)

// 数组操作符:$addToSet 去重添加、$pull 移除
db.users.updateOne({ _id: 1 }, { $addToSet: { tags: "vip" } })
db.users.updateOne({ _id: 1 }, { $pull: { tags: "new" } })

// 批量更新
db.orders.updateMany({ status: "pending" }, { $set: { status: "expired" } })

// 查找并更新并返回(原子,可返回更新后文档)
db.counters.findOneAndUpdate(
  { _id: "order_id" },
  { $inc: { seq: 1 } },
  { upsert: true, returnDocument: "after" }
)
Tip

$inc$push 等是服务端原子操作,比「读出 → 计算 → 写回」安全得多,能避免并发覆盖。计数器场景优先使用 $inc

聚合管道

聚合管道是一系列阶段(stage),文档依次流经各阶段被处理:

db.orders.aggregate([
  { $match: { status: "paid", createdAt: { $gte: ISODate("2026-01-01") } } },
  { $group: {
      _id: "$user_id",
      gmv: { $sum: "$amount" },
      orders: { $sum: 1 }
  } },
  { $sort: { gmv: -1 } },
  { $limit: 10 },
  { $lookup: {
      from: "users",
      localField: "_id",
      foreignField: "_id",
      as: "user"
  } },
  { $unwind: "$user" },
  { $project: { _id: 0, user: "$user.name", gmv: 1, orders: 1 } }
])
常用阶段作用
$match过滤(尽量放最前,可命中索引)
$group分组聚合($sum/$avg/$max/$push
$project选择/计算字段
$sort排序
$limit / $skip分页
$lookup左外连接(类似 LEFT JOIN)
$unwind展开数组
$facet一次查询多路聚合
$out / $merge结果写入集合

分组与展开综合示例

// 统计每个商品的总销量(items 为数组)
db.orders.aggregate([
  { $match: { status: "paid" } },
  { $unwind: "$items" },
  { $group: {
      _id: "$items.sku",
      qty:   { $sum: "$items.qty" },
      revenue: { $sum: { $multiply: ["$items.qty", "$items.price"] } }
  } },
  { $sort: { revenue: -1 } }
])

多路聚合 $facet

db.products.aggregate([
  { $match: { category: "phone" } },
  { $facet: {
      byPrice:  [{ $group: { _id: "$brand", avg: { $avg: "$price" } } }],
      total:    [{ $count: "count" }],
      cheapest: [{ $sort: { price: 1 } }, { $limit: 1 }]
  } }
])
Warning

聚合管道中 $match$sort 应尽量放在最前面:放在 $group 之后会丢失索引能力,处理的数据量也更大。用 explain() 验证聚合是否走索引。

小结

  • 查询操作符覆盖比较、逻辑、数组与正则;嵌套字段用点号。
  • 更新优先使用原子操作符($inc$push$addToSet),配合 upsert
  • 聚合管道的阶段顺序决定性能:$match/$sort 尽量前置。
  • $lookup 弥补了关联查询,但性能不如关系型 JOIN,建模时优先考虑嵌入。