Mongoose sub document pre remove middleware not called

半世苍凉 提交于 2019-12-12 03:41:57

问题


I would prevent any sub document from being removed, thus I added an error to the pre('remove') middleware of each sub document Schema.

When calling the .remove() function, it effectively calls the middleware. But when it is deleted without calling remove(), the middleware doesn't check if it has been removed.

The cases where is matters is when I receive an object from a remote source, I'd like to perform all the integrity checks via mongoose middlewares to keep everything at the same place. The remote source can have, by mistake or not, deleted one of the sub docs. So when Mongoose is checking the whole doc, the sub doc has already been removed without triggering the .remove() function.

Here is the minimal working example of my problem:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var subDateSchema = new Schema({
    date_add: {type: Date, default: Date.now},
    date_remove: {type: Date, default: null}
});

var ResourceSchema = new Schema({
    activation_dates: [subDateSchema]
});

subDateSchema.pre('remove', function(next){
    next(new Error("YOU CAN'T DELETE ANY ACTIVATION DATE"));
});

var Resource = mongoose.model('Resource', ResourceSchema);

var newresource = new Resource({
    activation_dates: [{
        date_add: Date.now()
    }]
});

newresource.save(function(err){
    if(err) throw err;
    newresource.activation_dates.splice(0, 1);
    /**
      * Here I tried
      * newresource.markModified('activation_dates');
      * On update it *DOES* trigger pre save and pre validate
      * But it does nothing to deleted content
    **/ 
    newresource.save(function(err){
        if(err) throw err;
    });
});

So my question is: Is there a clean way to call sub doc removing middleware without proceeding to check for all the previous elements and compare with the new ones to see which ones are being deleted ?


回答1:


After a little bit of researches, I found this:

A workaround can be to hook an event to the whole array of Sub Documents, and to have a copy of the previous array of data.

This is a complete working example on how to be sure an array element hasn't been deleted or pulled out. To check for modifications, you'll need further modifications.

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var ResourceSchema = new Schema({
    activation_dates: [subDateSchema]
});

// This virtual permits to store the original array accross the middlewares
ResourceSchema.virtual("original").set(function(item){
    this._original = item;
}).get(function(){
    return this._original;
});

// This middleware checks for a previous version of the "Resource"
ResourceSchema.pre("validate", function(next){
    var self = this;
    mongoose.model("Resource").findById(this._id, function(err, doc){
        if(err) throw err;
        self.original = doc;
        next();
    });
});

// This validation block checks for any modification of the array of sub documents
ResourceSchema.path("activation_dates").validate(function(value){
    var j;
    if(this.original){
        // if the new array is smaller than the original, no need to go further
        if(this.original.activation_dates.length > value.length){
            return false;
        }

        for(var i=0; i < this.original.activation_dates.length; i++){
            j=0;
            // if the array element has been deleted but not pulled out, we need to check it
            if(typeof value[j] == "undefined" || typeof value[j]._id == "undefined"){
                return false;
            }

            while(value.length > j && this.original.activation_dates[i]._id.toString() != value[j]._id.toString()){
                j++;
            }
            if(j == value.length){
                return false;
            }
        }
    }
    return true;
}, "You deleted at least one element of the array");

var Resource = mongoose.model('Resource', ResourceSchema);

var newresource = new Resource({
    activation_dates: [{
        date_add: Date.now()
    }]
});

newresource.save(function(err){
    if(err) throw err;

    newresource.activation_dates.splice(0, 1);
    // OR:
    //delete newresource.activation_dates[0];
    // this line is essential in the case you *delete* but not pull out
    newresource.markModified('activation_dates');

    newresource.save(function(err){
        if(err) throw err;
    });
});

Unfortunately I couldn't find another solution than doing a loop over all elements and retrieving the original document.



来源:https://stackoverflow.com/questions/32025375/mongoose-sub-document-pre-remove-middleware-not-called

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!