什么是聚合查询
在真实的业务环境,我们的表/文档肯定不止一个,常常需要在多个表之间进行关联查询。那么在MongoDB
中如何做多张表的关联查询了,这就需要用到聚合查询。
MongoDB中聚合(aggregate) 操作将来自多个document的value组合在一起,并通过对分组数据进行各种操作处理,并返回计算后的数据结果,主要用于处理数据(诸如统计平均值,求和等)。有点类似sql语句中的 count(*)、group by,但聚合查询功能要更加强大。
有哪些聚合查询的方法
MongoDB
提供了三种方式去执行聚合操作:
- Pipeline 聚合管道
- 单一目的聚合方法
- Map-Reduce
不过从从MongoDB 5.0 开始,map-reduce 已被弃用。
聚合管道
聚合管道是由aggregation framework将文档进入一个由多个阶段(stage)组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的聚合结果。
如图所示:
上图的操作,代码如下:
php
db.orders.aggregate([
{ $match: { status: "A" } },
{ $group: { _id: "$cust_id", total: { $sum: "$amount" } } }
])
- $match阶段:通过status字段过滤出符合条件的Document(即是Status等于"A"的Document);
- $group 阶段:按cust_id字段对Document进行分组,以计算每个唯一cust_id的金额总和。
相当于 SQL 中的以下语句:
vbnet
select cust_id as _id, sum(amount) as total from orders where status like "%A%" group by cust_id;
管道操作符
MongoDB管道 | 功能描述 | SQL操作/函数 |
---|---|---|
$match | 文档过滤器 | where |
$group | 分组统计器 | group by |
$project | 字段整形师:修改输入文档的结构(例如重命名,增加、删除字段等) | select |
$sort | 对结果排序 | order by |
$limit | 限制管道输出的结果个数 | limit |
$skip | 跳过制定数量的结果,并且返回剩下的结果 | |
处理数组类型字段 | ||
$lookup | 实现类似JOIN操作 | join |
$addFields | 新增计算字段 | |
$bucket | 数据分布分析 |
想象你在玩俄罗斯套娃:
- $match:先筛掉不想要的娃娃(文档过滤)
- $unwind:拆开大礼包(数组展开)
- $group:按颜色分类(分组统计)
- $project:给娃娃穿新衣服(字段重塑)
字段路径
字段路径 是访问文档中字段的特殊语法,使用 $
前缀标识字段层级关系,支持访问嵌套结构和数组元素。
核心语法规则:
- 根字段 :
$字段名
例:$name
访问顶层name字段 - 嵌套字段 :
$父字段.子字段
例:$address.city
访问address对象中的city - 数组元素 :
$数组字段.子字段
→ 访问数组内元素的子字段
$数组字段[index]
→ 访问指定索引元素
文档结构:
css
// orders集合文档示例
{
_id: 1,
order_no: "ORD202301",
items: [
{ product: "手机", price: 2999, qty: 2 },
{ product: "耳机", price: 399, qty: 1 }
],
customer: {
name: "张三",
vip_level: 3
}
}
聚合管道应用:
php
db.orders.aggregate([
{ $unwind: "$items" }, // 展开订单商品数组
{ $project: {
order_id: "$order_no", // 重命名字段
product_name: "$items.product", // 访问嵌套数组元素
total_price: {
$multiply: ["$items.price", "$items.qty"] // 计算字段
},
is_vip: {
$cond: { if: { $gte: ["$customer.vip_level", 3] }, then: "是", else: "否" } // 条件判断
}
}},
{ $match: {
total_price: { $gt: 1000 }, // 过滤高价商品
is_vip: "是"
}}
])
输出结果:
css
[ { "_id": 1, "order_id": "ORD202301", "product_name": "手机", "total_price": 5998, "is_vip": "是" }]
注意事项
- 严格区分大小写 :
$Customer
≠$customer
- 空值处理 :访问不存在的字段返回
null
- 特殊字符 :字段名含
.
时需用$["field.name"]
语法 - 性能影响:频繁访问深层嵌套字段可能影响查询速度
通过合理使用字段路径,可以像操作对象属性一样灵活处理文档数据,是构建高效聚合管道的基石!
单一目的聚合方法
单一目的聚合方法聚合单个集合中的文档。这些方法很简单,但缺乏聚合管道的功能。
方法 | 说明 |
---|---|
db.collection.estimatedDocumentCount() | 返回集合或视图中文档的近似数量 |
db.collection.count() | 返回集合或视图中文档的数量 |
db.collection.distinct() | 返回具有指定字段的不同值的文档数组 |
总结
聚合查询就像给你的MongoDB
装上涡轮增压引擎,当你能熟练驾驭这个工具时,海量数据将不再是令人头疼的负担,而是等待挖掘的金矿。记住:优秀的开发者用find
,卓越的数据工程师用aggregate
!