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

Mongoose操作MongoDB如何单次聚合返回计数与分页文档

问题根因

当前触发MongoDB索引扫描占比告警的直接原因是重复查询:countDocuments 统计总数、aggregate 拉取分页数据两个请求使用了完全一致的匹配条件,会触发两次相同范围的索引扫描,相当于同样的扫描工作量翻倍执行。随着视频集合数据量涨到当前的2-3倍,除了告警之外,还会快速占用数据库CPU、内存资源,导致查询响应时间陡增。

你之前调研的$count确实只能单独返回计数,无法同时返回文档,但聚合框架提供的$facet阶段可以完美解决这个问题:它支持在同一次聚合请求中并行运行多个独立子管道,且所有子管道共享前置$match阶段的索引扫描结果,不会重复扫描数据。

优化实现

直接把两次查询合并为单次聚合请求,代码如下:

const [queryResult] = await videoModel.aggregate([
  {
    // 匹配条件只写一次,仅执行一次索引扫描
    $match: {
      ...sortBy,
      $or: [
        {
          videoTitle: {
            $regex: keyword,
            $options: "i",
          },
        },
      ],
    },
  },
  {
    $facet: {
      // 子管道1:统计匹配总数
      totalCount: [
        { $count: "count" }
      ],
      // 子管道2:执行排序、分页,返回当前页数据
      videoList: [
        { $sort: { createdAt: -1 } },
        { $skip: pageSize * (page - 1) },
        { $limit: pageSize }
      ]
    }
  }
]);

// 解析结果,无匹配数据时总数默认为0
const count = queryResult.totalCount[0]?.count || 0;
const videos = queryResult.videoList;
额外优化建议
  • 目前使用的无左锚点、大小写不敏感的正则查询无法命中普通B树索引,数据量继续上涨后会出现全集合扫描问题,建议给videoTitle字段创建文本索引,改用$text操作符做检索,可大幅降低索引扫描量。
  • 如果存在页码较大的深分页场景,$skip会随页码增大线性降低性能,可替换为基于上一页最后一条文档_id/createdAt的游标分页,查询性能不会随页码增长衰减。

内容的提问来源于stack exchange,提问作者Austin Howard

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 15:39:16