Skip to content

聚合框架

聚合框架(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聚合框架的相关知识:

  1. 聚合管道概述:理解聚合管道的工作原理
  2. 常用阶段:掌握$match、$group、$project、$sort等常用阶段
  3. 关联查询:学会使用$lookup实现表关联
  4. 数组处理:掌握$unwind展开数组的用法
  5. 表达式操作符:学会使用算术、字符串、日期、条件等操作符
  6. 实战案例:通过销售报表和用户分析案例掌握聚合的应用

下一章,我们将学习事务,了解MongoDB的多文档事务支持。