Associations
A
Mongoid::Document can have associations to other documents through 3 traditional
ActiveRecord-style macros:
embeds_one,
embeds_many and
embedded_in.
When setting up associations, one document will act as the root for all other objects in the
graph, and all associations will be
embedded within that root document in the graph
as well as the database itself. Relational associations to documents in other collections are not handled by these macros. Please see the Relational associations below.
Considering the person model from previous examples, associations to other documents would
be set up like so:
person.rb:
class Person
include Mongoid::Document
field :first_name
field :last_name
embeds_one :address
embeds_many :phones
end
address.rb:
class Address
include Mongoid::Document
field :street
field :city
field :state
field :post_code
embedded_in :person, :inverse_of => :address
end
phone.rb:
class Phone
include Mongoid::Document
field :country_code, :type => Integer, :default => 1
field :number
embedded_in :person, :inverse_of => :phones
end
Given the above models, an example person saved to the database would have this structure in BSON,
where a
embeds_one gets embedded as a hash and a
embeds_many gets embedded as an
array of hashes.
Note that the
embedded_in macro MUST be defined in order for the embedding to work -
don't forget it!
{
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" } ]
}
Associations may have options associated with them, the most important of which is the
required
option of
inverse_of on a
embedded_in association. In order to properly set up the
relationships and make sure the object graph is always up to date with any modifications to any object,
the
embedded_in association must provide this option (and be present itself). The value should
be set to the name of the association in its parent object. In addition, a
class_name option
may be provided if the name of the association differs from a singular or plural form of the document's
class name. We can modify the examples above to show what an updated person would look like with the
phones association name changed:
person.rb:
class Person
include Mongoid::Document
field :first_name
field :last_name
embeds_one :address
embeds_many :phone_numbers, :class_name => "Phone"
end
Building and Creating Associations
Associations can be set directly, appended to, built, or created in certain cases:
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")
Polymorphic Associations
By default, all
embedded_in associations are polymorphic. No matter what name you provide
to the macro it will always return the parent object. You may provide the
:polymorphic => true
option if you like as a "security blanket", but it will actually do nothing extra. An example of
this given the above models would be:
address.rb:
class Address
include Mongoid::Document
field :street
field :city
field :state
field :post_code
embedded_in :addressable, :inverse_of => :address
end
In the above example, address.addressable would actually return the parent object, which is the Person.
Association Extensions
Mongoid supports anonymous association extensions, that have access to the proxied target
via the target instance variable.
person.rb:
class Person
include Mongoid::Document
field :name
embeds_many :addresses do
def california
@target.select { |address| address.state == "CA" }
end
end
end
In the above example, person.addresses.california would return only CA addresses.
Relational Associations
Mongoid supports basic relational associations to documents in another collection, or objects
that reside in another database. The related object must support ActiveRecord style finders
in order for the association to work. The 3 macros provided are
references_one,
references_many, and
referenced_in. When using the macros an id
field will be created on the object that is on the
referenced_in side of the association,
unless using the
:stored_as option, which will store ids for the other objects
as an array on the defined document and vise-versa. Note that the
inverse_of option
is a requirement in this case.
person.rb
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]
Cascading Removals
Similar to ActiveRecord, if you want child relational associations to be
deleted when the parent record is deleted, simply supply the :dependent
option on the references_one or references_many macro.
class Person
include Mongoid::Document
references_one :policy, :dependent => :destroy
references_many :prescriptions, :dependent => :delete
end