Overview
Associations in Mongoid allow you to create relationships between models. In this guide, you can learn how to use Mongoid to customize how associations behave in your application. The following sections describe ways to customize association behaviors.
Extensions
Extensions allow you to add custom functionality to an association. You can define an extension on an association by specifying a block in the association definition, as shown in the following example:
class Band include Mongoid::Document embeds_many :albums do def find_by_name(name) where(name: name).first end end end band.albums.find_by_name("Omega") # returns album "Omega"
Custom Association Names
You can use the class_name macro to specify a custom class name for an
association. This is useful when you want to name the association something other
than the name of the class. The following example uses the
class_name macro to specify that an embedded association called records
represents the Album class:
class Band include Mongoid::Document embeds_many :records, class_name: "Album" end
Custom Keys
By default, Mongoid uses the _id field of the parent class when looking up
associations. You can specify different fields to use by using the
primary_key and foreign_key macros. The following example specifies a new
primary and foreign key for the albums association on a Band class:
class Band include Mongoid::Document field :band_id, type: String has_many :albums, primary_key: 'band_id', foreign_key: 'band_id_ref' end class Album include Mongoid::Document field :band_id_ref, type: String belongs_to :band, primary_key: 'band_id', foreign_key: 'band_id_ref' end
If you are specifying a has_and_belongs_to_many association, you can also
use the inverse_primary_key and inverse_foreign_key macros. The
inverse_primary_key macro specifies the field on the local model that the
remote model uses to look up the documents.
The inverse_foreign_key macro specifies the field on the remote model
that stores the values found in inverse_primary_key.
The following example specifies a new primary and foreign key for the
Band and Members classes in a has_and_belongs_to_many association:
class Band include Mongoid::Document field :band_id, type: String field :member_ids, type: Array has_many :members, primary_key: 'member_id', foreign_key: 'member_ids', inverse_primary_key: 'band_id', inverse_foreign_key: 'band_ids' end class Member include Mongoid::Document field :member_id, type: String field :band_ids, type: Array has_many :bands, primary_key: 'band_id', foreign_key: 'band_ids', inverse_primary_key: 'member_id', inverse_foreign_key: 'member_ids' end
Custom Scopes
You can specify the scope of an association by using the scope parameter.
The scope parameter determines the documents that Mongoid considers part
of an association. A scoped association returns only documents that match the
scope conditions when queried. You can set the scope to either a Proc with an arity
of zero or a Symbol that references a named scope on the associated model.
The following example sets custom scopes on associations in a Band class:
class Band include Mongoid::Document has_many :albums, scope: -> { where(published: true) } # Uses a scope called "upcoming" on the Tour model has_many :tours, scope: :upcoming end
Note
You can add documents that do not match the scope conditions to an association. Mongoid saves the documents to the database and they will appear in associated memory. However, you won't see the documents when querying the association.
Validations
When Mongoid loads an association into memory, by default, it uses the
validates_associated macro to validate that any children are also present.
Mongoid validates children for the following association types:
embeds_manyembeds_onehas_manyhas_onehas_and_belongs_to_many
You can turn off this validation behavior by setting the validate macro to
false when defining the association, as shown in the following example:
class Band include Mongoid::Document embeds_many :albums, validate: false end
Polymorphism
Mongoid supports polymorphism on the child classes of one-to-one and one-to-many associations.
Polymorphic associations allows a single association to contain objects of different class
types. You can define a polymorphic association by setting the polymorphic
option to true in a child association and adding the as option to the
parent association. The following example creates a polymorphic association in a
Band class:
class Tour include Mongoid::Document has_one :band, as: :featured end class Label include Mongoid::Document has_one :band, as: :featured end class Band include Mongoid::Document belongs_to :featured, polymorphic: true end
In the preceding example, the :featured association in the Band class can contain either a
Label or Album document.
Important
Mongoid supports polymorphism only from child to parent. You cannot specify
a parent has_one or has_many association as polymorphic.
has_and_belongs_to_many associations do not support polymorphism.
Custom Polymorphic Types
Starting in version 9.0.2, Mongoid supports custom polymorphic types through
a global registry. You can specify alternative keys to represent different
classes, decoupling your code from the data. The following example specifies
the string "artist" as an alternate key for the Band class:
class Band include Mongoid::Document identify_as 'artist' has_many :albums, as: :record end
In the preceding example, the identify_as directive instructs Mongoid
to store the Band class in the database as the string "artist".
You can also specify multiple aliases, as shown in the following example:
class Band include Mongoid::Document identify_as 'artist', 'group', 'troupe' has_many :albums, as: :record end
In the preceding example, artist is the default name and the others are used
only for looking up records. This allows you to refactor your
code without breaking the associations in your data.
Polymorphic type aliases are global. The keys you specify must be unique across your entire code base. However, you can register alternative resolvers that can be used for different subsets of your models. In this case, the keys must be unique only for each resolver. The following example shows how to register alternate resolvers:
Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :mus Mongoid::ModelResolver.register_resolver Mongoid::ModelResolver.new, :tool module Music class Band include Mongoid::Document identify_as 'bnd', resolver: :mus end end module Tools class Band include Mongoid::Document identify_as 'bnd', resolver: :tool end end
Both Music::Band and Tools::Band are aliased as
"bnd", but each model uses its own resolver to avoid conflicts.
Dependent Behavior
You can provide dependent options to referenced associations to specify how
Mongoid handles associated documents when a document is deleted. You can specify
the following options:
delete_all: Deletes all child documents without running any model callbacks.destroy: Deletes the child documents and runs all model callbacks.nullify: Sets the foreign key of the child documents tonil. The child document might become orphaned if it is referenced by only the parent.restrict_with_exception: Raises an exception if the child document is not empty.restrict_with_error: Cancels the operation and returnsfalseif the child document is not empty.
If you don't specify any dependent options, Mongoid leaves the child
document unchanged when the parent document is deleted. The child document
continues to reference the deleted parent document, and if it is
referenced through only the parent, the child document becomes orphaned.
The following example specifies dependent options on the Band class:
class Band include Mongoid::Document has_many :albums, dependent: :delete_all belongs_to :label, dependent: :nullify end
Autosave Referenced Associations
By default, Mongoid does not automatically save associated documents from non-embedded associations when saving the parent document. This can result in dangling references to documents that don't exist.
You can use the autosave option on a referenced association to
automatically save associated documents when saving the parent document. The
following example creates a Band class with an associated Album class
and specifies the autosave option:
class Band include Mongoid::Document has_many :albums end class Album include Mongoid::Document belongs_to :band, autosave: true end band = Band.new album = Album.create!(band: band) # The band is persisted at this point.
Note
Mongoid automatically adds autosave functionality to an association that uses
the accepts_nested_attributes_for option.
You do not need to specify the autosave option for embedded associations
because Mongoid saves embedded documents in the parent document.
Autobuild
You can add the autobuild option to one-to-one associations, such as
has_one and embeds_one, to automatically instantiate a new document when
accessing a nil association. The following example adds the autobuild
option to an association on the Band class:
class Band include Mongoid::Document embeds_one :label, autobuild: true has_one :producer, autobuild: true end
Touch
When Mongoid touches a document, it updates the document's
updated_at field to the current date and time. You can add the touch
option to any belongs_to association to ensure that Mongoid touches the
parent document whenever the child document is updated. The following example
adds the touch option to an association on the Band class:
class Band include Mongoid::Document field :name belongs_to :label, touch: true end
You can also use the touch option to specify another field on the parent
association, as a string or a symbol. When Mongoid touches the parent
association, it sets both the updated_at field and the specified field
to the current date and time.
The following example instructs Mongoid to touch the bands_updated_at field:
class Band include Mongoid::Document belongs_to :label, touch: :bands_updated_at end
Note
In embedded associations, when an embedded document is touched, Mongoid
touches its parents recursively. Because of this, adding a touch
attribute to an embedded_in association is unnecessary.
Mongoid does not support specifying additional fields to touch in
embedded_in associations.
Counter Cache
Use the counter_cache option to store the number of objects
that belong to an associated field. When you specify this option,
Mongoid stores a counter field on the associated parent model.
The counter_cache option accepts one of the two following values for the
counter field name:
true: Mongoid uses a default counter field name, based on the name of the association. For example, if the association is calledBand, the default counter field name isbands_count.A
stringorsymbol: Mongoid uses that value directly as the counter field name.
You can provide the container for the counter cache in one of the following ways:
Define an explicit counter field in the parent model.
Include the
Mongoid::Attributes::Dynamicmodule in the parent model, which allows Mongoid to create the counter field dynamically on first use.
Explicit Counter Field
In this approach, declare the counter field directly on the parent model. This is useful when you want a fixed schema and explicit field types.
The following example declares an explicit bands_count field
on the Label class and uses the default counter field name:
class Band include Mongoid::Document # Uses the default counter field name: bands_count belongs_to :label, counter_cache: true end class Label include Mongoid::Document field :bands_count, type: Integer has_many :bands end
You can also specify a custom counter field name by passing a
string to the counter_cache option:
class Band include Mongoid::Document belongs_to :label, counter_cache: :active_bands_count end class Label include Mongoid::Document field :active_bands_count, type: Integer has_many :bands end
Dynamic Counter Field
If you include the Mongoid::Attributes::Dynamic module in
the parent model, Mongoid can create and update the counter
field dynamically during runtime. This is useful when you want a
flexible schema and don't want to declare the counter field
explicitly.
The following example uses Mongoid::Attributes::Dynamic to
allow Mongoid to create the counter field dynamically on the Label
class:
class Band include Mongoid::Document belongs_to:label, counter_cache: true end class Label include Mongoid::Document include Mongoid::Attributes::Dynamic has_many :bands end
When you use the Mongoid::Attributes::Dynamic module, Mongoid uses the
same naming rules for the counter field as when you declare an explicit
counter field: it defaults to a *_count field name based on the
association, unless you specify a custom field name with a string in the
counter_cache option.