Querying

Mongoid supports querying the database for documents usings 2 styles. The first is an ActiveRecord-like finder/dynamic finder syntax, and the second and preferred method is through Mongoid's Criteria API.

Finders
Finders are class methods on a document that take a hash of conditions or a string to find an object in the database. They are Document.all, Document.count, Document.find, Document.first, Document.last, and Document.paginate. In addition to those a finder/creation syntax is also supported where the supplied attributes will create or instantiate and new document if the attributes don't provide a match.

Finding all documents given some conditions:
Person.all(:conditions => { :first_name => "Syd" })
Person.find(:all, :conditions => { :first_name => "Syd" })

Find the first document given some conditions:
Person.first(:conditions => { :first_name => "Syd" })
Person.find(:first, :conditions => { :first_name => "Syd" })

Find the last document given some conditions. If no sorting parameter is provided then the id field will be used to reverse the sort.
Person.last(:conditions => { :first_name => "Syd" })
Person.find(:last, :conditions => { :first_name => "Syd" })

Find using a dynamic finder - if the object does not exist then create/instantiate it:
Person.find_or_create_by(:first_name => "Syd")
Person.find_or_initialize_by(:first_name => "Syd")
Criteria API
The preferred method of querying the database is through the criteria API, which is a DSL which will make those familiar with SQL very comfortable. Criteria queries are lazy loaded from the database, and can be chained infinitely.

There are several different ways of creating a new criteria:
all_people = Mongoid::Criteria.new(Person)
all_people_again = Person.criteria
all_people_names_only = Person.only(:first_name, :last_name)
people_over_18 = Person.where(:age.gt => 18)
In the above example the first two will create an empty criteria for the person, where the third will automatically add a field selection criterion to it and the fourth will create one with a where selector already added. You may use any entry point method defined in Mongoid::Finders, which are class methods on your document class. These are listed below.

Criteria Methods
Various types of criteria can be added by chaining - the list of available methods are:

Criteria#all_in: Matches if all values provided match, useful for doing exact matches on arrays.
Person.all_in(:aliases => [ "Jeffrey", "The Dude" ])

Criteria#any_in: Matches documents that have a field matching any value in the array.
Person.any_in(:status => ["Single", "Divorced", "Separated"])

Criteria#any_of: Matches documents that have any of the provided matches. This is a $or query in MongoDB and can include multiple fields or multiple conditions for the same field.
Person.any_of({ :status => "Single" }, { :preference => "Open" })

Criteria#and: Matches documents for each field/value pair. Aliased from where, it's just nice syntactic sugar.
Person.and(:age.gt => 18, :gender => "Male")

Criteria#count: This must be chained behind another criterion not to interfere with the ActiveRecord style count.
Person.where(:status => "Married").count

Criteria#excludes: Matches documents that don't match the key value pairs
Person.excludes(:status => "Married")

Criteria#id: Matches a document with the supplied id.
Person.criteria.id("4b2fe28ee2dc9b5f7b000029")

Criteria#limit: Limits the results to a certain number.
Person.limit(20)

Criteria#near: Do a geospacial query for a point a certain distance away.
Address.near(:position => [ 37.7, -122.4, 10 ])

Criteria#not_in: Matches when document values are not in the list.
Person.not_in(:status => ["Divorced", "Single"])

Criteria#only: Limits the fields returned from the database. Useful for list optimization.
Person.only(:first_name, :last_name)

Criteria#order_by: Adds sorting criteria. (Note - always index fields you sort on)
# chain asc/ascending and desc/descending to the criteria
Person.desc(:last_name).asc(:first_name)
Person.descending(:last_name).ascending(:first_name)
# pass in a list of symbols
Person.order_by(:last_name.desc, :first_name.asc, :city.desc)
# you can alternatively pass in an array of arrays
Person.order_by([[:last_name, :desc], [:first_name, :asc]])

Criteria#skip: Skips n number of documents, similar to a traditional offset.
Person.skip(100)

Criteria#where: Matches documents for each field/value pair.
Person.where(:age.gt => 18, :gender => "Male")

Regular Expressions
You can pass regular expressions for string matching.
Person.where(:last_name => /^Jord/)

Multiple Expressions on the Same Field
Mongoid will combine them into a single expression.
# Creates a { "age" => { "$gt" => 18, "$lt" => 30 } } selector.
Person.where(:age.gt => 18, :age.lt => 30)

Putting It All Together
Return only the first and last names for a person with a post code of 94133:
Person.only(:first_name, :last_name).where("address.post_code" => "94133")

Return only the first names for people whos last names are "Vicious" and have a US phone number.
Person.only(:first_name).where("phones.country_code" => 1).in(:last_name => ["Vicious"])

Return all fields for people with last names of "Zorg" and middle initals of "J"
Person.where(:last_name => "Zorg").and(:middle_initial => "J")

Criteria where clause examples using MongoDB expressions:
Person.where(:title.all => ["Sir"])
Person.where(:age.exists => true)
Person.where(:age.gt => 18)
Person.where(:age.gte => 18)
Person.where(:title.in => ["Sir", "Madam"])
Person.where(:age.lt => 55)
Person.where(:age.lte => 55)
Person.where(:title.ne => "Mr")
Person.where(:title.nin => ["Esquire"])
Person.where(:aliases.size => 2)
Person.where(:location.near => [ 22.5, -21.33 ])
Person.where(:location.within => { "$center" => [ [ 50, -40 ], 1 ] })

Chaining Criteria
Criteria can be chained using class methods on a document, similar to DataMapper. The following example will return results for men over age 60:

person.rb:
class Person
  include Mongoid::Document

  field :gender
  field :age

  class << self
    def men
      criteria.where(:gender => "Male")
    end
    def old
      criteria.where(:age => { "$gt" => 60 })
    end
  end
end

Person.old.men # Returns a new criteria of the 2 merged.

Arithmetic, Grouping and Aggregation
Mongoid currently supports some basic arithmetic operations, grouping, and aggregation out of the box.

Find the max value or min value in the database for a single field, returns a float:
Person.max(:age)
Person.min(:age)

Find the sum of a field across all documents, returns a float:
Invoice.sum(:total)

Get aggregate counts for supplied fields on all documents, this returns an array of hashes with key being the field value, and value being the count:
Person.only(:first_name).where(:age.gt => 18).aggregate

Get groups of documents for supplied fields, this returns an array of hashes with key being the field value, and value being an array of documents. The entire result set must fit in memory.
Person.only(:first_name).where(:age.gt => 18).group

Scopes
Mongoid supports scopes very similar to ActiveRecord 3.0 scopes. A `scope` can provide a hash, a proc with a hash, a criteria, or a proc with a criteria. You may also provide an additional block to a scope if you want extensions added to it. Scopes can exist on root document classes or embedded has many association classes. Scopes on association classes cannot be called directly off the class, they must be called on the association itself on the parent class instance. These scopes also do not require the association to have been persisted. An Example is at the bottom.

Named scopes can be chained together, or can be chained with any class method that returns a criteria. The following example describes all cases:
class Player
  include Mongoid::Document
  field :active, :type => Boolean
  field :frags, :type => Integer
  field :deaths, :type => Integer
  field :status

  embeds_many :games

  scope :active, where(:active => true) do
    def count
      size
    end
  end
  scope :inactive, :where => { :active => false }
  scope :frags_over, lambda { |count| { :where => { :frags.gt => count } } }
  scope :deaths_under, lambda { |count| where(:deaths.lt => count) }

  class << self
    def alive
      where(:status => "Alive")
    end
  end
end

class Game
  include Mongoid::Document
  field :saved, :type => Boolean, :default => false
  field :level, :type => Integer, :default => 1
  field :studio
  embedded_in :player, :inverse_of => :games

  scope :saved, where(:saved => true)

  class << self
    def blizzard
      where(:studio => "Blizzard")
    end
  end
end

Player.active # Returns active players.
Player.active.count # Returns the count of active players.
Player.active.alive # Returns active players that are alive.
Player.inactive.frags_over(10) # Returns inactive players with over 10 frags.
Player.deaths_under(30) # Returns players with under 30 deaths.

player = Player.find(id)
player.games.saved # Returns the players saved games
player.games.saved.blizzard # Returns the players saved Blizzard games

Fork me on GitHub!