0

映画とタグの2つのモデルを使用するアプリがあります。アイデアは、映画のレコードを作成し、それにタグを付けることができるということです。ムービーは、対応するタグのObjectIdを含む配列であるMovie.tags属性を介して1つ以上のタグモデルを参照します。

クライアント側でMovieのプロパティを表示する場合、タグのObjectIdではなく、タグのテキストを表示するのが理にかなっています(Movie.tagsはObjectIdの配列であることに注意してください)。問題について考え、getter関数を使用するのが最善の方法であると結論付けました。これにより、Movieドキュメントを取得するときに、tags属性の値がObjectIdの配列から対応するタグ名の配列に変換されます。

これを行うには、配列Movie.tagsの各ObjectIdに対してdbクエリを実行する必要があります。データベースクエリはMongooseで非同期に実行されるため、Asyncモジュールのasync.forEach()関数を使用してgetter関数を実装してみました。問題は、async.forEach関数の最後に最終値が返されないことです。

この問題に関して2つの質問があります。

  1. 私の全体的な目的を考えると、これを行うには、ゲッター関数を使用するのが最善の方法ですか?
  2. async.forEach()が更新された配列を返さないのはなぜですか?

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
        }
      });
    }
  });
}
4

1 に答える 1

0

tagsObjectId 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
于 2012-08-16T13:41:33.313 に答える