node.jsmongodbmongoosemongoose-middleware

Mongoose pushing to an Array using presave middleware


I'm trying to update an array using presave hooks available in mongoose. Im using this array for Audit Purposes and hence i wanted to enforce correct values using these middle wares

Here is my schema

const taskSchema = new mongoose.Schema({
id: { type: String, required: true },
status: { type: String, required: true },
code: { type: String, required: true }, // ENQUIRY
events:{
    type:[{
            status: {type: String, required: true},
            date:{ type: Date, required: true}

    }], required: false
},
assignedTo: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: false },
}, {timestamps:true});

The logic works great in save middleware hook.

taskSchema.pre('save', async function(next) {
    try {
        let event = {}
        let now = Date.now();
        event.status = this.status;
        event.date = now;
        this.events.push(event);
        next();
    } catch (error) {
        console.log(error);
        next(error);
    }
});

Error when used in findOneAndUpdate hook

taskSchema.pre('findOneAndUpdate', async function(next) {
    try {

        let event={}
        let now = Date.now();
        event.status = this._update.status;
        event.date = now;
        this.events.push(event);
        next();
    } catch (error) {
        console.log(error);
        next(error);
    }
});

I'm not sure what I'm missing here. Here is the error

TypeError: Cannot read property 'push' of undefined 

I see that this.events is undefined.

How do I access that array to have it updated? I also should ignore whatever is sent in request.body.events

Thanks in advance.


Solution

  • Refer to the Types of Middleware, save is document middleware and findOneAndUpdate is query middleware. And in notes, it says that:

    Query middleware differs from document middleware in a subtle but important way: in document middleware, this refers to the document being updated. In query middleware, mongoose doesn't necessarily have a reference to the document being updated, so this refers to the query object rather than the document being updated.

    So in your findOneAndUpdate pre, this.events is undefined.

    In your case, using below code may solve the problem:

    taskSchema.pre('findOneAndUpdate', async function(next) {
      try {
    
        let event={}
        let now = Date.now();
        event.status = this._update.status;
        event.date = now;
        this._update['$push'] = {events: event};
        next();
      } catch (error) {
        console.log(error);
        next(error);
      }
    });