MongoDB聚合操作和普通的 find 查询有什么区别?
MongoDB 的 普通查询 (find) 和 聚合操作 (aggregate) 是 MongoDB 中获取数据的两种主要方式。
简单来说:find 用于“搬运”数据(原样拿出来),而 aggregate 用于“加工”数据(计算、统计、重组后再拿出来)。
以下是它们在功能、思维模式和使用场景上的详细对比:
1. 核心概念对比
| 特性 | 普通查询 (find) |
聚合操作 (aggregate) |
|---|---|---|
| 主要目的 | 筛选和检索。从集合中找出符合条件的文档。 | 数据处理和分析。对数据进行计算、分组、转换。 |
| 数据形态 | 返回的文档结构基本保持原样(虽然可以指定返回哪些字段,但不能改变字段值的逻辑)。 | 返回的数据可以是全新的结构,可以是统计结果、计算后的新字段,甚至是完全重组的 JSON。 |
| 操作模式 | 单一步骤:筛选 -> 排序 -> 分页。 | 流水线 (Pipeline) 模式:数据像流过管道一样,经过多个阶段(Stage)的处理。 |
| SQL 类比 | SELECT * FROM table WHERE ... |
GROUP BY, JOIN, SUM, AVG, HAVING 等复杂操作。 |
2. 功能详细区别
A. 数据转换能力
- find: 只能决定“要”或“不要”某个字段(Projection)。
- aggregate: 可以创造新字段。例如:将
price和quantity相乘生成一个total字段;或者将字符串拆分、格式化日期等。
B. 分组与统计 (Grouping)
- find: 不支持分组。你不能用
find算出“每个用户的订单总额”。 - aggregate: 核心功能之一。使用
$group阶段可以进行求和 ($sum)、平均值 ($avg)、最大/最小值等统计。
C. 多表关联 (Joins)
- find: 不支持服务端关联。你必须在代码里查两次(先查表A,拿到ID再去查表B)。
- aggregate: 支持
$lookup阶段,可以在数据库层面实现类似 SQL 的左连接(Left Join),一次性返回关联数据。
D. 执行逻辑
- find: 声明式。你告诉数据库“我要什么”,数据库决定怎么查。
- aggregate: 过程式。你定义一个“流水线”,告诉数据库“先过滤,再分组,再排序,最后格式化”。
3. 代码示例对比
假设有一个 订单集合 (orders),数据结构如下:
json
{ "_id": 1, "user": "Alice", "product": "Apple", "price": 10, "qty": 2 }
{ "_id": 2, "user": "Alice", "product": "Banana", "price": 5, "qty": 10 }
{ "_id": 3, "user": "Bob", "product": "Apple", "price": 10, "qty": 1 }
场景 1:找出 Alice 的所有订单
这是典型的检索需求。
- find:javascript
db.orders.find({ user: "Alice" }) - aggregate: (也能做,但没必要,性能略低)javascript
db.orders.aggregate([ { $match: { user: "Alice" } } ])
场景 2:计算 Alice 买了多少件商品,总共花了多少钱
这是统计需求,find 做不到,必须用 aggregate。
- aggregate:结果:javascript
db.orders.aggregate([ // 第一步:筛选出 Alice 的订单 { $match: { user: "Alice" } }, // 第二步:计算每单的总价 (price * qty) { $addFields: { total_price: { $multiply: ["$price", "$qty"] } } }, // 第三步:分组统计 { $group: { _id: "$user", total_items: { $sum: "$qty" }, total_spend: { $sum: "$total_price" } }} ]){ "_id": "Alice", "total_items": 12, "total_spend": 70 }
4. 性能与限制
- 速度: 对于简单的读取,
find通常比aggregate更快,开销更小。 - 内存限制:
aggregate的每个阶段默认有 100MB 的内存限制。如果排序或分组的数据量过大,会报错(需要开启allowDiskUse: true来使用磁盘临时存储)。find主要是流式返回,对内存压力较小。 - 索引使用: 两者都可以利用索引。但在
aggregate中,只有管道的第一阶段(通常是$match或$sort)能高效利用索引。如果把$match放在$group之后,索引就失效了。
5. 总结:什么时候用哪个?
使用
find当:- 你只需要读取原始数据。
- 你只需要做简单的筛选(Where)、排序(Order By)和分页(Limit/Skip)。
- 你希望操作尽可能快且轻量。
- 你在做简单的 CRUD 操作。
使用
aggregate当:- 你需要对数据进行统计(求和、计数、平均值)。
- 你需要分组数据(Group By)。
- 你需要关联其他集合的数据(Join / $lookup)。
- 你需要修改数据结构(重命名字段、计算新字段、嵌套/展开数组)。
- 你需要生成报表或图表数据。
右滑查看面试常问