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

时间:2011-11-15 18:11:34

标签: node.js mongoose

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

var PageSchema = new Schema({
    title: String
  , media: {
        digest: String
      , name: String
    }
});

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

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

PageSchema.virtual('media.url').get(function () {
    return appPaths.fileUrl(this.media);
});

但是,宽度/高度或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();
    });
});

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

1 个答案:

答案 0 :(得分: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);
  }
}

然后

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