MongoDB优化查询:按资产类统计最新运行日期的测试用例结果
MongoDB聚合查询优化:按资产类统计最新运行日期的测试用例结果
需求说明
获取每个asset_class(资产类)对应**最新run_date(运行日期)**的failed(失败)与passed(通过)测试用例总数。
集合示例文档
{"failed": 2,"passed": 4,"asset_class": "A","run_date": ISODate("2024-08-28")}, {"failed": 1,"passed": 3,"asset_class": "A","run_date": ISODate("2024-08-29")}, {"failed": 5,"passed": 1,"asset_class": "A","run_date": ISODate("2024-08-29")}, {"failed": 2,"passed": 4,"asset_class": "B","run_date": ISODate("2024-08-22")}, {"failed": 5,"passed": 8,"asset_class": "B","run_date": ISODate("2024-08-22")}, {"failed": 5,"passed": 1,"asset_class": "B","run_date": ISODate("2024-08-26")}
期望输出
{"failed": 6,"passed": 4,"asset_class": "A","run_date": ISODate("2024-08-29")}, {"failed": 5,"passed": 1,"asset_class": "B","run_date": ISODate("2024-08-26")}
现有查询及问题
已创建复合索引{asset_class:1, run_date:1},现有聚合查询可运行,但存在字段冗余和性能优化空间:
db.getCollection("test").aggregate([ { $group: { _id: {asset_class: "$asset_class", run_date: "$run_date"}, passed: {$sum: "$passed"}, failed: {$sum: "$failed"}, run_date: { $push: "$run_date" }, asset_class: { $push: "$asset_class" } } }, {$sort: {asset_class:1, run_date:1}}, {$group: { _id: "$_id.asset_class", "result": {$last: "$$ROOT"} }}, {$replaceRoot: {newRoot: "$result"}} ])
更高效的优化写法
方案1:利用索引先排序再分组(性能最优)
直接复用已创建的复合索引,通过排序+分组的组合减少冗余操作:
db.getCollection("test").aggregate([ // 利用复合索引直接排序,避免内存排序开销 { $sort: { asset_class: 1, run_date: -1 } }, // 按资产类分组,直接取最新日期并求和统计 { $group: { _id: "$asset_class", run_date: { $first: "$run_date" }, passed: { $sum: "$passed" }, failed: { $sum: "$failed" }, asset_class: { $first: "$asset_class" } } }, // 调整输出格式,移除默认的_id字段 { $project: { _id: 0, asset_class: 1, run_date: 1, passed: 1, failed: 1 } } ])
方案2:使用窗口函数(MongoDB 5.0+,可读性更高)
如果使用MongoDB 5.0及以上版本,可通过窗口函数直观筛选最新日期的分组:
db.getCollection("test").aggregate([ // 按资产类分区,计算每个分区内的最新运行日期 { $setWindowFields: { partitionBy: "$asset_class", sortBy: { run_date: -1 }, output: { latest_run_date: { $max: "$run_date", window: { documents: ["current", "unbounded"] } } } } }, // 只保留属于最新运行日期的文档 { $match: { $expr: { $eq: ["$run_date", "$latest_run_date"] } } }, // 按资产类+日期分组统计总数 { $group: { _id: { asset_class: "$asset_class", run_date: "$run_date" }, passed: { $sum: "$passed" }, failed: { $sum: "$failed" } } }, // 调整输出格式 { $project: { _id: 0, asset_class: "$_id.asset_class", run_date: "$_id.run_date", passed: 1, failed: 1 } } ])
性能说明
- 方案1完全利用已创建的复合索引(可反向使用
{asset_class:1, run_date:1}索引实现降序排序),排序阶段无需占用内存,分组逻辑简洁,是性能最优的选择。 - 方案2适合需要复杂窗口计算的场景,代码可读性更强,但多了一次过滤阶段,性能略逊于方案1。
内容的提问来源于stack exchange,提问作者Sachin
相关产品推荐
相关产品推荐

