查询与聚合
MongoDB 查询使用 BSON Filter 表达条件,Projection 控制返回字段,Update Operator 描述局部修改。复杂数据处理使用 Aggregation Pipeline,让文档依次通过多个 Stage。
查询组成
javascript
db.users.find(
{
enabled: true,
age: { $gte: 18 },
tags: "vip",
"profile.city": { $in: ["Hefei", "Shanghai"] }
},
{ username: 1, age: 1, "profile.city": 1 }
).sort({ createdAt: -1, _id: -1 }).limit(20)| 部分 | 作用 | SQL 类比 |
|---|---|---|
| Filter | 匹配文档 | WHERE |
| Projection | 选择或排除字段 | SELECT column |
| Sort | 指定结果顺序 | ORDER BY |
| Limit / Skip | 限制和跳过结果 | LIMIT / OFFSET |
| Collation | 字符串比较和排序规则 | Collation |
点路径可以访问嵌套 Field。查询数组中的多个条件是否要求同一个数组元素满足,需要区分普通点路径与 $elemMatch,否则语义可能扩大到不同元素分别满足。
局部更新
javascript
db.users.updateOne(
{ _id: userId, version: 3 },
{
$set: { "profile.city": "Hefei", updatedAt: new Date() },
$inc: { balance: 10, version: 1 },
$addToSet: { tags: "vip" }
}
)| Operator | 作用 |
|---|---|
$set / $unset | 设置或移除字段 |
$inc / $mul | 原子数值增量或乘法 |
$push / $pull | 向数组追加或移除元素 |
$addToSet | 不重复地加入数组元素 |
$rename | 重命名 Field |
| Positional Operator | 更新匹配的数组元素 |
用整个新文档 replaceOne 会替换除 _id 外的文档内容;普通业务更新通常使用操作符,避免覆盖并发写入的其他字段。Filter 中加入 Version 可以实现乐观并发控制。
Aggregation Pipeline
javascript
db.orders.aggregate([
{ $match: { status: "paid", createdAt: { $gte: start } } },
{ $unwind: "$items" },
{
$group: {
_id: "$items.productId",
quantity: { $sum: "$items.quantity" },
amount: { $sum: { $multiply: ["$items.price", "$items.quantity"] } }
}
},
{ $sort: { amount: -1 } },
{ $limit: 20 }
])| Stage | 作用 |
|---|---|
$match | 过滤文档,应尽量尽早缩小数据集 |
$project / $set | 选择、计算或增加 Field |
$unwind | 把数组元素展开为多条 Pipeline Document |
$group | 按键聚合,可能消耗大量内存 |
$sort | 排序;前置条件匹配时可能使用索引 |
$lookup | 与另一个 Collection 关联 |
$facet | 对同一输入并行执行多个子管道 |
$setWindowFields | 执行窗口计算 |
$merge / $out | 把结果写入 Collection |
Pipeline 的 Stage 顺序会改变语义和成本。应尽早 $match、减少不必要字段和数组展开,并通过 explain 验证索引、扫描文档数、排序和执行时间。
$lookup 的边界
$lookup 让 MongoDB 能够做 Collection 间关联,但不能据此照搬高度规范化模型:
- 关联字段需要合适索引。
- 分片环境中的路由和数据分布会影响成本。
- 多层
$lookup、$unwind可能放大中间结果。 - 经常共同读取且生命周期一致的数据,嵌入通常更直接。