You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何优化MongoDB聚合查询?需将慢查询提速至300ms以内

MongoDB聚合查询性能优化方案

问题描述

我拥有users和attendances两个集合,当前通过MongoDB聚合查询中的$lookup关联两个集合获取用户考勤数据,随后使用$filter筛选符合条件的考勤数据,再通过$reduce精简数据结构。但该查询在本地(150mbps网络环境)执行时,仅查询10条用户数据(总用户数为1000)却耗时1分钟,希望将查询耗时优化至300ms以内。

当前使用的聚合查询代码:

const attendanceData = await User.aggregate([
    {
      $match: {
        lastLocationId: Mongoose.Types.ObjectId(typeId),
        isActive: true,
      },
    },
    {
      $project: {
        _id: 1,
        workerId: 1,
        workerFirstName: 1,
        workerSurname: 1,
      },
    },
    {
      $lookup: {
        from: "attendances",
        localField: "_id",
        foreignField: "employeeId",
        as: "attendances",
      },
    },
    {
      $set: {
        attendances: {
          $filter: {
            input: "$attendances",
            cond: {
              $and: [
                {
                  $gte: ["$$this.Date", new Date(fromDate)],
                },
                {
                  $lte: ["$$this.Date", new Date(toDate)],
                },
                {
                  $eq: ["$$this.createdAs", dataType],
                },
                {
                  $eq: ["$$this.status", true],
                },
                {
                  $eq: ["$$this.workerType", workerType],
                },
              ],
            },
          },
        },
      },
    },
    {
      $set: {
        attendances: {
          $reduce: {
            input: "$attendances",
            initialValue: [],
            in: {
              $concatArrays: [
                "$$value",
                [
                  {
                    _id: "$$this._id",
                    createdAs: "$$this.createdAs",
                    Date: "$$this.Date",
                  },
                ],
              ],
            },
          },
        },
      },
    },
    { $skip: 0 },
    { $limit: 10 },
  ]);

优化方案

1. 添加针对性复合索引

索引是提升MongoDB查询性能的核心,针对当前查询的过滤和关联逻辑,创建以下索引:

  • users集合索引:加速$match阶段的用户筛选
    db.users.createIndex({ lastLocationId: 1, isActive: 1 })
    
  • attendances集合索引:加速关联查询和考勤数据筛选
    db.attendances.createIndex({ employeeId: 1, Date: 1, createdAs: 1, status: 1, workerType: 1 })
    

2. 重构聚合流程,减少无效数据处理

原查询的问题在于:先筛选所有符合条件的用户,再全量关联他们的考勤数据,最后才取10条结果,导致大量无效数据被加载和处理。优化思路是提前限制用户数量,并在$lookup阶段直接完成考勤数据的筛选和字段精简:

优化后的聚合代码:

const attendanceData = await User.aggregate([
  // 第一步:筛选目标用户
  {
    $match: {
      lastLocationId: Mongoose.Types.ObjectId(typeId),
      isActive: true,
    },
  },
  // 第二步:先取需要的10条用户数据,减少后续关联量
  { $skip: 0 },
  { $limit: 10 },
  // 第三步:仅保留需要的用户字段
  {
    $project: {
      _id: 1,
      workerId: 1,
      workerFirstName: 1,
      workerSurname: 1,
    },
  },
  // 第四步:使用带pipeline的$lookup,在关联阶段完成考勤数据的筛选和字段投影
  {
    $lookup: {
      from: "attendances",
      let: { userId: "$_id" },
      pipeline: [
        {
          $match: {
            $expr: {
              $and: [
                { $eq: ["$employeeId", "$$userId"] },
                { $gte: ["$Date", new Date(fromDate)] },
                { $lte: ["$Date", new Date(toDate)] },
                { $eq: ["$createdAs", dataType] },
                { $eq: ["$status", true] },
                { $eq: ["$workerType", workerType] },
              ],
            },
          },
        },
        // 直接投影需要的字段,替代原有的$reduce逻辑
        {
          $project: {
            _id: 1,
            createdAs: 1,
            Date: 1,
          },
        },
      ],
      as: "attendances",
    },
  },
]);

3. 优化点说明

  • 提前$skip/$limit:将分页操作移到$match之后,只对10个用户进行关联查询,避免处理大量无关用户的考勤数据。
  • 带pipeline的$lookup:在关联考勤集合时直接筛选符合条件的记录,并仅保留需要的字段,无需先加载全量数据再过滤,大幅减少数据传输和内存占用。
  • 移除冗余的$reduce:原$reduce逻辑仅用于重组数组结构,在$lookup的pipeline中用$project即可直接实现,简化计算流程。

内容的提问来源于stack exchange,提问作者chirag prajapati

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 22:30:54