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

如何通过Mongoose在MongoDB中同时查询文档并统计总数

解决方案:用MongoDB聚合管道合并分页查询与总数统计

你当前的问题在于两次重复执行相同过滤逻辑的数据库查询,数据量较大时会产生不必要的性能损耗。可以通过MongoDB的聚合管道,借助$facet阶段在单次查询中同时获取分页数据和符合条件的总文档数,彻底避免重复计算。

优化后的完整代码

const getAllBooks = asyncHandler(async (req, res) => {
  // 处理查询参数,修正类型与格式问题
  const page = parseInt(req.query.page) || 1;
  const limit = 5;
  const skip = (page - 1) * limit;
  const allGenres = ["action", "drama", "sci-fi", "romance", "comedy"];
  const reqGenre = req.query.genre === "" ? allGenres : req.query.genre.split(",").map(g => g.trim());
  const search = req.query.search || "";
  
  // 构建搜索过滤条件
  const searchBy = req.query.searchBy === "title" 
    ? { title: { $regex: search, $options: "i" } } 
    : { author: { $regex: search, $options: "i" } };
  
  // 构建排序规则
  const sortDir = req.query.sort === "asc" ? 1 : -1;
  const sortBy = req.query.sortBy === "author" 
    ? { author: sortDir } 
    : { title: sortDir };

  // 单次聚合查询同时获取分页数据与总数
  const aggregateResult = await Book.aggregate([
    // 应用搜索过滤
    { $match: searchBy },
    // 过滤指定分类的书籍
    { $match: { genre: { $in: reqGenre } } },
    // 执行排序
    { $sort: sortBy },
    // 用$facet拆分两个处理分支
    {
      $facet: {
        // 分支1:获取当前页的书籍数据
        books: [
          { $skip: skip },
          { $limit: limit },
          { $project: { createdAt: 0, updatedAt: 0, __v: 0 } } // 排除冗余字段
        ],
        // 分支2:统计符合条件的总文档数
        totalCount: [
          { $count: "value" }
        ]
      }
    },
    // 提取总数的具体数值(从数组结构转为单个数字)
    {
      $addFields: {
        totalCount: { $arrayElemAt: ["$totalCount.value", 0] }
      }
    }
  ]);

  // 解析聚合结果
  const { books, totalCount } = aggregateResult[0];
  const totalPages = Math.ceil(totalCount / limit); // 修正总页数计算逻辑

  res.status(200).json({ books, totalCount, totalPages });
});

核心优化说明

  • $facet阶段:允许在同一聚合管道内同时执行多个独立操作分支,这里一个分支处理分页(skip+limit),另一个分支用$count统计总数,只需要执行一次过滤、排序逻辑,大幅减少数据库IO开销。
  • 参数处理优化:将page转为整数避免字符串计算错误;给reqGenre添加trim()处理,防止传入带空格的无效分类值;简化排序条件的判断逻辑,提升代码可读性。
  • 总页数修正:原代码用documents / limit会得到小数,改用Math.ceil()得到符合实际分页逻辑的总页数(比如11条数据、每页5条时,总页数应为3)。

额外性能建议

如果模糊搜索是高频场景,建议给title和author字段创建文本索引,替代正则表达式匹配,进一步提升搜索性能:

// 在你的LibrarySchema中添加文本索引
LibrarySchema.index({ title: "text", author: "text" });

之后可将搜索条件改为文本搜索格式:

const searchBy = search ? { $text: { $search: search } } : {};

内容的提问来源于stack exchange,提问作者Roshan Shetty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 05:55:29