Rails3 対応 MongoDB ORM、Mongoid 詳解―関連
Mongoid::Document は、embeds_one, embeds_many, embedded_in といった、ActiveRecord スタイルの3つのマクロを通して、他のドキュメントに対して関連を設定することができます。関連を設定すると、1つのドキュメントが他のすべてのドキュメントのルートになり、全ての関連付けられたオブジェクトはルートドキュメントに埋め込まれます。リレーショナルな関連はこれらのマクロでは設定できません。後述するリレーショナルな関連の項を見てください。
先の例の Person モデルが、他のドキュメントと関連する場合を考えてみましょう。
app/models/person.rb:
class Person include Mongoid::Document field :first_name field :last_name embeds_one :address embeds_many :phones end
app/models/address.rb:
class Address include Mongoid::Document field :street field :city field :state field :post_code embedded_in :person, :inverse_of => :address end
app/models/phone.rb:
class Phone include Mongoid::Document field :country_code, :type => Integer, :default => 1 field :number embedded_in :person, :inverse_of => :phones end
上記のモデルが与えられた場合、person は、embeds_one はハッシュ、embeds_many は配列の、BSON 構造としてデータベースに保存されます。embedded_in マクロは、エンベッドがうまく働くように、必ず定義されなければなりません。忘れないで下さい。
{ first_name: "Durran", last_name: "Jordan", address: { street: "30 Rockefeller Plaza", city: "New York", state: "NY", post_code: "10112" }, phones: [ { country_code: 1, number: "212-555-1212" }, { country_code: 1, number: "212-555-1213" } ] }
関連はオプションを取ります。最も大事な必須オプションは、embedded_in マクロにおける inverse_of オプションです。関連を適切に設定し、オブジェクトグラフが、どのオブジェクトに対するどんな変更にも確かに追従することができるように、embedded_in マクロはこのオプションを提供します。その値は親オブジェクトの関連名にしなければなりません。さらに、関連名にクラス名の単数形・複数形と違うものを使いたい場合には、class_name オプションが使用します。前述の例の Person クラスを変更して、phones という関連名を phone_numbers に変更してみましょう。
person.rb:
class Person include Mongoid::Document field :first_name field :last_name embeds_one :address embeds_many :phone_numbers, :class_name => "Phone" end
関連のビルドと作成
関連は、以下のように、セット、追加、ビルド、作成することができます。
embeds_one:
person = Person.new person.build_address(:street => "Oxford Street") person.create_address(:street => "Oxford Street") person.address = Address.new(:street => "Oxford Street")
embeds_many:
person = Person.new person.phone_numbers.build(:number => "415-555-1212") person.phone_numbers.create(:number => "415-555-1212") person.phone_numbers << Phone.new(:number => "415-555-1212") person.phone_numbers = [ Phone.new(:number => "415-555-1212") ]
embedded_in:
address = Address.new address.person = Person.new(:first_name => "Mark")
ポリモーフィックな関連
デフォルトでは、embedded_in 関連は既にポリモーフィックです。どんな名前を与えようと、常に親オブジェクトを返します。「安心毛布」として、:polymorphic => true オプションを付けられますが、実際には何も行ないません。
address.rb:
class Address include Mongoid::Document field :street field :city field :state field :post_code embedded_in :addressable, :inverse_of => :address end
この例は、address.addressable が実際には Person である親オブジェクトを返します。
関連の拡張
Mongoid は無名の関連の拡張をサポートします。@target インスタンス変数を使用して、プロクシされたターゲットにアクセスできます。
person.rb:
class Person include Mongoid::Document field :name embeds_many :addresses do def california @target.select { |address| address.state == "CA" } end end end
上記の例では、person.addresses.california はカルフォルニアのアドレスのみを返します。
リレーショナルな関連
Mongoid は、他のコレクションのドキュメントや他のデータベースにあるオブジェクトへの、基本的なリレーショナルな関連をサポートします。関連付けられたオブジェクトは、関連がうまく動くように、ActiveRecord スタイルのファインダーをサポートしていなければなりません。リレーショナルな関連では、references_one, references_many, referenced_in の3つのマクロが提供されます。マクロを使用したとき、foo_id フィールドが referenced_in 側に作成されます。stored_as オプションを使ったときは、逆に bar_ids が配列として保存されます。
class Person include Mongoid::Document references_one :policy references_many :prescriptions references_many :preferences, :stored_as => :array, :inverse_of => :people end class Policy include Mongoid::Document referenced_in :person end class Prescription include Mongoid::Document referenced_in :person end class Preference include Mongoid::Document references_many :people, :stored_as => :array, :inverse_of => :preferences end person = Person.create policy = Policy.create prescription = Prescription.create person.policy = policy person.prescriptions = [prescription]
(筆者注:Mongoid 2.0.0.beta.16 では以下のようにしないと保存できませんでした。この辺はリファクタリングしている最中っぽいです。バージョンアップ時にこの記事も更新します。)
person.save #=> ダメ。 policy.save prescription.save p1 = Preference.create p2 = Preference.create person.preference_ids = [p1._ids, p2._ids] p1.person_ids = [person._id] p2.person_ids = [person._id] person.save p1.save p2.save
カスケード削除
ActiveRecord と同じように、親オブジェクトが削除された場合に、子オブジェクトも削除したい場合は、references_one と references_many マクロに :dependent オプションを付けます。
class Person include Mongoid::Document references_one :policy, :dependent => :destroy references_many :prescriptions, :dependent => :delete end
関連については以上です。