Mongoose: Can I access the plain query object inside pre find middleware?

Created on 6 Mar 2017  路  4Comments  路  Source: Automattic/mongoose

I am attaching a pre find middleware to Schema.

Inside the handler I want to check whether the given query set has a strict property (say 'companyId'), if the query misses that property then I would call next callback with error object.

It would be great if I can access the plain query object which I pass during find function call.

Example:

Item Schema:

var ItemSchema = mongoose.Schema({
  name: { type: String, },
  companyId: { type :mongoose.Schema.Types.ObjectId, ref: 'Company', required: true, },
});

ItemSchema.pre('find', function () {
  if (this.queryHas('companyId')) { // Something like this
    next();
  } else {
    next(new Error('query misses companyId.'))
  }
});
Item.find({ name: 'car' }).then(function (err, itemDocs) {
  if (err) {
    console.error('Failed to query items. Error:', err);
  } else {
    console.error('Items:', itemDocs);
  }
});

Expected Output:

Failed to query items: Error: [query misses companyId.]

So when I query item collection without companyId the middleware should throw me an error like the above example.

How do I achieve the above?

Most helpful comment

Inside query middleware, the context is the Query. Each query has a getQuery() method: http://mongoosejs.com/docs/api.html#query_Query-getQuery

so inside the hook if you call this.getQuery(), you'll get all the fields passed in in the query.

All 4 comments

Inside query middleware, the context is the Query. Each query has a getQuery() method: http://mongoosejs.com/docs/api.html#query_Query-getQuery

so inside the hook if you call this.getQuery(), you'll get all the fields passed in in the query.

Thanks @varunjayaraman.
I read through lib/query.js file and came to know about getQuery method. It serves my purpose. 馃憤

I am facing a different problem now.
Problem: pre find hooks are invoked for populate queries as well.

Example:

// Item Schema
var ItemSchema = mongoose.Schema({
  name: { type: String, },
  companyId: { type :mongoose.Schema.Types.ObjectId, ref: 'Company', required: true, },
  category: { type :mongoose.Schema.Types.ObjectId, ref: 'Category', required: true, },
});

ItemSchema.pre('find', function () {
  if (this.getQuery().companyId) {
    next();
  } else {
    next(new Error('Item query misses companyId.'))
  }
});

ItemModel = mongoose.model('Item', ItemSchema);

// Category Schema
var CategorySchema = mongoose.Schema({
  name: { type: String, },
  companyId: { type :mongoose.Schema.Types.ObjectId, ref: 'Company', required: true, },
});

CategorySchema.pre('find', function () {
  if (this.getQuery().companyId) {
    next();
  } else {
    next(new Error('Category query misses companyId.'))
  }
});

CategoryModel = mongoose.model('Category', CategorySchema);
ItemModel.find({
  name: 'car',
  companyId: '58914ea17beda294fae143dd'
})
.populate('category') // <-- NOTE
.exec(function (err, itemDocs) {
  if (err) {
    console.error('Failed to query items. Error:', err);
  } else {
    console.error('Items:', itemDocs);
  }
});

Output:

Failed to query items. Error: [Category query misses companyId.]

From the output you can see that populate query hits pre find hook of Category Schema and checks for companyId.

Inside pre find hook, is there a way to differentiate whether the query is a populate query or normal query?
So that if its a populate query I can bypass companyId check.

I have the same problem as @Sundarasan has, how to check whether the query is a populate query or normal query. This issue is closed, but there's no answer to the question?

@bojanbass @Sundarasan You'll need to use getPopulatedPaths: https://mongoosejs.com/docs/api.html#query_Query-getPopulatedPaths

Was this page helpful?
0 / 5 - 0 ratings