使用mongoose中间件添加异步虚拟

Phi*_*ier 3 mongoose node.js

在node.js/Mongoose项目中,我有一个包含对外部图像文件的引用的模式.

var PageSchema = new Schema({
    title: String
  , media: {
        digest: String
      , name: String
    }
});
Run Code Online (Sandbox Code Playgroud)

这些文件具有存储在文件本身中的其他属性:url,width,height,exif字段等.在将模型发送到res.render()之前,需要填充这些字段.

对于某些领域,事情是同步的,虚拟只是完成工作:

PageSchema.virtual('media.url').get(function () {
    return appPaths.fileUrl(this.media);
});
Run Code Online (Sandbox Code Playgroud)

但是,宽度/高度或exif字段需要异步调用.我想过使用中间件来填充它们,但这似乎不起作用:

PageSchema.post('init', function(next) {
    var media = this.media;
    var fileName = filedb.absoluteFilePath(media);

    im.identify(fileName, function(err, features) {
        if (err) {
            media.width = 0;
            media.height = 0;
        } else {
            media.width = features.width;
            media.height = features.height;
        }

        next();
    });
});
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?是否有解决此类问题的通用设计模式?(除了在数据库本身中复制这些信息外?)

Dan*_*del 5

这里真正的问题是,mongoose目前似乎有一个不稳定的post回调实现.虽然pre('init',function(next){ ... });按预期工作,post('init',function(next){ ... });但实际上并没有传递next函数.事实上,post init回调在调用时不会收到任何参数.

因此,我经常为查询回调编写一个包装器来制作一种DIY中间件:

var setAsyncVirtuals = function(callback){
  return function(err, docs){
    if(err) return callback(err);
    var i = done = docs.length;
    if(i > 0)
      while(i--){
        (function(i){
          var filename = getFilename();
          im.identify(filename, function(err, features) {
            if (err) {
              docs[i].media.width = 0;
              docs[i].media.height = 0;
            } else {
              docs[i].media.width = features.width;
              docs[i].media.height = features.height;
            }
            done--;
            if(done <= 0) callback(null, docs);
          });
        })(i); // bind i to hold value for async call
      }
    else callback(null, docs);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后

Page.find({}, setAsyncVirtuals(function(err,docs){
  res.send(docs); // these have media.width & media.height assigned
}));
Run Code Online (Sandbox Code Playgroud)