Hearner
Hearner

Reputation: 2729

Express pre remove triggers another pre remove on remove

Let's say I have 4 models like so:

┌────────────┐      ┌────────────┐
│    User    │      │    Goal    │
├────────────┤ 1    ├────────────┤ 1
│    _id     │◄──┐  │    _id     │◄──┐
└────────────┘   └──┤  ref_user  │   │
                1..*└────────────┘   │
                                     │
┌───────────┐     ┌───────────────┐  │
│   Date    │     │     Data      │  │
├───────────┤ 1   ├───────────────┤  │
│ _id: date │◄─┐  │     _id       │  │
└───────────┘  └──┤    ref_date   │  │
                1 │    ref_goal   ├──┘
                  └───────────────┘ *

I created a pre remove when deleting a data it automatically removes the date associated.

DataSchema.pre('remove', async function (next) {
    try {
        await DateModel.findByIdAndRemove(this.ref_date);
        console.log("Date removed");
        next();
    } catch (err) {
        console.log(err);
        next(err);
    }
});

The issue I am facing is that when I remove a goal, I want all the Data associated to be deleted (it works) BUT I want the DataModel.remove to triggers its pre remove and remove its Date. In a logical perspective, when removing data on the pre remove hook in Goal, this also should trigger the pre remove in Data since Data data are removed. Why when removing a data from another pre remove hook it doesn't trigger its pre remove ?

From Goal, I can retreive an array of Data, but I don't think that looping through the array and removing one by one the date is a good practrice.

Is there a way to triggeres the pre remove from Data, from the pre remove from Goal ? Or a way to remove all the Date from an array of Data ?

ps: in the futur, I want the same thing again when deleting a user (deleting all its goals that deletes all the data and the date).

Edit :

As requested, he is my goal pre-remove hook

GoalSchema.pre('remove', async function (next) {
    try {
        await DataModel.deleteMany({ 'ref_goal': this._id });
        console.log("Data removed");
        next();
    } catch (err) {
        console.log(err);
        next(err);
    }
});

I tried deleteMany, remove, etc. but none seems to trigger the Data pre-remove hook

Env:

"express": "^4.17.1",
"mongoose": "^5.12.2",

Upvotes: 3

Views: 360

Answers (1)

OfirD
OfirD

Reputation: 10460

There are two issues with your current code:

  1. You're registering a remove hook on your data schema:

    DataSchema.pre('remove', ...)
    

    while you're goal's remove hook's callback triggers a deleteMany middleware:

    await DataModel.deleteMany(...)
    

    hence data's remove hook is not triggered.

    Now, you mention that you tried other functions, such as remove, which takes us to the second issue:

  2. You're registering remove hooks on documents (that's the default), while executing functions on models.

    See this note on the docs:

    Note: If you specify schema.pre('remove'), Mongoose will register this middleware for doc.remove() by default. If you want to your middleware to run on Query.remove() use schema.pre('remove', { query: true, document: false }, fn).

    Of relevance is also this explanation:

    You can pass options to Schema.pre() and Schema.post() to switch whether Mongoose calls your remove() hook for Document.remove() or Model.remove(). Note here that you need to set both document and query properties in the passed object:

    // Only document middleware 
    schema.pre('remove', { document: true, query: false }, function() {   
       console.log('Removing doc!'); 
    });
    
    // Only query middleware. This will get called when you do `Model.remove()` 
    // but not `doc.remove()`. 
    schema.pre('remove', { query: true, document: false }, function() {  
       console.log('Removing!'); 
    });
    

I think the following should work:

const onRemoveData = async function (next) {
   try {
        let removed = [this];
        if (this instanceof mongoose.Query) {
           removed = await this.model.find(this.getQuery());
        }
        removed.forEach(function(doc) {
            await DateModel.findByIdAndRemove(doc.ref_date);
            console.log("Date removed");
        });
        next();
    } catch (err) {
        console.log(err);
        next(err);
    }
}
DataSchema.pre(['remove', 'deleteMany'], { document: true, query: false}, onRemoveData);
DataSchema.pre(['remove', 'deleteMany'], { document: false, query: true}, onRemoveData);

Upvotes: 1

Related Questions