************ Associations ************ .. default-domain:: mongodb .. contents:: On this page :local: :backlinks: none :depth: 2 :class: singlecol 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: .. code-block:: ruby class Band include Mongoid::Document has_one :studio end When using ``has_one``, the child model must use ``belongs_to`` to declare the association with the parent: .. code-block:: ruby class Studio include Mongoid::Document belongs_to :band end Given the above definitions, every child document contains a reference to its respective parent document: .. code-block:: ruby band = Band.create!(studio: Studio.new) # => # band.studio # => # Use validations to require that the child is present: .. code-block:: ruby class Band include Mongoid::Document has_one :studio validates_presence_of :studio end Has Many -------- Use the ``has_many`` association to declare that the parent has zero or more children stored in a separate collection: .. code-block:: ruby class Band include Mongoid::Document has_many :members end Like with ``has_one``, the child model must use ``belongs_to`` to declare the association with the parent: .. code-block:: ruby class Member include Mongoid::Document belongs_to :band end Also as with ``has_one``, the child documents contain references to their respective parents: .. code-block:: ruby band = Band.create!(members: [Member.new]) # => # band.members # => [#] Use validations to require that at least one child is present: .. code-block:: ruby class Band include Mongoid::Document has_many :members validates_presence_of :members end Queries ``````` .. _has-many-any: ``any?`` ~~~~~~~~ Use the ``any?`` method on the association to efficiently determine whether the association contains any documents, without retrieving the entire set of documents from the database: .. code-block:: ruby band = Band.first band.members.any? ``any?`` also implements the `Enumerable#any? API `_, allowing filtering with a block: .. code-block:: ruby band = Band.first band.members.any? { |member| member.instrument == 'piano' } ... or by a class name which can be useful for polymorphic associations: .. code-block:: ruby class Drummer < Member end band = Band.first band.members.any?(Drummer) If the association is already loaded, ``any?`` inspects the loaded documents and does not query the database: .. code-block:: ruby band = Band.first # Queries the database band.members.any? band.members.to_a # Does not query the database band.members.any? Note that simply calling ``any?`` would *not* load the association (since ``any?`` only retrieves the _id field of the first matching document). ``exists?`` ~~~~~~~~~~~ The ``exists?`` method on the association determines whether there are any *persisted* documents in the association. Unlike the ``any?`` method: - ``exists?`` always queries the database, even if the association is already loaded. - ``exists?`` does not consider non-persisted documents. - ``exists?`` does not allow filtering in the application like ``any?`` does, and does not take any arguments. The following example illustrates the difference between ``exists?`` and ``any?``: .. code-block:: ruby band = Band.create! # Member is not persisted. band.members.build band.members.any? # => true band.members.exists? # => false # Persist the member. band.members.map(&:save!) band.members.any? # => true band.members.exists? # => true 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: .. code-block:: ruby class Band include Mongoid::Document has_one :studio end class Studio include Mongoid::Document belongs_to :band, optional: true end studio = Studio.create! # => # To change the default behavior of ``belongs_to`` associations to not require their respective parents globally, set the ``belongs_to_required_by_default`` :ref:`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: .. code-block:: ruby class Band include Mongoid::Document end class Studio include Mongoid::Document belongs_to :band end For clarity it is possible to add the ``inverse_of: nil`` option in cases when the parent does not define the association: .. code-block:: ruby class Band include Mongoid::Document end class Studio include Mongoid::Document belongs_to :band, inverse_of: nil end Has And Belongs To Many ----------------------- Use the ``has_and_belongs_to_many`` macro to declare a many-to-many association: .. code-block:: ruby class Band include Mongoid::Document has_and_belongs_to_many :tags end class Tag include Mongoid::Document has_and_belongs_to_many :bands end Both model instances store a list of ids of the associated models, if any: .. code-block:: ruby band = Band.create!(tags: [Tag.create!]) # => # band.tags # => [#] 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: .. code-block:: ruby class Band include Mongoid::Document has_and_belongs_to_many :tags, inverse_of: nil end class Tag include Mongoid::Document end band = Band.create!(tags: [Tag.create!]) # => # band.tags # => [#] A one-sided ``has_and_belongs_to_many`` association is, naturally, only usable from the model where it is defined. .. note:: Given two models, A and B where A ``has_and_belongs_to_many`` B, when adding a document of type B to the HABTM association on a document of type A, Mongoid will not update the ``updated_at`` field for the document of type A, but will update the ``updated_at`` field for the document of type B. Querying Referenced Associations -------------------------------- In most cases, efficient queries across referenced associations (and in general involving data or conditions or multiple collections) are performed using the aggregation pipeline. Mongoid helpers for constructing aggregation pipeline queries are described in the :ref:`aggregation pipeline ` section. For simple queries, the use of aggregation pipeline may be avoided and associations may be queried directly. When querying associations directly, all conditions must be on that association's collection only (which typically means association in question and any associations embedded in it). For example, given the following models: .. code-block:: ruby class Band include Mongoid::Document has_many :tours has_many :awards field :name, type: String end class Tour include Mongoid::Document belongs_to :band field :year, type: Integer end class Award include Mongoid::Document belongs_to :band field :name, type: String end One could retrieve all bands that have toured since 2000 as follows: .. code-block:: ruby band_ids = Tour.where(year: {'$gte' => 2000}).pluck(:band_id) bands = Band.find(band_ids) 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: .. code-block:: ruby band_ids = Tour.where(year: {'$gte' => 2000}).pluck(:band_id) awards = Award.where(band_id: {'$in' => band_ids}) 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. .. code-block:: ruby 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. .. code-block:: ruby { "_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 the ``:store_as`` option. .. code-block:: ruby 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 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. .. code-block:: ruby 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. .. code-block:: ruby { "_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 the ``:store_as`` option. .. code-block:: ruby class Band include Mongoid::Document embeds_many :albums, store_as: "albs" end 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. .. code-block:: ruby 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 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: .. code-block:: ruby class Band include Mongoid::Document field :started_on, type: Date embeds_one :label end class Label include Mongoid::Document field :name, type: String embedded_in :band end # Retrieve labels for bands started in the last year. # # Sends a find query like this: # {"find"=>"bands", # "filter"=>{"started_on"=>{"$gt"=>2018-07-01 00:00:00 UTC}}, # "projection"=>{"_id"=>1, "label"=>1}} Band.where(started_on: {'$gt' => Time.now - 1.year}).only(:label).map(&:label).compact.uniq Setting Stale Values on Referenced Associations `````````````````````````````````````````````` Setting a stale value to a referenced association can sometimes result in a ``nil`` value being persisted to the database. Take the following case: .. code-block:: ruby class Post include Mongoid::Document has_one :comment, inverse_of: :post end class Comment include Mongoid::Document belongs_to :post, inverse_of: :comment, optional: true end post.comment = comment1 post.reload At this point, ``post.comment`` is set to ``comment1``, however since a reload happened, ``post.comment`` does not refer to the same object as ``comment1``. Meaning, updating one object does not implicitly update the other. This matters for the next operation: .. code-block:: ruby post.comment = comment2 post.reload Now, ``post.comment`` is set to ``comment2``, and the ``post_id`` of the old comment is set to ``nil``. However, the value that was assigned to ``post.comment`` did not refer to the same object as ``comment1``, therefore, while the old value of ``post.comment`` was updated to have a ``nil`` ``post_id``, ``comment1`` still has the ``post_id`` set. .. code-block:: ruby post.comment = comment1 post.reload Finally, this last assignment attempts to set the ``post_id`` on ``comment1``, which should be ``nil`` at this point, but is set to the old ``post_id``. During this operation, the ``post_id`` is cleared from ``comment2``, and the new ``post_id`` is set on ``comment1``. However, since the ``post_id`` was already set on ``comment1``, nothing is persisted, and we end up with both comments having a ``nil`` ``post_id``. At this point, running ``post.comment`` returns ``nil``. Querying Embedded Associations ------------------------------ When querying top-level documents, conditions can be specified on documents in embedded associations using the dot notation. For example, given the following models: .. code-block:: ruby class Band include Mongoid::Document embeds_many :tours embeds_many :awards field :name, type: String end class Tour include Mongoid::Document embedded_in :band field :year, type: Integer end class Award include Mongoid::Document embedded_in :band field :name, type: String end To retrieve bands based on tour attributes, use the dot notation as follows: .. code-block:: ruby # Get all bands that have toured since 2000 Band.where('tours.year' => {'$gte' => 2000}) To retrieve only documents of embedded associations, without retrieving top-level documents, use the ``pluck`` projection method: .. code-block:: ruby # Get awards for bands that have toured since 2000 Band.where('tours.year' => {'$gte' => 2000}).pluck(:awards) .. _embedded-matching: Querying Loaded Associations ```````````````````````````` Mongoid query methods can be used on embedded associations of documents which are already loaded in the application. This mechanism is sometimes called "embedded matching" or "embedded document matching" and it is implemented entirely in Mongoid - the queries are NOT sent to the server. Embedded matching is supported for most general-purpose query operators. It is not implemented for :ref:`text search `, :manual:`geospatial query operators `, operators that execute JavaScript code (:manual:`$where `) and operators that are implemented via other server functionality such as :manual:`$expr ` and :manual:`$jsonSchema `. The following operators are supported: - :manual:`Bitwise operators ` - :manual:`Comparison operators ` - :manual:`Logical operators ` - :manual:`$comment ` - :manual:`$exists ` - :manual:`$mod ` - :manual:`$type ` - :manual:`$regex ` (``$options`` field is only supported when the ``$regex`` argument is a string) - :manual:`Array query operators ` For example, using the model definitions just given, we could query tours on a loaded band: .. code-block:: ruby band = Band.where(name: 'Astral Projection').first tours = band.tours.where(year: {'$gte' => 2000}) Embedded Matching Vs Server Behavior ```````````````````````````````````` Mongoid aims to provide the same semantics when performing embedded matching as those of MongoDB server. This means, for example, when the server only accepts arguments of particular types for a particular operator, Mongoid would also only accept arguments of the corresponding types. The following deviations are known: - Mongoid embedded matchers, because they are implemented on the client side, behave the same regardless of the server version that backs the application. As such, it is possible for Mongoid to deviate from server behavior if the server itself behaves differently in different versions. All operators are implemented in Mongoid regardless of backing deployment's server version. As of this writing, the known cases of such deviation are: - 3.2 and earlier servers not validating ``$size`` arguments as strictly as newer versions do. - 4.0 and earlier servers not validating ``$type`` arguments as strictly as newer versions do (allowing invalid arguments like 0, for example). - 3.2 and earlier servers not supporting ``Decimal128`` for ``$type``, as well as allowing invalid arguments such as negative numbers (smaller than -1) and numbers that are greater than 19 (not including 127, the argument for the ``MaxKey`` type). - 3.4 and earlier servers not supporting arrays for ``$type``. - 3.0 and earlier servers not supporting bitwise operators. - Mongoid DSL expands ``Range`` arguments to hashes with ``$gte`` and ``$lte`` conditions. `In some cases `_ this creates bogus queries. Embedded matchers raise the ``InvalidQuery`` exception in these cases. The operators that are known to be affected are ``$elemMatch``, ``$eq``, ``$gt``, ``$gte``, ``$lt``, ``$lte`` and ``$ne``. - When performing embedded matching with ``$regex``, `it is not currently possible `_ to specify a regular expression object as the pattern and also provide options. In general, Mongoid adopts the behavior of current server versions and validates more strictly. .. _omit-id: Omitting ``_id`` Fields ----------------------- By default, Mongoid adds an ``_id`` field to each embedded document. This permits easy referencing of and operations on the embedded documents. These ``_id`` fields may be omitted to save storage space. To do so, :ref:`override the _id field definition in the child documents ` and remove the default value: .. code-block:: ruby class Order include Mongoid::Document embeds_many :line_items end class LineItem include Mongoid::Document embedded_in :order field :_id, type: Object end In the current version of Mongoid the field definition is required, but without a default value specified no value will be stored in the database. A future version of Mongoid may allow removing previously defined fields. .. note:: Removing the ``_id`` field means that embedded documents must be identified by their content attribute values during queries, updates and deletes. Deleting -------- Mongoid provides three methods for deleting children from ``embeds_many`` associations: ``clear``, ``destroy_all`` and ``delete_all``. ``clear`` ````````` The ``clear`` method uses the :manual:`$unset operator ` to remove the entire association from the host document. It does not run destroy callbacks on the documents being removed, acting like ``delete_all`` in this regard: .. code-block:: ruby band = Band.find(...) band.tours.clear If ``clear`` is called on an association in an unsaved host document, it will still try to remove the association from the database based on the host document's ``_id``: .. code-block:: ruby band = Band.find(...) band.tours << Tour.new(...) unsaved_band = Band.new(id: band.id, tours: [Tour.new]) # Removes all tours from the persisted band due to _id match. unsaved_band.tours.clear band.tours # => [] ``delete_all`` `````````````` The ``delete_all`` method removes the documents that are in the association using the :manual:`$pullAll operator `. Unlike ``clear``, ``delete_all``: - Loads the association, if it wasn't yet loaded; - Only removes the documents that exist in the application. ``delete_all`` does not run destroy callbacks on the documents being removed. Example: .. code-block:: ruby band = Band.find(...) band.tours.delete_all ``destroy_all`` ``````````````` The ``delete_all`` method removes the documents that are in the association using the :manual:`$pullAll operator ` while running the destroy callbacks. Like ``delete_all``, ``destroy_all`` loads the entire association if it wasn't yet loaded and it only removes documents that exist in the application: .. code-block:: ruby band = Band.find(...) band.tours.destroy_all 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. .. code-block:: ruby 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. .. code-block:: ruby class Car include Mongoid::Document embeds_one :engine, class_name: "Motor", inverse_of: :machine end class Motor include Mongoid::Document embedded_in :machine, class_name: "Car", inverse_of: :engine 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: .. code-block:: ruby 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: .. code-block:: ruby 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 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 documents. - ``:inverse_foreign_key`` is the field on the remote model storing the values in ``:inverse_primary_key``. An example might make this more clear: .. code-block:: ruby class Company include Mongoid::Document field :c_id, type: Integer field :e_ids, type: Array has_and_belongs_to_many :employees, primary_key: :e_id, foreign_key: :e_ids, inverse_primary_key: :c_id, inverse_foreign_key: :c_ids end class Employee include Mongoid::Document field :e_id, type: Integer field :c_ids, type: Array has_and_belongs_to_many :companies, primary_key: :c_id, foreign_key: :c_ids, inverse_primary_key: :e_id, inverse_foreign_key: :e_ids end company = Company.create!(c_id: 123) # => # employee = Employee.create!(e_id: 456) # => # company.employees << employee company # => # employee # => # 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. .. _association-scope: Custom Scopes ------------- You may set a specific scope on an association using the ``:scope`` parameter. The scope is an additional filter that restricts which objects are considered to be a part of the association - a scoped association will return only documents which satisfy the scope condition.. The scope may be either: - a ``Proc`` with arity zero, or - a ``Symbol`` which references a :ref:`named scope ` on the associated model. .. code-block:: ruby class Trainer has_many :pets, scope: -> { where(species: 'dog') } has_many :toys, scope: :rubber end class Pet belongs_to :trainer end class Toy scope :rubber, where(material: 'rubber') belongs_to :trainer end .. note:: It is possible to add documents that do not satisfy an association's scope to that association. In this case, such documents will appear associated in memory, and will be saved to the database, but will not be present when the association is queried in the future. For example: .. code-block:: ruby trainer = Trainer.create! dog = Pet.create!(trainer: trainer, species: 'dog') cat = Pet.create!(trainer: trainer, species: 'cat') trainer.pets #=> [dog, cat] trainer.reload.pets #=> [dog] .. note:: Mongoid's syntax for scoped association differs from that of ActiveRecord. Mongoid uses the ``:scope`` keyword argument for consistency with other association options, whereas in ActiveRecord the scope is a positional argument. 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. .. code-block:: ruby class Person include Mongoid::Document embeds_many :addresses, validate: false has_many :posts, validate: false end 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: .. code-block:: ruby class Department include Mongoid::Document has_one :manager, as: :unit end class Team include Mongoid::Document has_one :manager, as: :unit end class Manager include Mongoid::Document belongs_to :unit, polymorphic: true end dept = Department.create! team = Team.create! alice = Manager.create!(unit: dept) alice.unit == dept # => true dept.manager == alice # => true 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: .. code-block:: ruby class Product include Mongoid::Document field :name, type: String has_and_belongs_to_many :bundles embeds_many :prices, as: :item end class Bundle include Mongoid::Document field :name, type: String has_and_belongs_to_many :products embeds_many :prices, as: :item end class Price include Mongoid::Document embedded_in :item, polymorphic: true end pants = Product.create!(name: 'Pants', prices: [Price.new, Price.new]) costume = Bundle.create!(name: 'Costume', products: [pants], prices: [Price.new, Price.new]) 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: .. code-block:: ruby class Bundle include Mongoid::Document # Does not work: has_many :items, polymorphic: true end ``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. .. code-block:: ruby 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: 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_all``: Delete the child document(s) without running any of the model callbacks. - ``:destroy``: Destroy the child document(s) and run all of the model callbacks. - ``:nullify``: Set the foreign key field of the child document to nil. The child may become orphaned if it is ordinarily only referenced via the parent. - ``:restrict_with_exception``: ``raise`` an error if the child is not empty. - ``:restrict_with_error``: Cancel operation and return false if the child is not empty. If no ``:dependent`` option is provided, deleting the parent document leaves the child document unmodified (in other words, the child document continues to reference the now deleted parent document via the foreign key field). The child may become orphaned if it is ordinarily only referenced via the parent. .. code-block:: ruby 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_with_exception 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 ActiveRecord is that Mongoid does not automatically save associated documents for referenced (i.e., non-embedded) associations when the parent is saved, for performance reasons. If autosaving is not used, it is possible to create dangling references to non-existent documents via associations: .. code-block:: ruby class Band include Mongoid::Document has_many :albums end class Album include Mongoid::Document belongs_to :band end band = Band.new album = Album.create!(band: band) # The band is not persisted at this point. album.reload album.band_id # => BSON::ObjectId('6257699753aefe153121a3d5') # Band does not exist. album.band # => nil To make referenced associations save automatically when the parent is saved, add the ``:autosave`` option to the association: .. code-block:: ruby 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. album.reload album.band_id # => BSON::ObjectId('62576b4b53aefe178b65b8e3') album.band # => # The autosaving functionality is automatically added to an association when using ``accepts_nested_attributes_for``, so that the application does not need to track which associations were modified when processing a form submission. Embedded associations always autosave, because they are stored as part of the parent document. Some operations on associations always save the parent and the child documents as part of the operation, regardless of whether autosaving is enabled. A non-exhaustive list of these operations is as follows: - Assignment to the association: .. code-block:: ruby # Saves the band and the album. band.albums = [Album.new] - ``push``, ``<<``: .. code-block:: ruby band.albums << Album.new band.albums.push(Album.new) Existence Predicates -------------------- All associations have existence predicates on them in the form of ``name?`` and ``has_name?`` to check if the association is blank. .. code-block:: ruby 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``. .. code-block:: ruby 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 can take an optional ``:touch`` option which will cause the parent document be touched whenever the child document is touched: .. code-block:: ruby class Band include Mongoid::Document belongs_to :label, touch: true end band = Band.first band.touch # Calls touch on the parent label. ``:touch`` can also take a string or symbol argument specifying a field to be touched on the parent association in addition to updated_at: .. code-block:: ruby class Label include Mongoid::Document include Mongoid::Timestamps field :bands_updated_at, type: Time has_many :bands end class Band include Mongoid::Document belongs_to :label, touch: :bands_updated_at end label = Label.create! band = Band.create!(label: label) band.touch # Updates updated_at and bands_updated_at on the label. When an embedded document is touched, its parents are recursively touched through the composition root (because all of the parents are necessarily saved when the embedded document is saved). The ``:touch`` attribute therefore is unnecessary on ``embedded_in`` associations. Mongoid currently `does not support specifying an additional field to be touched on an embedded_in association `_. ``:touch`` should not be set to ``false`` on an ``embedded_in`` association, since composition hierarchy is always updated upon a touch of an embedded document. This is currently not enforced but enforcement is `intended in the future `_. 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: .. code-block:: ruby 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 Association Proxies ------------------- Associations employ transparent proxies to the target objects. This can cause surprising behavior in some situations. The method visibility may be lost when methods on association targets are accessed, depending on the association: .. code-block:: ruby class Order include Mongoid::Document belongs_to :customer private def internal_status 'new' end end class Customer include Mongoid::Document has_many :orders private def internal_id 42 end end order = Order.new customer = Customer.create!(orders: [order]) # has_many does not permit calling private methods on the target customer.orders.first.internal_status # NoMethodError (private method `internal_status' called for #) # belongs_to permits calling private methods on the target order.customer.internal_id # => 42 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. .. code-block:: ruby # 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] 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. .. code-block:: ruby 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 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. .. list-table:: :header-rows: 1 :widths: 30 60 * - 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 ActiveRecord. * - ``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.