Specify a Query
On this page
- Overview
- Sample Data
- Queries in Mongoid
- Create a Query Filter
- Field Syntax
- Query API Syntax
- Symbol Operator Syntax
- Query on Different Field Types
- Defined Fields
- Raw Values
- Field Aliases
- Embedded Documents
- Logical Operations
- Operator Combinations
- not Behavior
- Incremental Query Construction
- Merge Strategies
- Supported Operator Methods
- Operator Value Expansion
- Element Match
- Associations
- Querying by _id Value
- find Variations
- Regular Expressions
- Field Type Query Conversions
- Additional Query Methods
- Count Documents
- Ordinal Methods
- Survey Field Values
- Miscellaneous
- Additional Information
Overview
In this guide, you can learn how to specify a query by using Mongoid.
You can refine the set of documents that a query returns by creating a query filter. A query filter is an expression that specifies the search criteria MongoDB uses to match documents in a read or write operation. When creating a query filter, you can prompt the driver to search for documents with an exact match to your query, or you can compose query filters to express more complex matching criteria.
Mongoid provides a query domain-specific language (DSL) similar to the one used in Active Record.
Sample Data
The examples in this guide use the Band
model, which represents a
band or musical group. The definition of the Band
model might be
different for each section to demonstrate different query
functionalities. Some sections might use other models
to demonstrate query functionality.
Queries in Mongoid
Mongoid query methods return Mongoid::Criteria
objects, which are
chainable and lazily evaluated wrappers for the MongoDB Query API.
The queries are executed when you iterate through the results. The
following example demonstrates the return type for a simple query:
# Creates a simple query Band.where(name: "Deftones") # Returns a Criteria object # => #<Mongoid::Criteria # selector: {"name"=>"Deftones"} # options: {} # class: Band # embedded: false> # Evaluate the query by converting to JSON Band.where(name: "Deftones").to_json # Returns matching documents # => [{"_id":"...","name":"Deftones"}]
You can use methods such as first
and last
to return
individual documents. You can also iterate a Criteria
object by using
methods such as each
or map
to retrieve documents from the
server. You can use to_json
to convert a Criteria
object to
JSON.
Tip
Chaining methods
If you chain other query methods on an existing Criteria
object,
Mongoid merges the filter criteria.
Create a Query Filter
This section describes the syntax patterns that you can use to create filter criteria. You can specify queries in Mongoid by using any of the following syntax patterns:
Field syntax
Query API syntax
Symbol operator syntax
Note
Syntax Behaviors
These syntaxes support querying embedded documents by using dot notation. The syntaxes also respect field aliases and field types, if the field being queried is defined in the model class.
The examples in this section use the following model definition:
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 field querying syntax uses the basic Ruby hashes. The keys can be symbols or strings and correspond to field names in MongoDB documents.
The following code shows two equivalent queries that use field querying
syntax to retrieve documents in which the name
field value is
'Depeche Mode'
:
Band.where(name: 'Depeche Mode') Band.where('name' => 'Depeche Mode')
Query API Syntax
You can specify a Query API operator on any field by using the hash syntax, as shown by the following equivalent queries:
Band.where(founded: {'$gt' => 1980}) Band.where('founded' => {'$gt' => 1980})
Symbol Operator Syntax
You can specify Query API operators as methods on symbols for the respective field name, as shown in the following code:
Band.where(:founded.gt => 1980)
Query on Different Field Types
This section describes how to perform queries on fields with different types of values.
Defined Fields
To query on a field, the field does not need to be in the the model class definition. However, if a field is defined in the model class, Mongoid coerces query values to match the defined field types when constructing the query.
The following code specifies a string value when querying on the
founded
field. Because the founded
field is defined in the model
class to have Integer
values, Mongoid coerces the string '2020'
to 2020
when performing the query:
Band.where(founded: '2020')
To learn more about defining fields in Mongoid, see the Field Types guide.
Raw Values
To bypass Mongoid's query type coercion behavior and query
directly for the raw-typed value in the database, wrap the query value in
the Mongoid::RawValue
class, as shown in the following code:
Band.where(founded: Mongoid::RawValue('2020'))
Field Aliases
Queries follow the storage field names and field aliases that you might have set in your model class definition.
The id
and _id
fields are aliases, so you can use either field
name in queries:
Band.where(id: '5ebdeddfe1b83265a376a760') Band.where(_id: '5ebdeddfe1b83265a376a760')
Embedded Documents
To query on values of fields of embedded documents, you can use dot
notation. The following code retrieves documents in which the name
field of the embedded Manager
document is 'Smith'
:
Band.where('manager.name' => 'Smith')
The following code demonstrates how to use a symbol operator when querying on an embedded field:
Band.where(:'manager.name'.ne => 'Smith')
Note
Queries always return top-level model instances, even if all the conditions reference embedded document fields.
Logical Operations
Mongoid supports the following logical operations on Criteria
objects:
and
or
nor
not
These methods take one or more hashes of conditions or another
Criteria
object as their arguments. The not
operation has an
argument-free version.
The following code demonstrates how to use the logical operations in queries:
# Uses "and" to combine criteria Band.where(label: 'Trust in Trance').and(name: 'Astral Projection') # Uses "or" to specify criteria Band.where(label: 'Trust in Trance').or(Band.where(name: 'Astral Projection')) # Uses "not" to specify criteria Band.not(label: 'Trust in Trance', name: 'Astral Projection') # Uses "not" without arguments Band.not.where(label: 'Trust in Trance', name: 'Astral Projection')
Note
Array Parameters
To ensure backwards compatibility with earlier Mongoid versions, the logical operation methods accept arrays of parameters, which are flattened to obtain the criteria.
Passing arrays to logical operations is deprecated and might be removed in a future version.
The following queries produce the same conditions:
# Conditions passed to separate "and" calls Band.and(name: 'Sun Kil Moon').and(member_count: 2) # Multiple conditions in the same "and" call Band.and({name: 'Sun Kil Moon'}, {member_count: 2}) # Multiple conditions in an array - Deprecated Band.and([{name: 'Sun Kil Moon'}, {member_count: 2}]) # Condition in "where" and a scope Band.where(name: 'Sun Kil Moon').and(Band.where(member_count: 2)) # Condition in "and" and a scope Band.and({name: 'Sun Kil Moon'}, Band.where(member_count: 2)) # Scope as an array element, nested arrays - Deprecated Band.and([Band.where(name: 'Sun Kil Moon'), [{member_count: 2}]])
Operator Combinations
The logical operators have the the same semantics as those from Active Record.
When conditions are specified on the same field multiple times, all conditions are added to the criteria, as shown by the queries in the following code:
# Combines as "and" Band.where(name: 'Swans').where(name: 'Feist') # Combines as "or" Band.where(name: 'Swans').or(name: 'Feist')
The any_of
, none_of
, nor
, and not
operations
behave similarly.
When you use and
, or
, and nor
logical operators, they
operate on the criteria built up to that point:
# "or" applies to the first condition, and the second is combined # as "and" Band.or(name: 'Sun').where(label: 'Trust') # Same as previous example - "where" and "and" are aliases Band.or(name: 'Sun').and(label: 'Trust') # Same operator can be stacked any number of times Band.or(name: 'Sun').or(label: 'Trust') # The last label condition is added to the top level as "and" Band.where(name: 'Sun').or(label: 'Trust').where(label: 'Feist') # Interpreted query: # {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}], "label"=>"Feist"}
not Behavior
You can use the not
method without arguments, in which case it
negates the next condition that is specified. The not
method can
be called with one or more hash conditions or Criteria
objects,
which are all negated and added to the criteria.
The following examples demonstrate the behavior of not
:
# "not" negates "where" Band.not.where(name: 'Best') # The second "where" is added as "$and" Band.not.where(name: 'Best').where(label: /Records/) # "not" negates its argument Band.not(name: 'Best')
Note
You cannot use the $not
operator in MongoDB with a string argument.
Mongoid uses the $ne
operator to achieve negation:
# String negation - uses "$ne" Band.not.where(name: 'Best') # Regex negation - uses "$not" Band.not.where(name: /Best/)
Similarly to and
, the not
operation negates individual
conditions for simple field criteria. For complex conditions and when a
field already has a condition defined on it, Mongoid emulates $not
by using an {'$and' => [{'$nor' => ...}]}
construct, because MongoDB
supports the $not
operator only on a per-field basis rather than
globally:
# Simple condition Band.not(name: /Best/) # Complex conditions Band.where(name: /Best/).not(name: 'Astral Projection') # Symbol operator syntax Band.not(:name.ne => 'Astral Projection')
If you are using not
with arrays or regular expressions, view the
limitations of $not
in the Server manual.
Incremental Query Construction
By default, when you add conditions to a query, Mongoid considers each
condition complete and independent from any other conditions
present in the query. For example, calling in
twice adds two separate
$in
conditions:
Band.in(name: ['a']).in(name: ['b']) # Interpreted query: # {"name"=>{"$in"=>["a"]}, "$and"=>[{"name"=>{"$in"=>["b"]}}]}
Some operator methods support building the condition incrementally. When you add a condition which uses one of the supported operators, Mongoid sees if there already is a condition on the same field using the same operator. If so, the operator expressions are combined according to the specified merge strategy. The following section describes the available merge strategies.
Merge Strategies
Mongoid provides the following merge strategies:
Override: The new operator instance replaces any existing conditions on the same field by 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 code demonstrates how the merge strategies produce
criteria by using in
as the example operator:
Band.in(name: ['a']).override.in(name: ['b']) # Interpreted query: # {"name"=>{"$in"=>["b"]}} Band.in(name: ['a', 'b']).intersect.in(name: ['b', 'c']) # Interpreted query: # {"name"=>{"$in"=>["b"]}} Band.in(name: ['a']).union.in(name: ['b']) # Interpreted query: # {"name"=>{"$in"=>["a", "b"]}}
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:
Band.in(name: ['a']).union.ne(name: 'c').in(name: ['b']) # Interpreted query: # {"name"=>{"$in"=>["a"], "$ne"=>"c"}, "$and"=>[{"name"=>{"$in"=>["b"]}}]}
Because ne
does not support merge strategies, the union
strategy
is ignored and reset. Then, when in
is invoked the second time,
there is no active strategy.
Warning
Merge strategies assume that the previous conditions have been added
to the top level of the query. However, this is not always the case,
as conditions might be nested under an $and
clause. Using merge
strategies with complex criteria can generate incorrect queries.
Supported Operator Methods
The following operator methods support merge strategies:
all
in
nin
The set of methods might be expanded in future releases of Mongoid. To ensure future compatibility, invoke a strategy method only when the next method call is an operator that supports merge strategies.
Merge strategies are applied only when conditions are
added through the designated methods. In the following example, the
merge strategy is not applied because the second condition is added as
where
, not by using in
:
Band.in(name: ['a']).union.where(name: {'$in' => 'b'}) # Interpreted query: # {"foo"=>{"$in"=>["a"]}, "$and"=>[{"foo"=>{"$in"=>"b"}}]}
Operator Value Expansion
Operator methods that support merge strategies take Array
as their
value type. Mongoid expands Array
-compatible types, such as a
Range
, when they are used with these operator methods.
The following example demonstrates how you can pass a Range
object
as the query value when using the in
method:
Band.in(year: 1950..1960) # Interpreted query: # {"year"=>{"$in"=>[1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960]}}
Mongoid wraps non-Array
values in arrays,
as the shown in the following example:
Band.in(year: 1950) # Interpreted query: {"year"=>{"$in"=>[1950]}}
Element Match
You can use the elem_match
method to match documents that contain
an array field with at least one element that matches all the specified
query criteria.
The following example creates a sample document that contains an array
field. Then, it uses the elem_match
method to match documents in
which the tour
array field contains an entry in which the city
value is 'London'
:
aerosmith = Band.create!(name: 'Aerosmith', tours: [ {city: 'London', year: 1995}, {city: 'New York', year: 1999}, ]) swans = Band.create!(name: 'Swans', tours: [ {city: 'Milan', year: 2014}, {city: 'Montreal', year: 2015}, ]) # Returns only "Aerosmith" Band.elem_match(tours: {city: 'London'})
Associations
You can use the elem_match
method to match embedded associations.
This example uses the following models that define an embedded
association between Band
and Tour
:
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
The following code creates a Band
object and embedded Tour
objects, then uses the elem_match
method to query on the city
field:
aerosmith = Band.create!(name: 'Aerosmith') Tour.create!(band: aerosmith, city: 'London', year: 1995) Tour.create!(band: aerosmith, city: 'New York', year: 1999) # Returns the "Aerosmith" document Band.elem_match(tours: {city: 'London'})
Note
You cannot use elem_match
on non-embedded associations because
MongoDB does not perform a join operation on the collections.
If you perform this query, the conditions are added to the collection
that is the source of the non-embedded association rather than the
collection of the association.
You can use elem_match
to query recursively embedded associations,
as shown in the following example:
class Tag include Mongoid::Document field name:, type: String recursively_embeds_many end # Creates the root Tag root = Tag.create!(name: 'root') # Adds embedded Tags sub1 = Tag.new(name: 'sub_tag_1', child_tags: [Tag.new(name: 'sub_sub_tag_1')]) root.child_tags << sub1 root.child_tags << Tag.new(name: 'sub_tag_2') root.save! # Searches for Tag in which one child Tag tame is "sub_tag_1" Tag.elem_match(child_tags: {name: 'sub_tag_1'}) # Searches for a child Tag in which one child Tag tame is "sub_sub_tag_1" root.child_tags.elem_match(child_tags: {name: 'sub_sub_tag_1'})
To learn more about associations, see the Associations guide.
Querying by _id Value
Mongoid provides the find
method, which allows you to query
documents by their _id
values.
The following example uses the find
method to match a document
with the specified _id
field value:
Band.find('6725342d4cb3e161059f91d7')
Note
Type Conversion
When you pass an ID value to the find
method, the method
converts it to the data type declared for the _id
field in the
model. By default, the _id
field is defined as a
BSON::ObjectId
type.
The preceding example is equivalent to the following code, which
passes an BSON::ObjectId
instance as the argument to find
:
Band.find(BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10'))
If you use the Ruby driver to query on the _id
field,
find
does not internally perform the type conversion.
The find
method accepts multiple arguments, or an array of arguments.
Mongoid interprets each argument or array element as an
_id
value, and returns documents with all the specified _id
values in an array, as shown in the following example:
# Equivalent ways to match multiple documents Band.find('5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e') Band.find(['5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e'])
The find
method exhibits the following behavior:
If you provide the same
_id
value more than once, Mongoid returns only one document, if one exists.Mongoid does not return documents in an ordered way. Documents might be returned in different order from the order of the provided
_id
values.If any of the
_id
values are not found in the database, the result depends on the value of theraise_not_found_error
configuration option.If you set the
raise_not_found_error
option totrue
,find
raises aMongoid::Errors::DocumentNotFound
error if any of the_id
values are not found.If you set the
raise_not_found_error
option tofalse
and query for a single_id
value,find
returnsnil
if Mongoid does not match a document. If you pass multiple_id
values and some or all are not matched, the return value is an array of any documents that match, or an empty array if no documents match.
find Variations
This section describes methods that are similar to the find
method
described in the preceding section.
You can use the find_by
method to retrieve documents based on the
provided criteria. If no documents are found, it raises an error or
returns nil
depending on how you set the raise_not_found_error
configuration option.
The following code demonstrates how to use the find_by
method:
# Simple equality query Band.find_by(name: "Photek") # Performs an action on each returned result Band.find_by(name: "Tool") do |band| band.fans += 1 end
You can use the find_or_create_by
method to retrieve documents
based on the provided criteria. If no documents are found, it creates
and returns an instance that is saved to MongoDB.
The following code demonstrates how to use the find_or_create_by
method:
# If no matches, creates a Band with just the "name" field Band.find_or_create_by(name: "Photek") # If no matches, creates a Band with just the "name" field because the # query condition is not a literal Band.where(:likes.gt => 10).find_or_create_by(name: "Photek") # Creates a Band in which the name is Aerosmith because there is no # document in which "name" is Photek and Aerosmith at the same time Band.where(name: "Photek").find_or_create_by(name: "Aerosmith")
You can use the find_or_initialize_by
method to retrieve documents
based on the provided criteria. If no documents are found, it returns a
new one, without persisting it to MongoDB. Use the same syntax for
find_or_initialize_by
as you do for the find_or_create_by
method.
Regular Expressions
Mongoid allows you to query documents by using regular expressions in your filter criteria.
The following code creates a sample Band
model:
Band.create!(name: 'Tame Impala', description: "Tame\nImpala is an American band")
You can perform queries by using Ruby regular expressions, as shown in the following code:
# Matches documents in which the "name" field includes the string "impala" Band.where(name: /impala/i) # => Returns sample document
You can also perform queries by using Perl Compatible Regular Expression
(PCRE) syntax and BSON::Regexp::Raw
objects:
# Matches "description" values that start exactly with "Impala" Band.where(description: /\AImpala/) # => nil # Matches "description" values that start exactly with "Impala" Band.where(description: BSON::Regexp::Raw.new('^Impala')) # => nil # Matches "description" values that start exactly with "Impala" with # the multiline option Band.where(description: BSON::Regexp::Raw.new('^Impala', 'm')) # => Returns sample document
Field Type Query Conversions
When you specify a query on a field defined in a model, if the field has a specified data type, Mongoid converts the query value based on how the field is defined.
Consider the following Album
model definition that
contains a Date
-valued field, a Time
-valued field, and an
implicit Object
-valued field. The model also intentionally does not define
a field named last_reviewed
:
class Album include Mongoid::Document field :release_date, type: Date field :last_commented, type: Time field :last_purchased end
You can query on the release_date
and last_commented
fields
by using Date
and Time
values, as shown in the following code:
Album.where(release_date: Date.today) # Interpreted query: # {"release_date"=>2024-11-05 00:00:00 UTC} Album.where(last_commented: Time.now) # Interpreted query: # {"last_commented"=>2024-11-04 17:20:47.329472 UTC}
However, if you query by using only Date
values on fields defined
as other types, the generated queries display the default conversion
behavior, as shown in the following example:
Album.where(last_commented: Date.today) # Interpreted query: # {"last_commented"=>Mon, 04 Nov 2024 00:00:00.000000000 EST -05:00} Album.where(last_purchased: Date.today) # Interpreted query: # {"last_purchased"=>"2024-11-04"} Album.where(last_reviewed: Date.today) # Interpreted query: # {"last_reviewed"=>2024-11-04 00:00:00 UTC}
In the preceding example, the following conversions apply:
When using a
Date
value to query theTime
-valuedlast_commented
field, Mongoid interprets the date to be in local time and applies the configured time zone.When querying on the
last_purchased
field, which has no explicit type, the date is used unmodified in the constructed query.When querying on the undefined
last_reviewed
field, Mongoid interprets theDate
to be in UTC and converts to a time, matching the behavior of querying aDate
-valued field such asrelease_date
.
Additional Query Methods
This section describes more query methods that you can use in Mongoid.
Count Documents
You can use the count
and estimated_count
methods to count
the number of documents in a collection.
You can count the number of documents that match filter criteria by
using the count
method:
# Counts all documents in collection Band.count # Counts documents that match criteria Band.where(country: 'England').count
Tip
length and size Methods
You can also use the length
or size
method to count documents.
These methods cache subsequent calls to the database, which might
produce performance improvements.
You can get an approximate number of documents in the collection from
the collection metadata by using the estimated_count
method:
Band.estimated_count
The estimated_count
method does not accept query conditions,
including conditions set by a scope on
the model. If you are calling this method on a model that has a
default scope, you must first call the unscoped
method to
disable the scope.
Ordinal Methods
The methods described in the following list allow you to select a specific result from the list of returned documents based on its position.
first
: Returns the first matching document. You can get the firstn
documents by passing an integer-valued parameter. This method automatically uses a sort on the_id
field. See lines 1-8 in the following code for examples.last
: Returns the last matching document. You can get the lastn
documents by passing an integer-valued parameter. This method automatically uses a sort on the_id
field. See line 11 in the following code for an example.first_or_create
: Returns the first matching document. If no document matches, creates and returns a newly saved one.first_or_initialize
: Returns the first matching document. If no document matches, returns a new one.second
: Returns the second matching document. Automatically uses a sort on the_id
field.third
: Returns the third matching document. Automatically uses a sort on the_id
field.fourth
: Returns the fourth matching document. Automatically uses a sort on the_id
field.fifth
: Returns the fifth matching document. Automatically uses a sort on the_id
field.second_to_last
: Returns the second-to-last matching document. Automatically uses a sort on the_id
field. See line 14 in the following code for an example.third_to_last
: Returns the third-to-last matching document. Automatically uses a sort on the_id
field.
The following code demonstrates how to use some methods described in the preceding list:
1 # Returns the first document in the collection 2 Band.first 3 4 # Returns the first matching document 5 Band.where(founded: {'$gt' => 1980}).first 6 7 # Returns the first two matching documents 8 Band.first(2) 9 10 # Returns the last matching document 11 Band.where(founded: {'$gt' => 1980}).last 12 13 # Returns the second to last document 14 Band.second_to_last
Tip
Error Generation
Each method described in this section has a variation that is
suffixed with !
that returns an error if Mongoid doesn't match
any documents. For example, to implement error handling in your
application when your query returns no results, use the first!
method instead of first
.
Survey Field Values
To inspect the values of specified fields of documents in a collection, you can use the following methods:
distinct
: Gets a list of distinct values for a single field. See lines 1-7 in the following code for examples.pick
: Gets the values from one document for the provided fields. Returnsnil
for unset fields and for non-existent fields. See line 10 in the following code for an example.pluck
: Gets all values for the provided field. Returnsnil
for unset fields and for non-existent fields. See line 13 in the following code for an example.tally
: Gets a mapping of values to counts for the specified field. See line 16 in the following code for an example.
The preceding methods accept field names referenced by using dot notation, which allows you to reference fields in embedded associations. They also respect field aliases, including those defined in embedded documents.
The following code demonstrates how to use these methods:
1 Band.distinct(:name) 2 # Example output: "Ghost Mountain" "Hello Goodbye" "She Said" 3 4 Band.where(:members.gt => 2).distinct(:name) 5 # Example output: "Arctic Monkeys" "The Smiths" 6 7 Band.distinct('tours.city') 8 # Example output: "London" "Sydney" "Amsterdam" 9 10 Band.all.pick(:name) 11 # Example output: "The Smiths" 12 13 Band.all.pluck(:country) 14 # Example output: "England" "Spain" "England" "Japan" 15 16 Band.all.tally(:country) 17 # Example output: ["England",2] ["Italy",3]
Miscellaneous
The following list describes Mongoid methods that do not fit into another category:
each
: Iterates over all matching documents.
# Print each matching document "name" to console Band.where(:members.gt => 1).each do |band| p band.name end
exists?
: Determines if any matching documents exist, returningtrue
if at least one matching document is found.
# Checks existence of any document Band.exists? # Checks existence based on query Band.where(name: "Le Tigre").exists? Band.exists?(name: "Le Tigre") # Checks existence based on "_id" value Band.exists?('6320d96a3282a48cfce9e72c') # Always returns false Band.exists?(false) Band.exists?(nil)
Additional Information
To learn how to modify the way that Mongoid returns results to you, see Modify Query Results.
To learn more about defining scopes on your models, see Scoping.
To learn about methods that you can chain to your queries to persist data, see Persist Data from Queries.
To learn about the query cache feature, see Query Cache.
To learn about performing asychronous queries, see Asynchronous Queries.