中间件(也称为pre and post hook)是执行异步函数期间传递控制权的函数。中间件在schema级别上被指定并对于编写插件很是有用。Mongoose 4.0有两种中间件:document 中间件和query 中间件。document中间件支持如下document 函数。html
query中间件支持如下model和query函数。api
document中间件和query中间件都支持 pre hooks 和 post hooks。下面详细描述pre hooks 和 post hooks如何工做。异步
注意:没有对remove()的query hook,只有对document的。若是你设置了一个remove hook,执行myDoc.remove()时它会激活,而不是执行MyModel.remove()。async
有2种类型的pre hook,串行(seria)和并行(parallel)。mongoose
串行中间件是一个接一个执行,每一个中间件调用next。函数
var schema = new Schema(..); schema.pre('save', function(next) { // 作些什么 next(); });
并行中间件提供更细粒度的操做post
var schema = new Schema(..); // 'true'表示这是一个并行中间件. 若是你想要使用并行中间件,你必须指定true做为第二个参数 schema.pre('save', true, function(next, done) { // 下一个要执行的中间件并行执行 next(); setTimeout(done, 100); });
在这种状况下,hooked方法直到每一个中间件都调用了done才会执行保存。this
中间件有用于雾化model逻辑和避免异步代码嵌套。这里有一些其余的例子:spa
若是任何一个中间件调用next或done 处理错误实例,流程会被阻止,而且该错误被传递给回调。插件
schema.pre('save', function(next) { // You **must** do `new Error()`. `next('something went wrong')` will // **not** work var err = new Error('something went wrong'); next(err); }); // later... myDoc.save(function(err) { console.log(err.message) // something went wrong });
post中间件在hooked方法和全部它的pre中间件完成后才执行。post中间件不直接接收流控制,如没有next和done回调都传递给它。post hook可以为这些方法注册传统的事件监听器。
schema.post('init', function(doc) { console.log('%s has been initialized from the db', doc._id); }); schema.post('validate', function(doc) { console.log('%s has been validated (but not saved yet)', doc._id); }); schema.post('save', function(doc) { console.log('%s has been saved', doc._id); }); schema.post('remove', function(doc) { console.log('%s has been removed', doc._id); });
虽然post中间件不接受流控制,你仍然可以确保异步post hook能按预先定义的顺序执行。若是你的post hook方法至少须要2个参数,mongoose会假设第二个参数是你将调用的next()函数来依次触发next中间件
// Takes 2 parameters: this is an asynchronous post hook schema.post('save', function(doc, next) { setTimeout(function() { console.log('post1'); // Kick off the second post hook next(); }, 10); }); // Will not execute until the first middleware calls `next()` schema.post('save', function(doc, next) { console.log('post2'); next(); });
save()函数触发validate() hook,由于mongoose 有一个内置的pre('save') hook 执行validate()。这意味着全部pre('validate') 和post('validate') hook在任何pre('save') hook前被调用。
schema.pre('validate', function() { console.log('this gets printed first'); }); schema.post('validate', function() { console.log('this gets printed second'); }); schema.pre('save', function() { console.log('this gets printed third'); }); schema.post('save', function() { console.log('this gets printed fourth'); });
Pre 和 post save() hooks 不在update()、findOneAndUpdate()等执行。 想知道为何你能够看这个GitHub问题。Mongoose 4.0 对这些函数有清楚的hook。
schema.pre('find', function() { console.log(this instanceof mongoose.Query); // true this.start = Date.now(); }); schema.post('find', function(result) { console.log(this instanceof mongoose.Query); // true // prints returned documents console.log('find() returned ' + JSON.stringify(result)); // prints number of milliseconds the query took console.log('find() took ' + (Date.now() - this.start) + ' millis'); });
query中间件与document中间件在一个微秒而重要的地方不一样:在document中间件,这是指正在更新的document。在query中间件,mongoose不必定与document更新有关,所以这是指query对象而不是更新document。
例如,若是你想要增长一个updatedAt时间戳给每一个update(),你要使用下面的pre hook。
schema.pre('update', function() { this.update({},{ $set: { updatedAt: new Date() } }); });