0

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

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

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

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

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

来自模型.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
        }
      });
    }
  });
}
4

1 回答 1

0

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

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
于 2012-08-16T13:41:33.313 回答