Mongoose で MongoDB の Embedded Documents の扱いで嵌まったこと
node(.js)と相性が良いということで、MongoDB とそのJavaScript O/R マッパーライクなモデリングライブラリの Mongoose を使い始めました。
MongoDBと言えばドキュメント指向モデルでそれを特徴づける Embedded Documents が有名ですが、この機能を Mongoose から利用する上で躓いたので、メモしておきます。
Embbed Document の Array について
var Users = new Schema({ name : String }); var Comments = new Schema({ title : String , body : String , date : Date }); var BlogPost = new Schema({ author : { type: ObjectId, ref: 'User' } , title : String , body : String , date : Date , comments : [Comments] , meta : { votes : Number , favs : Number } }); mongoose.model('User', Users); mongoose.model('BlogPost', BlogPost);
上記のような定義で、あるBlogPostのCommentsを追加して保存するときのコードは次のとおりになります。
BlogPost.findById('xxxx', function(err, blogpost) { var comment = { 'title': "タイトル", 'body': "ボディ" }; blogpost.comments.push(comment); // comment.date = new Date(); blogpost.save(function(err) {}); });
ここでcommentオブジェクトをcommentsプロパティにpushしてるわけですが、上記のコメント行のように push 後にもとのオブジェクトにプロパティをセットしてもblogpostのcommentsプロパティの中身には反映されません。
Array 内のオブジェクトは参照を持っていると思っていると嵌まります。push メソッドであってもcommentsの中身はコピーされただけだと考えてないといけません。
ObjectID の等号比較
各ドキュメントモデルには _id という主キーのようなものが付きますが、これの比較を単純に等号比較してもうまくいきません。先ほどの例でBlogPostのauthorにはUserの_idがセットされるわけですが、下記のようなことができません。
User.findById('xxxx', function(err, user) { BlogPost.find({}, function(err, blogposts) { blogposts.forEach(function(blogpost, array) { if (blogpost.author === user._id) { // 来ない } }); }); })
ObjectIdなのですが、その状態での比較がうまくいかないため、String に変換して比較する必要があるようです。
blogposts.forEach(function(blogpost, array) { if (String(blogpost.author) === String(user._id)) { // 来る } });