Hi,
How can I override the save method of a model? I'm trying to find an example but I'm not being lucky.
Thanks for your help!!
Maybe you need a pre, post save middleware? http://mongoosejs.com/docs/middleware.html
Awesome. I was looking for this actually in the docs. I didn't connect on the word "middleware."
I would like to know how to do this. I want to change the signature of the save object so it can accept the new and the old models to be able to compare model values and perform 'middleware' style functions based on the changes.
What do you mean "accept the new and old models"? Mongoose already tracks changes for you, you can check if a path is modified with the Document.isModified()
function.
In addition to checking if it was modified, I would also like to get the original value before modification. Is this possible currently?
Also, i'm trying to keep my code as library agnostic as possible for if we want to switch database at a later stage. By intercepting the calls I can wrap the data access library (in this case mongoose, but potentially any other library).
Good point, we don't keep the original values. I'd imagine you'd have to make a copy using toObject()
first to keep the original values.
As for a wrapper around save, you could pretty easily just use a schema method:
mySchema.methods.myCustomSave = function(old, callback) {
// Do stuff comparing `old` and `this`
this.save(callback);
}:
Then you could access it using doc.myCustomSave()
for any doc whose model uses mySchema
This is the current temporary solution i'm using whilst testing, but the clean semantic nature of "save" is something i'd like to try and maintain.
Tracking past values of fields in a document sounds like a pretty cool idea, and it would be difficult to implement with the current implementation of mongoose and won't really be a priority for mongoose during the next 6 months. What sort of clean semantic nature are you envisioning?
How about a scenario using random-generated short IDs (e.g. nanoid
), where it's OK to have a rare collision, but would like to override save
to retry until there's no collision? The custom method would do, of course, but maintaining the save
name would hide the nanoid implementation detail.
export async function saveAvoidingCollision(doc) {
let retries = 0;
do
try {
return await doc.save();
} catch (e) {
// If another error occurred than the id already existing, re-throw
if (e.code !== 11000)
throw e;
doc._id = nanoid();
if (retries++ > 10)
console.warn(`High number of ${e.message.match(/collection: (.*) index/)[1]} _id collisions: ${retries}. Increase nanoid alphabet size.`);
}
while (true);
}
@dandv I think a custom method is the way to go there. You could also do this with a post('save')
hook using error handling middleware
Most helpful comment
Good point, we don't keep the original values. I'd imagine you'd have to make a copy using
toObject()
first to keep the original values.As for a wrapper around save, you could pretty easily just use a schema method:
Then you could access it using
doc.myCustomSave()
for any doc whose model usesmySchema