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

MongoDB 如何统计各分组最高版本对应的文档数量

MongoDB 分组取字段最大值后多维度计数实现

问题场景

需求为按指定一级字段分组,找到每组内另一字段的最大值,过滤掉组内低于该最大值的历史文档后,再按多维度分组统计文档总数。
本次测试数据集如下:

[
  { "id": 1, "scanner": "bandit", "severity": "low", "version": 1 },
  { "id": 2, "scanner": "bandit", "severity": "low", "version": 1 },
  { "id": 3, "scanner": "bandit", "severity": "medium", "version": 1 },
  { "id": 4, "scanner": "bandit", "severity": "medium", "version": 2 },
  { "id": 5, "scanner": "bandit", "severity": "high", "version": 2 },
  { "id": 6, "scanner": "semgrep", "severity": "critical", "version": 2 },
  { "id": 7, "scanner": "semgrep", "severity": "critical", "version": 3 },
  { "id": 8, "scanner": "semgrep", "severity": "info", "version": 4 },
  { "id": 9, "scanner": "semgrep", "severity": "info", "version": 4 }
]

其中bandit对应的最高version为2,semgrep对应的最高version为4,需要过滤所有低于最高version的文档,按scanner、severity维度分组计数,预期返回结果:

[
    { "scanner": "bandit", "severity": "medium", "count": 1, "version": 2 },
    { "scanner": "bandit", "severity": "high", "count": 1, "version": 2 },
    { "scanner": "semgrep", "severity": "info", "count": 2, "version": 4 }
]

兼容所有MongoDB版本的通用实现

通过聚合管道自关联实现,不需要依赖高版本特性,可直接运行,注意将代码中的你的集合名替换为实际集合名称:

db.你的集合名.aggregate([
  // 按一级分组字段scanner分组,计算每个scanner对应的最高version
  {
    $group: {
      _id: "$scanner",
      maxVersion: { $max: "$version" }
    }
  },
  // 关联原集合,筛选出每个scanner下version等于最高值的文档
  {
    $lookup: {
      from: "你的集合名",
      let: { currentScanner: "$_id", topVersion: "$maxVersion" },
      pipeline: [
        {
          $match: {
            $expr: {
              $and: [
                { $eq: ["$scanner", "$$currentScanner"] },
                { $eq: ["$version", "$$topVersion"] }
              ]
            }
          }
        }
      ],
      as: "validDocs"
    }
  },
  // 展开筛选后的有效文档
  { $unwind: "$validDocs" },
  // 提取有效文档为根节点,简化后续分组逻辑
  { $replaceRoot: { newRoot: "$validDocs" } },
  // 按scanner、severity维度分组统计数量
  {
    $group: {
      _id: {
        scanner: "$scanner",
        severity: "$severity"
      },
      count: { $sum: 1 },
      version: { $first: "$version" }
    }
  },
  // 调整输出字段结构,移除自动生成的_id字段
  {
    $project: {
      _id: 0,
      scanner: "$_id.scanner",
      severity: "$_id.severity",
      count: 1,
      version: 1
    }
  }
])

MongoDB 5.0+ 简化实现

如果使用的是5.0及以上版本,支持窗口函数,可以省去自关联步骤,执行效率更高:

db.你的集合名.aggregate([
  // 按scanner分区,计算每个分区内的最大version,附加到每条文档上
  {
    $setWindowFields: {
      partitionBy: "$scanner",
      sortBy: { version: -1 },
      output: {
        groupMaxVersion: { $max: "$version" }
      }
    }
  },
  // 过滤掉version不等于所属分组最大值的历史文档
  { $match: { $expr: { $eq: ["$version", "$groupMaxVersion"] } } },
  // 按目标维度分组计数
  {
    $group: {
      _id: { scanner: "$scanner", severity: "$severity" },
      count: { $sum: 1 },
      version: { $first: "$version" }
    }
  },
  // 调整输出结构
  {
    $project: {
      _id: 0,
      scanner: "$_id.scanner",
      severity: "$_id.severity",
      count: 1,
      version: 1
    }
  }
])

逻辑说明

  • 核心思路是先拿到每个一级分组的阈值(最高version),再基于阈值过滤有效数据,最后做维度统计,避免直接分组时把低版本历史数据计入结果。
  • 两种写法执行后返回的结果和预期完全一致,可根据实际MongoDB版本选择使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 06:51:20