Associations¶
On this page
Referenced Associations¶
Mongoid supports the has_one
, has_many
, belongs_to
and
has_and_belongs_to_many
associations familiar to ActiveRecord users.
Has One¶
Use the has_one
macro to declare that the parent has a child stored in
a separate collection. The child is optional by default:
When using has_one
, the child model must use belongs_to
to declare the
association with the parent:
Given the above definitions, every child document contains a reference to its respective parent document:
Use validations to require that the child is present:
Has Many¶
Use the has_many
association to declare that the parent has zero or more
children stored in a separate collection:
Like with has_one
, the child model must use belongs_to
to declare the
association with the parent:
Also as with has_one
, the child documents contain references to their
respective parents:
Use validations to require that at least one child is present:
Queries¶
Use the #exists?
method on the association to efficiently determine whether
the association contains any documents, without retrieving the entire set
of documents from the database:
Belongs To¶
Use the belongs_to
macro to associate a child with a parent stored in a
separate collection. The _id
of the parent (if a parent is associated)
is stored in the child.
By default, if a belongs_to
association is defined on a model, it must be
provided a value for a model instance to be saved. Use the optional: true`
option to make the instances persistable without specifying the parent:
To change the default behavior of belongs_to
associations to not require
their respective parents globally, set the belongs_to_required_by_default
configuration option to false
.
Although has_one
and has_many
associations require the
corresponding belongs_to
association to be defined on the child,
belongs_to
may also be used without a corresponding has_one
or
has_many
macro. In this case the child is not accessible from the parent
but the parent is accessible from the child:
For clarity it is possible to add the inverse_of: nil
option in cases when
the parent does not define the association:
Has And Belongs To Many¶
Use the has_and_belongs_to_many
macro to declare a many-to-many
association:
Both model instances store a list of ids of the associated models, if any:
You can create a one-sided has_and_belongs_to_many
association to store
the ids only in one document using the inverse_of: nil
option:
A one-sided has_and_belongs_to_many
association is, naturally, only
usable from the model where it is defined.
Embedded Associations¶
Thanks to MongoDB’s document model, Mongoid also offers embedded associations
which allow documents of different types to be stored hierarchically
in the same collection. Embedded associations are defined using
embeds_one
, embeds_many
and embedded_in
macros, plus
recursively_embeds_one
and ``recursively_embeds_many``for recursive
embedding.
Embeds One¶
One to one associations where the children are embedded in the parent
document are defined using Mongoid’s embeds_one
and embedded_in
macros.
Defining¶
The parent document of the association should use the embeds_one
macro to
indicate is has one embedded child, where the document that is embedded uses
embedded_in
. Definitions are required on both sides to the association
in order for it to work properly.
Storage¶
Documents that are embedded using the embeds_one
macro are stored as a
hash inside the parent in the parent’s database collection.
You can optionally tell Mongoid to store the embedded document in a different
attribute other than the name, by providing the :store_as
option.
Embeds Many¶
One to many relationships where the children are embedded in the parent
document are defined using Mongoid’s embeds_many
and embedded_in
macros.
Defining¶
The parent document of the association should use the embeds_many
macro
to indicate it has many embedded children, where the document that is
embedded uses embedded_in
. Definitions are required on both sides of
the association in order for it to work properly.
Storage¶
Documents that are embedded using the embeds_many
macro are stored as
an array of hashes inside the parent in the parent’s database collection.
You can optionally tell Mongoid to store the embedded document in a different
attribute other than the name, by providing the :store_as
option.
Recursive Embedding¶
A document can recursively embed itself using recursively_embeds_one
or
recursively_embeds_many
, which provides accessors for the parent and
children via parent_
and child_
methods.
Referencing Vs Embedding¶
While a complete discussion of referencing vs embedding is beyond the scope of this tutorial, here are some high level considerations for choosing one over the other.
When an association is embedded, both parent and child documents are stored in the same collection. This permits efficient persistence and retrieval when both are used/needed. For example, if the navigation bar on a web site shows attributes of a user that are stored in documents themselves, it is often a good idea to use embedded associations.
Using embedded associations allows using MongoDB tools like the aggregation pipeline to query these documents in a powerful way.
Because embedded documents are stored as part of their parent top-level documents, it is not possible to persist an embedded document by itself, nor is it possible to retrieve embedded documents directly. However, embedded documents can still be efficiently queried and retrieved with the help of MongoDB projection operation:
Common Behavior¶
Extensions¶
All associations can have extensions, which provides a way to add application specific functionality to the association. They are defined by providing a block to the association definition.
Custom Association Names¶
You can name your associations whatever you like, but if the class cannot be inferred by Mongoid from the name, and neither can the opposite side you’ll want to provide the macro with some additional options to tell Mongoid how to hook them up.
Custom Primary & Foreign Keys¶
The fields used when looking up associations can be explicitly specified.
The default is to use id
on the “parent” association and #{association_name}_id
on the “child” association, for example with a has_many/belongs_to:
Specify a different primary_key
to change the field name on the “parent”
association and foreign_key
to change the field name on the “child”
association:
With a has_and_belongs_to_many association, since the data is stored on both sides of the association, there are 4 fields configurable when the association is defined:
:primary_key
is the field on the remote model that contains the value by which the remote model is looked up.:foreign_key
is the field on the local model which stores the:primary_key
values.:inverse_primary_key
is the field on the local model that the remote model uses to look up the local model docuemnts.:inverse_foreign_key
is the field on the remote model storing the values in:inverse_primary_key
.
An example might make this more clear:
Note that just like with the default #{association_name}_id
field,
Mongoid automatically adds a field for the custom foreign key c_ref
to
the model. However, since Mongoid doesn’t know what type of data should be
allowed in the field, the field is created with a type of Object. It is a
good idea to explicitly define the field with the appropriate type.
Validations¶
It is important to note that by default, Mongoid will validate the children of any
association that are loaded into memory via a validates_associated
. The associations that
this applies to are:
embeds_many
embeds_one
has_many
has_one
has_and_belongs_to_many
If you do not want this behavior, you may turn it off when defining the association.
Polymorphism¶
One to one and one to many associations support polymorphism, which is having a single association potentially contain objects of different classes. For example, we could model an organization in which departments and teams have managers as follows:
To provide another example, suppose we want to track price history for products and bundles. This can be achieved via an embedded one to many polymorphic association:
To define a polymorphic association, specify the polymorphic: true
option
on the child association and add the as: :association_name
option to the
parent association.
Note that Mongoid currently supports polymorphism only in one direction - from the child to the parent. For example, polymorphism cannot be used to specify that a bundle may contain other bundles or products:
has_and_belongs_to_many
associations do not support polymorphism.
Cascading Callbacks¶
If you want the embedded document callbacks to fire when calling a persistence operation on its parent, you will need to provide the cascade callbacks option to the association.
Dependent Behavior¶
You can provide dependent options to referenced associations to instruct Mongoid how to handle situations where one side of the association is deleted, or is attempted to be deleted. The options are as follows:
:delete
: Delete the child document without running any of the model callbacks.:destroy
: Destroy the child document and run all of the model callbacks.:nullify
: Orphan the child document.:restrict
: Raise an error if the child is not empty.
The default behavior of each association when no dependent option is provided is to nullify.
Autosaving¶
One core difference between Mongoid and Active Record from a behavior standpoint is that Mongoid does not automatically save associated documents for non-embedded associations. This is for performance reasons.
To enable an autosave on a non-embedded association (embedded associations do not need this since they are actually part of the parent in the database) add the autosave option to the association.
Note that autosave functionality will automatically be added to an association when using
accepts_nested_attributes_for
or validating presence of the association.
Existence Predicates¶
All associations have existence predicates on them in the form of name?
and has_name?
to check if the association is blank.
Autobuilding¶
One to one associations (embeds_one
, has_one
) have an autobuild option which tells
Mongoid to instantiate a new document when the association is accessed and it is nil
.
Touching¶
Any belongs_to
association, no matter where it hangs off from, can take an optional :touch
option which will call the touch method on it and any parent associations with the option defined
when the base document calls #touch
.
The counter_cache Option¶
As with ActiveRecord, the :counter_cache
option can be used on an association
to make finding the number of belonging objects more efficient. Also similar
to ActiveRecord, you must take into account that there will be an extra
attribute on the associated model. This means that with Mongoid,
you need to include Mongoid::Attributes::Dynamic
on the associated model.
For example:
Association Metadata¶
All associations in Mongoid contain metadata that holds information about the association in question, and is a valuable tool for third party developers to use to extend Mongoid.
You can access the association metadata of the association in a few different ways.
Attributes¶
All associations contain a _target
, which is the proxied document or documents, a _base
which is the document the association hangs off, and _association
which provides information
about the association.
The Association Object¶
The association object itself contains more information than one might know what to do with, and is useful for developers of extensions to Mongoid.
Method | Description |
---|---|
Association#as |
Returns the name of the parent to a polymorphic child. |
Association#as? |
Returns whether or not an as option exists. |
Association#autobuilding? |
Returns whether or not the association is autobuilding. |
Association#autosaving? |
Returns whether or not the association is autosaving. |
Association#cascading_callbacks? |
Returns whether the association has callbacks cascaded down from the parent. |
Association#class_name |
Returns the class name of the proxied document. |
Association#cyclic? |
Returns whether the association is a cyclic association. |
Association#dependent |
Returns the association’s dependent option. |
Association#destructive? |
Returns true if the association has a dependent delete or destroy. |
Association#embedded? |
Returns whether the association is embedded in another document. |
Association#forced_nil_inverse? |
Returns whether the association has a nil inverse defined. |
Association#foreign_key |
Returns the name of the foreign key field. |
Association#foreign_key_check |
Returns the name of the foreign key field dirty check method. |
Association#foreign_key_setter |
Returns the name of the foreign key field setter. |
Association#indexed? |
Returns whether the foreign key is auto indexed. |
Association#inverses |
Returns the names of all inverse association. |
Association#inverse |
Returns the name of a single inverse association. |
Association#inverse_class_name |
Returns the class name of the association on the inverse side. |
Association#inverse_foreign_key |
Returns the name of the foreign key field on the inverse side. |
Association#inverse_klass |
Returns the class of the association on the inverse side. |
Association#inverse_association |
Returns the metadata of the association on the inverse side. |
Association#inverse_of |
Returns the explicitly defined name of the inverse association. |
Association#inverse_setter |
Returns the name of the method used to set the inverse. |
Association#inverse_type |
Returns the name for the polymorphic type field of the inverse. |
Association#inverse_type_setter |
Returns the name for the polymorphic type field setter of the inverse. |
Association#key |
Returns the name of the field in the attributes hash to use to get the association. |
Association#klass |
Returns the class of the proxied documents in the association. |
Association#name |
Returns the association name. |
Association#options |
Returns self, for API compatibility with Active Record. |
Association#order |
Returns the custom sorting options on the association. |
Association#polymorphic? |
Returns whether the association is polymorphic. |
Association#setter |
Returns the name of the field to set the association. |
Association#store_as |
Returns the name of the attribute to store an embedded association in. |
Association#touchable? |
Returns whether or not the association has a touch option. |
Association#type |
Returns the name of the field to get the polymorphic type. |
Association#type_setter |
Returns the name of the field to set the polymorphic type. |
Association#validate? |
Returns whether the association has an associated validation. |
Querying Associations¶
Mongoid supports several forms of efficient querying of documents based on associations.
Embedded Associations¶
Given the following models:
Mongoid allows retrieving bands whose tours have certain attributes via the dot notation, as follows:
Mongoid also can retrieve embedded documents only, without retrieving top-level documents, using projection:
Referenced Associations¶
If the associations are referenced rather than embedded, performing queries through them takes a bit more work. Given the following models modified from the previous example:
One could retrieve all bands that have toured since 2000 as follows:
The conditions on Tour
can be arbitrarily complex, but they must all
be on the same Tour
document (or documents embedded in Tour
).
To find awards for bands that have toured since 2000:
Aggregation Pipeline¶
Mongoid exposes MongoDB’s aggregation pipeline for queries involving multiple referenced associations at the same time. Given the same setup as before with referenced associations:
To retrieve bands that toured since 2000 and have at least one award, one could do the following:
Note that the aggregation pipeline, since it is implemented by the Ruby driver
for MongoDB and not Mongoid, returns raw BSON::Document
objects rather than
Mongoid::Document
model instances. The above example projects only
the _id
field which is then used to load full models. An alternative is
to not perform such a projection and work with raw fields, which would eliminate
having to send the list of document ids to Mongoid in the second query
(which could be large).
Aggregation Pipeline Builder DSL¶
Mongoid provides limited support for constructing the aggregation pipeline itself using a high-level DSL. The following aggregation pipeline operators are supported:
To construct a pipeline, call the corresponding aggregation pipeine methods
on a Criteria
instance. Aggregation pipeline operations are added to the
pipeline
attribute of the Criteria
instance. To execute the pipeline,
pass the pipeline
attribute value to Collection#aggragegate
method.
For example, given the following models:
We could find out which states a participant visited:
group¶
The group
method adds a $group aggregation pipeline stage.
The field expressions support Mongoid symbol-operator syntax:
Alternatively, standard MongoDB aggregation pipeline syntax may be used:
project¶
The project
method adds a $project aggregation pipeline stage.
The argument should be a Hash specifying the projection:
unwind¶
The unwind
method adds an $unwind aggregation pipeline stage.
The argument can be a field name, specifyable as a symbol or a string, or
a Hash or a BSON::Document
instance: