Overview
Associations in Mongoid allow you to create relationships between models. In this guide, you can learn how to use embedded associations to store different types of documents in the same collection. Mongoid supports embedded associations with the following macros:
embeds_oneembeds_manyembedded_inrecursively_embeds_onerecursively_embeds_many
The following sections describe how to use these association types.
Embeds One
To specify that a class model contains an embedded document of a different
class type, use the embeds_one macro in the parent class and the embedded_in
macro in the embedded class. The following example creates a Band class with
an embedded Label class:
class Band include Mongoid::Document embeds_one :label end class Label include Mongoid::Document field :name, type: String embedded_in :band end
Mongoid stores documents embedded with the embeds_one macro in the
parent document as a field with the same name as the embedded class. The
preceding Label documents are stored in the Band document, as shown in
the following example:
# Band document { "_id" : ObjectId("..."), "label" : { "_id" : ObjectId("..."), "name" : "Periphery", } }
You can store the embedded document with a different name by using the
store_as option, as shown in the following example:
class Band include Mongoid::Document embeds_one :label, store_as: "record_label" end
Embeds Many
To specify that a class model contains multiple embedded documents of a
different class type, use the embeds_many macro in the parent class and the
embedded_in macro in the embedded class. The following example creates a
Band class with multiple embedded Album type documents:
class Band include Mongoid::Document embeds_many :albums end class Album include Mongoid::Document field :name, type: String embedded_in :band end
Mongoid stores documents embedded with the embeds_many macro in the
parent document as an array field with the same name as the embedded class. The
preceding Album documents are stored in the Band document, as shown in
the following example:
{ "_id" : ObjectId("..."), "albums" : [ { "_id" : ObjectId("..."), "name" : "Omega", } ] }
You can store the embedded document with a different name by using the
store_as option, as shown in the following example:
class Band include Mongoid::Document embeds_many :albums, store_as: "records" end
Recursive Embedding
You can embed one or more documents of the same type into a parent
class by using the recursively_embeds_one and recursively_embeds_many
macros. Both macros provide accessors for the parent and child documents through
a parent_* method and a child_* method, where * represents the name
of the class. The following example creates a Band class that recursively
embeds multiple other Band documents to represent multiple band names:
class Band include Mongoid::Document field :name, type: String recursively_embeds_many end
You can access the parent and child documents through the parent_band and
child_band methods, as shown in the following example:
root = Band.new(name: "Linkin Park") # Add child bands child_one = root.child_band.build(name: "Lincoln Park") child_two = root.child_band.build(name: "Xero") # Access parent band child_one.parent_band # Outputs: root
Query Embedded Associations
You can access embedded documents when querying the collection of the parent class by using dot notation.
The following example uses dot notation to query Tour type documents that
are embedded in a Band class. The query returns documents that have a
tours.year value of 2000 or greater:
Band.where('tours.year' => {'$gte' => 2000})
You can use the pluck projection method to retrieve embedded documents
without retrieving their associated parent documents, as shown in the following
example:
# Get awards for bands that have toured since 2000 Band.where('tours.year' => {'$gte' => 2000}).pluck(:awards)
You can use Mongoid query methods to perform embedded matching, which allows you to query on embedded associations of documents that are already loaded in the application. Mongoid implements embedded matching without sending queries to the server.
The following query operators are supported with embedded matching:
The following example queries the embedded tours field of a loaded Band
document by using the $gte comparison operator:
band = Band.where(name: 'Astral Projection').first tours = band.tours.where(year: {'$gte' => 2000})
Embedded matching on loaded documents has the following known limitations:
Embedded matching is not implemented for the following features:
Operators that execute JavaScript code, such as $where
Operators that are implemented through other server functionality, such as $expr and $jsonSchema
Mongoid expands
Rangearguments to hashes with$gteand$lteconditions. This can lead to invalid queries in some cases and raises a anInvalidQueryexception.With the
$regexoperator, you cannot specify a regular expression object as a pattern while also providing options to the$optionsfield. You can only provide options if the regular expression pattern is a string.
Omit _id Fields
By default, Mongoid adds an _id field to embedded documents. You can omit
this field from embedded documents by explicitly specifying the _id field in
your model and omitting the default value. The following example instructs Mongoid
not to add an _id field to the Albums class:
class Album include Mongoid::Document field :name, type: String field :_id, type: Object embedded_in :band end
In the preceding Albums class, the _id field is not automatically added.
Without a default value, Mongoid does not store the value in the database
unless you provide one in your model.
Delete Embedded Associations
You can delete child documents from embeds_many associations by using one of
the following methods:
cleardelete_alldestroy_all
The clear method uses the $unset operator operator to remove an entire embedded
association from the parent document. The clear method does not run any
destroy callbacks. The following example uses the clear
method to remove all embedded associations from the Band class:
band = Band.find(...) band.tours.clear
The delete_all method uses the $pullAll operator operator to remove documents in an
embedded association. delete_all loads the association if it has not
yet been loaded, then only removes the documents that exist in the application.
The delete_all method does not run any destroy callbacks.
The following example uses the delete_all method to remove all embedded
Album documents from the Band class:
band = Band.find(...) band.tours.delete_all
The destroy_all method also uses the $pullAll operator operator to remove documents in an
embedded association. It also runs any destroy callbacks that are defined on
the associated documents. The following example uses the destroy_all
method to remove all embedded Album documents from the Band class:
band = Band.find(...) band.tours.destroy_all