node.js + mongoose + mongodbで遊ぶ

NoSQLのmongodbは何がいいって、Javascriptととの親和性が高い。とりたてnode.jsで使うには最適と言っても良いんではないでしょうか。コマンドラインインターフェイスがモロにJavascriptというのもアツイです

$ mongo
MongoDB shell version: 1.6.5
connecting to: test
> show dbs
admin
local
test
blog
> use blog
switched to db blog
> show collections
modelskills
posts
syste,indexes
> db.posts.drop()
true
> db.modelskills.find().forEach(printjson)
...

これだけでmongodbを選ぶ理由になります←

mongooseを入れる

さて、こいつをシェルから使うだけでもまあ面白いですが、折角node.jsでも遊んでるのでnode.jsからmongodbを使いたいと思います。今回はmongooseというmongodbに対してORM風にアクセスできるライブラリを利用します。導入にはnpmを利用しました。

$ npm install mongoose


必要なものは勝手に揃えていれてくれるのであまり気にしなくていいです。あ、えーと、このORM風なmongooseですが、ここ最近大きく改修が入りまして(というかバージョンアップ)、最近ネットでググると出てくるチュートリアル的なソースはほとんど使えません(ver 0.0.5辺りの物)


1.0.xになって大きく変わったと思う点が2つ

  • Schemaの導入
  • Middlewareの導入

でしょうか。


Schemaの使い方

mongooseのサイトにも簡単なモデルの定義例がありますが、Schemaとは要するにモデルの雛形的な物です。

var mongoose = require('mongoose');

// Default Schemaを取得
var Schema = mongoose.Schema;

//配列に格納する埋め込みドキュメントのスキーマ定義
var CommentsSchema = new Schema({
  title: String,
  body: String,
  date: Date
});

// Defaultのスキーマから新しいスキーマを定義
var PostSchema = new Schema({
  title: String,
  body: String,
  date: Date
  comments: [CommentsSchema], //配列もOK。ここにコメントモデルが格納される
  metadata: {
    votes: Number,
    favs: Number
  }
});

// モデル化。model('[登録名]', '定義したスキーマクラス')
mongoose.model('Post', PostSchema);

特に難しくは無いですね。とてもわかりやすいです。このスキーマ定義、fieldの定義だけならわざわざSchemaなんて仰々しい物を使う必要ありません。ここで先ほど書いた目玉の2つ目、middlewareが出てくるんですね

//
PostSchema.pre('init', function(next) {
  //ドキュメント初期化時のフックとして処理したいこと
  console.log('init done');
  next();
}

PostSchema.pre('save', function(next) {
  //ドキュメント保存時にフックして処理したいこと
  console.log('save done');
  next();
}

Schemaにpre-hookとして予めメソッドを指定して、必要な処理を記述すると、そのメソッドが実行されたときに自動的に処理されます。これはいろいろ使い道があるんじゃないでしょうか。なお、上に書いたのは同期的な記述で非同期でも可能です

PostSchema.pre('save', function(next, done) {
  //next()は即時実行されるが…
  otherFunc(done);
  next();
}

…と思いきやなんか非同期で動いていないような気がする。otherFunc()内でdone()を叩けばいいんですが?なんか使い方間違ってる?ここ、非同期の部分だけちょっとよくわかりません><w

Schemaには他にも仮想属性(Virtual Attribute)やField定義のカスタムオプションとかいろいろあるんですがまだ勉強ちゅー

カスタムオプションの例
 var Person = new Schema({
      title   : { type: String, required: true }
    , age     : { type: Number, min: 5, max: 20 }
    , meta    : {
          likes : [String]
        , birth : { type: Date, default: Date.now }
      }
  });

使い方

さてSchemaを定義したはいいが、ようは使わないと意味がありません。新しいドキュメントを保存する場合は

//mongodb://[hostname]/[dbname]
mongoose.connect('mongodb://localhost/blogdb');

//定義したときの登録名で呼び出し
var Post = mongoose.model('Post');

var post = new Post();

//ドット表記で各Fieldへアクセス
post.title = 'test title';
post.body = 'this is a pen';

//埋め込みドキュメントな配列にはpush()を使う
post.comments.push({title: 'comment title', body: 'comment body'});

//pre-hookを定義しているとsave時に定義した関数が走る
post.save(function(err) {
  if(!err) console.log('saved!')
});

な感じです。モデルを使ってドキュメントを検索する場合は

//find()の第1引数にmongodbのQueryが入ります。
Post.find({title:/title/m}, function(err, docs) {
  if(!err) {
    for (var i = 0; i < docs.length; i++ ) {
      console.log(docs[i]);
    }
  }
});

てな感じです。


基本的にはORM風なので、もっと手軽でもいいかなぁ、なんて思ってたりしますがこれはこれで非常に便利ではないでしょうか。