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.