******* 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') # => #"Deftones"} # options: {} # class: Band # embedded: false> # Evaluate the query and get matching documents: Band.where(name: 'Deftones').to_a # => [#] 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") # => #{"$gte"=>"1980-01-01"}} # options: {} # class: Band # embedded: false> scope.where(:founded.lte => "2020-01-01") # => #{"$gte"=>"1980-01-01", "$lte"=>"2020-01-01"}} # options: {} # class: Band # embedded: false> scope # => #{"$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") # => #"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}) # => #{"$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) # => #{"$gt"=>1980}} # 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') # => #"Smith"} # options: {} # class: Band # embedded: false> Band.where(:'manager.name'.ne => 'Smith') # => #{"$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. Field Types =========== In order to query on a field, it is not necessary to add the field to :ref:`the model class definition `. However, if a field is defined in the model class, the type of the field is taken into account when constructing the query: .. code-block:: ruby Band.where(name: 2020) # => #"2020"} # options: {} # class: Band # embedded: false> Band.where(founded: 2020) # => #2020} # options: {} # class: Band # embedded: false> Aliases ======= Queries take into account :ref:`storage field names ` and :ref:`field aliases `: .. code-block:: ruby Band.where(name: 'Astral Projection') # => #"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') # => #BSON::ObjectId('5ebdeddfe1b83265a376a760')} # options: {} # class: Band # embedded: false> .. _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: # => #"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 `_. 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``, ``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"} ``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') # => #[{"$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 `_. 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']) => #{"$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']) => #{"$in"=>["b"]}} options: {} class: Band embedded: false> Band.in(name: ['a', 'b']).intersect.in(name: ['b', 'c']) => #{"$in"=>["b"]}} options: {} class: Band embedded: false> Band.in(name: ['a']).union.in(name: ['b']) => #{"$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']) => #{"$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 `_. 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'}) => #{"$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']) => #{"$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) => #{"$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) => #{"$in"=>[1950]}} options: {} class: Band embedded: false> This wrapping behavior is deprecated and should not be relied on. It may be removed in a future release of Mongoid. 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 `_) and should therefore be avoided. ``only`` can also be used with embedded associations: .. code-block:: ruby band = Band.only(:name, 'tours.year').last # => # band.tours.first # => # .. 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 `_). 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 # => [#] .. _without: ``without`` ----------- The opposite of ``only``, ``without`` causes the specified fields to be omitted: .. code-block:: ruby Band.without(:name) # => # #{"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) # => # #{"name"=>0}} # class: Band # embedded: false> Band.without(:name, :_id) # => # #{"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) # => #{"name"=>1}} # class: Band # embedded: false> Band.order_by(name: -1, description: 1) # => #{"name"=>-1, "description"=>1}} # class: Band # embedded: false> Band.order_by(name: :desc, description: 'asc') # => #{"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') # => #{"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') # => #{"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) # => #{"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) # => # #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 ` to ensure consistent results. .. code-block:: ruby Band.skip(10) # => # #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 `_ to load results in batches. The default ``batch_size`` is 1000, however you may set it explicitly: .. code-block:: ruby Band.batch_size(500) # => # #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') # => # 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')) # => # .. 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.find(['5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e']) # => [#, #] If the same ``_id`` value is given more than once, the corresponding document is only returned once: .. code-block:: ruby Band.find('5f0e41b02c97a64a26aabd0e', '5f0e41b02c97a64a26aabd0e') # => [#] 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 `, 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.* - .. code-block:: ruby Band.exists? Band.where(name: "Photek").exists? * - ``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.* *In MongoDB 3.6 and higher, prefer* :manual:`$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 `, 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 `, 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 `, 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 # => # 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.where(description: BSON::Regexp::Raw.new('^Project')).first # => nil Band.where(description: BSON::Regexp::Raw.new('^Project', 'm')).first # => # 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 `). 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 ` 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 # => # 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 # => # 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 is not possible: .. 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! # exception: BSON::String::IllegalKey ('tags.foo' is an illegal key in MongoDB. Keys may not start with '$' or contain a '.'.) 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'}) 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') # => # #true, "name"=>"Infected Mushroom"} # options: {} # class: Band # embedded: false> Band.where(name: 'Infected Mushroom').or(touring: true) # => # #[{"active"=>true, "name"=>"Infected Mushroom"}, {"touring"=>true}]} # options: {} # class: Band # embedded: false> Band.or(touring: true) # => # #[{"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 # => # #"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 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. The query cache has historically been provided by Mongoid but as of driver version 2.14.0, the query cache implementation has been moved into the driver. When Mongoid is used with driver version 2.14.0 or later, Mongoid delegates all work to the driver's query cache implementation. Please review the `driver query cache documentation `_ 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 and Mongoid delegates to the driver's query cache. Enabling Query Cache -------------------- The query cache may be enabled by using the driver's namespace or Mongoid's namespace. .. _enabling-query-cache-via-middleware: Enabling Query Cache Via Middleware ----------------------------------- Applications that utilize a Rack-based web framework (Rails, etc.) can take advantage of the Rack middleware provided by Mongoid to automatically enable the query cache for each web request, and clear the query cache after each request completes. Please see the :ref:`Query Cache Rack Middleware ` section on the configuration page for instructions. Note that the Query Cache Middleware does not apply to code executed outside web requests. .. _enabling-query-cache-manually: Enabling Query Cache Manually ----------------------------- To enable the Query Cache manually for a code segment, use: .. code-block:: ruby Mongoid::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 Mongoid::QueryCache.enabled = true # ... Mongoid::QueryCache.enabled = false Alternatively, you can reference the driver's query cache module directly: .. code-block:: ruby Mongo::QueryCache.cache do # ... end Mongo::QueryCache.enabled = true Mongo::QueryCache.enabled = true Query Cache Shared With Driver ------------------------------ When Mongoid delegates to the driver's query cache, the same cache is used by both Mongoid and the driver. For example, the following two queries are implemented via the same server query and only the first of them would be sent to the server: .. code-block:: ruby Mongoid::QueryCache.cache do Band.first Band.collection.find.sort(_id: 1).first end Similarly, write operations that clear the cache would do so when the read is performed by the driver and write by Mongoid, or vice versa: .. code-block:: ruby Mongoid::QueryCache.cache do Band.first Band.collection.insert_one({}) # Queries again Band.first 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. .. _legacy-query-cache-limitations: