MongoDB查询实现夜班考勤记录合并次日首条打卡数据
问题描述
我是MongoDB和Node.js新手,正在开发考勤系统,需求如下:
- 当当日班次为夜班时,将次日的第一条打卡记录合并到当日的考勤记录中
当前数据结构
[ { "date": "2023-04-01", "shift": { "date": "2023-04-01", "shiftType": { "isNight": true } }, "attendances": [ { "_id": "6422d1b9994726677e9105c6", "employee": "622061b73b2eaac4b15d42e4", "dateTime": "2023-04-01T05:30:00.000Z" } ] }, { "date": "2023-04-02", "shift": { "isNight": false }, "attendances": [ { "_id": "6422d1b9994726677e9105c7", "employee": "622061b73b2eaac4b15d42e4", "dateTime": "2023-04-02T00:30:00.000Z" }, { "_id": "6423286e2746f65c2480a13e", "employee": "622061b73b2eaac4b15d42e4", "dateTime": "2023-04-02T04:30:00.000Z" }, { "_id": "6423286e2746f65c2480a13f", "employee": "622061b73b2eaac4b15d42e4", "dateTime": "2023-04-02T12:40:00.000Z" } ] } ]
期望数据结构
[ { "date": "2023-04-01", "shift": { "date": "2023-04-01", "shiftType": { "isNight": true } }, "attendances": [ { "_id": "6422d1b9994726677e9105c6", "employee": "622061b73b2eaac4b15d42e4", "dateTime": "2023-04-01T05:30:00.000Z" }, { "_id": "6422d1b9994726677e9105c7", "employee": "622061b73b2eaac4b15d42e4", "dateTime": "2023-04-02T00:30:00.000Z" } ] }, { "date": "2023-04-02", "shift": { "isNight": false }, "attendances": [ { "_id": "6423286e2746f65c2480a13e", "employee": "622061b73b2eaac4b15d42e4", "dateTime": "2023-04-02T04:30:00.000Z" }, { "_id": "6423286e2746f65c2480a13f", "employee": "622061b73b2eaac4b15d42e4", "dateTime": "2023-04-02T12:40:00.000Z" } ] } ]
请问是否可通过MongoDB查询实现该需求?望得到帮助,谢谢!
解决方案
可以通过MongoDB的聚合管道实现该需求,核心逻辑是通过自连接匹配次日记录,再根据班次类型合并或清理打卡数据,具体实现如下:
db.yourCollectionName.aggregate([ // 1. 计算每条记录的次日日期,用于关联次日考勤数据 { $addFields: { nextDay: { $dateToString: { format: "%Y-%m-%d", date: { $add: [{ $toDate: "$date" }, 86400000] } // 86400000毫秒 = 1天 } } } }, // 2. 自连接集合,获取次日对应的考勤记录 { $lookup: { from: "yourCollectionName", localField: "nextDay", foreignField: "date", as: "nextDayRecord" } }, // 3. 处理考勤数据:合并夜班当日与次日第一条打卡,清理次日记录的已合并打卡 { $project: { date: 1, shift: 1, attendances: { $cond: [ // 判断当前是否为夜班(兼容两种shift结构) { $or: [{ $eq: ["$shift.shiftType.isNight", true] }, { $eq: ["$shift.isNight", true] }] }, // 夜班:合并当前考勤与次日第一条打卡 { $concatArrays: [ "$attendances", { $slice: [{ $arrayElemAt: ["$nextDayRecord.attendances", 0] }, 1] } ] }, // 非夜班:如果是被夜班关联的次日记录,移除第一条打卡;否则保留原数据 { $cond: [ { $exists: { $arrayElemAt: [ { $filter: { input: "$$ROOT", as: "item", cond: { $and: [ { $or: [{ $eq: ["$$item.shift.shiftType.isNight", true] }, { $eq: ["$$item.shift.isNight", true] }] }, { $eq: ["$$item.nextDay", "$date"] } ] } } }, 0 ] } }, { $slice: ["$attendances", 1, { $size: "$attendances" }] }, "$attendances" ] } ] }, nextDay: 0, nextDayRecord: 0 } } ])
关键步骤说明
- $addFields:将当前记录的
date转为日期类型后加一天,再转回YYYY-MM-DD格式的字符串,作为关联次日记录的标识 - $lookup:自连接集合,通过
nextDay匹配次日的考勤记录 - $project:通过条件判断处理考勤数组:
- 夜班记录:将自身考勤与次日记录的第一条打卡合并
- 非夜班记录:如果它是某条夜班记录的次日,则移除已被合并的第一条打卡;否则保留原考勤数据
注意事项
- 替换代码中的
yourCollectionName为实际集合名称 - 确保
date字段是YYYY-MM-DD格式的字符串,若为Date类型需调整日期转换逻辑 - 该查询支持多员工、多夜班场景的自动匹配
内容的提问来源于stack exchange,提问作者Pallab Kole
相关产品推荐
相关产品推荐

