nomadoda
nomadoda

Reputation: 4942

Mongoose: update document & use middleware

I have a case where I'm using mongoose .pre('save') hooks to denormalize some data to other documents. I'm struggling with a good solution to update data and keep denormalization consistent across the database. If I want to update a document, I consider three alternatives:

  1. Performing an atomic update

The reason why I prefer not to do this is that it doesn't trigger my mongoose middleware that I need to denormalize my data.

  1. Using .post('update') hooks

The problem with this method is for one that I can't tell in the hook which fields have been updated, right? So I should then assume all fields have been updated and denormalize data across the entire database? Seems very intense.

  1. Getting the document, modifying it, then saving it

It seems convenient, but poses a risk to overwriting data, i.e. if the document was to be updated from elsewhere in the 'between'-moment.

in models/car.model.js:

const { mongoose } = require('mongoose');
const Schema = mongoose.Schema;

const Manufacturer = require('./manufacturer.model.js');

const CarSchema = new Schema({
    name: String,
    manufacturer: {
        type: {
            _id: Schema.Types.ObjectId,
            name: String,
        },
    },
});

CarSchema.pre('save', async function(next) {
    /*
    if (this.isModified('name')) {
        await Manufacturer.findByIdAndUpdate(this.manufacturer, {
            $set: {
                car-name: this.name,
            },
        });
    }
    */
    if (this.isModified('manufacturer') {
        const manufacturer = await Manufacturer.findOne({
            _id: this.manufacturer._id,
        }, {
            name: 1,
        });
        this.manufacturer.name = manufacturer.name;
    }
});

in controllers/car.controller.js:

app.patch('/:id', async function (req, res, next) {
    const car = await Car.findById(req.params.id);
    // Bad solution, helps please
    for (var key in req.body) {
        car[key] = req.body[key];
    }
    await car.save();
    res.sendStatus(200);
});

Any solution that could bring the best of both worlds?

Upvotes: 0

Views: 2487

Answers (1)

vkarpov15
vkarpov15

Reputation: 3872

Number 2 is a viable option, you can just do this.getUpdate() using context: 'query' in your post('update') hook. Also, what's wrong with performing an atomic update, like updateOne()?

Also, glad to see you're using async middleware, but just FYI you don't need next() if you have an async hook function.

Upvotes: 2

Related Questions