mongodb/mongoid

View on GitHub
docs/reference/queries.txt

Summary

Maintainability
Test Coverage
*******
Queries
*******

.. default-domain:: mongodb

.. contents:: On this page
   :local:
   :backlinks: none
   :depth: 2
   :class: singlecol


Mongoid provides a rich query DSL inspired by ActiveRecord. A trivial query
looks as follows:

.. code-block:: ruby

  Band.where(name: "Depeche Mode")

A more complex query utilizing various Mongoid features could be as follows:

.. code-block:: ruby

  Band.
    where(:founded.gte => "1980-01-01").
    in(name: [ "Tool", "Deftones" ]).
    union.
    in(name: [ "Melvins" ])

The query methods return ``Mongoid::Criteria`` objects, which are chainable
and lazily evaluated wrappers for MongoDB query language (MQL).
The queries are executed when their result sets are iterated. For example:

.. code-block:: ruby

  # Construct a Criteria object:

  Band.where(name: 'Deftones')
  # => #<Mongoid::Criteria
  #   selector: {"name"=>"Deftones"}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

  # Evaluate the query and get matching documents:

  Band.where(name: 'Deftones').to_a
  # => [#<Band _id: 5ebdeddfe1b83265a376a760, name: "Deftones", description: nil>]

Methods like ``first`` and ``last`` return the individual documents immediately.
Otherwise, iterating a Criteria object with methods like ``each`` or ``map``
retrieves the documents from the server. ``to_a`` can be used to force
execution of a query that returns an array of documents, literally converting
a Criteria object to an Array.

When a query method is called on a Criteria instance, the method returns a new
Criteria instance with the new conditions added to the existing conditions:

.. code-block:: ruby

  scope = Band.where(:founded.gte => "1980-01-01")
  # => #<Mongoid::Criteria
  #   selector: {"founded"=>{"$gte"=>"1980-01-01"}}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

  scope.where(:founded.lte => "2020-01-01")
  # => #<Mongoid::Criteria
  #   selector: {"founded"=>{"$gte"=>"1980-01-01", "$lte"=>"2020-01-01"}}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

  scope
  # => #<Mongoid::Criteria
  #   selector: {"founded"=>{"$gte"=>"1980-01-01"}}
  #   options:  {}
  #   class:    Band
  #   embedded: false>


Condition Syntax
================

Mongoid supports three ways of specifying individual conditions:

1. Field syntax.
2. MQL syntax.
3. Symbol operator syntax.

All syntaxes support querying embedded documents using the dot notation.
All syntaxes respect field types, if the field being queried is defined in the
model class, and field aliases.

The examples in this section use the following model definition:

.. code-block:: ruby

  class Band
    include Mongoid::Document

    field :name, type: String
    field :founded, type: Integer
    field :m, as: :member_count, type: Integer

    embeds_one :manager
  end

  class Manager
    include Mongoid::Document

    embedded_in :band

    field :name, type: String
  end

Field Syntax
------------

The simplest querying syntax utilizes the basic Ruby hashes. Keys can be
symbols or strings, and correspond to field names in MongoDB documents:

.. code-block:: ruby

  Band.where(name: "Depeche Mode")
  #   => #<Mongoid::Criteria
  #   selector: {"name"=>"Depeche Mode"}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

  # Equivalent to:

  Band.where("name" => "Depeche Mode")

MQL Syntax
----------

An MQL operator may be specified on any field using the hash syntax:

.. code-block:: ruby

  Band.where(founded: {'$gt' => 1980})
  # => #<Mongoid::Criteria
  #   selector: {"founded"=>{"$gt"=>1980}}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

  # Equivalent to:

  Band.where('founded' => {'$gt' => 1980})

Symbol Operator Syntax
----------------------

MQL operators may be specified as methods on symbols for the respective field
name, as follows:

.. code-block:: ruby

  Band.where(:founded.gt => 1980)
  # => #<Mongoid::Criteria
  #   selector: {"founded"=>{"$gt"=>1980}}
  #   options:  {}
  #   class:    Band
  #   embedded: false>


Fields
======

Querying on Defined Fields
--------------------------

In order to query on a field, it is not necessary to add the field to
:ref:`the model class definition <fields>`. However, if a field is defined in
the model class, Mongoid will coerce query values to match defined field types
when constructing the query:

.. code-block:: ruby

  Band.where(name: 2020, founded: "2020")
  #   => #<Mongoid::Criteria
  #   selector: {"name"=>"2020", "founded"=>2020}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

Querying for Raw Values
-----------------------

If you'd like to bypass Mongoid's query type coercion behavior and query
directly for the raw-typed value in the database, wrap the query value in
``Mongoid::RawValue`` class. This can be useful when working with legacy data.

.. code-block:: ruby

  Band.where(founded: Mongoid::RawValue("2020"))
  # => #<Mongoid::Criteria
  #   selector: {"founded"=>"2020"}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

Field Aliases
-------------

Queries take into account :ref:`storage field names <storage-field-names>`
and :ref:`field aliases <field-aliases>`:

.. code-block:: ruby

  Band.where(name: 'Astral Projection')
  #   => #<Mongoid::Criteria
  #   selector: {"n"=>"Astral Projection"}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

Since ``id`` and ``_id`` fields are aliases, either one can be used for queries:

.. code-block:: ruby

  Band.where(id: '5ebdeddfe1b83265a376a760')
  # => #<Mongoid::Criteria
  #   selector: {"_id"=>BSON::ObjectId('5ebdeddfe1b83265a376a760')}
  #   options:  {}
  #   class:    Band
  #   embedded: false>


Embedded Documents
==================

To match values of fields of embedded documents, use the dot notation:

.. code-block:: ruby

  Band.where('manager.name' => 'Smith')
  # => #<Mongoid::Criteria
  #   selector: {"manager.name"=>"Smith"}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

  Band.where(:'manager.name'.ne => 'Smith')
  # => #<Mongoid::Criteria
  #   selector: {"manager.name"=>{"$ne"=>"Smith"}}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

.. note::

  Queries always return top-level model instances, even if all of the
  conditions are referencing embedded documents.


.. _logical-operations:

Logical Operations
==================

Mongoid supports ``and``, ``or``, ``nor`` and ``not`` logical operations on
``Criteria`` objects. These methods take one or more hash of conditions
or another ``Criteria`` object as their arguments, with ``not`` additionally
having an argument-free version.

.. code-block:: ruby

  # and with conditions
  Band.where(label: 'Trust in Trance').and(name: 'Astral Projection')

  # or with scope
  Band.where(label: 'Trust in Trance').or(Band.where(name: 'Astral Projection'))

  # not with conditions
  Band.not(label: 'Trust in Trance', name: 'Astral Projection')

  # argument-less not
  Band.not.where(label: 'Trust in Trance', name: 'Astral Projection')

For backwards compatibility with earlier Mongoid versions, all of the logical
operation methods also accept arrays of parameters, which will be flattened
to obtain the criteria. Passing arrays to logical operations is deprecated and
may be removed in a future version of Mongoid.

The following calls all produce the same query conditions:

.. code-block:: ruby

  # Condition hashes passed to separate and invocations
  Band.and(name: 'SUN Project').and(member_count: 2)

  # Multiple condition hashes in the same and invocation
  Band.and({name: 'SUN Project'}, {member_count: 2})

  # Multiple condition hashes in an array - deprecated
  Band.and([{name: 'SUN Project'}, {member_count: 2}])

  # Condition hash in where and a scope
  Band.where(name: 'SUN Project').and(Band.where(member_count: 2))

  # Condition hash in and and a scope
  Band.and({name: 'SUN Project'}, Band.where(member_count: 2))

  # Scope as an array element, nested arrays - deprecated
  Band.and([Band.where(name: 'SUN Project'), [{member_count: 2}]])

  # All produce:
  # => #<Mongoid::Criteria
  #   selector: {"name"=>"SUN Project", "member_count"=>2}
  #   options:  {}
  #   class:    Band
  #   embedded: false>


Operator Combinations
---------------------

As of Mongoid 7.1, logical operators (``and``, ``or``, ``nor`` and ``not``)
have been changed to have the the same semantics as `those of ActiveRecord
<https://guides.rubyonrails.org/active_record_querying.html>`_.
To obtain the semantics of ``or`` as it behaved in Mongoid 7.0 and earlier,
use ``any_of`` which is described below.

When conditions are specified on the same field multiple times, all
conditions are added to the criteria:

.. code-block:: ruby

  Band.where(name: 1).where(name: 2).selector
  # => {"name"=>"1", "$and"=>[{"name"=>"2"}]}

  Band.where(name: 1).or(name: 2).selector
  # => {"$or"=>[{"name"=>"1"}, {"name"=>"2"}]}

``any_of``, ``none_of``, ``nor`` and ``not`` behave similarly, with ``not`` producing
different query shapes as described below.

When ``and``, ``or`` and ``nor`` logical operators are used, they
operate on the criteria built up to that point and its argument.
``where`` has the same meaning as ``and``:

.. code-block:: ruby

  # or joins the two conditions
  Band.where(name: 'Sun').or(label: 'Trust').selector
  # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}]}

  # or applies only to the first condition, the second condition is added
  # to the top level as $and
  Band.or(name: 'Sun').where(label: 'Trust').selector
  # => {"$or"=>[{"name"=>"Sun"}], "label"=>"Trust"}

  # Same as previous example - where and and are aliases
  Band.or(name: 'Sun').and(label: 'Trust').selector
  # => {"$or"=>[{"name"=>"Sun"}], "label"=>"Trust"}

  # Same operator can be stacked any number of times
  Band.or(name: 'Sun').or(label: 'Trust').selector
  # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}]}

  # The label: Foo condition is added to the top level as $and
  Band.where(name: 'Sun').or(label: 'Trust').where(label: 'Foo').selector
  # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}], "label"=>"Foo"}


``and`` Behavior
----------------

The ``and`` method will add new simple conditions to the top level of the
criteria, unless the receiving criteria already has a condition on the
respective fields, in which case the conditions will be combined with ``$and``.

.. code-block:: ruby

  Band.where(label: 'Trust in Trance').and(name: 'Astral Projection').selector
  # => {"label"=>"Trust in Trance Records", "name"=>"Astral Projection"}

  Band.where(name: /Best/).and(name: 'Astral Projection').selector
  # => {"name"=>/Best/, "$and"=>[{"name"=>"Astral Projection"}]}

As of Mongoid 7.1, specifying multiple criteria on the same field with ``and``
combines all criteria so specified, whereas in previous versions of Mongoid
conditions on a field sometimes replaced previously specified conditions on
the same field, depending on which form of ``and`` was used.


``or``/``nor`` Behavior
_----------------------

``or`` and ``nor`` produce ``$or`` and ``$nor`` MongoDB operators, respectively,
using the receiver and all of the arguments as operands. For example:

.. code-block:: ruby

  Band.where(name: /Best/).or(name: 'Astral Projection')
  # => {"$or"=>[{"name"=>/Best/}, {"name"=>"Astral Projection"}]}

  Band.where(name: /Best/).and(name: 'Astral Projection').
    or(Band.where(label: /Records/)).and(label: 'Trust').selector
  # => {"$or"=>[{"name"=>/Best/, "$and"=>[{"name"=>"Astral Projection"}]}, {"label"=>/Records/}], "label"=>"Trust"}

If the only condition on the receiver is another ``or``/``nor``, the new
conditions are added to the existing list:

.. code-block:: ruby

  Band.where(name: /Best/).or(name: 'Astral Projection').
    or(Band.where(label: /Records/)).selector
  # => {"$or"=>[{"name"=>/Best/}, {"name"=>"Astral Projection"}, {"label"=>/Records/}]}

Use ``any_of`` to add a disjunction to a Criteria object while maintaining
all of the conditions built up so far as they are.


.. _any-of:

``any_of`` Behavior
-------------------

``any_of`` adds a disjunction built from its arguments to the existing
conditions in the criteria. For example:

.. code-block:: ruby

  Band.where(label: /Trust/).any_of({name: 'Astral Projection'}, {name: /Best/})
  # => {"label"=>/Trust/, "$or"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]}

The conditions are hoisted to the top level if possible:

.. code-block:: ruby

  Band.where(label: /Trust/).any_of({name: 'Astral Projection'})
  # => {"label"=>/Trust/, "name"=>"Astral Projection"}


.. _none-of:

``none_of`` Behavior
--------------------

``none_of`` adds a negated disjunction ("nor") built from its arguments to
the existing conditions in the criteria. For example:

.. code-block:: ruby

  Band.where(label: /Trust/).none_of({name: 'Astral Projection'}, {name: /Best/})
  # => {"label"=>/Trust/, "$nor"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]}


``not`` Behavior
----------------

``not`` method can be called without arguments, in which case it will negate
the next condition that is specified. ``not`` can also be called with one
or more hash conditions or ``Criteria`` objects, which will all be negated and
added to the criteria.

.. code-block:: ruby

  # not negates subsequent where
  Band.not.where(name: 'Best').selector
  # => {"name"=>{"$ne"=>"Best"}}

  # The second where is added as $and
  Band.not.where(name: 'Best').where(label: /Records/).selector
  # => {"name"=>{"$ne"=>"Best"}, "label"=>/Records/}

  # not negates its argument
  Band.not(name: 'Best').selector
  # => {"name"=>{"$ne"=>"Best"}}

.. note::

  ``$not`` in MongoDB server cannot be used with a string argument.
  Mongoid uses ``$ne`` operator to achieve such a negation:

  .. code-block:: ruby

    # String negation - uses $ne
    Band.not.where(name: 'Best').selector
    # => {"name"=>{"$ne"=>"Best"}}

    # Regexp negation - uses $not
    Band.not.where(name: /Best/).selector
    # => {"name"=>{"$not"=>/Best/}}

Similarly to ``and``, ``not`` will negate individual conditions for simple
field criteria. For complex conditions and when a field already has a condition
defined on it, since MongoDB server only supports the ``$not`` operator on
a per-field basis rather than globally, Mongoid emulates ``$not`` by using
an ``{'$and' => [{'$nor' => ...}]}`` construct:

.. code-block:: ruby

  # Simple condition
  Band.not(name: /Best/).selector
  # => {"name"=>{"$not"=>/Best/}}

  # Complex conditions
  Band.where(name: /Best/).not(name: 'Astral Projection').selector
  # => {"name"=>/Best/, "$and"=>[{"$nor"=>[{"name"=>"Astral Projection"}]}]}

  # Symbol operator syntax
  Band.not(:name.ne => 'Astral Projection')
  # => #<Mongoid::Criteria
  #   selector: {"$and"=>[{"$nor"=>[{"name"=>{"$ne"=>"Astral Projection"}}]}]}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

If using ``not`` with arrays or regular expressions, please note the
caveats/limitations of ``$not`` `stated in the MongoDB server documentation
<https://mongodb.com/docs/manual/reference/operator/query/not/>`_.


Incremental Query Construction
==============================

By default, when conditions are added to a query, Mongoid considers each
condition complete and independent from any other conditions potentially
present in the query. For example, calling ``in`` twice adds two separate
``$in`` conditions:

.. code-block:: ruby

  Band.in(name: ['a']).in(name: ['b'])
  => #<Mongoid::Criteria
    selector: {"name"=>{"$in"=>["a"]}, "$and"=>[{"name"=>{"$in"=>["b"]}}]}
    options:  {}
    class:    Band
    embedded: false>

Some operator methods support building the condition incrementally. In this
case, when an condition on a field which uses one of the supported operators
is being added, if there already is a condition on the same field using the
same operator, the operator expressions are combined according to the
specified *merge strategy*.

.. _merge-strategies:

Merge Strategies
----------------

Mongoid provides three merge strategies:

- **Override**: the new operator instance replaces any existing conditions on
  the same field using the same operator.
- **Intersect**: if there already is a condition using the same operator on the
  same field, the values of the existing condition are intersected with the
  values of the new condition and the result is stored as the operator value.
- **Union**: if there already is a condition using the same operator on the
  same field, the values of the new condition are added to the values of the
  existing condition and the result is stored as the operator value.

The following snippet demonstrates all of the strategies, using ``in`` as the
example operator:

.. code-block:: ruby

  Band.in(name: ['a']).override.in(name: ['b'])
  => #<Mongoid::Criteria
    selector: {"name"=>{"$in"=>["b"]}}
    options:  {}
    class:    Band
    embedded: false>

  Band.in(name: ['a', 'b']).intersect.in(name: ['b', 'c'])
  => #<Mongoid::Criteria
    selector: {"name"=>{"$in"=>["b"]}}
    options:  {}
    class:    Band
    embedded: false>

  Band.in(name: ['a']).union.in(name: ['b'])
  => #<Mongoid::Criteria
    selector: {"name"=>{"$in"=>["a", "b"]}}
    options:  {}
    class:    Band
    embedded: false>

The strategy is requested by calling ``override``, ``intersect`` or ``union``
on a ``Criteria`` instance. The requested strategy applies to the next
condition method called on the query. If the next condition method called does
not support merge strategies, the strategy is reset, as shown in the following
example:

.. code-block:: ruby

  Band.in(name: ['a']).union.ne(name: 'c').in(name: ['b'])
  => #<Mongoid::Criteria
    selector: {"name"=>{"$in"=>["a"], "$ne"=>"c"}, "$and"=>[{"name"=>{"$in"=>["b"]}}]}
    options:  {}
    class:    Band
    embedded: false>

Since ``ne`` does not support merge strategies, the ``union`` strategy was
ignored and reset and when ``in`` was invoked the second time there was no
strategy active.

.. warning::

  Merge strategies currently assume the previous condition(s) have been added
  to the top level of the query, however this is not always the case
  (conditions may be nested under an ``$and`` clause). Using merge strategies
  with complex criteria may cause incorrect queries to be constructed.
  This misbehavior is `intended to be fixed in the future
  <https://jira.mongodb.org/browse/MONGOID-5350>`_.


Supported Operator Methods
--------------------------

The following operator methods support merge strategies:

- ``all``
- ``in``
- ``nin``

The set of methods may be expanded in future releases of Mongoid. For
future compatibility, only invoke a strategy method when the next method call
is an operator that supports merge strategies.

Note that the merge strategies are currently only applied when conditions are
added through the designated methods. In the following example merge strategy
is not applied because the second condition is added via ``where``, not via
``in``:

.. code-block:: ruby

  Band.in(foo: ['a']).union.where(foo: {'$in' => 'b'})
  => #<Mongoid::Criteria
    selector: {"foo"=>{"$in"=>["a"]}, "$and"=>[{"foo"=>{"$in"=>"b"}}]}
    options:  {}
    class:    Band
    embedded: false>

This behavior may change in a future release of Mongoid and should not be
relied upon.

In contrast, it does not matter how the existing query was built when a
merge strategy-supporting operator method is invoked. In the following
example, the first condition was added through ``where`` but the strategy
mechanism still applies:

.. code-block:: ruby

  Band.where(foo: {'$in' => ['a']}).union.in(foo: ['b'])
  => #<Mongoid::Criteria
    selector: {"foo"=>{"$in"=>["a", "b"]}}
    options:  {}
    class:    Band
    embedded: false>

Operator Value Expansion
------------------------

Operator methods that support merge strategies all take ``Array`` as their value
type. Mongoid expands ``Array``-compatible types, such as a ``Range``,
when they are used with these operator methods:

.. code-block:: ruby

  Band.in(year: 1950..1960)
  => #<Mongoid::Criteria
    selector: {"year"=>{"$in"=>[1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960]}}
    options:  {}
    class:    Band
    embedded: false>

Additionally, Mongoid has historically wrapped non-``Array`` values in arrays,
as the following example demonstrates:

.. code-block:: ruby

  Band.in(year: 1950)
  => #<Mongoid::Criteria
    selector: {"year"=>{"$in"=>[1950]}}
    options:  {}
    class:    Band
    embedded: false>


Query Methods
=============

elem_match
----------

This matcher finds documents with array fields where one of the array values
matches all of the conditions. For example:

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    field :tours, type: Array
  end

  aerosmith = Band.create!(name: 'Aerosmith', tours: [
    {city: 'London', year: 1995},
    {city: 'New York', year: 1999},
  ])

  Band.elem_match(tours: {city: 'London'}).to_a # => [aerosmith]

``elem_match`` also works with embedded associations:

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    embeds_many :tours
  end

  class Tour
    include Mongoid::Document
    field :city, type: String
    field :year, type: Integer
    embedded_in :band
  end

  dm = Band.create!(name: 'Depeche Mode')
  aerosmith = Band.create!(name: 'Aerosmith')
  Tour.create!(band: aerosmith, city: 'London', year: 1995)
  Tour.create!(band: aerosmith, city: 'New York', year: 1999)

  Band.elem_match(tours: {city: 'London'}).to_a # => [aerosmith]

``elem_match`` does not work with non-embedded associations because MongoDB
does not have joins - the conditions would be added to the collection
that is the source of a non-embedded association rather than the collection
of the association's target.

``elem_match`` can also be used with recursively embedded associations,
as the following example shows:

.. code-block:: ruby

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

  root = Tag.create!(name: 'root')
  sub1 = Tag.new(name: 'sub1', child_tags: [Tag.new(name: 'subsub1')])
  root.child_tags << sub1
  root.child_tags << Tag.new(name: 'sub2')
  root.save!

  Tag.elem_match(child_tags: {name: 'sub1'}).to_a # => [root]

  root.child_tags.elem_match(child_tags: {name: 'subsub1'}).to_a # => [sub1]


.. _projection:

Projection
==========

Mongoid provides two projection operators: ``only`` and ``without``.


.. _only:

``only``
--------

The ``only`` method retrieves only the specified fields from the database. This
operation is sometimes called "projection".

.. code-block:: ruby

  class Band
    include Mongoid::Document

    field :name, type: String
    field :label, type: String

    embeds_many :tours
  end

  class Tour
    include Mongoid::Document

    field :city, type: String
    field :year, type: Integer

    embedded_in :band
  end

  band = Band.only(:name).first

Attempting to reference attributes which have not been loaded results in
``Mongoid::Errors::AttributeNotLoaded``.

.. code-block:: ruby

  band.label
  #=> raises Mongoid::Errors::AttributeNotLoaded

Even though Mongoid currently allows writing to attributes that have not
been loaded, such writes will not be persisted
(`MONGOID-4701 <https://jira.mongodb.org/browse/MONGOID-4701>`_) and
should therefore be avoided.

``only`` can also be used with embedded associations:

.. code-block:: ruby

  band = Band.only(:name, 'tours.year').last
  # => #<Band _id: 5c59afb1026d7c034dba46ac, name: "Aerosmith">

  band.tours.first
  # => #<Tour _id: 5c59afdf026d7c034dba46af, city: nil, year: 1995>

.. note::

  Server versions 4.2 and lower allowed projecting both an association and
  the association's fields in the same query, as follows:

  .. code-block:: ruby

    band = Band.only(:tours, 'tours.year').last

  The most recent projection specification overrides the earlier one.
  For example, the above query was equivalent to:

  .. code-block:: ruby

    band = Band.only('tours.year').last

  Server versions 4.4 and higher prohibit specifying an association and its
  fields in projection in the same query.

``only`` can be specified with referenced associations (has_one, has_many,
has_and_belongs_to_many) but is currently ignored for referenced associations -
all fields of referenced associations will be loaded
(`MONGOID-4704 <https://jira.mongodb.org/browse/MONGOID-4704>`_).

Note that if a document has ``has_one`` or ``has_and_belongs_to_many`` associations,
the fields with foreign keys must be included in the list of attributes
loaded with ``only`` for those associations to be loaded. For example:

.. code-block:: ruby

  class Band
    include Mongoid::Document

    field :name, type: String

    has_and_belongs_to_many :managers
  end

  class Manager
    include Mongoid::Document

    has_and_belongs_to_many :bands
  end

  band = Band.create!(name: 'Astral Projection')
  band.managers << Manager.new

  Band.where(name: 'Astral Projection').only(:name).first.managers
  # => []

  Band.where(name: 'Astral Projection').only(:name, :manager_ids).first.managers
  # => [#<Manager _id: 5c5dc2f0026d7c1730969843, band_ids: [BSON::ObjectId('5c5dc2f0026d7c1730969842')]>]


.. _without:

``without``
-----------

The opposite of ``only``, ``without`` causes the specified fields to be omitted:

.. code-block:: ruby

  Band.without(:name)
  # =>
  # #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:fields=>{"name"=>0}}
  #   class:    Band
  #   embedded: false>

Because Mongoid requires the ``_id`` field for various operations, it (as well
as its ``id`` alias) cannot be omitted via ``without``:

.. code-block:: ruby

  Band.without(:name, :id)
  # =>
  # #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:fields=>{"name"=>0}}
  #   class:    Band
  #   embedded: false>

  Band.without(:name, :_id)
  # =>
  # #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:fields=>{"name"=>0}}
  #   class:    Band
  #   embedded: false>


.. _ordering:

Ordering
========

Mongoid provides the ``order`` method on ``Criteria`` objects and its alias,
``order_by``, to specify the ordering of documents. These methods take a
hash indicating which fields to order the documents by, and whether to use
ascending or descending order for each field.

.. code-block:: ruby

  Band.order(name: 1)
  # => #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:sort=>{"name"=>1}}
  #   class:    Band
  #   embedded: false>

  Band.order_by(name: -1, description: 1)
  # => #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:sort=>{"name"=>-1, "description"=>1}}
  #   class:    Band
  #   embedded: false>

  Band.order_by(name: :desc, description: 'asc')
  # => #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:sort=>{"name"=>-1, "description"=>1}}
  #   class:    Band
  #   embedded: false>

The direction may be specified as integers ``1`` and ``-1`` for ascending
and descending, respectively, or as symbols ``:asc`` and ``:desc``, or as
strings ``"asc"`` and ``"desc"``.

Alternatively, ``order`` accepts an array of two-element arrays specifying
the ordering. Field names and directions may be strings or symbols.

.. code-block:: ruby

  Band.order([['name', 'desc'], ['description', 'asc']])

  Band.order([[:name, :desc], [:description, :asc]])

Another way of providing the order is to use ``#asc`` and ``#desc`` methods
on symbols, as follows:

.. code-block:: ruby

  Band.order(:name.desc, :description.asc)

The arguments can be provided as a string using SQL syntax:

.. code-block:: ruby

  Band.order('name desc, description asc')

Finally, there are ``asc`` and ``desc`` methods that can be used instead of
``order``/``order_by``:

.. code-block:: ruby

  Band.asc('name').desc('description')
  # => #<Mongoid::Criteria
    selector: {}
    options:  {:sort=>{"name"=>1, "description"=>-1}}
    class:    Band
    embedded: false>

``order`` calls can be chained, in which case the oldest calls define the
most significant criteria and the newest calls define the least significant
ones (since in Ruby hashes maintain the order of their keys):

.. code-block:: ruby

  Band.order('name desc').order('description asc')
  # => #<Mongoid::Criteria
    selector: {}
    options:  {:sort=>{"name"=>-1, "description"=>1}}
    class:    Band
    embedded: false>

This can sometimes lead to surprising results if there are scopes, including
the default scope, that use ``order``/``order_by``. For example, in the
following snippet bands are ordered by name first because the order in the
default scope takes precedence over the order given in the query, due to
the default scope being evaluated first:

.. code-block:: ruby

  class Band
    include Mongoid::Document

    field :name, type: String
    field :year, type: Integer

    default_scope -> { order(name: :asc) }
  end

  Band.order(year: :desc)
  # => #<Mongoid::Criteria
    selector: {}
    options:  {:sort=>{"name"=>1, "year"=>-1}}
    class:    Band
    embedded: false>


Pagination
==========

Mongoid provides the pagination operators ``limit``, ``skip``, and ``batch_size`` on ``Criteria``.

.. _limit:

``limit``
---------

``limit`` sets the total number of documents to be returned by a query:

.. code-block:: ruby

  Band.limit(5)
  # =>
  # #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:limit=>5}
  #   class:    Band
  #   embedded: false>

.. _skip:

``skip``
--------

``skip`` (alias: ``offset``) sets the number of query results to skip
before returning documents. The ``limit`` value, if specified, will be applied
after documents are skipped. When performing pagination, ``skip`` is recommended
to be combined with :ref:`ordering <ordering>` to ensure consistent results.

.. code-block:: ruby

  Band.skip(10)
  # =>
  # #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:skip=>10}
  #   class:    Band
  #   embedded: false>

.. _batch-size:

``batch_size``
--------------

When executing large queries, or when iterating over query results with an enumerator method such as
``Criteria#each``, Mongoid automatically uses the `MongoDB getMore command
<https://mongodb.com/docs/manual/reference/command/getMore/>`_ to load results in batches.
The default ``batch_size`` is 1000, however you may set it explicitly:

.. code-block:: ruby

  Band.batch_size(500)
  # =>
  # #<Mongoid::Criteria
  #   selector: {}
  #   options:  {:batch_size=>500}
  #   class:    Band
  #   embedded: false>


Finding By ``_id``
==================

Mongoid provides the ``find`` method on ``Criteria`` objects to find documents
by their ``_id`` values:

.. code-block:: ruby

  Band.find('5f0e41d92c97a64a26aabd10')
  # => #<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor">

The ``find`` method performs type conversion, if necessary, of the argument
to the type declared in the model being queried for the ``_id`` field.
By default, the ``_id`` type is ``BSON::ObjectId``, thus the query above
is equivalent to:

.. code-block:: ruby

  Band.find(BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10'))
  # => #<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor">

.. note::

  When querying collections directly using the driver, type conversion is not
  automatically performed:

.. code-block:: ruby

  Band.collection.find(_id: BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10')).first
  # => {"_id"=>BSON::ObjectId('5f0e41d92c97a64a26aabd10'), "name"=>"Juno Reactor"}

  Band.collection.find(_id: '5f0e41d92c97a64a26aabd10').first
  # => nil

The ``find`` method can accept multiple arguments, or an array of arguments.
In either case each of the arguments or array elements is taken to be an ``_id``
value, and documents with all of the specified ``_id`` values are returned in
an array:

.. code-block:: ruby

  Band.find('5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e')
  # => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>,
    #<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor", description: nil, likes: nil>]

  Band.find(['5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e'])
  # => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>,
    #<Band _id: 5f0e41d92c97a64a26aabd10, name: "Juno Reactor", description: nil, likes: nil>]

If the same ``_id`` value is given more than once, the corresponding document
is only returned once:

.. code-block:: ruby

  Band.find('5f0e41b02c97a64a26aabd0e', '5f0e41b02c97a64a26aabd0e')
  # => [#<Band _id: 5f0e41b02c97a64a26aabd0e, name: "SUN Project", description: nil, likes: nil>]

The documents returned are *not* ordered, and may be returned in a different
order from the order of provided ``_id`` values, as illustrated in the above
examples.

If any of the ``_id`` values are not found in the database, the behavior of
``find`` depends on the value of the ``raise_not_found_error`` configuration
option. If the option is set to ``true``, ``find`` raises
``Mongoid::Errors::DocumentNotFound`` if any of the ``_id``\s are not found.
If the option is set to ``false`` and ``find`` is given a single ``_id`` to
find and there is no matching document, ``find`` returns ``nil``. If the
option is set to ``false`` and ``find`` is given an array of ids to find
and some are not found, the return value is an array of documents that were
found (which could be empty if no documents were found at all).


.. _additional-query-methods:

Additional Query Methods
========================

Mongoid also has some helpful methods on criteria.

.. list-table::
   :header-rows: 1
   :widths: 30 60

   * - Operation
     - Example

   * - ``Criteria#count``

       *Get the total number of documents matching a filter, or the total
       number of documents in a collection. Note this will always hit
       the database for the count.*

       *As of Mongoid 7.2, the* ``count`` *method uses the*
       ``count_documents`` *driver helper to obtain the accurate count.
       previously the* ``count`` *driver helper was used which used
       collection metadata and was thus not necessarily accurate (but
       may have returned the result faster). Use* ``estimated_count``
       *method to obtain an approximate number of documents in the collection
       quickly.*

     -
        .. code-block:: ruby

          Band.count
          Band.where(name: "Photek").count

   * - ``Criteria#estimated_count``

       *Get an approximate number of documents in the collection using the
       collection metadata. The* ``estimated_count`` *method does not accept
       query conditions; if any are given, it will raise*
       ``Mongoid::Errors::InvalidEstimatedCountCriteria``.
       *If a model defines a default scope,* ``estimated_count`` *must be
       called on the unscoped model*.

     -
        .. code-block:: ruby

          Band.count
          Band.where(name: "Photek").count

          class Contract
            include Mongoid::Document

            field :active, type: Boolean

            default_scope -> { where(active: true) }
          end

          Contract.estimated_count
          # => raises Mongoid::Errors::InvalidEstimatedCountCriteria

          Contract.unscoped.estimated_count
          # => 0

   * - ``Criteria#distinct``

       *Get a list of distinct values for a single field. Note this will always hit
       the database for the distinct values.*

       *This method accepts the dot notation, thus permitting referencing
       fields in embedded associations.*

       *This method respects :ref:`field aliases <field-aliases>`,
       including those defined in embedded documents.*

     -
        .. code-block:: ruby

          Band.distinct(:name)
          Band.where(:fans.gt => 100000).
            distinct(:name)

          Band.distinct('cities.name')

          # Assuming an aliased field:
          class Manager
            include Mongoid::Document
            embedded_in :band
            field :name, as: :n
          end

          # Expands out to "managers.name" in the query:
          Band.distinct('managers.n')

   * - ``Criteria#each``

       *Iterate over all matching documents in the criteria.*

     -
        .. code-block:: ruby

          Band.where(members: 1).each do |band|
            p band.name
          end

   * - ``Criteria#exists?``

       *Determine if any matching documents exist. Will return true if there
       are 1 or more.*

       ``#exists?`` *now takes a number of argument types:*

       - ``Hash``: *A hash of conditions.*
       - ``Object``: *An _id to search for.*
       - ``false``/``nil``: *Always returns false.*

     -
        .. code-block:: ruby

          Band.exists?
          Band.where(name: "Photek").exists?
          Band.exists?(name: "Photek")
          Band.exists?(BSON::ObjectId('6320d96a3282a48cfce9e72c'))
          Band.exists?('6320d96a3282a48cfce9e72c')
          Band.exists?(false)
          Band.exists?(nil)

   * - ``Criteria#fifth``

       *Get the fifth document for the given criteria.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.fifth

   * - ``Criteria#fifth!``

       *Get the fifth document for the given criteria, or raise an error if
       none exist.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.fifth!

   * - ``Criteria#find_by``

       *Find a document by the provided attributes. If not found,
       raise an error or return nil depending on the value of the*
       ``raise_not_found_error`` *configuration option.*

     -
        .. code-block:: ruby

          Band.find_by(name: "Photek")

          Band.find_by(name: "Tool") do |band|
            band.impressions += 1
          end

   * - ``Criteria#find_or_create_by``

       *Find a document by the provided attributes, and if not found
       create and return a newly persisted one. Note that attributes provided in the arguments to
       this method will override any set in ``create_with``*.

     -
        .. code-block:: ruby

          Band.find_or_create_by(name: "Photek")
          Band.where(:likes.gt => 10).find_or_create_by(name: "Photek")

        ``find_or_create_by`` can be used on any scope, but in this case
        the criteria given by the scope and by ``find_or_create_by`` are
        combined. The following creates three bands:

        .. code-block:: ruby

          Band.find_or_create_by(name: "Photek")
          Band.where(name: "Photek").find_or_create_by(name: "Aerosmith")
          # creates Aerosmith again because there is no band whose name
          # is Photek and Aerosmith at the same time
          Band.where(name: "Photek").find_or_create_by(name: "Aerosmith")

   * - ``Criteria#find_or_initialize_by``

       *Find a document by the provided attributes, and if not found
       return a new one.*

     -
        .. code-block:: ruby

           Band.find_or_initialize_by(name: "Photek")
           Band.where(:likes.gt => 10).find_or_initialize_by(name: "Photek")

   * - ``Criteria#first|last``

       *Finds a single document given the provided criteria. Get a list of
       documents by passing in a limit argument. This method automatically adds
       a sort on _id. This can cause performance issues, so if the sort is
       undesirable, Criteria#take can be used instead.*

     -
        .. code-block:: ruby

          Band.first
          Band.where(:members.with_size => 3).first
          Band.where(:members.with_size => 3).last
          Band.first(2)

   * - ``Criteria#first!|last!``

       *Finds a single document given the provided criteria, or raises an error
       if none are found. This method automatically adds a sort on _id if no
       sort is given. This can cause performance issues, so if the sort is
       undesirable, Criteria#take! can be used instead.*

     -
        .. code-block:: ruby

          Band.first!
          Band.where(:members.with_size => 3).first!
          Band.where(:members.with_size => 3).last!

   * - ``Criteria#first_or_create``

       *Find the first document by the provided attributes, and if not found
       create and return a newly persisted one.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").first_or_create

   * - ``Criteria#first_or_create!``

       *Find the first document by the provided attributes, and if not found
       create and return a newly persisted one using* ``create!``.

     -
        .. code-block:: ruby

          Band.where(name: "Photek").first_or_create!

   * - ``Criteria#first_or_initialize``

       *Find the first document by the provided attributes, and if not found
       return a new one.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").first_or_initialize

   * - ``Criteria#for_js``

       *Find documents for a provided JavaScript expression, optionally with
       the specified variables added to the evaluation scope. The scope
       argument is supported in MongoDB 4.2 and lower.*
       *Prefer* :manual:`$expr </reference/operator/query/expr/>` *over* ``for_js``.

     -
        .. code-block:: ruby

          # All MongoDB versions
          Band.for_js("this.name = 'Tool'")

          # MongoDB 4.2 and lower
          Band.for_js("this.name = param", param: "Tool")

   * - ``Criteria#fourth``

       *Get the fourth document for the given criteria.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.fourth

   * - ``Criteria#fourth!``

       *Get the fourth document for the given criteria, or raise an error if
       none exist.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.fourth!

   * - ``Criteria#length|size``

       *Same as count but caches subsequent calls to the database*

     -
        .. code-block:: ruby

          Band.length
          Band.where(name: "FKA Twigs").size

   * - ``Criteria#pick``

       *Get the values from one document for the provided fields.
       Returns nil for unset fields and for non-existent fields.*

       *This method does not apply a sort to the documents, so it
       will not necessarily return the values from the first document.*

       *This method accepts the dot notation, thus permitting referencing
       fields in embedded associations.*

       *This method respects :ref:`field aliases <field-aliases>`,
       including those defined in embedded documents.*

     -
        .. code-block:: ruby

          Band.all.pick(:name)

          Band.all.pick('cities.name')

          # Using the earlier definition of Manager,
          # expands out to "managers.name" in the query:
          Band.all.pick('managers.n')


   * - ``Criteria#pluck``

       *Get all the values for the provided field.
       Returns nil for unset fields and for non-existent fields.*

       *This method accepts the dot notation, thus permitting referencing
       fields in embedded associations.*

       *This method respects :ref:`field aliases <field-aliases>`,
       including those defined in embedded documents.*

     -
        .. code-block:: ruby

          Band.all.pluck(:name)
            #=> ["Daft Punk", "Aphex Twin", "Ween"]

          Band.all.pluck('address.city')
            #=> ["Paris", "Limerick", "New Hope"]

          # Using the earlier definition of Manager,
          # expands out to "managers.name" in the query:
          Band.all.pluck('managers.n')
            #=> [ ["Berry Gordy", "Tommy Mottola"], [], ["Quincy Jones"] ]

          # Accepts multiple field arguments, in which case
          # the result will be returned as an Array of Arrays.
          Band.all.pluck(:name, :likes)
            #=> [ ["Daft Punk", 342], ["Aphex Twin", 98], ["Ween", 227] ]

   * - ``Criteria#read``

       *Sets the read preference for the criteria.*

     -
        .. code-block:: ruby

          Band.all.read(mode: :primary)

   * - ``Criteria#second``

       *Get the second document for the given criteria.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.second

   * - ``Criteria#second!``

       *Get the second document for the given criteria, or raise an error if
       none exist.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.second!

   * - ``Criteria#second_to_last``

       *Get the second to last document for the given criteria.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.second_to_last

   * - ``Criteria#second_to_last!``

       *Get the second to last document for the given criteria, or raise an
       error if none exist.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.second_to_last!

   * - ``Criteria#take``

       *Get a list of n documents from the database or just one if no parameter
       is provided.*

       *This method does not apply a sort to the documents, so it can return
       different document(s) than #first and #last.*

     -
        .. code-block:: ruby

          Band.take
          Band.take(5)

   * - ``Criteria#take!``

       *Get a document from the database or raise an error if none exist.*

       *This method does not apply a sort to the documents, so it can return
       different document(s) than #first and #last.*

     -
        .. code-block:: ruby

          Band.take!

   * - ``Criteria#tally``

       *Get a mapping of values to counts for the provided field.*

       *This method accepts the dot notation, thus permitting referencing
       fields in embedded associations.*

       *This method respects :ref:`field aliases <field-aliases>`,
       including those defined in embedded documents.*

     -
        .. code-block:: ruby

          Band.all.tally(:name)

          Band.all.tally('cities.name')

          # Using the earlier definition of Manager,
          # expands out to "managers.name" in the query:
          Band.all.tally('managers.n')

   * - ``Criteria#third``

       *Get the third document for the given criteria.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.third

   * - ``Criteria#third!``

       *Get the third document for the given criteria, or raise an error if
       none exist.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.third!

   * - ``Criteria#third_to_last``

       *Get the third to last document for the given criteria.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.third_to_last

   * - ``Criteria#third_to_last!``

       *Get the third to last document for the given criteria, or raise an
       error if none exist.*

       *This method automatically adds a sort on _id if no sort is given.*

     -
        .. code-block:: ruby

          Band.third_to_last!


Eager Loading
=============

Mongoid provides a facility to eager load documents
from associations to prevent the n+1 issue when
iterating over documents with association access. Eager loading is supported on
all associations with the exception of polymorphic ``belongs_to``
associations.

.. code-block:: ruby

  class Band
    include Mongoid::Document
    has_many :albums
  end

  class Album
    include Mongoid::Document
    belongs_to :band
  end

  Band.includes(:albums).each do |band|
    p band.albums.first.name # Does not hit the database again.
  end


Regular Expressions
===================

MongoDB, and Mongoid, allow querying documents by regular expressions.

Given the following model definitions:

.. code-block:: ruby

  class Band
    include Mongoid::Document

    field :name, type: String
    field :description, type: String
  end

  Band.create!(name: 'Sun Project', description: "Sun\nProject")

... we can query using simple Ruby regular expressions in a natural way:

.. code-block:: ruby

  Band.where(name: /project/i).first
  # => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">

It is also possible to query using PCRE syntax by constructing
``BSON::Regexp::Raw`` objects explicitly:

.. code-block:: ruby

  Band.where(description: /\AProject/).first
  # => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">

  Band.where(description: BSON::Regexp::Raw.new('^Project')).first
  # => nil

  Band.where(description: BSON::Regexp::Raw.new('^Project', 'm')).first
  # => #<Band _id: 5dc9f7d5ce4ef34893354323, name: "Sun Project", description: "Sun\nProject">


Conditions On Fields
====================

When a condition uses a field defined in the model, the value being specified
in the condition is converted according to the rules of the field, if any.
For example, consider the following model definition that contains a ``Time``
field, a ``Date`` field and an implicit ``Object`` field, and also
intentionally does not define a field called ``deregistered_at``:

.. code-block:: ruby

  class Voter
    include Mongoid::Document

    field :born_on, type: Date
    field :registered_at, type: Time
    field :voted_at
  end

Queries on ``born_on`` and ``registered_at`` fields using ``Date`` and ``Time``
values, respectively, are straightforward:

.. code-block:: ruby

  Voter.where(born_on: Date.today).selector
  # => {"born_on"=>2020-12-18 00:00:00 UTC}

  Voter.where(registered_at: Time.now).selector
  # => {"registered_at"=>2020-12-19 04:33:36.939788067 UTC}

But, note the differences in behavior when providing a ``Date`` instance
in all possible scenarios:

.. code-block:: ruby

  Voter.where(born_on: Date.today).selector
  # => {"born_on"=>2020-12-18 00:00:00 UTC}

  Voter.where(registered_at: Date.today).selector
  # => {"registered_at"=>2020-12-18 00:00:00 -0500}

  Voter.where(voted_at: Date.today).selector
  # => {"voted_at"=>Fri, 18 Dec 2020}

  Voter.where(deregistered_at: Date.today).selector
  # => {"deregistered_at"=>2020-12-18 00:00:00 UTC}

When using the ``registered_at`` field which is of type ``Time``, the date
was interpreted to be in local time (as per the :ref:`configured time zone
<time-zones>`). When using the ``born_on`` field which is of type ``Date``,
the date was interpreted to be in UTC. When using the ``voted_at`` field
which was defined without a type (hence implicitly as an ``Object``),
the date was used unmodified in the constructed query. When using a
nonexistent field ``deregistered_at`` the date was interpreted to be in UTC
and converted to a time, matching the behavior of querying a ``Date`` field.


Scoping
=======

Scopes provide a convenient way to reuse common criteria with more
business domain style syntax.


.. _named-scopes:

Named Scopes
------------

Named scopes are simply criteria defined at class load that are referenced
by a provided name. Just like normal criteria, they are lazy and chainable.

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :country, type: String
    field :genres, type: Array

    scope :english, ->{ where(country: "England") }
    scope :rock, ->{ where(:genres.in => [ "rock" ]) }
  end

  Band.english.rock # Get the English rock bands.

Named scopes can take procs and blocks for accepting parameters or
extending functionality.

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    field :country, type: String
    field :active, type: Boolean, default: true

    scope :named, ->(name){ where(name: name) }
    scope :active, ->{
      where(active: true) do
        def deutsch
          tap do |scope|
            scope.selector.store("origin" => "Deutschland")
          end
        end
      end
    }
  end

  Band.named("Depeche Mode") # Find Depeche Mode.
  Band.active.deutsch # Find active German bands.

By default, Mongoid allows defining a scope that would shadow an existing
class method, as the following example shows:

.. code-block:: ruby

  class Product
    include Mongoid::Document

    def self.fresh
      true
    end

    scope :fresh, ->{ where(fresh: true) }
  end

To have Mongoid raise an error when a scope would overwrite an existing class
method, set the ``scope_overwrite_exception`` :ref:`configuration option
<configuration-options>` to ``true``.


Default Scopes
--------------

Default scopes can be useful when you find yourself applying the same
criteria to most queries, and wish to specify these criteria as the default.
Default scopes are procs that return criteria objects.

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    field :active, type: Boolean

    default_scope ->{ where(active: true) }
  end

  Band.each do |band|
    # All bands here are active.
  end

Specifying a default scope also initializes the fields of new models to
the values given in the default scope, if the values are simple literals:

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    field :active, type: Boolean
    field :num_tours, type: Integer

    default_scope ->{ where(active: true, num_tours: {'$gt' => 1}) }
  end

  # active is set, num_tours is not set
  Band.new # => #<Band _id: 5c3f7452ce4ef378295ca5f5, name: nil, active: true, num_tours: nil>

Note that if a default value is provided both in the field definition and
in the default scope, the value in the default scope takes precedence:

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    field :active, type: Boolean, default: true

    default_scope ->{ where(active: false) }
  end

  Band.new # => #<Band _id: 5c3f74ddce4ef3791abbb088, name: nil, active: false>

Because a default scope initializes fields in new models as just described,
defining a default scope with a dotted key and a simple literal value, while
possible, is not recommended:

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    field :tags, type: Hash

    default_scope ->{ where('tags.foo' => 'bar') }
  end

  Band.create!
  # => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"}
  Band.create!(tags: { 'foo' => 'bar' })
  # => Created document: {"_id"=>BSON::ObjectId('632de4ad3282a404bee1877c'), "tags.foo"=>"bar", "tags"=>{"foo"=>"bar"}}
  Band.all.to_a
  # => [ #<Band _id: 632de4ad3282a404bee1877c, tags: {"foo"=>"bar"}> ]

Mongoid 8 allows dotted keys to be used in Mongoid, and when creating a document,
the scope is added as a dotted key in the attributes:

.. code-block:: ruby

  Band.new.attribute
  # => {"_id"=>BSON::ObjectId('632de97d3282a404bee1877d'), "tags.foo"=>"bar"}

Whereas when querying, Mongoid looks for an embedded document:

.. code-block:: ruby

  Band.create!
  # => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"}
  Band.where
  # => #<Mongoid::Criteria
         selector: {"tags.foo"=>"bar"}
         options:  {}
         class:    Band
         embedded: false>
  # This looks for something like: { tags: { "foo" => "bar" } }
  Band.count
  # => 0

A workaround is to define the default scope as a complex query:

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    field :tags, type: Hash

    default_scope ->{ where('tags.foo' => {'$eq' => 'bar'}) }
  end

  Band.create!(tags: { hello: 'world' })
  Band.create!(tags: { foo: 'bar' })
  # does not add a "tags.foo" dotted attribute
  Band.count
  # => 1

You can tell Mongoid not to apply the default scope by using
``unscoped``, which can be inline or take a block.

.. code-block:: ruby

  Band.unscoped.where(name: "Depeche Mode")
  Band.unscoped do
    Band.where(name: "Depeche Mode")
  end

You can also tell Mongoid to explicitly apply the default scope
again later to always ensure it's there.

.. code-block:: ruby

  Band.unscoped.where(name: "Depeche Mode").scoped

If you are using a default scope on a model that is part of an association,
you must reload the association to have scoping reapplied.
This is important to note if you change a value of a document in the association
that would affect its visibility within the scoped association.

.. code-block:: ruby

  class Label
    include Mongoid::Document
    embeds_many :bands
  end

  class Band
    include Mongoid::Document
    field :active, default: true
    embedded_in :label
    default_scope ->{ where(active: true) }
  end

  label.bands.push(band)
  label.bands # [ band ]
  band.update_attribute(:active, false)
  label.bands # [ band ] Must reload.
  label.reload.bands # []

.. note::

  After the default scope is applied, it is no longer distinguished from
  other query conditions. This can lead to surprising behavior when using
  ``or`` and ``nor`` operators in particular:

  .. code-block:: ruby

    class Band
      include Mongoid::Document

      field :name
      field :active
      field :touring

      default_scope ->{ where(active: true) }
    end

    Band.where(name: 'Infected Mushroom')
    # =>
    # #<Mongoid::Criteria
    #   selector: {"active"=>true, "name"=>"Infected Mushroom"}
    #   options:  {}
    #   class:    Band
    #   embedded: false>

    Band.where(name: 'Infected Mushroom').or(touring: true)
    # =>
    # #<Mongoid::Criteria
    #   selector: {"$or"=>[{"active"=>true, "name"=>"Infected Mushroom"}, {"touring"=>true}]}
    #   options:  {}
    #   class:    Band
    #   embedded: false>

    Band.or(touring: true)
    # =>
    # #<Mongoid::Criteria
    #   selector: {"$or"=>[{"active"=>true}, {"touring"=>true}]}
    #   options:  {}
    #   class:    Band
    #   embedded: false>

  In the last example, you might expect the two conditions
  (``active: true`` and ``touring: true``) to be combined with an ``$and``,
  but because the ``Band`` class already has the scope applied to it,
  it becomes one of the disjunction branches of the ``or``.


Runtime Default Scope Override
------------------------------

You can use the ``with_scope`` method to change the default scope in a block
at runtime:

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :country, type: String
    field :genres, type: Array

    scope :english, ->{ where(country: "England") }
  end

  criteria = Band.with_scope(Band.english) do
    Band.all
  end

  criteria
  # =>
  # #<Mongoid::Criteria
  #   selector: {"country"=>"England"}
  #   options:  {}
  #   class:    Band
  #   embedded: false>

.. note::

  If with_scope calls are nested, when the nested with_scope block completes
  Mongoid 7 sets the current scope to nil instead of the parent scope.
  Mongoid 8 will set the current scope to the correct parent scope.
  To get Mongoid 8 behavior in Mongoid 7.4 and higher, set the
  ``Mongoid.broken_scoping`` global option to false.


Class Methods
-------------

Class methods on models that return criteria objects are also
treated like scopes, and can be chained as well.

.. code-block:: ruby

  class Band
    include Mongoid::Document
    field :name, type: String
    field :active, type: Boolean, default: true

    def self.active
      where(active: true)
    end
  end

  Band.active


Queries + Persistence
=====================

Mongoid supports persistence operations off of criteria
in a light capacity for when you want to expressively perform multi
document inserts, updates, and deletion.

.. warning::

  Criteria ordering and pagination conditions, including ``order``, ``limit``,
  ``offset``, and ``batch_size``, will be ignored on the following operations.

.. list-table::
   :header-rows: 1
   :widths: 30 60

   * - Operation
     - Example

   * - ``Criteria#create``

       *Create a newly persisted document.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").create

   * - ``Criteria#create!``

       *Create a newly persisted document and raise an exception on validation failure.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").create!

   * - ``Criteria#build|new``

       *Create a new (unsaved) document.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").build
          Band.where(name: "Photek").new

   * - ``Criteria#update``

       *Update attributes of the first matching document.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").update(label: "Mute")

   * - ``Criteria#update_all``

       *Update attributes of all matching documents.*

     -
        .. code-block:: ruby

          Band.where(members: 2).update_all(label: "Mute")

   * - ``Criteria#add_to_set``

       *Perform an $addToSet on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").add_to_set(label: "Mute")

   * - ``Criteria#bit``

       *Perform a $bit on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").bit(likes: { and: 14, or: 4 })

   * - ``Criteria#inc``

       *Perform an $inc on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").inc(likes: 123)

   * - ``Criteria#pop``

       *Perform a $pop on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Photek").pop(members: -1)
          Band.where(name: "Photek").pop(members: 1)

   * - ``Criteria#pull``

       *Perform a $pull on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Tool").pull(members: "Maynard")

   * - ``Criteria#pull_all``

       *Perform a $pullAll on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Tool").
            pull_all(:members, [ "Maynard", "Danny" ])

   * - ``Criteria#push``

       *Perform a $push on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Tool").push(members: "Maynard")

   * - ``Criteria#push_all``

       *Perform a $push with $each on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Tool").
            push_all(members: [ "Maynard", "Danny" ])

   * - ``Criteria#rename``

       *Perform a $rename on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Tool").rename(name: :title)

   * - ``Criteria#set``

       *Perform a $set on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Tool").set(likes: 10000)

   * - ``Criteria#unset``

       *Perform a $unset on all matching documents.*

     -
        .. code-block:: ruby

          Band.where(name: "Tool").unset(:likes)

   * - ``Criteria#delete``

       *Deletes all matching documents in the database.*

     -
        .. code-block:: ruby

          Band.where(label: "Mute").delete

   * - ``Criteria#destroy``

       *Deletes all matching documents in the database while running callbacks for all.
       This loads all documents into memory and can be an expensive operation.*

     -
        .. code-block:: ruby

          Band.where(label: "Mute").destroy


.. _query-cache:

Query Cache
===========

The Ruby MongoDB driver versions 2.14 and above provide query caching functionality. When enabled, the
query cache saves the results of previously executed find and aggregation
queries and reuses them in the future instead of performing the queries again,
thus increasing application performance and reducing database load.

Please review the `driver query cache documentation
<https://mongodb.com/docs/ruby-driver/current/reference/query-cache/>`_
for details about the driver's query cache behavior.

The rest of this section assumes that driver 2.14.0 or later is being used.


Enabling Query Cache
--------------------

The query cache may be enabled by using the driver's namespace or Mongoid's
namespace.


.. _enabling-query-cache-automatically:

Enabling Query Cache Automatically
----------------------------------

The MongoDB Ruby Driver provides middleware to automatically enable the query cache for
Rack web requests and ActiveJob job runs. Please see the :ref:`Query Cache Rack Middleware
<query-cache-middleware>` section on the configuration page for instructions.

Note that the Query Cache Middleware does not apply to code executed outside web requests
and/or jobs.


.. _enabling-query-cache-manually:

Enabling Query Cache Manually
-----------------------------

To enable the Query Cache manually for a code segment, use:

.. code-block:: ruby

  Mongo::QueryCache.cache do
    # ...
  end

The Query Cache can also be explicitly enabled and disabled, although we
recommend to use the block form described above:

.. code-block:: ruby

  begin
    Mongo::QueryCache.enabled = true
    # ...
  ensure
    Mongo::QueryCache.enabled = false
  end


.. _query-cache-first-method:

Caching the Result of ``#first``
--------------------------------

Calling the ``first`` method on a model class imposes an ascending sort by
the ``_id`` field on the underlying query. This may produce unexpected behavior
with query caching.

For example, when calling ``all`` on a model class and then ``first``,
one would expect the second query to use the cached results from the first.
However, because of the sort imposed on the second query, both methods
will query the database and separately cache their results.

.. code-block:: ruby

  Band.all.to_a
  #=> Queries the database and caches the results

  Band.first
  #=> Queries the database again because of the sort

To use the cached results, call ``all.to_a.first`` on the model class.


.. _load-async:

Asynchronous Queries
====================

Mongoid allows running database queries asynchronously in the background.
This can be beneficial when there is a need to get documents from different
collections.

In order to schedule an asynchronous query call the ``load_async`` method on a
``Criteria``:

.. code-block:: ruby

  class PagesController < ApplicationController
    def index
      @active_bands = Band.where(active: true).load_async
      @best_events = Event.best.load_async
      @public_articles = Article.where(public: true).load_async
    end
  end

In the above example three queries will be scheduled for asynchronous execution.
Results of the queries can be later accessed as usual:

.. code-block:: html

  <ul>
    <%- @active_bands.each do -%>
      <li><%= band.name %></li>
    <%- end -%>
  </ul>

Even if a query is scheduled for asynchronous execution, it might be executed
synchronously on the caller's thread. There are three possible scenarios depending
on when the query results are being accessed:

#. If the scheduled asynchronous task has been already executed, the results are returned.
#. If the task has been started, but not finished yet, the caller's thread blocks until the task is finished.
#. If the task has not been started yet, it is removed from the execution queue, and the query is executed synchronously on the caller's thread.

.. note::

  Even though ``load_async`` method returns a ``Criteria`` object, you should not
  do any operations on this object except accessing query results. The query is
  scheduled for execution immediately after calling ``load_async``, therefore
  later changes to the ``Criteria`` object may not be applied.


Configuring asynchronous query execution
----------------------------------------

Asynchronous queries are disabled by default. When asynchronous queries are
disabled, ``load_async`` will execute the query immediately on the current thread,
blocking as necessary. Therefore, calling ``load_async`` on criteria in this case
is roughly the equivalent of calling ``to_a`` to force query execution.

In order to enable asynchronous query execution, the following config options
must be set:

.. code-block:: yaml

  development:
    ...
    options:
      # Execute asynchronous queries using a global thread pool.
      async_query_executor: :global_thread_pool
      # Number of threads in the pool. The default is 4.
      # global_executor_concurrency: 4