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

如何修正Mongoose聚合查询,按clubType和swingSize统计距离的总和与平均值

修正MongoDB聚合查询:按clubType和swingSize分组统计距离

问题背景

现有MongoDB文档结构如下,range为子对象数组:

{
  "_id" : ObjectId("62f60ba0ed0f1a1a0v"),
  "userId" : "1431",
  "range" : [
     {
        "index" : 0,
        "clubType" : "driver", 
        "swingSize" : "full", 
        "distance" : 200,
        "createdAt" : "2022-08-12T08:13:20.435+00:00"
     },
     {
        "index" : 0,
        "clubType" : "driver", 
        "swingSize" : "full", 
        "distance" : 150,
        "createdAt" : "2022-08-12T08:13:20.435+00:00"
     },
     {
        "index" : 0,
        "clubType" : "wood", 
        "swingSize" : "full", 
        "distance" : 180,
        "createdAt" : "2022-08-12T08:13:20.435+00:00"
     }     
  ]
}

需求是按clubType和swingSize分组,统计distance的总和与平均值,但原聚合查询返回字段重复、总和计算错误的结果。

原查询的问题分析

原查询代码:

result = await ClubRangeResultSchema.aggregate([
   {
      $match : {
        userId : "1431",
        range  : {
           $elemMatch : {
              $and : [
                 {
                    createdAt : { $gte : lastDate }
                 },
                 {
                    createdAt : { $lte : lastDate }
                 }
              ]
           }
        }
      }
   },
   {
      $group : {
         '_id' : {
            'clubName' : '$range.clubName',
            'swingSize' : '$range.swingSize'
         },
         'totalDistance' : { $sum : { $sum : '$range.distance' }}
      }
   }
]);

存在的问题:

  • 未展开range数组:直接使用数组字段作为分组键,会将整个数组的值作为分组依据,导致重复结果
  • 字段名错误:文档中是clubType,查询里误写为clubName
  • 总和计算逻辑错误:嵌套$sum会先计算每个文档内所有distance的总和,再将该值重复累加,导致结果远大于实际值
  • $match条件冗余:$and可以省略,直接合并createdAt的范围条件;且原$elemMatch仅过滤包含符合条件元素的文档,展开后需要确保每个元素都满足时间条件

修正后的聚合查询

result = await ClubRangeResultSchema.aggregate([
   // 第一步:过滤符合userId和包含目标时间元素的文档
   {
      $match: {
         userId: "1431",
         "range.createdAt": { $gte: lastDate, $lte: lastDate }
      }
   },
   // 第二步:展开range数组,将每个数组元素转为独立文档
   { $unwind: "$range" },
   // 第三步:过滤展开后符合时间条件的元素(确保只统计目标时间范围内的数据)
   {
      $match: {
         "range.createdAt": { $gte: lastDate, $lte: lastDate }
      }
   },
   // 第四步:按clubType和swingSize分组,计算总和与平均值
   {
      $group: {
         _id: {
            clubType: "$range.clubType",
            swingSize: "$range.swingSize"
         },
         totalDistance: { $sum: "$range.distance" },
         avgDistance: { $avg: "$range.distance" },
         count: { $sum: 1 } // 可选:统计该分组下的记录数
      }
   },
   // 可选:重命名字段,让结果更直观
   {
      $project: {
         _id: 0,
         clubType: "$_id.clubType",
         swingSize: "$_id.swingSize",
         totalDistance: 1,
         avgDistance: 1,
         count: 1
      }
   }
]);

关键步骤说明

  • $unwind: 将range数组拆分成单个文档,这样才能基于数组内的clubType和swingSize进行正确分组
  • 两次$match: 第一次过滤掉完全不包含目标时间元素的文档,减少后续数据处理量;第二次确保展开后的每个元素都符合时间条件,避免统计无关数据
  • $group逻辑:直接使用$range.distance进行$sum和$avg计算,得到正确的总和与平均值
  • 可选$project: 将_id中的字段提取出来,让返回结果的结构更清晰

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 13:45:33