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 tofindOneAndUpdate
in 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: true
option. 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 ofupdate
as-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
newfield
isn’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: true
option by default on certain models, access control, and other possibilities.findOneAndUpdate()
is a bit of a misnomer, it uses the underlying mongodbfindAndModify
command, 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().value
does not contain my propertyvalue
as 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 ?
this
is referring to the query herepre
method 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.