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

MongoDB 4.4中如何在$group操作中取每组前5条数据求和?

问题描述

生产服务器使用MongoDB 4.4,现有如下聚合查询可正常运行:

db.step_tournaments_results.aggregate([
     { "$match": { "tournament_id": "6377f2f96174982ef89c48d2" } },
     { "$sort": { "total_points": -1, "time_spent": 1 } },
     {
         $group: {
             _id: "$club_name", 
             'total_points': { $sum: "$total_points"}, 
             'time_spent': { $sum: "$time_spent"}
         },
     },
])

当前$group操作会对每组所有数据的total_points和time_spent进行求和,但需求是仅取每组前5条数据计算总和,请问如何实现?

实现方案

可以通过两种方式实现需求,以下是具体方案:

方案一:分组存数+切片求和

先按俱乐部收集排序后的所有数据,截取前5条后再展开求和,代码如下:

db.step_tournaments_results.aggregate([
    // 筛选目标赛事数据
    { "$match": { "tournament_id": "6377f2f96174982ef89c48d2" } },
    // 按积分降序、耗时升序排序
    { "$sort": { "total_points": -1, "time_spent": 1 } },
    // 按俱乐部分组,收集所有排序后文档到数组
    {
        "$group": {
            "_id": "$club_name",
            "top5_docs": { "$push": "$$ROOT" },
        }
    },
    // 截取数组前5条,保留每组排名靠前的数据
    {
        "$addFields": {
            "top5_docs": { "$slice": ["$top5_docs", 5] }
        }
    },
    // 将数组拆分为单个文档,方便后续求和
    { "$unwind": "$top5_docs" },
    // 重新分组,计算前5条数据的总和
    {
        "$group": {
            "_id": "$_id",
            "total_points": { "$sum": "$top5_docs.total_points" },
            "time_spent": { "$sum": "$top5_docs.time_spent" }
        }
    }
])

关键步骤说明

  • $push: "$$ROOT":把每个俱乐部的所有排序后文档存入数组
  • $slice:直接截取数组前5个元素,过滤掉每组排名靠后的数据
  • $unwind:将数组拆分为单条文档,让后续的$group能正确求和

方案二:窗口函数过滤求和

利用MongoDB 4.2+支持的窗口函数给每条数据标记组内排名,再过滤前5条后求和,代码更简洁:

db.step_tournaments_results.aggregate([
    { "$match": { "tournament_id": "6377f2f96174982ef89c48d2" } },
    // 按俱乐部分组,组内排序并添加排名字段
    {
        "$setWindowFields": {
            "partitionBy": "$club_name",
            "sortBy": { "total_points": -1, "time_spent": 1 },
            "output": {
                "rank": { "$rank": {} }
            }
        }
    },
    // 只保留每组排名前5的数据
    { "$match": { "rank": { "$lte": 5 } } },
    // 分组计算总和
    {
        "$group": {
            "_id": "$club_name",
            "total_points": { "$sum": "$total_points" },
            "time_spent": { "$sum": "$time_spent" }
        }
    }
])

关键步骤说明

  • $setWindowFields:按club_name划分窗口,在窗口内按规则排序后给每条数据分配排名
  • $match过滤:直接筛选出每组排名≤5的数据,避免了收集全组数据的开销
  • 最终$group直接对过滤后的数据求和,逻辑更直观

两种方案都能满足需求:如果每组数据量极大,窗口函数方案性能更优;数据量适中时,两种方案差异不大。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 07:45:48