MongoDB中创建关联文档时如何自动更新引用模型?
问题解决:创建评论后Doctor模型reviews属性未自动更新
问题背景
我在MongoDB(Mongoose)中定义了Patient、Doctor、Reviews三个模型,Doctor模型通过reviews数组存储关联的Review文档ID。当患者调用Review.create()为特定医生发布评论时,评论文档能成功创建,但Doctor模型的reviews属性并未自动更新,无法实现评论与医生的关联。
相关模型代码
Patient模型
const PatientSchema = new Schema<patientType>({ name: { type: String, required: [true, 'please provide a product name'], maxlength: 50, minlength: 3 }, email: { type: String, required: [true, 'please provide an email'], match: [ /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 'Please provide a valid email', ], unique: true, }, password: { type: String, required: [true, 'please provide a password'], minlength: 8 }, phone: { type: Number, default: 0 }, photo: { type: String }, role: { type: String, }, gender: { type: String, enum: ["male", "female", "other"] }, bloodtype: { type: String }, appointments: [{ type: mongoose.Types.ObjectId, ref: 'Booking', required: true }] })
Doctor模型
const DoctorSchema = new Schema<doctorType>({ name: { type: String, required: [true, 'please provide a product name'], maxlength: 50, minlength: 3 }, email: { type: String, required: [true, 'please provide an email'], match: [ /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 'Please provide a valid email', ], unique: true, }, password: { type: String, required: [true, 'please provide a password'], minlength: 8 }, phone: { type: Number, default: 0 }, photo: { type: String }, role: { type: String, }, ticketPrice: { type: Number, default: 1000, required: true }, specialization: { type: String, }, qualifications: { type: [Object], }, experiences: { type: [Object] }, bio: { type: String, minlength: 5, }, about: { type: String, minlength: 4 }, reviews: [{ // 关联Review模型 type: mongoose.Types.ObjectId, ref: "Review", required: true, default: [] }], averageRating: { type: Number, default: 0 }, totalRatings: { type: Number, default: 0 }, isApproved: { type: String, enum: ["pending", "approved", "cancelled"], default: 'pending' }, appointments: [{ type: mongoose.Types.ObjectId, ref: 'Booking', required: true }] })
Review模型
const ReviewSchema = new Schema<reviewType>({ doctor: { type: mongoose.Types.ObjectId, ref: "Doctor", }, patient: { type: mongoose.Types.ObjectId, ref: "User", }, text: { type: String, required: [true, 'please add a review text'], }, rating: { type: Number, required: true, min: 0, max: 5, default: 0, }, },{ timestamps: true });
原创建评论函数(问题代码)
const postDoctorReviews = async (req: MyUserRequest, res: Response) => { const {id: doctorId} = req.params const {patientId} = req.user const {text, rating} = req.body if(!text || !rating){ throw new BadRequestError('Field cannot be empty') } const doctorReview = await Review.create({doctor: doctorId, patient: patientId, ...req.body}) const doctor = await Doctor.findById(doctorId) doctor?.save() res.status(StatusCodes.CREATED).json({doctorReview}) }
问题原因
原代码仅查询了医生文档但未将新创建的评论ID添加到Doctor的reviews数组中,Mongoose不会自动维护这种双向引用关系,必须手动处理关联更新。
修复方案
修改创建评论的函数,手动将新评论ID添加到医生的reviews数组,同时更新医生的评分统计数据:
const postDoctorReviews = async (req: MyUserRequest, res: Response) => { const {id: doctorId} = req.params const {patientId} = req.user const {text, rating} = req.body if(!text || !rating){ throw new BadRequestError('Field cannot be empty') } // 创建评论文档 const doctorReview = await Review.create({doctor: doctorId, patient: patientId, text, rating}) // 查找目标医生,若不存在则抛出错误 const doctor = await Doctor.findById(doctorId) if (!doctor) { throw new NotFoundError('Doctor not found') } // 将新评论ID添加到医生的reviews数组 doctor.reviews.push(doctorReview._id) // 更新医生的总评分数量和平均评分 doctor.totalRatings += 1 doctor.averageRating = (doctor.averageRating * (doctor.totalRatings - 1) + rating) / doctor.totalRatings // 保存更新后的医生文档 await doctor.save() res.status(StatusCodes.CREATED).json({doctorReview, updatedDoctor: doctor}) }
进阶优化:使用事务保证数据一致性
为避免评论创建成功但医生更新失败导致的数据不一致问题,可以使用Mongoose事务:
const postDoctorReviews = async (req: MyUserRequest, res: Response) => { const {id: doctorId} = req.params const {patientId} = req.user const {text, rating} = req.body if(!text || !rating){ throw new BadRequestError('Field cannot be empty') } // 启动事务会话 const session = await mongoose.startSession() session.startTransaction() try { // 在事务中创建评论 const doctorReview = await Review.create( [{doctor: doctorId, patient: patientId, text, rating}], {session} ) // 在事务中查找医生 const doctor = await Doctor.findById(doctorId).session(session) if (!doctor) { throw new NotFoundError('Doctor not found') } // 更新医生数据 doctor.reviews.push(doctorReview[0]._id) doctor.totalRatings += 1 doctor.averageRating = (doctor.averageRating * (doctor.totalRatings - 1) + rating) / doctor.totalRatings // 在事务中保存医生 await doctor.save({session}) // 提交事务 await session.commitTransaction() res.status(StatusCodes.CREATED).json({ doctorReview: doctorReview[0], updatedDoctor: doctor }) } catch (error) { // 回滚事务 await session.abortTransaction() throw error } finally { // 结束会话 session.endSession() } }
内容的提问来源于stack exchange,提问作者gerard
相关产品推荐
相关产品推荐

