Ruby Freaks Lounge
第33回 RubyistのためのMongoDB入門(2)
第31回に続いて,今回はMongoDBをRubyから使う方法をご紹介します。
MongoDBをRubyから使うためのライブラリ
MongoDBをRubyから使うには,以下のライブラリ等を利用する必要があります。
- Ruby driver for MongoDB [GitHub, チュートリアル]
-
RubyからMongoDBを使う上で基本となるのが,公式のRubyドライバです。
MongoDBのほぼ全機能にアクセスできるため,複雑なことをしたいときには頼りになるでしょう。後述する他のライブラリも内部ではこのドライバを使っています。
ドキュメントをオブジェクトにマッピングしてActiveRecordライクに扱う,オブジェクトマッパーの機能を提供するライブラリもあります。今回は代表的なものを2つご紹介します。
- MongoMapper [GitHub, 作者による紹介]
-
比較的ActiveRecordに近いAPIを持つライブラリです。Rails 2または3と組み合わせて使えます。
早くから開発が進められてきたため,周辺ライブラリの対応が充実しています。
- Devise(認証フレームワーク)
- Navvy(バックグラウンドジョブワーカ)
- CarrierWave(ファイルアップロードプラグイン)
- Machinist Mongo(Fixtures代替ライブラリMachinist用のアダプタ)
欠点はnamed_scope相当の機能がないことでしょうか。
- Mongoid [GitHub, 公式サイト]
-
後発のライブラリですが,その開発スピードには目を見張るものがあります。Rails 3で日の目を見たモデル抽象インターフェースActiveModelも一早く取り入れられました。
新しいということもあり,周辺ライブラリの対応はまだこれからというところです。MongoMapperのところで挙げたライブラリの中では,CarrierWaveが唯一Mongoidにも対応しています。
MongoMapperとSinatraでブログを作る
それでは実際にMongoDBを使ったアプリケーションを作ってみましょう。今回題材にするのはブログアプリケーションです。
機能はばっさりと削って以下の3つに絞りました。
- 記事を投稿する
- 記事の一覧を表示する
- 記事にコメントを付ける
モデルにMongoMapper,ビューにHaml,繋ぎにSinatraを使います。SinatraとHamlについては第7回・第9回の記事も合わせてご覧ください。
少々コードが長いため,全文はGitHubのリポジトリをご参照ください。ファイル構成は以下のようになっています。
- blog.rb -- アプリケーション本体
- models.rb -- モデル定義
- views/layout.haml -- レイアウト定義
- views/new.haml -- 記事投稿画面
- views/show.haml -- 記事表示/コメント投稿画面
- views/posts.haml -- 記事一覧画面
まずモデルを考えます。リレーショナルデータベース的に考えれば,記事とコメントにそれぞれコレクションを割り当てて参照を持たせれば良さそうです。
もちろんこれでも実装はできるのですが,MongoDBの場合はコメントを記事に埋め込むという選択肢があります。コメントを埋め込んだ記事をJSONで表現すると以下のようになります。
{
'title': 'Hello',
'content': 'hello, world!',
'comments': [
{
'name': 'ursm',
'comment': 'hi!'
}
]
}
関連ドキュメントを埋め込むか,参照にするかはケースバイケースです。
- 速度:
- ドキュメントとそれに埋め込まれたドキュメントは1クエリで高速に取得できますが,参照は都度クエリが発生します。
- アクセスしやすさ:
- 埋め込まれたドキュメントには埋め込み元のドキュメント経由でしかアクセスできません。
- 一貫性:
- 本来同じドキュメントを複数のドキュメントに埋め込むと,更新時に整合性を保つのが大変になります。
- サイズ:
- 1つのドキュメントのサイズは埋め込まれたドキュメントを含めて4MBまでという制限があります。
基本的には埋め込んだ方がクエリ数が減ってパフォーマンス上有利なのですが,例えばコメント一覧ページが欲しいなら参照にした方がいいかもしれません。アプリケーションの要求に応じて適切な構成を考える必要があります。
今回はコメントを記事に埋め込むことにします。モデルを定義するコードは以下のようになります。
MongoMapperによるモデル定義(models.rb)
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
end
EmbeddedDocumentであるCommentをPostが複数所有する関係になっています。
モデルの作成が完了したので,次はアプリケーション本体に移ります。記事の投稿処理を見てみましょう。
記事の投稿処理(blog.rb)
# 投稿画面を表示する
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/newからフォーム経由で/postsに送信されます。ビューも見てみましょう。
投稿画面(views/new.haml)
%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')
エラーの表示も含め,ActiveRecordとほとんど変わりませんね。
おわりに
本連載では,ドキュメント指向データベースMongoDBについて簡単に紹介させていただきました。
MongoDBについてもっと詳しく知りたいと思われた方は,ドキュメントの日本語訳をされているMasatomo Nakanoさんの記事「MongoDBを今日から始めるためのドキュメント」がとても参考になりますので一読をおすすめします。この記事を執筆するときも日本語訳を大いに参考にさせていただきました。この場を借りてお礼申し上げます。

