MongoDB 4.2:按状态分组统计时长的聚合查询实现
MongoDB 4.2 按考勤状态统计总时长的聚合查询
你需要对MongoDB 4.2中的考勤文档,按history数组里的status字段分组,计算每个状态下用户花费的总时长。你的现有查询已经完成了$match和$unwind,但后续分组逻辑不对,以下是正确的实现方案:
核心思路
- 过滤目标日期的考勤文档
- 展开
history数组,让每条状态记录成为独立文档 - 计算单条状态记录的时长(注意处理无
endTime的当前状态) - 按
status分组累加总时长 - (可选)格式化时长输出,提升可读性
完整聚合查询
db.collectionname.aggregate([ // 过滤指定日期的考勤记录 { "$match": { "attendanceDate": "07/25/2022" } }, // 展开history数组,每条状态记录单独成为文档 { "$unwind": "$history" }, // 计算单条状态的时长:无endTime的用currentStatusTime作为结束时间,结果为毫秒数 { "$addFields": { "duration": { "$subtract": [ { "$ifNull": ["$history.endTime", "$currentStatusTime"] }, "$history.startTime" ] } } }, // 按status分组,累加总时长,同时保留用户名信息 { "$group": { "_id": "$history.status", "totalDurationMs": { "$sum": "$duration" }, "displayName": { "$first": "$displayName" }, "attendanceDate": { "$first": "$attendanceDate" } } }, // 可选:将毫秒数转换为小时、分钟格式,提升可读性 { "$project": { "_id": 0, "status": "$_id", "displayName": 1, "attendanceDate": 1, "totalDuration": { "$concat": [ { "$toString": { "$floor": { "$divide": ["$totalDurationMs", 3600000] } } }, "小时 ", { "$toString": { "$floor": { "$divide": [{ "$mod": ["$totalDurationMs", 3600000] }, 60000] } } }, "分钟" ] }, "totalDurationMs": 1 // 保留原始毫秒数,方便后续计算 } } ]);
关键细节说明
- 处理无
endTime的状态:你的文档中最后一条Training状态没有endTime,这是当前未结束的状态,所以用$ifNull判断,当endTime不存在时使用currentStatusTime作为结束时间来计算时长。 - 时长计算:MongoDB中日期类型相减会得到毫秒数,通过
$subtract计算单条记录的时长,再用$sum累加总时长。 - 分组逻辑:
$group的_id指定为$history.status,这样就能按状态分组,同时用$first保留用户姓名、考勤日期等非分组字段的信息。 - 可选格式化:最后一步
$project将毫秒数转换为“X小时Y分钟”的友好格式,你可以根据需求调整输出样式。
内容的提问来源于stack exchange,提问作者San G
相关产品推荐
相关产品推荐

