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と組み合わせて使えます。

早くから開発が進められてきたため、周辺ライブラリの対応が充実しています。

欠点はnamed_scope相当の機能がないことでしょうか。

Mongoid [GitHub, 公式サイト]

後発のライブラリですが、その開発スピードには目を見張るものがあります。Rails 3で日の目を見たモデル抽象インターフェースActiveModelも一早く取り入れられました。

新しいということもあり、周辺ライブラリの対応はまだこれからというところです。MongoMapperのところで挙げたライブラリの中では、CarrierWaveが唯一Mongoidにも対応しています。

MongoMapperとSinatraでブログを作る

それでは実際にMongoDBを使ったアプリケーションを作ってみましょう。今回題材にするのはブログアプリケーションです。

図1 MongoDBによるブログアプリケーション「MongoBlog」
図1 MongoDBによるブログアプリケーション「MongoBlog」

機能はばっさりと削って以下の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 -- 記事一覧画面

まずモデルを考えます。リレーショナルデータベース的に考えれば、記事とコメントにそれぞれコレクションを割り当てて参照を持たせれば良さそうです。

図2 参照を使ったモデル構成
図2 参照を使ったモデル構成

もちろんこれでも実装はできるのですが、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を今日から始めるためのドキュメントがとても参考になりますので一読をおすすめします。この記事を執筆するときも日本語訳を大いに参考にさせていただきました。この場を借りてお礼申し上げます。

おすすめ記事

記事・ニュース一覧