进行异步调用时,Mongoose getter函数不会返回更新的数组

时间:2012-08-16 05:58:48

标签: node.js mongoose async.js

我有一个应用程序,它使用两个模型:电影和标签。我们的想法是,可以创建电影记录并为其附加标签。电影通过Movie.tags属性引用一个或多个Tag模型,该属性是包含相应标签的ObjectId的数组。

在客户端显示Movie的属性时,显示标记的文本而不是标记的ObjectId是有意义的(请记住:Movie.tags是ObjectIds的数组)。我想到了这个问题并得出结论,最好的方法是使用getter函数,这样当我检索Movie文档时,tags属性的值将从ObjectIds数组转换为相应标记名的数组。

为此,我必须为数组Movie.tags中的每个ObjectId执行db查询。由于db查询是在Mongoose中异步完成的,我尝试使用Async模块中的async.forEach()函数实现getter函数。问题是在async.forEach函数结束时没有返回最终值。

关于这个问题我有两个问题:

  1. 鉴于我的总体目标,使用getter函数是解决此问题的最佳方式吗?
  2. 为什么async.forEach()无法返回更新的数组?
  3. 来自model.js的

    /**
     * Mongo database models
     */
    
    function defineModels(mongoose, async, fn) {
      var Schema = mongoose.Schema,
          ObjectId = Schema.ObjectId;
    
      /**
       * Model - Movie
       */
    
      /**
       * Getter function
       * 
       * Gets tag text as well as tag ObjectId.
       */
      function getTagNames(tags) {
        var newArray = new Array();
        async.forEach(
          tags,
          function(id, done) {
            mongoose.models['Tag'].findOne({ _id: id }, function(err, doc) {
              if (err) {
                done(err);
              }
              else if (doc) {
                newArray.push(doc);
                done(null);
              }
              else {
                console.log(doc);
                // Just incase something weird like no document is found.  
                // Technically this condition should not occur in reality. But we 
                // put something here to catch it just in case.
                done(new Error('No tag document found.'));
              }
            });
          },
          function(err) {
            if (err) {
              throw err;
            }
            console.log(newArray);
            return newArray;
          }
        );
      }
    
      /**
       * Define schema
       */
      Movie = new Schema({
        'name': String,
        'machineFileName': String,
        'originalFileName': String,
        'size': Number,
        'type': String,
        'permanent': { 
          type: Boolean, 
          default: false 
        },
        'dateUploaded': Date,
        'amountUploaded': {
          type: [], 
          default: 0 
        },
        'viewed': Number,
        'uid': String,
        'flags': [],
        'tags': {
          type: Array, 
          get: getTagNames
        }
      }, { strict: true });
    
      mongoose.model('Movie', Movie);
    
      /**
       * Model - Tag
       */
      Tag = new Schema({
        'title': { type: String, unique: true, sparse: true }
      }, { strict: true });
    
      mongoose.model('Tag', Tag);
    
      fn();
    }
    
    exports.defineModels = defineModels; 
    

    检索文档:

    /**
     * View individual movie.
     */
    exports.movie = function(req, res) {
      var Movie = mongoose.model('Movie');
      Movie.findById(req.params.id, function(err, doc) {
        if (err) {
          res.send('An error occured.', 500);
        }
        else {
          console.log('View movie');
          console.log(doc);
          res.render('movie', {
            locals: {
              title: 'Watch Movie',
              movie: doc
            }
          });
        }
      });
    }
    

1 个答案:

答案 0 :(得分:0)

我认为你最好使tags成为一个ObjectId refs数组,然后使用Mongoose的populate在需要时用实际的标记对象填充它。

Movie架构中,tags变为:

tags: [{ type: ObjectId, ref: 'Tag' }]

然后当您查询Movie时:

Movie.findById(req.params.id).populate('tags').exec(function(err, doc) {
    // doc.tags is now an array of Tag instances