Docs Menu
Docs Home
/ / /
Mongoid
/

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

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.

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.

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.

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

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')

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})

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)

This section describes how to perform queries on fields with different types of values.

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.

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'))

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')

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.

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}]])

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"}

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.

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.

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.

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 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]}}

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'})

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.

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 the raise_not_found_error configuration option.

    If you set the raise_not_found_error option to true, find raises a Mongoid::Errors::DocumentNotFound error if any of the _id values are not found.

    If you set the raise_not_found_error option to false and query for a single _id value, find returns nil 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.

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.

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

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 the Time-valued last_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 the Date to be in UTC and converts to a time, matching the behavior of querying a Date-valued field such as release_date.

This section describes more query methods that you can use in Mongoid.

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.

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 first n 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 last n 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
2Band.first
3
4# Returns the first matching document
5Band.where(founded: {'$gt' => 1980}).first
6
7# Returns the first two matching documents
8Band.first(2)
9
10# Returns the last matching document
11Band.where(founded: {'$gt' => 1980}).last
12
13# Returns the second to last document
14Band.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.

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. Returns nil 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. Returns nil 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:

1Band.distinct(:name)
2# Example output: "Ghost Mountain" "Hello Goodbye" "She Said"
3
4Band.where(:members.gt => 2).distinct(:name)
5# Example output: "Arctic Monkeys" "The Smiths"
6
7Band.distinct('tours.city')
8# Example output: "London" "Sydney" "Amsterdam"
9
10Band.all.pick(:name)
11# Example output: "The Smiths"
12
13Band.all.pluck(:country)
14# Example output: "England" "Spain" "England" "Japan"
15
16Band.all.tally(:country)
17# Example output: ["England",2] ["Italy",3]

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, returning true 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)

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.

Back

Perform Data Operations