Appearance
聚合框架
聚合框架(Aggregation Framework)是MongoDB强大的数据处理工具,可以对文档进行过滤、分组、排序、计算等复杂操作。
聚合管道概述
什么是聚合管道
javascript
// 聚合管道是将多个阶段(Stage)串联起来处理数据的机制
// 数据依次通过每个阶段,每个阶段对数据进行处理
// 基本语法
db.collection.aggregate([
{ stage1 }, // 第一个阶段
{ stage2 }, // 第二个阶段
{ stage3 }, // 第三个阶段
]);
// 聚合管道的特点:
// 1. 数据流式处理:数据依次通过各个阶段
// 2. 声明式语法:描述要做什么,而不是怎么做
// 3. 高效执行:MongoDB优化器会优化执行计划
// 4. 功能强大:支持复杂的数据转换和计算管道阶段
javascript
// 常用管道阶段:
// $match - 过滤文档
// $group - 分组聚合
// $project - 投影字段
// $sort - 排序
// $limit - 限制数量
// $skip - 跳过文档
// $unwind - 展开数组
// $lookup - 关联查询
// $addFields - 添加字段
// $count - 计数
// $facet - 多管道处理常用聚合阶段
$match - 过滤文档
javascript
// $match:过滤文档,类似find的查询条件
// 准备测试数据
db.orders.insertMany([
{
orderId: "ORD001",
userId: 1,
product: "iPhone",
amount: 6999,
status: "completed",
},
{
orderId: "ORD002",
userId: 1,
product: "iPad",
amount: 4999,
status: "pending",
},
{
orderId: "ORD003",
userId: 2,
product: "MacBook",
amount: 12999,
status: "completed",
},
{
orderId: "ORD004",
userId: 2,
product: "AirPods",
amount: 1299,
status: "completed",
},
]);
// 基本过滤
db.orders.aggregate([{ $match: { status: "completed" } }]);
// 多条件过滤
db.orders.aggregate([
{
$match: {
status: "completed",
amount: { $gte: 5000 },
},
},
]);
// $match通常放在管道开头,可以利用索引提高性能$group - 分组聚合
javascript
// $group:按指定字段分组,进行聚合计算
// 按字段分组计数
db.orders.aggregate([
{
$group: {
_id: "$status", // 分组键
count: { $sum: 1 }, // 计算每组的文档数
},
},
]);
// 输出示例:
// { "_id" : "completed", "count" : 3 }
// { "_id" : "pending", "count" : 1 }
// 按字段分组求和
db.orders.aggregate([
{
$group: {
_id: "$status",
totalAmount: { $sum: "$amount" }, // 计算每组金额总和
avgAmount: { $avg: "$amount" }, // 计算每组平均金额
maxAmount: { $max: "$amount" }, // 计算每组最大金额
minAmount: { $min: "$amount" }, // 计算每组最小金额
},
},
]);
// 按用户分组统计
db.orders.aggregate([
{
$group: {
_id: "$userId",
orderCount: { $sum: 1 },
totalAmount: { $sum: "$amount" },
products: { $push: "$product" }, // 收集每组的所有产品
},
},
]);
// 全局聚合(不分组)
db.orders.aggregate([
{
$group: {
_id: null, // null表示不分组
totalOrders: { $sum: 1 },
totalAmount: { $sum: "$amount" },
},
},
]);$project - 投影字段
javascript
// $project:选择、重命名、计算字段
// 选择字段
db.orders.aggregate([
{
$project: {
orderId: 1, // 包含orderId
product: 1, // 包含product
amount: 1, // 包含amount
_id: 0, // 排除_id
},
},
]);
// 重命名字段
db.orders.aggregate([
{
$project: {
orderNo: "$orderId", // 重命名
productName: "$product",
price: "$amount",
},
},
]);
// 计算新字段
db.orders.aggregate([
{
$project: {
orderId: 1,
amount: 1,
discountedAmount: { $multiply: ["$amount", 0.9] }, // 打9折
},
},
]);$sort - 排序
javascript
// $sort:对文档进行排序
// 升序排序
db.orders.aggregate([{ $sort: { amount: 1 } }]);
// 降序排序
db.orders.aggregate([{ $sort: { amount: -1 } }]);
// 多字段排序
db.orders.aggregate([{ $sort: { status: 1, amount: -1 } }]);$limit 和 $skip
javascript
// $limit:限制返回文档数量
db.orders.aggregate([{ $limit: 3 }]);
// $skip:跳过指定数量的文档
db.orders.aggregate([{ $skip: 2 }]);
// 分页查询
var page = 2;
var pageSize = 2;
db.orders.aggregate([{ $skip: (page - 1) * pageSize }, { $limit: pageSize }]);$unwind - 展开数组
javascript
// $unwind:将数组字段展开为多个文档
// 准备测试数据
db.products.insertMany([
{ name: "iPhone", tags: ["电子", "手机", "苹果"], price: 6999 },
{ name: "MacBook", tags: ["电子", "电脑", "苹果"], price: 12999 },
]);
// 展开数组
db.products.aggregate([{ $unwind: "$tags" }]);
// 输出:每个标签生成一个文档
// 展开后分组统计
db.products.aggregate([
{ $unwind: "$tags" },
{
$group: {
_id: "$tags",
count: { $sum: 1 },
avgPrice: { $avg: "$price" },
},
},
]);$lookup - 关联查询
javascript
// $lookup:实现类似SQL的LEFT JOIN操作
// 准备测试数据
db.users.insertMany([
{ _id: 1, name: "张三" },
{ _id: 2, name: "李四" },
]);
// 基本关联查询
db.orders.aggregate([
{
$lookup: {
from: "users", // 关联的集合
localField: "userId", // 本地字段
foreignField: "_id", // 外键字段
as: "userInfo", // 输出字段名
},
},
]);
// 展开关联结果
db.orders.aggregate([
{
$lookup: {
from: "users",
localField: "userId",
foreignField: "_id",
as: "userInfo",
},
},
{ $unwind: "$userInfo" },
{
$project: {
orderId: 1,
product: 1,
userName: "$userInfo.name",
},
},
]);聚合表达式操作符
算术操作符
javascript
// 算术操作符
db.orders.aggregate([
{
$project: {
amount: 1,
addResult: { $add: ["$amount", 100] }, // 加法
subResult: { $subtract: ["$amount", 100] }, // 减法
mulResult: { $multiply: ["$amount", 0.9] }, // 乘法
divResult: { $divide: ["$amount", 2] }, // 除法
},
},
]);字符串操作符
javascript
// 字符串操作符
db.users.aggregate([
{
$project: {
name: 1,
upperName: { $toUpper: "$name" }, // 转大写
lowerName: { $toLower: "$name" }, // 转小写
nameLength: { $strLenCP: "$name" }, // 字符串长度
fullName: { $concat: ["$name", " - 用户"] }, // 连接字符串
},
},
]);日期操作符
javascript
// 日期操作符
db.orders.aggregate([
{
$project: {
createdAt: 1,
year: { $year: "$createdAt" },
month: { $month: "$createdAt" },
day: { $dayOfMonth: "$createdAt" },
formattedDate: {
$dateToString: {
format: "%Y-%m-%d",
date: "$createdAt",
},
},
},
},
]);条件操作符
javascript
// 条件操作符
db.orders.aggregate([
{
$project: {
amount: 1,
level: {
$cond: {
if: { $gte: ["$amount", 10000] },
then: "高价值",
else: "普通",
},
},
},
},
]);聚合实战案例
销售报表统计
javascript
// 按日期统计销售额
db.orders.aggregate([
{
$match: {
status: "completed",
createdAt: {
$gte: new Date("2024-01-01"),
$lt: new Date("2024-02-01"),
},
},
},
{
$group: {
_id: {
year: { $year: "$createdAt" },
month: { $month: "$createdAt" },
day: { $dayOfMonth: "$createdAt" },
},
totalAmount: { $sum: "$amount" },
orderCount: { $sum: 1 },
},
},
{ $sort: { "_id.year": 1, "_id.month": 1, "_id.day": 1 } },
]);用户消费分析
javascript
// 用户消费分析
db.orders.aggregate([
{ $match: { status: "completed" } },
{
$group: {
_id: "$userId",
totalSpent: { $sum: "$amount" },
orderCount: { $sum: 1 },
avgOrderValue: { $avg: "$amount" },
},
},
{
$lookup: {
from: "users",
localField: "_id",
foreignField: "_id",
as: "userInfo",
},
},
{ $unwind: "$userInfo" },
{
$project: {
userName: "$userInfo.name",
totalSpent: 1,
orderCount: 1,
avgOrderValue: 1,
},
},
{ $sort: { totalSpent: -1 } },
]);本章小结
本章介绍了MongoDB聚合框架的相关知识:
- 聚合管道概述:理解聚合管道的工作原理
- 常用阶段:掌握$match、$group、$project、$sort等常用阶段
- 关联查询:学会使用$lookup实现表关联
- 数组处理:掌握$unwind展开数组的用法
- 表达式操作符:学会使用算术、字符串、日期、条件等操作符
- 实战案例:通过销售报表和用户分析案例掌握聚合的应用
下一章,我们将学习事务,了解MongoDB的多文档事务支持。
