Friday, August 18, 2017

What does the context:'query' option do when using mongoose?

Leave a Comment

In a failed attempt learning exercise to get validators to work with 'document.update', I came across something I don't understand.

I know now that it doesn't work, but one of the things I tried was setting my options to {runValidators:true, context:'query'}. In my validator function, I tried console.logging (this), with and without the context:"query" option.

There was no difference. I received a large object (is this called the 'query object'?) This seems to go against what I read here.

In the color validation function above, this refers to the document being validated when using document validation. However, when running update validators, the document being updated may not be in the server's memory, so by default the value of this is not defined.

It was not undefined , even without the context option.

I even tried making it an arrow function to see if the lexical this was any different. In that case, this was undefined, but again, changing the context option did not make a difference. (I'm still learning, so I don't know if that part is relevant).

in the model:

let Property = mongoose.model('Property', {     name: {type:String, required:true},     occupancy: {type:String},     maxTenants: Number,     tenants: [{ type:mongoose.Schema.Types.ObjectId, ref: 'Tenant', validate: [checkMaxTenants, "Maximum tenants exceeded for this property. Tenant not added."]}] }); function checkMaxTenants(val){     console.log("this",this);     // return this.tenants.length <= this.maxTenants;     return true; } 

and in the route:

        property.update({$set: {tenants:property.tenants}},{new:true,runValidators:true,context:'query'}, function(err,savedProperty){ 

Anything to help me better understand the discrepancy between what I think I'm reading and what I see would be great!

1 Answers

Answers 1

At the outset, let's be clear that validators are of two types: document validators and update validators (Maybe you know this already, but the snippet you posted updates a document, where as the issue you mention is of document validation).

There was no difference. I received a large object (is this called the 'query object'?) This seems to go against what I read here.

Document validators are run when you run save on documents as mentioned in the docs.

Validation is middleware. Mongoose registers validation as a pre('save') hook on every schema by default.

Or you can call it manually with .validate()

You can manually run validation using doc.validate(callback) or doc.validateSync()

Update validators are run for update operations

In the above examples, you learned about document validation. Mongoose also supports validation for update() and findOneAndUpdate() operations.

This can be illustrated with the following snippet. For convenience I have changed the type of tenants to a simple integer array, but that should matter for the purpose of our discussion.

// "use strict";  const mongoose = require('mongoose'); const assert = require('assert'); const Schema = mongoose.Schema;  let Property = mongoose.model('Property', {   name: { type: String, required: true },   occupancy: { type:String },   maxTenants: Number,   tenants: [     {       type: Number,       ref: 'Tenant',       validate: {         validator: checkMaxTenants,         message: "Maximum tenants exceeded for this property. Tenant not added."       }     }   ] });  function checkMaxTenants (val) {   console.log("this", this);   // return this.tenants.length <= this.maxTenants;   return true; }  mongoose.Promise = global.Promise; mongoose.createConnection('mongodb://localhost/myapp', {   useMongoClient: true, }).then(function(db) {    const property = new Property({ name: 'foo', occupancy: 'bar', tenants: [1] });    property.update(     { $set: { tenants: [2, 3] } },     {       new: true,       runValidators: true,       // context: 'query'     },     function(err, savedProperty) {      }   )    // property.save(); }); 

Above code with trigger a update validation not document validation

To see document validation in action uncomment property.save() and comment the update operation.

You'll notice that the value of this will be the property document.

this { name: 'foo', occupancy: 'bar', _id: 598e9d72992907120a99a367, tenants: [ 1 ] } 

Comment the save, uncomment back the update operation and you'll see the large object you mentioned.

Now the large object you got, you may not have realised, is the global object when you didn't set context: 'query' and the query object when you set the context.

This can be explained at this line in mongoose's source. When no context was set, mongoose sets the scope to null. And then here the .call is called with the scope.

Now, in non strict mode, when .call is called with null, this is replaced with the global object. So check contents of the large object you got. When context is not set, it would be a global object and not the query object. You can add "use strict"; and see that null will be logged. (The snippet posted can verify this for you). You can verify that you got a query object by running instanceof mongoose.Query against this.

Hope this helps you understand things better.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment