Rails で MongoDB を使ってみた(2)

2010/03/25

前回に引き続き、MongoDB で Rails アプリを作る話。

今回は関連づけに挑戦だ。

参考資料:

app/documents/person.rb に has_one :address を追加。

class Person
  include Mongoid::Document
  field :first_name
  field :last_name
  has_one :address

  def full_name
    "#{first_name} #{last_name}"
  end
end

app/documents/address.rb を作成。ただし、ほとんど参考資料からのパクリ。

class Address
  include Mongoid::Document
  field :street
  field :city
  field :state
  field :post_code
  belongs_to :person, :inverse_of => :address

  def to_s
    [ street, city, state, post_code ].select { |value| value.present? }.join(', ')
  end
end

app/views/people/_form.html.erb

<% form_for @person do |f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name %><br />
<%= f.label :last_name %>
<%= f.text_field :last_name %><br />
<fieldset>
  <legend>Address</legend>
  <% f.fields_for :address do |g| %>
    <%= g.label :street %>
    <%= g.text_field :street %><br />
    <%= g.label :city %>
    <%= g.text_field :city %><br />
    <%= g.label :state %>
    <%= g.text_field :state %><br />
    <%= g.label :post_code %>
    <%= g.text_field :post_code %><br />
  <% end %>
</fieldset>
<%= f.submit %>
<% end %>

app/views/people/new.html.erb

<h1>People#new</h1>

<%= render :partial => 'form' %>

app/views/people/edit.html.erb

<h1>People#edit</h1>

<%= render :partial => 'form' %>

app/views/people/show.html.erb

<h1>People#show</h1>

First Name: <%= @person.first_name %><br />
Last Name: <%= @person.last_name %><br />
Address: <%= @person.address %><br />

<%= link_to 'List', :people %>

これで完成。ほとんど ActiveRecord と同じように使えることが分かった。


さて参考文献によれば、実際には Person オブジェクトと Address オブジェクトが「関連づけられる」というより、Address オブジェクトがハッシュとして Person オブジェクトに「埋め込まれる」ようだ。

ちょっと説明しづらいが、RDBMSと対比すると理解しやすいだろう。

RDBMSでは persons テーブルと address テーブルにそれぞれ 1 つずつレコードがあって、これらの2つのレコードが関連づけられる。

しかし、MongoDB は「ドキュメント」を集合として管理するデータベースだ。

つまり JSON で

{
  first_name: "Tsutomu",
  last_name: "Kuroda",
  address: {
    street: "30 Rockefeller Plaza",
    city: "New York",
    state: "NY",
    post_code: "10112"
  }
}

と表現できるようなオブジェクトを丸ごとデータベースに放り込む。

ただし、person.rb の has_one を has_one_related に変更すれば、Address オブジェクトはコレクションの要素としてデータベースに格納されるようになる。この場合、Person オブジェクトには Address オブジェクトの id だけが記録される。

http://github.com/durran/mongoid/ で最新のコードを見ると、has_one メソッドは embed_one メソッドに名前が変わっている。ActiveRecord と明らかに振る舞いが異なるので、作者の気が変わったようだ。