Navigation

Associations

Common Behavior

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.

class Person
  include Mongoid::Document
  embeds_many :addresses
end

person.addresses = [ address ]
person.addresses._target # returns [ address ]
person.addresses._base # returns person
person.addresses._association # returns the association metadata

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.

class Person
  include Mongoid::Document
  embeds_many :addresses do
    def find_by_country(country)
      where(country: country).first
    end
    def chinese
      @target.select { |address| address.country == "China" }
    end
  end
end

person.addresses.find_by_country("Mongolia") # returns address
person.addresses.chinese # returns [ address ]

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.

class Lush
  include Mongoid::Document
  embeds_one :whiskey, class_name: "Drink", inverse_of: :alcoholic
end

class Drink
  include Mongoid::Document
  embedded_in :alcoholic, class_name: "Lush", inverse_of: :whiskey
end

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:

class Company
  include Mongoid::Document
  has_many :emails
end

class Email
  include Mongoid::Document
  belongs_to :company
end

company = Company.find(id)
# looks up emails where emails.company_id == company.id
company.emails

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:

class Company
  include Mongoid::Document
  field :c, type: String
  has_many :emails, foreign_key: 'c_ref', primary_key: 'c'
end

class Email
  include Mongoid::Document
  # This definition of c_ref is automatically generated by Mongoid:
  # field :c_ref, type: Object
  # But the type can also be specified:
  field :c_ref, type: String
  belongs_to :company, foreign_key: 'c_ref', primary_key: 'c'
end

company = Company.find(id)
# looks up emails where emails.c_ref == company.c
company.emails

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.

class Person
  include Mongoid::Document

  embeds_many :addresses, validate: false
  has_many :posts, validate: false
end

Polymorphism

When a child embedded document can belong to more than one type of parent document, you can tell Mongoid to support this by adding the as option to the definition on the parents, and the polymorphic option on the child. On the child object, an additional field will be stored that indicates the type of the parent. Polymorphic behavior is allowed on all associations with the exception of has_and_belongs_to_many.

class Band
  include Mongoid::Document
  embeds_many :photos, as: :photographic
  has_one :address, as: :addressable
end

class Photo
  include Mongoid::Document
  embedded_in :photographic, polymorphic: true
end

class Address
  include Mongoid::Document
  belongs_to :addressable, polymorphic: true
end

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.

 class Band
   include Mongoid::Document
   embeds_many :albums, cascade_callbacks: true
   embeds_one :label, cascade_callbacks: true
 end

band.save # Fires all save callbacks on the band, albums, and label.

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.

class Band
  include Mongoid::Document
  has_many :albums, dependent: :delete_all
  belongs_to :label, dependent: :nullify
end

class Album
  include Mongoid::Document
  belongs_to :band
end

class Label
  include Mongoid::Document
  has_many :bands, dependent: :restrict
end

label = Label.first
label.bands.push(Band.first)
label.delete # Raises an error since bands is not empty.

Band.first.delete # Will delete all associated albums.

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.

class Band
  include Mongoid::Document
  has_many :albums, autosave: true
end

band = Band.first
band.albums.build(name: "101")
band.save # Will save the album as well.

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.

class Tag
  include Mongoid::Document
  field :name, type: String
  recursively_embeds_many
end

root = Tag.new(name: "programming")
child_one = root.child_tags.build
child_two = root.child_tags.build

root.child_tags # [ child_one, child_two ]
child_one.parent_tag # [ root ]
child_two.parent_tag # [ root ]

class Node
  include Mongoid::Document
  recursively_embeds_one
end

root = Node.new
child = Node.new
root.child_node = child

root.child_node # child
child.parent_node # root

Existence Predicates

All associations have existence predicates on them in the form of name? and has_name? to check if the association is blank.

class Band
  include Mongoid::Document
  embeds_one :label
  embeds_many :albums
end

band.label?
band.has_label?
band.albums?
band.has_albums?

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.

class Band
  include Mongoid::Document
  embeds_one :label, autobuild: true
  has_one :producer, autobuild: true
end

band = Band.new
band.label # Returns a new empty label.
band.producer # Returns a new empty producer.

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.

class Band
  include Mongoid::Document
  belongs_to :label, touch: true
end

band = Band.first
band.touch # Calls touch on the parent label.

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.

# Get the metadata for a named association from the class or document.
Model.reflect_on_association(:association_name)
model.reflect_on_association(:association_name)

# Get the metadata with a specific association itself on a specific
# document.
model.associations[:association_name]

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.

Embeds One

One to one relationships 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 1 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.

class Band
  include Mongoid::Document
  embeds_one :label
end

class Label
  include Mongoid::Document
  field :name, type: String
  embedded_in :band
end

Storage

Documents that are embedded using the embeds_one macro are stored as a hash inside the parent in the parent’s database collection.

{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "label" : {
    "_id" : ObjectId("4d3ed089fb60ab534684b7e0"),
    "name" : "Mute",
  }
}

You can optionally tell Mongoid to store the embedded document in a different attribute other than the name, by providing a :store_as option.

class Band
  include Mongoid::Document
  embeds_one :label, store_as: "lab"
end

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 n number of embedded children, 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.

class Band
  include Mongoid::Document
  embeds_many :albums
end

class Album
  include Mongoid::Document
  field :name, type: String
  embedded_in :band
end

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.

{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "albums" : [
    {
      "_id" : ObjectId("4d3ed089fb60ab534684b7e0"),
      "name" : "Violator",
    }
  ]
}

You can optionally tell Mongoid to store the embedded document in a different attribute other than the name, by providing a :store_as option.

class Band
  include Mongoid::Document
  embeds_many :albums, store_as: "albs"
end

Has One

One to one relationships where the children are referenced in the parent document are defined using Mongoid’s has_one and belongs_to macros.

Defining

The parent document of the association should use the has_one macro to indicate is has 1 referenced child, where the document that is referenced in it uses belongs_to.

class Band
  include Mongoid::Document
  has_one :studio
end

class Studio
  include Mongoid::Document
  field :name, type: String
  belongs_to :band
end

Definitions are required on both sides to the association in order for it to work properly, unless one of the models is embedded.

Storage

When defining an association of this nature, each document is stored in its respective collection, but the child document contains a “foreign key” reference to the parent.

# The parent band document.
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9") }

# The child studio document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f1"),
  "band_id" : ObjectId("4d3ed089fb60ab534684b7e9")
}

Has Many

One to many relationships where the children are stored in a separate collection from the parent document are defined using Mongoid’s has_many and belongs_to macros. This exhibits similar behavior to Active Record.

Defining

The parent document of the association should use the has_many macro to indicate is has n number of referenced children, where the document that is referenced uses belongs_to.

class Band
  include Mongoid::Document
  has_many :members
end

class Member
  include Mongoid::Document
  field :name, type: String
  belongs_to :band
end

Definitions are required on both sides to the association in order for it to work properly, unless one of the models is embedded.

Storage

When defining an association of this nature, each document is stored in its respective collection, but the child document contains a “foreign key” reference to the parent.

# The parent band document.
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9") }

# A child member document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f1"),
  "band_id" : ObjectId("4d3ed089fb60ab534684b7e9")
}

Belongs To

A belongs_to macro is used when a document is the child in a has_one or has_many association. By default, a child document cannot be saved unless it’s associated with a parent. To override this requirement, you can use the option, optional: false.

Defining

The child document of the association uses the belongs_to macro to indicate it is associated with a parent. The document on the belongs_to side stores the reference to the parent.

class Band
  include Mongoid::Document
  has_one :studio
end

class Studio
  include Mongoid::Document
  field :name, type: String
  belongs_to :band
end

Definitions are required on both sides to the association in order for it to work properly, unless one of the models is embedded.

Storage

When defining an association of this nature, each document is stored in its respective collection, but the child document contains a “foreign key” reference to the parent.

# The parent band document.
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9") }

# The child studio document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f1"),
  "band_id" : ObjectId("4d3ed089fb60ab534684b7e9")
}

Has And Belongs To Many

Many to many relationships where the inverse documents are stored in a separate collection from the base document are defined using Mongoid’s has_and_belongs_to_many macro. This exhibits similar behavior to Active Record with the exception that no join collection is needed, the foreign key ids are stored as arrays on either side of the association.

Defining

Both sides of the association use the same macro.

class Band
  include Mongoid::Document
  has_and_belongs_to_many :tags
end

class Tag
  include Mongoid::Document
  field :name, type: String
  has_and_belongs_to_many :bands
end

You can create a one sided many to many if you want to mimic a has_many that stores the keys as an array on the parent.

class Band
  include Mongoid::Document
  has_and_belongs_to_many :tags, inverse_of: nil
end

class Tag
  include Mongoid::Document
  field :name, type: String
end

Storage

When defining an association of this nature, each document is stored in its respective collection, and each document contains a “foreign key” reference to the other in the form of an array.

# The band document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "tag_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}

# The tag document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f2"),
  "band_ids" : [ ObjectId("4d3ed089fb60ab534684b7e9") ]
}

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:

class Order
  include Mongoid::Document
  belongs_to :customer, counter_cache: true
end

class Customer
  include Mongoid::Document
  include Mongoid::Attributes::Dynamic
  has_many :orders
end