mongoose: mongoose 4.0.1: pre "update" middleware this object does not return model object
Hi, I’ve a problem with the newest version of Mongoose. I’m creating an API with Express and Mongoose 4.0.1, and I’m not sure if I’m doing anything wrong, but the fact is whenever I try to use the new pre update middleware the same way I use pre save middleware, this object does not return the object being updated, instead it returns the Query object.
Example of what I’m trying to explain:
ExerciseSchema.pre('save', function (next, done) {
var self = this;
console.log('exercise:', self); // returns exercise object
// some validations here
next();
});
ExerciseSchema.pre('update', function (next, done) {
var self = this;
console.log('exercise:', self); // returns Query object instead of exercise object
// some validations here
next();
});
This is what I get in this reference object within the middleware, and I don’t know how it’s useful to me.
{ _mongooseOptions: {},
mongooseCollection:
{ collection: { s: [Object] },
opts: { bufferCommands: true, capped: false },
name: 'exercises',
conn:
... },
...
}
Looking at the source code, some of its properties are defined inside Query function defined in ./node_modules/mongoose/lib/query.js:
Is this something unexpected or I am doing anything wrong? It would be interesting to have a solution because I don’t like the idea of validating within a middleware on object saving and being forced to run validations directly on controller when updating.
The same thing happens with findOneAndUpdate pre middleware, but I don’t expect it to return the exercise before finding it. In fact and in my honest opinion, I think it would be interesting that findAndUpdate and findOneAndUpdate pre middlewares triggered find(One) and update middlewares, in this order, instead of having its own middleware.
Thanks in advance for your help.
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Reactions: 1
- Comments: 40 (3 by maintainers)
Ah ok I see you’re using
findOneAndUpdate()rather thanupdate(). These are two distinct functions with distinct hooks, so if you want to add update ops tofindOneAndUpdatein a pre hook, you should doSorry I wrote that in a moment of stress 😃
Just the brittleness of version compatibility really. It’s not an obvious issue, and leaves a lot of people scratching their heads.
So if you use the native mongodb driver directly and do
coll.findOneAndUpdate({ a: 1 }, { __v: 3 }), mongodb will take the first document with a = 1 and replace it with the document{ __v: 3 }modulo_id. In other words, it will overwrite the existing document. In order to just set the key ‘__v’, you need to docoll.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } }).This behavior has always been controversial and error-prone, so by default mongoose prevents you from overwriting the document. In other words,
MyModel.findOneAndUpdate({ a: 1 }, { __v: 3 })becomesMyModel.collection.findOneAndUpdate({ a: 1 }, { $set: { __v: 3 } })unless you set theoverwrite: trueoption. However, you can do strange things likeMyModel.update({}, { $set: { b: 2 } }).findOneAndUpdate({ a: 1 }, { __v: 3 }, { overwrite: true })that make it quite confusing as to what the current state ofgetUpdate()is, so we just leave the status ofupdateas-is for middleware.I’m trying to grok all of this but am having a hard time determining if I can modify the doc that will be returned from a find using these middleware methods. Specifically, I’d like to “decorate” the response by adding additional fields.
For instance, if I have a Schema:
Is this possible?
The reason
newfieldisn’t defined in the schema is that depending on the values ofThing, different field names are needed.Thanks!
this.update({ field: val });will add{ $set: { field: val } }to the update operation before it happens.By design - the document being updated might not even be in the server’s memory. In order to do that, mongoose would have to do a findOne() to load the document before doing the update(), which is not acceptable.
The design is to enable you to manipulate the query object by adding or removing filters, update params, options, etc. For instance, automatically calling .populate() with find() and findOne(), setting the
multi: trueoption by default on certain models, access control, and other possibilities.findOneAndUpdate()is a bit of a misnomer, it uses the underlying mongodbfindAndModifycommand, it’s not the same asfindOne()+update(). As a separate operation, it should have its own middleware.@vkarpov15
Looking at the example you gave above …:
This does not work for me. The
this.getUpdate().valuedoes not contain my propertyvalueas the example suggests. Instead it’s available underthis.getUpdate().$set.property.Is it supposed to be like that? Am I understanding something wrong? Did the API change? Is there any documentation about this?
@PDS42 you’ll have to run a separate query to load the document from mongodb
How is one supposed to check for document fields before updates ? Say I need to check whether certain fields are filled to set document as “valid”, how can I access the whole document, and not just fields being updated ?
thisis referring to the query herepremethod should not work withthis.update, because it is “pre”-save and there is nothing to update yet, whilethis.newProp = "value"works perfectly, thanks guys.My case is:
Exactly the same for me as mentioned by @qqilihq
So desired behavior could be achieved with this code:
But it looks like I’m trying to use some fields that are not intended for this.