常用聚合阶段($match、$project、$group、$sort等)
MongoDB的聚合管道是一种强大的数据处理工具,通过将多个阶段(stage)串联起来,可以对集合中的文档进行复杂的转换和分析。聚合阶段是管道的核心组成部分,每个阶段对输入文档进行处理,并将结果传递给下一个阶段。以下是几种常用的聚合阶段及其典型用法。
$match
$match
是聚合管道中常用的筛选阶段,类似于查询中的 find
方法。它允许我们根据条件过滤文档,只将符合条件的文档传递到下一个阶段。$match
应尽量放在管道的早期,以减少后续阶段需要处理的数据量。
db.orders.aggregate([
{ $match: { status: "completed", total: { $gt: 100 } } }
])
这个例子筛选出 status
为 "completed" 且 total
大于 100 的订单。$match
支持所有常规查询操作符,如 $eq
、$gt
、$in
等。
$project
$project
用于重塑文档结构,可以包含、排除字段,或创建新的计算字段。它是聚合管道中最灵活的阶段之一,常用于数据投影和转换。
db.users.aggregate([
{
$project: {
name: 1,
email: 1,
yearOfBirth: { $year: "$birthDate" },
fullName: { $concat: ["$firstName", " ", "$lastName"] }
}
}
])
这个例子从 users
集合中提取 name
和 email
字段,同时计算 yearOfBirth
和拼接 fullName
。$project
还支持条件表达式(如 $cond
)和数组操作(如 $slice
)。
$group
$group
是聚合的核心阶段,用于按指定字段分组并计算聚合值。它可以与 $sum
、$avg
、$max
、$min
等累加器配合使用。
db.sales.aggregate([
{
$group: {
_id: "$product",
totalSold: { $sum: "$quantity" },
averagePrice: { $avg: "$price" },
maxQuantity: { $max: "$quantity" }
}
}
])
这个例子按 product
分组,计算每种产品的总销量、平均价格和最大单笔销量。$group
的 _id
字段决定了分组依据,可以是单个字段、多个字段的组合或计算表达式。
$sort
$sort
对文档进行排序,可以指定一个或多个字段,并控制升序(1)或降序(-1)。它通常出现在管道的后期,用于最终结果的排序。
db.employees.aggregate([
{ $sort: { department: 1, salary: -1 } }
])
这个例子先按 department
升序排列,再按 salary
降序排列。$sort
会消耗较多内存,尤其是在处理大量数据时,可能需要增加 allowDiskUse
选项。
$limit 和 $skip
$limit
限制输出文档数量,$skip
跳过指定数量的文档。它们通常配合使用实现分页功能。
db.products.aggregate([
{ $skip: 20 },
{ $limit: 10 }
])
这个例子跳过前 20 个文档,返回接下来的 10 个文档。注意 $skip
和 $limit
的顺序会影响结果。
$unwind
$unwind
展开数组字段,为数组中的每个元素创建一个单独的文档。它是处理数组数据的必备工具。
db.blogPosts.aggregate([
{ $unwind: "$comments" }
])
这个例子将 comments
数组展开,每个评论生成一个独立的文档。$unwind
还可以配合 preserveNullAndEmptyArrays
选项处理空数组或缺失字段的情况。
$lookup
$lookup
实现类似 SQL 的左连接(left outer join),可以从其他集合中查询并合并数据。
db.orders.aggregate([
{
$lookup: {
from: "products",
localField: "productId",
foreignField: "_id",
as: "productDetails"
}
}
])
这个例子将 orders
与 products
集合连接,通过 productId
匹配,结果存储在 productDetails
数组中。$lookup
还支持管道形式的复杂连接操作。
$addFields 和 $set
$addFields
和 $set
(MongoDB 4.2+)用于添加新字段或修改现有字段,而不改变其他字段。它们比 $project
更简洁,适合增量修改。
db.students.aggregate([
{
$addFields: {
finalGrade: { $avg: ["$test1", "$test2", "$test3"] },
passed: { $gte: [{ $avg: ["$test1", "$test2", "$test3"] }, 60] }
}
}
])
这个例子计算平均分并添加是否及格的标志。$set
是 $addFields
的别名,功能完全相同。
$count
$count
返回管道中当前文档的数量,通常用于统计结果集的大小。
db.customers.aggregate([
{ $match: { city: "New York" } },
{ $count: "nyCustomers" }
])
这个例子统计纽约客户的数量,结果形式为 { nyCustomers: 123 }
。$count
是 $group
和 $project
组合的快捷方式。
$facet
$facet
允许在单个聚合管道中执行多个子管道,生成多组结果。它非常适合需要同时计算多种聚合指标的场景。
db.sales.aggregate([
{
$facet: {
"totalSales": [{ $group: { _id: null, total: { $sum: "$amount" } } }],
"topProducts": [
{ $group: { _id: "$product", count: { $sum: 1 } } },
{ $sort: { count: -1 } },
{ $limit: 5 }
],
"monthlyTrend": [
{ $project: { month: { $month: "$date" } } },
{ $group: { _id: "$month", count: { $sum: 1 } } }
]
}
}
])
这个例子同时计算总销售额、最畅销的5个产品和月度销售趋势。$facet
的输出是一个文档,包含所有子管道的结果。
$bucket 和 $bucketAuto
$bucket
和 $bucketAuto
将文档分组到指定的范围区间(桶)中。$bucket
需要明确指定边界,而 $bucketAuto
自动确定边界。
db.products.aggregate([
{
$bucket: {
groupBy: "$price",
boundaries: [0, 50, 100, 200, 500],
default: "Other",
output: {
count: { $sum: 1 },
averagePrice: { $avg: "$price" }
}
}
}
])
这个例子按价格区间分组产品,统计每个区间的产品数量和平均价格。$bucketAuto
在不确定数据分布时特别有用。
$graphLookup
$graphLookup
实现递归查询,常用于处理树形或图状数据,如组织结构、社交网络等。
db.employees.aggregate([
{
$graphLookup: {
from: "employees",
startWith: "$reportsTo",
connectFromField: "reportsTo",
connectToField: "_id",
as: "managementChain",
maxDepth: 3
}
}
])
这个例子查找每个员工的管理链(向上3级)。$graphLookup
支持深度控制(maxDepth
)和条件过滤(restrictSearchWithMatch
)。
$merge 和 $out
$merge
(MongoDB 4.2+)和 $out
将聚合结果写入集合。$out
完全替换目标集合,而 $merge
提供更灵活的写入选项(合并、替换、保留等)。
db.sales.aggregate([
{ $group: { _id: "$product", total: { $sum: "$amount" } } },
{ $merge: { into: "productTotals", on: "_id", whenMatched: "merge" } }
])
这个例子计算产品总销售额,并将结果合并到 productTotals
集合中。$merge
支持复杂的冲突处理策略。
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:目录操作