如何在MongoDB(Mongoose)中更新并保存面试数组对象
问题背景与需求
需求:保存面试数组对象,实现第1轮面试安排及反馈完成后保存该轮次数据,后续轮次同理。interview数组的每个元素包含两个子对象:
schedule:用于存储面试轮次的安排信息feedback:用于收集对应轮次的面试反馈
提供的Mongoose Schema如下:
const mongoose = require('mongoose'); const mongoosePaginate = require('mongoose-paginate-v2'); const { Schema } = mongoose; const InterviewSchema = new Schema( { firstName: { type: String, required: true, trim: true, maxlength: 30, }, lastName: { type: String, trim: true, maxlength: 30, }, email: { type: String, required: true, trim: true, }, gender: { type: String, required: true, }, contactNumber: { type: Number, required: true, }, profile: { type: String, required: true, }, status: { type: String, }, // 面试轮次数组 interview: [ { schedule: { interviewerName: { type: String, trim: true, }, date: { type: Date, default: Date.now, }, mode: { type: String, }, meeting: { link: { type: String, trim: true, }, platform: { type: String, trim: true, }, }, round: { count: { type: Number, default: 1, }, type: { type: String, default: 'hr', }, }, }, feedback: { technical: { type: Number, required: true, min: 1, max: 5, }, logical: { type: Number, required: true, min: 1, max: 5, }, communication: { type: Number, required: true, min: 1, max: 5, }, comment: { type: String, min: 10, max: 200, default: '', }, interviewStatus: { type: String, }, recommendation: { type: String, default: '', }, }, }, ], }, { timestamps: true, }, ); InterviewSchema.plugin(mongoosePaginate); const InterviewProcess = mongoose.model('interviewprocess', InterviewSchema); module.exports = InterviewProcess;
遇到的问题
已完成数组对象中schedule轮次与反馈的更新,但无法保存数据,旧的安排会被覆盖。编写的updateSchedule代码如下:
static async updateSchedule(request, h) { try { const payload = request.payload.interview[0]; const params = request.query._id; const interviewSchedule = await interviewProcessService.findByinterviewProcessId(params); if (!interviewSchedule) { return h.response({ statusCode: MESSAGE.STATUS.ERROR, message: MESSAGE.ID_NOT_EXIST, }); } let mode, meeting, round, technical, logical, communication, comment, interviewStatus, recommendation, interviewerName; if (payload.schedule) { interviewerName = _.get(payload.schedule, 'interviewerName'); mode = _.get(payload.schedule, 'mode'); meeting = _.get(payload.schedule, 'meeting'); round = _.get(payload.schedule, 'round') || {}; } if (payload.feedback) { mode = _.get(interviewSchedule.interview[0].schedule, 'mode'); meeting = _.get(interviewSchedule.interview[0].schedule, 'meeting'); round = _.get(interviewSchedule.interview[0].schedule, 'round') || {}; interviewerName = _.get( interviewSchedule.interview[0].schedule, 'interviewerName', ); technical = _.get(payload.feedback, 'technical'); logical = _.get(payload.feedback, 'logical'); communication = _.get(payload.feedback, 'communication'); comment = _.get(payload.feedback, 'comment'); interviewStatus = _.get(payload.feedback, 'interviewStatus'); recommendation = _.get(payload.feedback, 'recommendation'); } const result = await interviewProcessService.update(params, { interview: { schedule: { interviewerName, mode, meeting, round, }, feedback: { technical, logical, communication, comment, interviewStatus, recommendation, }, }, }); return h.response({ result, statusCode: MESSAGE.STATUS.SUCCESS, message: MESSAGE.ROUTES.SCHEDULE_UPDATE.MESSAGE, }); } catch (error) { return ErrorHandler.error(error); } }
期望实现:调用GET方法时,能获取所有带有反馈的面试轮次信息。
问题原因与修复方案
核心问题
当前代码直接替换了整个interview数组,而非追加新轮次或更新指定轮次,导致旧数据被覆盖;同时代码仅处理数组第一个元素,无法支持多轮次的新增与更新。
修复步骤
1. 区分「新增轮次」与「更新已有轮次」
根据传入的round.count判断操作类型:
- 新增轮次:使用MongoDB的
$push操作符向interview数组追加元素 - 更新轮次:使用
$set配合数组索引定位,精准更新指定轮次的内容
2. 重构updateSchedule代码
static async updateSchedule(request, h) { try { const payload = request.payload.interview[0]; const params = request.query._id; const interviewRecord = await interviewProcessService.findByinterviewProcessId(params); if (!interviewRecord) { return h.response({ statusCode: MESSAGE.STATUS.ERROR, message: MESSAGE.ID_NOT_EXIST, }); } let updateQuery = {}; // 获取目标轮次编号,默认1 const targetRoundCount = payload.schedule?.round?.count || 1; // 检查是否存在该轮次 const existingRoundIndex = interviewRecord.interview.findIndex( item => item.schedule.round.count === targetRoundCount ); if (existingRoundIndex === -1) { // 新增轮次:用$push追加到数组 updateQuery.$push = { interview: { schedule: { interviewerName: payload.schedule?.interviewerName, mode: payload.schedule?.mode, meeting: payload.schedule?.meeting, round: payload.schedule?.round || { count: 1, type: 'hr' }, date: payload.schedule?.date || new Date() }, feedback: payload.feedback ? { technical: payload.feedback.technical, logical: payload.feedback.logical, communication: payload.feedback.communication, comment: payload.feedback.comment || '', interviewStatus: payload.feedback.interviewStatus, recommendation: payload.feedback.recommendation || '' } : { // 反馈为必填项,此处可根据需求调整默认值或抛出错误 technical: 1, logical: 1, communication: 1, comment: '', interviewStatus: 'pending', recommendation: '' } } }; } else { // 更新已有轮次:合并原有数据与新数据,避免丢失未修改字段 updateQuery.$set = {}; if (payload.schedule) { updateQuery.$set[`interview.${existingRoundIndex}.schedule`] = { ...interviewRecord.interview[existingRoundIndex].schedule, ...payload.schedule }; } if (payload.feedback) { updateQuery.$set[`interview.${existingRoundIndex}.feedback`] = { ...interviewRecord.interview[existingRoundIndex].feedback, ...payload.feedback }; } } const result = await interviewProcessService.update(params, updateQuery); return h.response({ result, statusCode: MESSAGE.STATUS.SUCCESS, message: MESSAGE.ROUTES.SCHEDULE_UPDATE.MESSAGE, }); } catch (error) { return ErrorHandler.error(error); } }
3. 优化GET接口返回结果
如果需要仅返回带有反馈的轮次,可在查询时添加过滤条件:
// 示例:查询所有包含有效反馈的面试记录 const records = await InterviewProcess.find({ 'interview.feedback.technical': { $exists: true } }).exec();
若需返回所有轮次(包括未填写反馈的),直接返回interview数组即可。
关键说明
- 使用
$push而非直接赋值数组,彻底避免旧数据被覆盖 - 通过
findIndex定位已有轮次,实现精准更新 - 用扩展运算符
...合并新旧数据,保留未修改的字段 - 同时支持「新增轮次」和「更新轮次」两种场景,满足多轮面试流程需求
内容的提问来源于stack exchange,提问作者RK Varun
相关产品推荐
相关产品推荐

