第31回に続いて、
MongoDBをRubyから使うためのライブラリ
MongoDBをRubyから使うには、
- Ruby driver for MongoDB [GitHub, チュートリアル]
- 
RubyからMongoDBを使う上で基本となるのが、 公式のRubyドライバです。 MongoDBのほぼ全機能にアクセスできるため、 複雑なことをしたいときには頼りになるでしょう。後述する他のライブラリも内部ではこのドライバを使っています。 
ドキュメントをオブジェクトにマッピングしてActiveRecordライクに扱う、
- MongoMapper [GitHub, 作者による紹介]
- 
比較的ActiveRecordに近いAPIを持つライブラリです。Rails 2または3と組み合わせて使えます。 早くから開発が進められてきたため、 周辺ライブラリの対応が充実しています。 - Devise(認証フレームワーク) 
- Navvy(バックグラウンドジョブワーカ) 
- CarrierWave(ファイルアップロードプラグイン) 
- Machinist Mongo(Fixtures代替ライブラリMachinist用のアダプタ) 
 欠点はnamed_ scope相当の機能がないことでしょうか。 
- Devise
- Mongoid [GitHub, 公式サイト]
- 
後発のライブラリですが、 その開発スピードには目を見張るものがあります。Rails 3で日の目を見たモデル抽象インターフェースActiveModelも一早く取り入れられました。 新しいということもあり、 周辺ライブラリの対応はまだこれからというところです。MongoMapperのところで挙げたライブラリの中では、 CarrierWaveが唯一Mongoidにも対応しています。 
MongoMapperとSinatraでブログを作る
それでは実際にMongoDBを使ったアプリケーションを作ってみましょう。今回題材にするのはブログアプリケーションです。
 
機能はばっさりと削って以下の3つに絞りました。
- 記事を投稿する
- 記事の一覧を表示する
- 記事にコメントを付ける
モデルにMongoMapper、
少々コードが長いため、
- blog.rb -- アプリケーション本体 
- models.rb -- モデル定義 
- views/layout. haml -- レイアウト定義 
- views/new. haml -- 記事投稿画面 
- views/show. haml -- 記事表示/コメント投稿画面 
- views/posts. haml -- 記事一覧画面 
まずモデルを考えます。リレーショナルデータベース的に考えれば、
 
もちろんこれでも実装はできるのですが、
{
  'title': 'Hello',
  'content': 'hello, world!',
  'comments': [
    {
      'name': 'ursm',
      'comment': 'hi!'
    }
  ]
}関連ドキュメントを埋め込むか、
- 速度:
- ドキュメントとそれに埋め込まれたドキュメントは1クエリで高速に取得できますが、参照は都度クエリが発生します。 
- アクセスしやすさ:
- 埋め込まれたドキュメントには埋め込み元のドキュメント経由でしかアクセスできません。
- 一貫性:
- 本来同じドキュメントを複数のドキュメントに埋め込むと、更新時に整合性を保つのが大変になります。 
- サイズ:
- 1つのドキュメントのサイズは埋め込まれたドキュメントを含めて4MBまでという制限があります。
基本的には埋め込んだ方がクエリ数が減ってパフォーマンス上有利なのですが、
今回はコメントを記事に埋め込むことにします。モデルを定義するコードは以下のようになります。
class Post
  include MongoMapper::Document
  key :title, String, :required => true
  key :content, String, :required => true
  timestamps! # created_at, updated_at を定義する
  many :comments
  validates_associated :comments # comments の検証を同時に行う
end
class Comment
  include MongoMapper::EmbeddedDocument
  key :name, String, :required => true
  key :comment, String, :required => true
endEmbeddedDocumentであるCommentをPostが複数所有する関係になっています。
モデルの作成が完了したので、
# 投稿画面を表示する
get '/posts/new' do
  @post = Post.new
  @title = 'New Post'
  haml :new
end
# 投稿処理を行う
post '/posts' do
  @post = Post.new(params[:post])
  if @post.save
    redirect "/posts/#{@post.id}"
  else
    @title = 'New Post'
    haml :new
  end
end/posts/
%ul.errors
  - @post.errors.full_messages.each do |e|
    %li= e
%form.post(action='/posts' method='post')
  %dl
    %dt Title
    %dd
      %input(name='post[title]' type='text'){:value => @post.title}
    %dt Content
    %dd
      %textarea(name='post[content]')!= @post.content
  .actions
    %input(type='submit' value='Post')エラーの表示も含め、
おわりに
本連載では、
MongoDBについてもっと詳しく知りたいと思われた方は、