如何通过MongoDB聚合查询员工月度缺勤日期并生成考勤表
月度考勤统计聚合查询方案
针对你提到的场景(员工打卡生成考勤文档,未打卡无记录),可以通过MongoDB聚合管道生成指定员工的月度考勤表,完整统计打卡日期与缺勤日期。
核心思路
- 筛选指定员工、指定月份的所有考勤记录;
- 生成目标月份的完整日期列表;
- 将考勤记录与日期列表做左关联,匹配到的为打卡日期,未匹配的即为缺勤日期;
- 统一整理结果,标记每个日期的考勤状态。
聚合查询代码(Mongoose 实现)
假设目标员工ID为employeeId,统计月份为targetMonth(格式如2022-12):
const ObjectId = require('mongoose').Types.ObjectId; const targetMonth = "2022-12"; // 示例统计月份 const employeeId = new ObjectId("638869649c443988469c151f"); // 示例员工ID // 解析月份的起始/结束UTC时间 const [year, month] = targetMonth.split('-').map(Number); const startOfMonth = new Date(Date.UTC(year, month - 1, 1)); const endOfMonth = new Date(Date.UTC(year, month, 1)); Attendance.aggregate([ // 阶段1:筛选指定员工+月份的考勤记录 { $match: { user: employeeId, date: { $gte: startOfMonth, $lt: endOfMonth } } }, // 阶段2:提取日期(仅保留年月日)与关键考勤字段 { $project: { attendanceDate: { $dateToString: { format: "%Y-%m-%d", date: "$date", timezone: "Asia/Karachi" } // 需匹配数据库date字段的时区,示例为巴基斯坦时区 }, status: 1, check_in: 1, check_out: 1, totalHours: 1 } }, // 阶段3:分面处理:保留考勤记录+生成当月完整日期列表 { $facet: { attendanceRecords: [{ $match: {} }], allMonthDates: [ { $group: { _id: null, existingDates: { $push: "$attendanceDate" } } }, { $project: { _id: 0, absentDates: { $setDifference: [ { $map: { input: { $range: [0, { $dayOfMonth: endOfMonth }] }, as: "day", in: { $dateToString: { format: "%Y-%m-%d", date: { $add: [startOfMonth, { $multiply: ["$$day", 86400000] }] }, timezone: "Asia/Karachi" } } } }, "$existingDates" ] } } } ] } }, // 阶段4:合并打卡/缺勤记录,生成统一结构的考勤列表 { $project: { attendanceList: { $concatArrays: [ // 处理打卡记录 { $map: { input: "$attendanceRecords", as: "record", in: { date: "$$record.attendanceDate", status: "$$record.status", check_in: "$$record.check_in", check_out: "$$record.check_out", totalHours: "$$record.totalHours", type: "打卡" } } }, // 处理缺勤记录 { $map: { input: { $arrayElemAt: ["$allMonthDates.absentDates", 0] }, as: "date", in: { date: "$$date", status: "absent", type: "缺勤" } } } ] } } }, // 阶段5:按日期排序并整理最终结果 { $unwind: "$attendanceList" }, { $sort: { "attendanceList.date": 1 } }, { $group: { _id: null, monthlyAttendance: { $push: "$attendanceList" } } }, { $project: { _id: 0, monthlyAttendance: 1 } } ]).then(result => { console.log('月度考勤表:', result[0]?.monthlyAttendance || []); }).catch(err => { console.error('查询失败:', err); });
关键说明
- 时区匹配:
$dateToString中的timezone必须和数据库date字段的时区一致,否则会出现日期匹配错误; - 缺勤日期计算:通过
$range生成当月所有日期,再用$setDifference排除已有打卡记录的日期,得到缺勤日期; - 扩展优化:若需排除周末/节假日,可在生成日期列表阶段添加
$filter逻辑,筛选出工作日。
内容的提问来源于stack exchange,提问作者hamza irfan
相关产品推荐
相关产品推荐

