前言
在MongoDB的聚合框架中,管道 $bucket
和 $facet
是两个功能强大的阶段操作符,分别用于数据的分组和多维度聚合。
以下是它们的详细说明及对比:
一. 用法详解
1. $bucket
功能:将文档按指定表达式的结果分组到预定义的区间(桶)中,适用于单维度范围分组。
语法
{
$bucket: {
groupBy: <expression>, // 分组的依据表达式
boundaries: [<lower1>, <lower2>, ...], // 桶的边界值(必须有序且递增)
default: <literal>, // 未命中任何桶的文档的默认分类
output: { // 每个桶的输出字段
<field1>: { <accumulator> },
...
}
}
}
示例
将订单按金额分桶:
db.orders.aggregate([
{
$bucket: {
groupBy: "$amount",
boundaries: [0, 50, 100, 200],
default: "200+",
output: {
count: { $sum: 1 },
total: { $sum: "$amount" }
}
}
}
]);
输出:
[
{ "_id": 0, "count": 5, "total": 250 },
{ "_id": 50, "count": 8, "total": 600 },
{ "_id": 100, "count": 3, "total": 450 },
{ "_id": "200+", "count": 2, "total": 500 }
]
注意事项:
boundaries
必须严格递增。- 未匹配且未设置
default
的文档会被丢弃。 - 每个桶的
_id
对应区间的下限。
2. $facet
功能:在同一聚合阶段内并行执行多个独立的子管道,输出多维度的聚合结果。
语法
{
$facet: {
<outputField1>: [<pipeline1>], // 子管道1
<outputField2>: [<pipeline2>], // 子管道2
...
}
}
示例
同时统计价格区间和地区销售额:
db.orders.aggregate([
{
$facet: {
"priceRanges": [
{
$bucket: {
groupBy: "$amount",
boundaries: [0, 50, 100, 200],
default: "200+",
output: { count: { $sum: 1 }, total: { $sum: "$amount" } }
}
}
],
"regionSales": [
{ $group: { _id: "$region", totalSales: { $sum: "$amount" } } }
]
}
}
]);
输出:
[
{
"priceRanges": [
{ "_id": 0, "count": 5, "total": 250 },
{ "_id": 50, "count": 8, "total": 600 },
{ "_id": 100, "count": 3, "total": 450 },
{ "_id": "200+", "count": 2, "total": 500 }
],
"regionSales": [
{ "_id": "North", "totalSales": 800 },
{ "_id": "South", "totalSales": 700 }
]
}
]
注意事项
- 每个子管道独立处理原始输入文档。
- 输出结果为嵌套文档,字段名为子管道名称。
- 适合需要多维度统计的场景,减少多次查询。
二. 对比与适用场景
特性 | $bucket | $facet |
---|---|---|
功能 | 单维度范围分组 | 多维度并行聚合 |
输出结构 | 线性数组 | 嵌套文档(多个结果集) |
复杂度 | 简单,单一分组逻辑 | 复杂,支持多个独立子管道 |
性能 | 高效,单次分组 | 可能较高开销,因多管道并行 |
典型场景 | 按价格、年龄等分区间统计 | 同时生成分桶、分类、排序等多结果 |
三. 总结
$bucket
:适合明确分桶边界的单维度统计,如销售区间、年龄段分布。$facet
:适用于复杂需求,需在单次查询中生成多个统计结果,如同时分析价格分布和地区销售趋势。
正确选择两者可显著提升聚合效率,并简化数据处理流程。