MongooseJS modify document during pre hook

13,014

pre hooks work for both doc.save() and doc.update(). In both cases this refers to the document itself.

Note that hooks need to be added to your schema before compiling your model.

schema.pre('save', function(next) {
    if(typeof this.tags === 'string') {
        this.tags = this.tags.split(',');
    }
});
var Location = mongoose.model('Location', schema);
Share:
13,014
user1363145
Author by

user1363145

Updated on June 04, 2022

Comments

  • user1363145
    user1363145 almost 2 years

    I'm having some issues with mongoose. My goal is that during pre-save, I would be able to modify the object, doing things like splitting the tags if needed, or in another case calculating the summation of a sub-document durations and update it within the main document.

    What I'm finding is that if I load a model, then call doc.update passing the new data, only schema.pre('update', ...) triggers, and any changes to this within my middleware are not updated. I also tried using this.set('...', ....); within my update middleware to no avail.

    It seems if I do doc.save(...) instead, then changes to this within schema.pre('save', ...) are appended as expected. Aside from extending the posted variables into my model's properties and saving, I'm not seeing any way of leveraging doc.update for this purpose.

    My Goals: - Update an existing document via doc.update(properties, ....) - Use middleware during saving to modify the document, do advanced validation, and update related documents - Use middleware during updating to modify the document, do advanced validation, and update related documents - Interchangeably use model.findByIdAndUpdate, model.save, model.findById->doc.update, model.findById->doc.save and all tap into my save/update middleware.

    Some arbitrary sample code:

    function loadLocation(c) {
        var self = this;
        c.Location.findById(c.params.id, function(err, location) {
            c.respondTo(function(format) {
                if (err | !location) {
                    format.json(function() {
                        c.send(err ? {
                            code: 500,
                            error: err
                        } : {
                            code: 404,
                            error: 'Location Not Found!'
                        });
                    });
                    format.html(function() {
                        c.redirect(c.path_to.admin_locations);
                    });
                } else {
                    self.location = location;
                    c.next();
                }
            });
        });
    }
    
    LocationController.prototype.update = function update(c) {
        var location = this.location;
        this.title = 'Edit Location Details';
    
        location.update(c.body.Location, function(err) {
            c.respondTo(function(format) {
                format.json(function() {
                    c.send(err ? {
                        code: 500,
                        error: location && location.errors || err
                    } : {
                        code: 200,
                        location: location.toObject()
                    });
                });
                format.html(function() {
                    if (err) {
                        c.flash('error', JSON.stringify(err));
                    } else {
                        c.flash('info', 'Location updated');
                    }
                    c.redirect(c.path_to.admin_location(location.id));
                });
            });
        });
    };
    
    module.exports = function(compound) {
        var schema = mongoose.Schema({
            name: String,
            address: String,
            tags: [{ type: String, index: true }],
            geo: {
                type: {
                    type: String,
                default:
                    "Point"
                },
                coordinates: [Number] // Longitude, Latitude
            }
        });
        schema.index({
            geo: '2dsphere'
        });
        var Location = mongoose.model('Location', schema);
        Location.modelName = 'Location';
        compound.models.Location = Location;
    
        schema.pre('save', function(next) {
            if(typeof this.tags === 'string') {
                this.tags = this.tags.split(',');
            }
        });
    };
    

    ==== * revised sample * ====

    module.exports = function(compound) {
        var schema = mongoose.Schema({
            name: String,
            bio: String
        });
    
        schema.pre('save', function(next) {
            console.log('Saving...');
            this.bio = "Tristique sed magna tortor?"; 
            next();
        });
    
        schema.pre('update', function(next) {
            console.log('Updating...');
            this.bio = "Quis ac, aenean egestas?"; 
            next();
        });
    
        var Author = mongoose.model('Author', schema);
        Author.modelName = 'Author';
        compound.models.Location = Author;
    };
    
  • user1363145
    user1363145 over 10 years
    Hmmm it doesn't help that I created poor sample code. I tried your recommendation with a simpler case and am still getting the same results: after looking up a doc with Author.findById, when I call author.update(c.req.body, ...), schema.pre('save', ...) does not trigger, however schema.pre('update', ...) does and the changes to this.name aren't being applied. schema.pre('save', ...) should trigger no? It does look like author.save triggers schema.pre('save', ...) and changes made then are being applied. Is the solution here to just abandon author.update altogether?
  • aaronheckmann
    aaronheckmann over 10 years
    correct. doc.update() will not trigger hooks for any other method but the one being called.
  • user1363145
    user1363145 over 10 years
    Ok, but this still answer the original question. Changings during schema.pre('update', ...) still aren't being appended. Is this a bug, intended, or should I simply be exclusively using doc.save and using some method to iterate over my req.body and merge properties?
  • aaronheckmann
    aaronheckmann over 10 years
    Not a bug. document.update(doc, cb) sends an update for the passed doc, not the currently changed values. Thats different from document.save().
  • JonRed
    JonRed over 9 years
    If you look at the docs: mongoosejs.com/docs/middleware.html it doesn't mention that schema.pre('update' event is valid. It only lists init, save, remove and validate.
  • steampowered
    steampowered almost 9 years
    Now update hooks work natively in Mongoose 4.0, but they are turned off by default. This blog post explains: mongodb.com/blog/post/…
  • Ludo
    Ludo over 8 years
    @aaronheckmann are you sure that this refers to the current doc ?
  • Capi Etheriel
    Capi Etheriel almost 7 years
    how can i use this with arrow functions (ES6)?