Estus Flask
Estus Flask

Reputation: 222369

Get model count in Mongoose pre hooks

How can document count for particular model be acquired in both save and update Mongoose pre hooks/middlewares?

Considering that this is query in update hook, this works well:

schema.pre('update', function (next) {
  this.model.count().then...
});

But in save hook this

schema.pre('save', function (next) {
  this.count().then...
});

results in

this.count is not a function

When debugging callbacks, this in save hook and this.model in update hook appear to be 'models' (instances of Model). What's the difference between them?

Why does model instance in save hook miss count method? How can these two callbacks be unified to get document count?

I would prefer to stick to this and not hard-code a model, because this allows to use ES6 classes conveniently for schema inheritance.

Upvotes: 3

Views: 6450

Answers (2)

Shaishab Roy
Shaishab Roy

Reputation: 16805

Actually this.model will not work for (pre save hook/middlewares) pre.('save' but this.model will work for pre hook of update, findOneAndUpdate .. etc

for pre.('save' hook you need to use this.constructor instead of this.model like : this.constructor.count or this.constructor.findOne etc.

In my example assume create Schema for Country

so you can use like bellow :

var mongoose = require('mongoose'),
    Schema   = mongoose.Schema;

var CountrySchema = new Schema({
  name: String,
  //....
});

CountrySchema.pre('save', function(next) {
  var self = this;
  self.constructor.count(function(err, data) {
    if(err){
       return next(err);
    }
    // if no error do something as you need and return callback next() without error
    console.log('pre save count==', data);
    return next();
  });
});

CountrySchema.pre('update', function (next) {
  var self = this;
  self.model.count(function(err, data) {
    if(err){
       return next(err);
    }
    // if no error do something as you need and return callback next() without error
    console.log('pre update count===', data);
    return next();
  });
});

module.exports = mongoose.model('Country', CountrySchema);

OR

Can use mongoose.models['modelName'] like: mongoose.models['Country'].count() for example

CountrySchema.pre('save', function(next) {
  mongoose.models['Country'].count(function(err, data) {
    if(err){
       return next(err);
    }
    console.log('pre save count==', data);
    return next();
  });
});

CountrySchema.pre('update', function (next) {
  mongoose.models['Country'].count(function(err, data) {
    if(err){
       return next(err);
    }
    console.log('pre update count===', data);
    return next();
  });
});

N.B: Why this.model not working in pre hook of save ?

Middleware (also called pre and post hooks) are functions which are passed control during execution of asynchronous functions.

In Mongoose has 2 types of middleware:

  1. Document middleware and
  2. Query middleware
  1. Document middleware is supported for functions.

    init, validate, save, remove

  2. Query middleware is supported for functions.

    count, find, findOne, findOneAndRemove, findOneAndUpdate, insertMany,update

In Mongoose Query middleware this.model is the instance of your model that generated/defined by Mongoose. in this middleware this return all instance variable that defined by mongoose.

where as in Document middleware this return all fields that you defined not by mongoose so this.model not your defined property. for above example I have name property so you can get that by this.name that will show your requested value. But when use this.contructor then you will return instance variable that are defined by mongoose like return Model instance variable.

Upvotes: 15

cjungel
cjungel

Reputation: 3781

You could use the name of the model variable instead of this.

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var schema = new mongoose.Schema;
schema.add({ name: 'string' });

var Cat = mongoose.model('Cat', schema);

schema.pre('save', function (next) {
  Cat.count(function (err, count) {
    if (err) {
      console.log(err)
    } else {
      console.log('there are %d kittens', count);
    }
    next();
  });
})

var kitty = new Cat({ name: 'Zildjian' });
kitty.save(function (err) {
  if (err) {
    console.log(err);
  } else {
    console.log('meow');
  }
});

Subsequent executions report the correct count and then perform the save operation.

$ node index.js 
there are 0 kittens
meow
$ node index.js 
there are 1 kittens
meow
$ node index.js 
there are 2 kittens
meow
$ node index.js 
there are 3 kittens
meow

And documents are being persisted in the database.

db.cats.find()
{ "_id" : ObjectId("5873b77454fc2a49fd7ec6cf"), "name" : "Zildjian", "__v" : 0 }
{ "_id" : ObjectId("5873b777c051094a0ad5dda2"), "name" : "Zildjian", "__v" : 0 }
{ "_id" : ObjectId("5873b7789aad6e4a15018ee8"), "name" : "Zildjian", "__v" : 0 }
{ "_id" : ObjectId("5873b77b7628684a24c90b07"), "name" : "Zildjian", "__v" : 0 }

Upvotes: 1

Related Questions