読者です 読者をやめる 読者になる 読者になる

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)) {
    // 来る
  }
});