skip to Main Content

I have two models Skill and Course. Whenever I update any existing skills name, I want it to update the skill name inside the course object, that already has these skills.

How can I automatically update the name of skills in course.skills when I update any skill?

Course models :

    const courseSchema = new mongoose.Schema(
        {
            name: {
                type: String,
                required: true,
                trim: true,
            },
            description: {
                type: String,
            },
            
            skills: [
                {
                    name: { type: String },
                    id: { type: mongoose.Schema.Types.ObjectId, ref: 'Skill' },
                },
            ],
        }
    );

Skill Model:

    const Course = require('./course.model');
    
    const skillSchema = new mongoose.Schema(
        {
            name: {
                type: String,
                required: true,
                trim: true,
            },
            description: {
                type: String,
            },
            points: {
                type: Number,
            },
        },
        {
            timestamps: true,
        }
    );

this is the function for updating the skills:

    module.exports.updateSkillById = async (req, res) => {
        const updates = Object.keys(req.body);
        const allowedUpdates = ['name', 'description', 'points'];
        const isValidOperation = updates.every((update) =>
            allowedUpdates.includes(update)
        );
        if (!isValidOperation) {
            return res.status(400).send({ error: 'Invalid updates!' });
        }
        try {
            const skill = await Skill.findOneAndUpdate(req.params.id, req.body, {
                new: true,
                runValidators: true,
            });
            if (!skill) {
                return res.status(404).send();
            }
          
            res.send(skill);
        } catch (err) {
            res.status(400).send(err);
        }
    };

2

Answers


  1. Chosen as BEST ANSWER

    I am solving this problem by using save() method. Using save() method instead of findByIdAndUpdate() will return the updated object in pre hook.

    the update function is like that :

    
         const skill = await Skill.findById(req.params.id);
                if (!skill) {
                    return res.status(404).send();
                }
                updates.forEach((update) => (skill[update] = req.body[update]));
                await skill.save();
    
    

    The pre-hook is :

    
    skillSchema.pre('save', async function (next) {
        const skill = this;
    
        const courses = await Course.find({ 'skills.id': skill._id });
       
        await updateModel(courses, skill, 'skills');
    
       if (courses.length > 0) {
        await Promise.all(
            courses.map((course) => {
                const skillIndex = course.skills.findIndex((sk) =>
                    sk.id.equals(skill._id)
                );
                course.skills[skillIndex].name = skill.name;
                return course.save();
            })
        );
    }
        next();
    });
    
    

  2. One way to achieve that is to use Mongoose middleware functions. You can use a pre(‘findOneAndUpdate) hook in the skill model.

    This will trigger before updating the skill object.

    Inside the hook you can update your courses manually with a transaction.

    skillSchema.pre('findOneAndUpdate', async function(next) {
        const skill = this.getUpdate();
        const courses = await Course.find({ 'skills.id': skill._id });
    
        if (courses.length > 0) {
            try {
                const session = await mongoose.startSession();
                session.startTransaction();
    
                for (let i = 0; i < courses.length; i++) {
                    const course = courses[i];
                    const skillIndex = course.skills.findIndex(s => s.id.equals(skill._id));
    
                    if (skillIndex >= 0) {
                        course.skills[skillIndex].name = skill.name;
                        await course.save({ session });
                    }
                }
    
                await session.commitTransaction();
            } catch (err) {
                await session.abortTransaction();
                throw err;
            }
        }
    
        next();
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search