You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:18:12