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

如何优化这段MongoDB聚合查询以提升响应速度?

MongoDB聚合查询优化方案

针对你的聚合查询性能问题,以下是具体的优化思路和修改后的代码:

1. 前置过滤,缩小数据集范围

原查询先拉取一周内所有search_history数据再关联过滤,会处理大量无关数据。建议先从positive表筛选出目标tag对应的search_id,再用这些search_id过滤search_history,从源头减少数据量。

2. 优化聚合管道顺序与Lookup逻辑

将match过滤提前,调整Lookup的方式,避免不必要的数据展开和合并:

  • 把positive表的过滤逻辑前置,直接获取目标search_id列表
  • 简化用户信息Lookup的字段提取,避免后续的unwind和replaceRoot操作

3. 聚合阶段完成去重,避免内存过滤

原代码在toArray()后用JS数组过滤去重,数据量大时性能极差。改用聚合的$group按url分组,保留最新的记录。

修改后的完整代码

async getHistoryByTag(context: StandardContext) {
  const { mongo } = context.state;
  const tag = context.params.tag.replace(/-+/g, " ");
  const today = new Date();
  const prevSunday = getOneWeekAgo(today);

  console.log("today: ", today, "prev sunday: ", prevSunday);

  // 先获取目标tag对应的search_id,缩小后续查询范围
  const targetSearchIds = await mongo.collection("positive")
    .find({ word: tag }, { search_id: 1, _id: 0 })
    .map(doc => doc.search_id)
    .toArray();

  if (targetSearchIds.length === 0) {
    context.response.body = [];
    return;
  }

  const all = await mongo
    .collection("search_history")
    .aggregate<SearchHistoryModel & { username: string; word: string }>([
      // 同时过滤时间和search_id,大幅减少初始数据集
      {
        $match: {
          created_at: { $gt: prevSunday.toISOString() },
          search_id: { $in: targetSearchIds }
        },
      },
      // Lookup用户信息,直接提取username
      {
        $lookup: {
          from: "users",
          localField: "user_id",
          foreignField: "id",
          as: "username",
          pipeline: [
            { $project: { username: 1, _id: 0 } },
            { $replaceRoot: { newRoot: "$username" } }
          ],
        },
      },
      // 提取username字段,避免unwind操作
      { $addFields: { username: { $first: "$username" } } },
      // 按url分组,保留最新的记录
      {
        $group: {
          _id: "$url",
          search_id: { $first: "$search_id" },
          user_id: { $first: "$user_id" },
          created_at: { $max: "$created_at" },
          username: { $first: "$username" },
          word: { $first: tag }
        }
      },
      // 整理字段结构
      {
        $project: {
          _id: 0,
          url: "$_id",
          search_id: 1,
          user_id: 1,
          created_at: 1,
          username: 1,
          word: 1
        }
      },
      // 按创建时间倒序
      {
        $sort: { created_at: -1 }
      }
    ])
    .toArray();

  context.response.body = all;
}

4. 索引优化建议

确保以下索引已创建,让每个过滤阶段都能命中索引:

  • search_history集合:{ created_at: 1, search_id: 1 } 复合索引(同时满足时间和search_id过滤)
  • positive集合:{ word: 1, search_id: 1 } 复合索引(快速匹配tag并返回search_id)
  • users集合:{ id: 1, username: 1 } 复合索引(加速Lookup时的查询和投影)

其他优化点

  • 避免在聚合中使用$unwind+$replaceRoot这类会膨胀数据的操作,尽量用$addFields或$project直接提取字段
  • 如果search_history数据量极大,可以考虑按时间分片,进一步缩小查询范围

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 20:00:58