MongoDB 聚合
聚合操作处理多个文档并返回计算结果。您可以使用聚合操作来:
-
将多个文档中的值组合在一起。
-
对分组数据执行操作,返回单一结果。
-
分析一段时间内的数据变化。
若要执行聚合操作,您可以使用:
-
聚合管道,这是执行聚合的首选方法。
-
单一目的聚合方法,这些方法很简单,但缺乏聚合管道的功能。
聚合管道
聚合管道由一个或多个处理文档的阶段组成:
- 每个阶段对输入文档执行一个操作。例如,某个阶段可以过滤文档、对文档进行分组并计算值。
- 从一个阶段输出的文档将传递到下一阶段。
- 一个聚合管道可以返回针对文档组的结果。例如,返回总值、平均值、最大值和最小值。
如使用通过聚合管道更新中显示的阶段,则可以通过聚合管道更新文档。
使用
db.collection.aggregate()
方法运行的聚合管道不会修改集合中的文档,除非管道包含$merge
或$out
阶段。
例一
本部分展示了使用以下披萨 orders
集合的聚合管道示例:
db.orders.insertMany( [
{ _id: 0, name: "Pepperoni", size: "small", price: 19,
quantity: 10, date: ISODate( "2021-03-13T08:14:30Z" ) },
{ _id: 1, name: "Pepperoni", size: "medium", price: 20,
quantity: 20, date : ISODate( "2021-03-13T09:13:24Z" ) },
{ _id: 2, name: "Pepperoni", size: "large", price: 21,
quantity: 30, date : ISODate( "2021-03-17T09:22:12Z" ) },
{ _id: 3, name: "Cheese", size: "small", price: 12,
quantity: 15, date : ISODate( "2021-03-13T11:21:39.736Z" ) },
{ _id: 4, name: "Cheese", size: "medium", price: 13,
quantity:50, date : ISODate( "2022-01-12T21:23:13.331Z" ) },
{ _id: 5, name: "Cheese", size: "large", price: 14,
quantity: 10, date : ISODate( "2022-01-12T05:08:13Z" ) },
{ _id: 6, name: "Vegan", size: "small", price: 17,
quantity: 10, date : ISODate( "2021-01-13T05:08:13Z" ) },
{ _id: 7, name: "Vegan", size: "medium", price: 18,
quantity: 10, date : ISODate( "2021-01-13T05:10:13Z" ) }
] )
以下聚合管道示例包含两个阶段,并返回按披萨名称分组后,各款中号披萨的总订单数量:
db.orders.aggregate( [
// Stage 1: Filter pizza order documents by pizza size
{
$match: { size: "medium" }
},
// Stage 2: Group remaining documents by pizza name and calculate total quantity
{
$group: { _id: "$name", totalQuantity: { $sum: "$quantity" } }
}
] )
$match
阶段:
- 从披萨订单文档过滤出
size
为medium
的披萨。 - 将剩余文档传递到
$group
阶段。
$group
阶段:
- 按披萨
name
对剩余文档进行分组。 - 使用
$sum
计算每种披萨name
的总订单quantity
。总数存储在聚合管道返回的totalQuantity
字段中。
示例输出:
[
{ _id: 'Cheese', totalQuantity: 50 },
{ _id: 'Vegan', totalQuantity: 10 },
{ _id: 'Pepperoni', totalQuantity: 20 }
]
例二
以下示例计算了两个日期之间的披萨订单总额和平均订单数量:
db.orders.aggregate( [
// Stage 1: Filter pizza order documents by date range
{
$match:
{
"date": { $gte: new ISODate( "2020-01-30" ), $lt: new ISODate( "2022-01-30" ) }
}
},
// Stage 2: Group remaining documents by date and calculate results
{
$group:
{
_id: { $dateToString: { format: "%Y-%m-%d", date: "$date" } },
totalOrderValue: { $sum: { $multiply: [ "$price", "$quantity" ] } },
averageOrderQuantity: { $avg: "$quantity" }
}
},
// Stage 3: Sort documents by totalOrderValue in descending order
{
$sort: { totalOrderValue: -1 }
}
] )
$match
阶段:
$group
阶段:
- 使用
$dateToString
按日期对文档进行分组。 - 对于每个群组,计算:
- 将分组的文档传递到
$sort
阶段。
$sort
阶段:
- 按每组的总订单值以降序对文档进行排序 (
-1
)。 - 返回排序文档。
示例输出:
[
{ _id: '2022-01-12', totalOrderValue: 790, averageOrderQuantity: 30 },
{ _id: '2021-03-13', totalOrderValue: 770, averageOrderQuantity: 15 },
{ _id: '2021-03-17', totalOrderValue: 630, averageOrderQuantity: 30 },
{ _id: '2021-01-13', totalOrderValue: 350, averageOrderQuantity: 10 }
]
单一目的聚合方法
单一目的聚合方法聚合单个集合中的文档。这些方法很简单,但缺乏聚合管道的功能。
方法 | 说明 |
---|---|
db.collection.estimatedDocumentCount() | 返回集合或视图中文档的近似数量。 |
db.collection.count() | 返回集合或视图中文档的数量。 |
db.collection.distinct() | 返回具有指定字段的不同值的文档数组。 |
字段路径
您可以使用字段路径(Field Path)表达式访问权限输入文档中的字段
要指定字段路径,加上美元符号$
。
可以在以下使用案例中使用字段路径: