如何修正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
相关产品推荐
相关产品推荐

