映画とタグの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つの質問があります。
- 私の全体的な目的を考えると、これを行うには、ゲッター関数を使用するのが最善の方法ですか?
- 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
}
});
}
});
}