0

I would like to increment a counter in my pre save callback. I found this stackoverflow really useful to do so: Does Mongoose support the Mongodb `findAndModify` method?

What I would like to do is use the findAndModify method. But when I implement the statics my squence of callbacks is not as expected. I do always and pre save and then execute findAndModify but I would like do execute findAndModify in between start and end of the pre save hook. If I define a general method with a callback it works as expected.

worked also with the done parameter of the pre-save hook without any different result

What do I miss here

My code looks like this:

var mongoose = require('mongoose');
var should = require('should');

mongoose.connect("localhost","test_db");

var CommentSchema = new mongoose.Schema({
  content:    {type:String},
  created_at: {type:Date, default:Date.now},
  _post:{type:mongoose.Schema.ObjectId,ref:'Post'}

});

var PostSchema = new mongoose.Schema({
  title:    {type:String},
  content:  {type:String},
  comments: [{type:mongoose.Schema.ObjectId, ref:'Comment'}],
  counter: {type:Number}

    });

PostSchema.statics.findAndModify= function(query,sort,doc,options,callback){
  return this.collection.findAndModify(query,sort,doc,options,callback);
 }

PostSchema.statics.test_me = function(clb){
   console.log("test_me");
   clb();
}
CommentSchema.pre('save',function(next,done){
  console.log("enter pre save comment");
  if(this.isNew){
   Post.findAndModify({_id:this._post},[],{$inc:{count:1}},{new:true},function(err,post){
    console.log("enter find-and-modify!");
    console.log(post);
   });
  Post.test_me(function(){
    console.log("callback of test_me");
  });
  console.log("exit pre save comment");

    next();
  }
});

var Post = mongoose.model('Post',PostSchema);
var Comment = mongoose.model('Comment',CommentSchema);

var post = new Post({title:"hello world"});
var comment = new Comment({content:"1st comment",_post:post});
post.comments.push(comment);

  var id = post.id;
  console.log(id);
  post.save(function(err){
    comment.save(function(err){
        Post.find({_id:id })
          .populate('comments')
          .exec(function(err,result){
          console.log("--------------- result -----------------");
          console.log(result);
          });
    });
  });

This is the result from my command-line:

5049f0d2e21547430a000001
enter pre save comment
test_me
callback of test_me
exit pre save comment
enter find-and-modify!
{ __v: 0,
  _id: 5049f0d2e21547430a000001,
  comments: [ 5049f0d2e21547430a000002 ],
  count: 1,
  title: 'hello world' }
--------------- result -----------------
[ { __v: 0,
    _id: 5049f0d2e21547430a000001,
    count: 1,
    title: 'hello world',
    comments: 
     [ { content: '1st comment',
         _post: 5049f0d2e21547430a000001,
         _id: 5049f0d2e21547430a000002,
         __v: 0,
         created_at: Fri Sep 07 2012 15:04:18 GMT+0200 (CEST) } ] } ]

EDIT: I do not want to know how to execute findAndModify with test_me in a sequence. I want to know why findAndMody enters after pre-saved is finished. Even it is embedded and should work as demonstrated with the test_me method. So the test_me method should illustrate that a async method should work nested... but findAndModify does not... like my command-line output shows...it always enters findAndModify after pre-save exits even when I use the done() callback...

Ahmad Baktash Hayeri
  • 5,802
  • 4
  • 30
  • 43
silverfighter
  • 6,762
  • 10
  • 46
  • 73

1 Answers1

0

Since we are dealing with async, it will run

console.log("enter pre save comment");
if(this.isNew){
  Post.findAndModify({_id:this._post},[],{$inc:{count:1}},{new:true},function(err,post){
    console.log("enter find-and-modify!");
    console.log(post);
  });
  Post.test_me(function(){
    console.log("callback of test_me");
  });
  console.log("exit pre save comment");
  next();
}

one after the other, not waiting for it to execute or a response before moving on to the next. Since Post.findAndModify() takes longer to execute, anything in the callback function will execute after anything that is outside the callback function.

If you want it to execute more in order

console.log("enter pre save comment");
if(this.isNew){
  Post.findAndModify({_id:this._post},[],{$inc:{count:1}},{new:true},function(err,post){
    console.log("enter find-and-modify!");
    console.log(post);
    Post.test_me(function(){
      console.log("callback of test_me");
      console.log("exit pre save comment");
      next();
    });
  });     
}
Last Rose Studios
  • 2,461
  • 20
  • 30
  • thanks for your reply... yes I know the reason but and it make sense but how could I avoid it? even when I pass the done callback in the pre save hook it does not work – silverfighter Sep 07 '12 at 20:04
  • if you want it to execute in order, either use async.js to get series or nest your callbacks. – Last Rose Studios Sep 07 '12 at 20:11
  • sorry for the misunderstanding I edited the question to hopfully make more sense.. I would like to know how comes that findAndModify enters always when pre save exits... – silverfighter Sep 07 '12 at 20:23
  • it's executing next() async to your findAndModify, meaning that it isn't waiting for findAndModify to finish before moving on to the next function. – Last Rose Studios Sep 07 '12 at 21:06
  • Yes that's what I thought too but it's not working... the native findAndModify behaves different then my self written async method – silverfighter Sep 08 '12 at 11:50
  • @silverfighter I don't know the exact order of callbacks(between the 2 collections) but based on your schema with each document pointing at each other seems like you would want to: 1- first save the Post, 2- Remove the embedded comments collection from the Post document which stores ids of comments and instead add an index on the Comment collection for the _post field. This will yield simpler logic – Monica Wilkinson Sep 13 '12 at 19:41