OPTIONS

Mongoid Tutorial (5.0.0)

This tutorial discusses using the object document mapper Mongoid.

Looking for the documentation for Mongoid 3 and 4? It is located here.

Installation

Installing The Gem

Mongoid is bundled as a gem, and is hosted on Rubygems. It can be installed manually or with bundler.

To install the gem manually:

gem install mongoid

To install the gem with bundler, include the following in your Gemfile:

gem 'mongoid', '~> 5.0.0.beta'

Compatibility

Note the following compatibility matrix to determine if Mongoid is supported on your runtime and server.

Ruby Version 2.4.x 2.6.x 3.0.x
MRI 1.8.x No No No
MRI 1.9.x Yes Yes Yes
MRI 2.0.x Yes Yes Yes
MRI 2.1.x Yes Yes Yes
MRI 2.2.x Yes Yes Yes
JRuby 1.7.x Yes Yes Yes

Configuration

Mongoid configuration can be done through a mongoid.yml that specifies your options and clients. The simplest configuration is as follows, which sets the default client to “localhost:27017” and provides a single database in that client named “mongoid”.

development:
  clients:
    default:
      database: mongoid
      hosts:
        - localhost:27017

Rails Applications

You can generate a config file by executing the generator and then editing myapp/config/mongoid.yml to your heart’s desire. Mongoid will then handle everything else from there.

$ rails g mongoid:config

When Mongoid loads its configuration, it chooses the environment to use based on the following order:

  • Rails.env if using Rails.
  • Sinatra::Base.environment if using Sinatra.
  • RACK_ENV environment variable.
  • MONGOID_ENV environment variable.

If you are not using any rack based application and want to override the environment programatically, you can pass a second paramter to load! and Mongoid will use that.

Mongoid.load!("path/to/your/mongoid.yml", :production)

Anatomy of a Mongoid Config

Let’s have a look at a full-blown mongoid.yml and explain the full power of what Mongoid can do. The following configuration has its explanation in the comments above each key.

development:
  # Configure available database clients. (required)
  clients:
    # Defines the default client. (required)
    default:
      # Defines the name of the default database that Mongoid can connect to.
      # (required).
      database: my_db
      # Provides the hosts the default client can connect to. Must be an array
      # of host:port pairs. (required)
      hosts:
        - myhost1.mydomain.com:27017
        - myhost2.mydomain.com:27017
        - myhost3.mydomain.com:27017
      options:
        # Change the default write concern. (default = { w: 1 })
        write:
          w: 1

        # Change the default read preference. Valid options for mode are: :secondary,
        # :secondary_preferred, :primary, :primary_preferred, :nearest
        # (default: primary)
        read:
          mode: :secondary_preferred

        # The name of the user for authentication.
        user: 'user'

        # The password of the user for authentication.
        password: 'password'

        # The user's database roles.
        roles:
          - 'dbOwner'

        # Change the default authentication mechanism. Valid options are: :scram,
        # :mongodb_cr, :mongodb_x509, and :plain. (default on 3.0 is :scram, default
        # on 2.4 and 2.6 is :plain)
        auth_mech: :scram

        # The database or source to authenticate the user against. (default: admin)
        auth_source: admin

        # Force a the driver cluster to behave in a certain manner instead of auto-
        # discovering. Can be one of: :direct, :replica_set, :sharded. Set to :direct
        # when connecting to hidden members of a replica set.
        connect: :direct

        # Changes the default time in seconds the server monitors refresh their status
        # via ismaster commands. (default: 10)
        heartbeat_frequency: 10

        # The time in seconds for selecting servers for a near read preference. (default: 5)
        local_threshold: 5

        # The timeout in seconds for selecting a server for an operation. (default: 30)
        server_selection_timeout: 30

        # The maximum number of connections in the connection pool. (default: 5)
        max_pool_size: 5

        # The minimum number of connections in the connection pool. (default: 1)
        min_pool_size: 1

        # The time to wait, in seconds, in the connection pool for a connection
        # to be checked in before timing out. (default: 5)
        wait_queue_timeout: 5

        # The time to wait to establish a connection before timing out, in seconds.
        # (default: 5)
        connect_timeout: 5

        # The timeout to wait to execute operations on a socket before raising an error.
        # (default: 5)
        socket_timeout: 5

        # The name of the replica set to connect to. Servers provided as seeds that do
        # not belong to this replica set will be ignored.
        replica_set: my_replica_set

        # Whether to connect to the servers via ssl. (default: false)
        ssl: true

        # The certificate file used to identify the connection against MongoDB.
        ssl_cert: /path/to/my.cert

        # The private keyfile used to identify the connection against MongoDB.
        # Note that even if the key is stored in the same file as the certificate,
        # both need to be explicitly specified.
        ssl_key: /path/to/my.key

        # A passphrase for the private key.
        ssl_key_pass_phrase: password

        # Whether or not to do peer certification validation. (default: false)
        ssl_verify: true

        # The file containing a set of concatenated certification authority certifications
        # used to validate certs passed from the other end of the connection.
        ssl_ca_cert: /path/to/ca.cert

  # Configure Mongoid specific options. (optional)
  options:
    # Includes the root model name in json serialization. (default: false)
    include_root_in_json: false

    # Include the _type field in serialization. (default: false)
    include_type_for_serialization: false

    # Preload all models in development, needed when models use
    # inheritance. (default: false)
    preload_models: false

    # Raise an error when performing a #find and the document is not found.
    # (default: true)
    raise_not_found_error: true

    # Raise an error when defining a scope with the same name as an
    # existing method. (default: false)
    scope_overwrite_exception: false

    # Use Active Support's time zone in conversions. (default: true)
    use_activesupport_time_zone: true

    # Ensure all times are UTC in the app side. (default: false)
    use_utc: false

Logging

Changing logging options is done simply by telling Mongoid or the driver’s logger to have a different level. Logging level is DEBUG by default.

Mongoid.logger.level = Logger::DEBUG
Mongo.logger.level = Logger::DEBUG

Documents

Documents are the core objects in Mongoid and any object that is to be persisted to the database must include Mongoid::Document. The representation of a Document in MongoDB is a BSON object that is very similar to a Ruby hash or JSON object. Documents can be stored in their own collections in the database, or can be embedded in other Documents n levels deep.

Storage

Mongoid by default stores documents in a collection that is the pluralized form of the class name. For the following Person class, the collection the document would get stored in would be named people.

class Person
  include Mongoid::Document
end

Model class names cannot end with “s”, because it will be considered as the pluralized form of the word. For example “Status” would be considered as the plural form of “Statu”, which will cause a few known problems.

This is a limitation of the ActiveSupport::Inflector#classify which Mongoid uses to convert from filenames and collection names to class names. You can overcome this by specifying a custom inflection rule for your model class. For example, the following code will take care of the model named Status.

ActiveSupport::Inflector.inflections do |inflect|
  inflect.singular("status", "status")
end

The collection for the model’s documents can be changed at the class level if you would like them persisted elsewhere. You can also change the database and client the model gets persisted in from the defaults.

class Person
  include Mongoid::Document
  store_in collection: "citizens", database: "other", client: "secondary"
end

The store_in macro can also take lambdas - a common case for this is multi-tenant applications.

class Band
  include Mongoid::Document
  store_in database: ->{ Thread.current[:database] }
end

When a document is stored in the database the ruby object will get serialized into BSON and have a structure like so:

{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "title" : "Sir",
  "name" : {
    "_id" : ObjectId("4d3ed089fb60ab534684b7ff"),
    "first_name" : "Durran"
  },
  "addresses" : [
    {
      "_id" : ObjectId("4d3ed089fb60ab534684b7e0"),
      "city" : "Berlin",
      "country" : "Deutschland"
    }
  ]
}

Fields

Even though MongoDB is a schemaless database, most uses will be with web applications where form parameters always come to the server as strings. Mongoid provides an easy mechanism for transforming these strings into their appropriate types through the definition of fields in a Mongoid::Document.

Consider a simple class for modeling a person in an application. A person may have a first name, last name, and middle name. We can define these attributes on a person by using the fields macro.

class Person
  include Mongoid::Document
  field :first_name, type: String
  field :middle_name, type: String
  field :last_name, type: String
end

Below is a list of valid types for fields.

  • Array
  • BigDecimal
  • Boolean
  • Date
  • DateTime
  • Float
  • Hash
  • Integer
  • BSON::ObjectId
  • BSON::Binary
  • Range
  • Regexp
  • String
  • Symbol
  • Time
  • TimeWithZone

If you decide not to specify the type of field with the definition, Mongoid will treat it as an object and not try to typecast it when sending the values to the database. This can be advantageous in 2 places, since the lack of attempted conversion will yield a slight performance gain. However some fields are not supported if not defined as fields. A rule of thumb for what fields you can use are:

  • You’re not using a web front end and values are already properly cast.
  • All of your fields are strings.
class Person
  include Mongoid::Document
  field :first_name
  field :middle_name
  field :last_name
end

Types that are not supported as dynamic attributes since they cannot be cast are:

  • BigDecimal
  • Date
  • DateTime
  • Range

Accessing Field Values

When a field is defined, Mongoid provides several different ways of accessing the field.

# Get the value of the first_name field
person.first_name
person[:first_name]
person.read_attribute(:first_name)


# Set the value for the first_name field
person.first_name = "Jean"
person[:first_name] = "Jean"
person.write_attribute(:first_name, "Jean")

If you have defined custom getter/setter, those will be used when using the dot notation.

class Person
  include Mongoid::Document

  field :first_name

  # custom getter
  def first_name
    "My name is Johnny"
  end

  # custom setter
  def first_name=(value)
    p 'Setting.. '
    self.first_name = value
    p '.. done!'
  end
end

# Like this:
person.first_name = 'John'
# Setting..
# .. done!

person.first_name                  # My name is Johnny
person[:first_name]                # John
person.read_attribute(:first_name) # John

In cases where you want to set multiple field values at once, there are a few different ways of handling this as well.

# Get the field values as a hash.
person.attributes

# Set the field values in the document.
Person.new(first_name: "Jean-Baptiste", middle_name: "Emmanuel")
person.attributes = { first_name: "Jean-Baptiste", middle_name: "Emmanuel" }
person.write_attributes(
  first_name: "Jean-Baptiste",
  middle_name: "Emmanuel"
)

Hash Fields

When using a field of type Hash, be wary of adhering to the legal key names for mongoDB, or else the values will not store properly.

class Person
  include Mongoid::Document
  field :first_name
  field :url, type: Hash

  # will update the fields properly and save the values
  def set_vals
    self.first_name = 'Daniel'
    self.url = {'home_page' => 'http://www.homepage.com'}
    save
  end

  # all data will fail to save due to the illegal hashkey
  def set_vals_fail
    self.first_name = 'Daniel'
    self.url = {'home.page' => 'http://www.homepage.com'}
    save
  end
end

Defaults

You can tell a field in Mongoid to always have a default value if nothing has been provided. Defaults are either static values or lambdas.

class Person
  include Mongoid::Document
  field :blood_alcohol_level, type: Float, default: 0.40
  field :last_drink, type: Time, default: ->{ 10.minutes.ago }
end

Be wary that default values that are not defined as lambdas or procs are evaluated at class load time, so the following 2 definitions are not equivalent. (One would probably prefer the second, which is at document creation time.)

field :dob, type: Time, default: Time.now
field :dob, type: Time, default: ->{ Time.now }

If you want to set a default with a dependency on the document’s state, self inside a lambda or proc evaluates to the document instance.

field :intoxicated_at, type: Time, default: ->{ new_record? ? 2.hours.ago : Time.now }

When defining a default value as a proc, Mongoid will apply the default after all other attributes are set. If you want this to happen before the other attributes, set pre_processed: true.

Aliasing Fields

One of the drawbacks of having a schemaless database is that MongoDB must store all field information along with every document, meaning that it takes up a lot of storage space in RAM and on disk. A common pattern to limit this is to alias fields to a small number of characters, while keeping the domain in the application expressive. Mongoid allows you to do this and reference the fields in the domain via their long names in getters, setters, and criteria while performing the conversion for you.

class Band
  include Mongoid::Document
  field :n, as: :name, type: String
end

band = Band.new(name: "Placebo")
band.attributes #=> { "n" => "Placebo" }

criteria = Band.where(name: "Placebo")
criteria.selector #=> { "n" => "Placebo" }

Custom Fields

You can define custom types in Mongoid and determine how they are serialized and deserialized. You simply need to provide 3 methods on it for Mongoid to call to convert your object to and from MongoDB friendly values.

class Profile
  include Mongoid::Document
  field :location, type: Point
end

class Point

  attr_reader :x, :y

  def initialize(x, y)
    @x, @y = x, y
  end

  # Converts an object of this instance into a database friendly value.
  def mongoize
    [ x, y ]
  end

  class << self

    # Get the object as it was stored in the database, and instantiate
    # this custom class from it.
    def demongoize(object)
      Point.new(object[0], object[1])
    end

    # Takes any possible object and converts it to how it would be
    # stored in the database.
    def mongoize(object)
      case object
      when Point then object.mongoize
      when Hash then Point.new(object[:x], object[:y]).mongoize
      else object
      end
    end

    # Converts the object that was supplied to a criteria and converts it
    # into a database friendly form.
    def evolve(object)
      case object
      when Point then object.mongoize
      else object
      end
    end
  end
end

The instance method mongoize takes an instance of your object, and converts it into how it will be stored in the database. In our example above, we want to store our point object as an array in the form [ x, y ].

The class method demongoize takes an object as how it was stored in the database, and is responsible for instantiating an object of your custom type. In this case, we take an array and instantiate a Point from it.

The class method mongoize takes an object that you would use to set on your model from your application code, and create the object as it would be stored in the database. This is for cases where you are not passing your model instances of your custom type in the setter:

point = Point.new(12, 24)
venue = Venue.new(location: point) #=> This uses the mongoize instance method.
venue = Venue.new(location: [ 12, 24 ]) #=> This uses the mongoize class method.

The class method evolve takes an object, and determines how it is to be transformed for use in criteria. For example we may want to write a query like so:

point = Point.new(12, 24)
Venue.where(location: point)

Not that when accessing custom fields from the document, you will get a new instance of that object with each call to the getter. This is because Mongoid is generating a new object from the object from the raw attributes on each access.

We need the point object in the criteria to be transformed to a Mongo friendly value when it is not as well, evolve is the method that takes care of this. We check if the passed in object is a Point first, in case we also want to be able to pass in ordinary arrays instead.

Reserved Names

If you define a field on your document that conflicts with a reserved method name in Mongoid, the configuration will raise an error. For a list of these you may look at Mongoid.destructive_fields.

Custom Ids

For cases when you do not want to have BSON::ObjectId ids, you can override Mongoid’s _id field and set them to whatever you like.

class Band
  include Mongoid::Document
  field :name, type: String
  field :_id, type: String, default: ->{ name }
end

Dynamic Fields

By default Mongoid doesn’t supports dynamic fields. You can tell mongoid that you want to add dynamic fields by including Mongoid::Attributes::Dynamic in model. Mongoid::Attributes::Dynamic will allow attributes to get set and persisted on the document even if a field was not defined for them. There is a slight ‘gotcha’ however when dealing with dynamic attributes in that Mongoid is not completely lenient about the use of method_missing and breaking the public interface of the Document class.

class Person
  include Mongoid::Document
  include Mongoid::Attributes::Dynamic
end

When dealing with dynamic attributes the following rules apply:

If the attribute exists in the document, Mongoid will provide you with your standard getter and setter methods. For example, consider a person who has an attribute of “gender” set on the document:

# Set the person's gender to male.
person[:gender] = "Male"
person.gender = "Male"

# Get the person's gender.
person.gender

If the attribute does not already exist on the document, Mongoid will not provide you with the getters and setters and will enforce normal method_missing behavior. In this case you must use the other provided accessor methods: ([] and []=) or (read_attribute and write_attribute).

# Raise a NoMethodError if value isn't set.
person.gender
person.gender = "Male"

# Retrieve a dynamic field safely.
person[:gender]
person.read_attribute(:gender)

# Write a dynamic field safely.
person[:gender] = "Male"
person.write_attribute(:gender, "Male")

Localized Fields

Mongoid now supports localized fields without the need of any external gem.

class Product
  include Mongoid::Document
  field :description, localize: true
end

By telling the field to localize, Mongoid will under the covers store the field as a hash of locale/value pairs, but normal access to it will behave like a string.

I18n.default_locale = :en
product = Product.new
product.description = "Marvelous!"
I18n.locale = :de
product.description = "Fantastisch!"

product.attributes
#=> { "description" => { "en" => "Marvelous!", "de" => "Fantastisch!" }

You can get and set all the translations at once by using the corresponding _translations method.

product.description_translations
#=> { "en" => "Marvelous!", "de" => "Fantastisch!" }
product.description_translations =
  { "en" => "Marvelous!", "de" => "Wunderbar!" }

Fallbacks

When using fallbacks, Mongoid will automatically use them when a translation is not available.

For Rails applications, set the fallbacks configuration setting to true in your environment.

config.i18n.fallbacks = true

For non-rails applications, you must include the fallbacks module straight to the I18n gem.

require "i18n/backend/fallbacks"
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)

Then when the fallbacks are defined, if a translation is not present Mongoid will fallback in order of the defined locales.

I18n.default_locale = :en
I18n.fallbacks = true
::I18n.fallbacks[:de] = [ :de, :en, :es ]
product = Product.new
product.description = "Marvelous!"
I18n.locale = :de
product.description #=> "Marvelous!"

Querying

When querying for localized fields using Mongoid’s criteria API, Mongoid will automatically alter the criteria to match the current locale.

# Match all prodcucts with Marvelous as the description. Locale is en.
Product.where(description: "Marvelous!")
# The resulting MongoDB query filter: { "description.en" : "Marvelous!" }

Indexing

If you plan to be querying extensively on localized fields, you should index each of the locales that you plan on searching on.

class Product
  include Mongoid::Document
  field :description, localize: true

  index "description.de" => 1
  index "description.en" => 1
end

Dirty Tracking

Mongoid supports tracking of changed or “dirty” fields with an API that mirrors that of Active Model. If a defined field has been modified in a model the model will be marked as dirty and some additional behavior comes into play.

Viewing Changes

There are various ways to view what has been altered on a model. Changes are recorded from the time a document is instantiated, either as a new document or via loading from the database up to the time it is saved. Any persistence operation clears the changes.

class Person
  include Mongoid::Document
  field :name, type: String
end

person = Person.first
person.name = "Alan Garner"

# Check to see if the document has changed.
person.changed? #=> true

# Get an array of the names of the changed fields.
person.changed #=> [ :name ]

# Get a hash of the old and changed values for each field.
person.changes #=> { "name" => [ "Alan Parsons", "Alan Garner" ] }

# Check if a specific field has changed.
person.name_changed? #=> true

# Get the changes for a specific field.
person.name_change #=> [ "Alan Parsons", "Alan Garner" ]

# Get the previous value for a field.
person.name_was #=> "Alan Parsons"

Resetting Changes

You can reset changes of a field to its previous value by calling the reset method.

person = Person.first
person.name = "Alan Garner"

# Reset the changed name back to the original
person.reset_name!
person.name #=> "Alan Parsons"

Notes On Persistence

Mongoid uses dirty tracking as the core of its persistence operations. It looks at the changes on a document and atomically updates only what has changed unlike other frameworks that write the entire document on each save. If no changes have been made, Mongoid will not hit the database on a call to Model#save.

Viewing Previous Changes

After a document has been persisted, you can see what the changes were previously by calling Model#previous_changes.

person = Person.first
person.name = "Alan Garner"
person.save #=> Clears out current changes.

# View the previous changes.
person.previous_changes #=> { "name" => [ "Alan Parsons", "Alan Garner" ] }

Readonly Attributes

You can tell Mongoid that certain attributes are readonly. This will allow documents to be created with theses attributes, but changes to them will be filtered out.

class Band
  include Mongoid::Document
  field :name, type: String
  field :origin, type: String

  attr_readonly :name, :origin
end

band = Band.create(name: "Placebo")
band.update_attributes(name: "Tool") # Filters out the name change.

If you explicitly try to update or remove the attribute by itself, then a ReadonlyAttribute error will be raised.

band.update_attribute(:name, "Tool") # Raises the error.
band.remove_attribute(:name) # Raises the error.

Inheritance

Mongoid supports inheritance in both root and embedded documents. In scenarios where documents are inherited from their fields, relations, validations and scopes get copied down into their child documents, but not vise-versa.

class Canvas
  include Mongoid::Document
  field :name, type: String
  embeds_many :shapes
end

class Browser < Canvas
  field :version, type: Integer
  scope :recent, where(:version.gt => 3)
end

class Firefox < Browser
end

class Shape
  include Mongoid::Document
  field :x, type: Integer
  field :y, type: Integer
  embedded_in :canvas
end

class Circle < Shape
  field :radius, type: Float
end

class Rectangle < Shape
  field :width, type: Float
  field :height, type: Float
end

In the above example, Canvas, Browser and Firefox will all save in the canvases collection. An additional attribute _type is stored in order to make sure when loaded from the database the correct document is returned. This also holds true for the embedded documents Circle, Rectangle, and Shape.

Querying Subclasses

Querying for subclasses is handled in the normal manner, and although the documents are all in the same collection, queries will only return documents of the correct type, similar to Single Table Inheritance in ActiveRecord.

# Returns Canvas documents and subclasses
Canvas.where(name: "Paper")
# Returns only Firefox documents
Firefox.where(name: "Window 1")

Associations

You can add any type of subclass to a has one or has many association, through either normal setting or through the build and create methods on the association:

firefox = Firefox.new
# Builds a Shape object
firefox.shapes.build({ x: 0, y: 0 })
# Builds a Circle object
firefox.shapes.build({ x: 0, y: 0 }, Circle)
# Creates a Rectangle object
firefox.shapes.create({ x: 0, y: 0 }, Rectangle)

rect = Rectangle.new(width: 100, height: 200)
firefox.shapes

Timestamping

Mongoid supplies a timestamping module in Mongoid::Timestamps which can be included to get basic behavior for created_at and updated_at fields.

class Person
  include Mongoid::Document
  include Mongoid::Timestamps
end

You may also choose to only have specific timestamps for creation or modification.

class Person
  include Mongoid::Document
  include Mongoid::Timestamps::Created
end

class Post
  include Mongoid::Document
  include Mongoid::Timestamps::Updated
end

If you want to turn off timestamping for specific calls, use the timeless method:

person.timeless.save
Person.timeless.create!

If you’d like shorter timestamp fields with aliases on them to save space, you can include the short versions of the modules.

class Band
  include Mongoid::Document
  include Mongoid::Timestamps::Short # For c_at and u_at.
end

class Band
  include Mongoid::Document
  include Mongoid::Timestamps::Created::Short # For c_at only.
end

class Band
  include Mongoid::Document
  include Mongoid::Timestamps::Updated::Short # For u_at only.
end

Persistence

Mongoid supports all expected CRUD operations for those familiar with other Ruby mappers like Active Record or Data Mapper. What distinguishes Mongoid from other mappers for MongoDB is that the general persistence operations perform atomic updates on only the fields that have changed instead of writing the entire document to the database each time.

The persistence sections will provide examples on what database operation is performed when executing the documented command.

Standard

Mongoid’s standard persistence methods come in the form of common methods you would find in other mapping frameworks. The following table shows all standard operations with examples.

Operation Example

Model.create

Insert a document or multiple documents into the database.

Person.create(
  first_name: "Heinrich",
  last_name: "Heine"
)

Person.create([
  { first_name: "Heinrich", last_name: "Heine" },
  { first_name: "Willy", last_name: "Brandt" }
])

Person.create(first_name: "Heinrich") do |doc|
  doc.last_name = "Heine"
end

Model.create!

Insert a document or multiple documents into the database, raising an error if a validation error occurs.

Person.create!(
  first_name: "Heinrich",
  last_name: "Heine"
)

Person.create!([
  { first_name: "Heinrich", last_name: "Heine" },
  { first_name: "Willy", last_name: "Brandt" }
])

Person.create!(first_name: "Heinrich") do |doc|
  doc.last_name = "Heine"
end

Model#save

Saves the changed attributes to the database atomically, or insert the document if flagged as a new record via Model#new_record? . Can bypass validations if wanted.

person = Person.new(
  first_name: "Heinrich",
  last_name: "Heine"
)
person.save
person.save(validate: false)

person.first_name = "Christian Johan"
person.save

Model#save!

Saves the changed attributes to the database atomically, or insert the document if new. Will raise an error of validations fail.

person = Person.new(
  first_name: "Heinrich",
  last_name: "Heine"
)
person.save!

person.first_name = "Christian Johan"
person.save!

Model#update_attributes

Update the provided attributes (and any other dirty fields).

person.update_attributes!(
  first_name: "Jean",
  last_name: "Zorg"
)

Model#update_attribute

Update a single attribute, bypassing validations.

person.update_attribute(:first_name, "Jean")

Model#upsert

Performs a MongoDB upsert on the document. If the document exists in the database, it will get overwritten with the current attributes of the document in memory. If the document does not exist in the database, it will be inserted. Note that this only runs the {before|after|around}_upsert callbacks.

person = Person.new(
  first_name: "Heinrich",
  last_name: "Heine"
)
person.upsert

Model#touch

Update the document’s updated_at timestamp, optionally with one extra provided time field. This will cascade the touch to all belongs_to relations of the document with the option set. This operation skips validations and callbacks.

person.touch
person.touch(:audited_at)

Model#delete

Deletes the document from the database without running callbacks.

person.delete

Model#destroy

Deletes the document from the database while running destroy callbacks.

person.destroy

Model.delete_all

Deletes all documents from the database without running any callbacks.

Person.delete_all

Model.destroy_all

Deletes all documents from the database while running callbacks. This is a potentially expensive operation since all documents will be loaded into memory.

Person.destroy_all

Atomic

Although Mongoid performs atomic operations under the covers by default, there may be cases where you want to do this explicitly without persisting other fields. Mongoid provides support for all of these operations as well. When executing atomic operations via these methods, no callbacks will ever get run, nor will any validations.

Operation Example

Model#add_to_set

Performs an atomic $addToSet on the field.

person.add_to_set(aliases: "Bond")

Model#bit

Performs an atomic $bit on the field.

person.bit(age: { and: 10, or: 12 })

Model#inc

Performs an atomic $inc on the field.

person.inc(age: 1)

Model#pop

Performs an atomic $pop on the field.

person.pop(aliases: 1)

Model#pull

Performs an atomic $pull on the field.

person.pull(aliases: "Bond")

Model#pull_all

Performs an atomic $pullAll on the field.

person.pull_all(aliases: [ "Bond", "James" ])

Model#push

Performs an atomic $push on the field.

person.push(aliases: ["007","008"])

Model#rename

Performs an atomic $rename on the field.

person.rename(bday: :dob)

Model#set

Performs an atomic $set on the field.

person.set(name: "Tyler Durden")

Model#unset

Performs an atomic $unset on the field.

person.unset(:name)

Custom

There may be cases where you want to persist documents to different sources from their defaults, or with different options from the default. Mongoid provides run-time support for this as well as support on a per-model basis.

Model Level Persistence Options

On a per-model basis, you can tell it to store in a custom collection name, a different database, or a different client. The following example would store the Band class by default into a collection named “artists” in the database named “music”, with the client “secondary”.

Note that the value supplied to the client option must be configured under clients in your mongoid.yml.

class Band
  include Mongoid::Document
  store_in collection: "artists", database: "music", client: "secondary"
end

If no store_in macro would have been provided, Mongoid would store the model in a collection named “bands” in the default database in the default client.

Runtime Persistence Options

You can change at runtime where to store, query, update, or remove documents by prefixing any operation with #with.

Band.with(database: "music-non-stop").create
Band.with(collection: "artists").delete_all
band.with(client: :tertiary).save!

Persisting using with is a one time switch in the persistence context - it creates a new client under the covers which will get garbage collected after use. Mongoid will not remember anything specific on the document level regarding how it was saved when using this method. A potential gotcha with this is persisting a document via with and then immediately updating it after.

band = Band.new(name: "Scuba")
band.with(collection: "artists").save!
band.update_attribute(likes: 1000) # This will not save - tries the collection "bands"
band.with(collection: "artists").update_attribute(likes: 1000) # This will update the document.

Also note that because a new client is created, it’s important not to change any options that would cause a cluster configuration change and instantiate a client that would never get garbage collected due to the new cluster’s monitor threads. To avoid this, only use with when changing the following options:

  • database
  • collection
  • client
  • read
  • write

If you want to switch the persistence context for all operations at runtime, but don’t want to be using with all over your code, Mongoid provides the ability to do this as the client and database level globally. The methods for this are Mongoid.override_client and Mongoid.override_database. A useful case for this are internationalized applications that store information for different locales in different databases or clients, but the schema in each remains the same.

class BandsController < ApplicationController
  before_filter :switch_database
  after_filter :reset_database

  private

  def switch_database
    I18n.locale = params[:locale] || I18n.default_locale
    Mongoid.override_database("my_db_name_#{I18n.locale}")
  end

  def reset_database
    Mongoid.override_database(nil)
  end
end

In the above example, all persistence operations would be stored in the alternative database for all remaining operations on this thread. This is why the after request set the override back to nil - it ensures subsequent requests with no local params use the default option.

Client and Collection Access

If you want to drop down to the driver level to perform operations, you can grab the Mongo client or collection from the model or document instance.

Band.mongo_client
band.mongo_client
Band.collection
band.collection

From here you also have the same runtime persistence options using the client’s #with.

client = Band.mongo_client.with(write: { w: 0 }, database: "musik")
client[:artists].find(...)

You can also override the :read or :write options on the collection using the collections #with.

collection_w_0 = Band.collection.with(write: { w: 0 })
collection_w_0[:artists].find(...)

Capped Collections

Mongoid does not provide a mechanism for creating capped collections on the fly - you will need to create these yourself one time up front either with the driver or via the Mongo console.

To create a capped collection with the driver:

client["name", :capped => true, :size => 1024].create

To create a capped collection from the Mongo console:

db.createCollection("name", { capped: true, size: 1024 });

Querying

One of MongoDB’s greatest features is its ability to execute dynamic queries, which Origin abstracts in a familiar Arel-style DSL that Mongoid includes.

Queries

All queries in Mongoid are Mongoid::Criteria, which is a chainable and lazily evaluated wrapper to a MongoDB dynamic query. Criteria only touch the database when they need to, for example on iteration of the results, and when executed wrap a cursor in order to keep memory management and performance predictable.

Queryable DSL

Mongoid’s main query DSL is provided by Origin. Any method that is available on an Origin::Queryable exists on a Mongoid::Criteria as well as off the model’s class.

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

With each chained method on a criteria, a newly cloned criteria is returned with the new query added. This is so that with scoping or exposures, for example, the original queries are unmodified and remain reusable.

Additional Query Methods

In addition to behavior that Origin provides, Mongoid also has some helpful methods on criteria.

Operation Example

Criteria#count

Get a count of persisted documents. Note this will always hit the database for the count.

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

Criteria#distinct

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

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

Criteria#each

Iterate over all matching documents in the criteria.

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.

Band.exists?
Band.where(name: "Photek").exists?

Criteria#find

Find a document or multiple documents by their ids. Will raise an error by default if any of the ids do not match.

Band.find("4baa56f1230048567300485c")
Band.find(
  "4baa56f1230048567300485c",
  "4baa56f1230048567300485d"
)
Band.where(name: "Photek").find(
  "4baa56f1230048567300485c"
)

Criteria#find_by

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

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.

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

Criteria#find_or_initialize_by

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

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. This guarantees no order.

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.

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!.

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.

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

Criteria#for_js

Find documents for a provided javascript expression. This will wrap the javascript in a `BSON::Code` object which is the safe way to avoid javascript injection attacks.*

Band.for_js("this.name = param", param: "Tool")

Criteria#length|size

Same as count but caches subsequent calls to the database

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

Criteria#pluck

Get all the non nil values for the provided field.

Band.all.pluck(:name)

Eager Loading

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

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

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.

Operation Example

Criteria#create

Create a newly persisted document.

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

Criteria#create!

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

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

Criteria#build|new

Create a new (unsaved) document.

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

Criteria#update

Update attributes of the first matching document.

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

Criteria#update_all

Update attributes of all matching documents.

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

Criteria#add_to_set

Perform an $addToSet on all matching documents.

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

Criteria#bit

Perform a $bit on all matching documents.

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

Criteria#inc

Perform an $inc on all matching documents.

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

Criteria#pop

Perform a $pop on all matching documents.

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

Criteria#pull

Perform a $pull on all matching documents.

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

Criteria#pull_all

Perform a $pullAll on all matching documents.

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

Criteria#push

Perform a $push on all matching documents.

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

Criteria#push_all

Perform a $pushAll on all matching documents.

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

Criteria#rename

Perform a $rename on all matching documents.

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

Criteria#set

Perform a $set on all matching documents.

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

Criteria#unset

Perform a $unset on all matching documents.

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

Criteria#delete

Deletes all matching documents in the database.

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.

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

Scoping

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

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.

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.

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 |scope| do
          scope.selector.store("origin" => "Deutschland")
        end
      end
    end
  }
end

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

Default Scopes

Default scopes can be useful when you find yourself applying the same criteria to most queries, and want something to be there by default. Default scopes take procs that return criteria objects.

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

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

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

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

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.

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

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

class Label
  include Mongoid::Document
  embeds_many :bands
end

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

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

Class Methods

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

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

Map/Reduce

Mongoid provides a DSL around MongoDB’s map/reduce framework, for performing custom map/reduce jobs or simple aggregations.

Execution

You can tell Mongoid off the class or a criteria to perform a map/reduce by calling map_reduce and providing map and reduce javascript functions.

map = %Q{
  function() {
    emit(this.name, { likes: this.likes });
  }
}

reduce = %Q{
  function(key, values) {
    var result = { likes: 0 };
    values.forEach(function(value) {
      result.likes += value.likes;
    });
    return result;
  }
}

Band.where(:likes.gt => 100).map_reduce(map, reduce).out(inline: true)

Just like criteria, map/reduce calls are lazily evaluated. So nothing will hit the database until you iterate over the results, or make a call on the wrapper that would need to force a database hit.

Band.map_reduce(map, reduce).out(replace: "mr-results").each do |document|
  p document # { "_id" => "Tool", "value" => { "likes" => 200 }}
end

The only required thing you provide along with a map/reduce is where to output the results. If you do not provide this an error will be raised. Valid options to #out are:

  • inline: 1: Don’t store the output in a collection.
  • replace: "name": Store in a collection with the provided name, and overwrite any documents that exist in it.
  • merge: "name": Store in a collection with the provided name, and merge the results with the existing documents.
  • reduce: "name": Store in a collection with the provided name, and reduce all existing results in that collection.

Relations

Common Behaviour

Attributes

All relations contain a target, which is the proxied document or documents, a base which is the document the relation hangs off, and metadata which provides information about the relation.

class Person
  include Mongoid::Document
  embeds_many :addresses
end

person.addresses = [ address ]
person.addresses.target # returns [ address ]
person.addresses.base # returns person
person.addresses.metadata # returns the metadata

Extensions

All relations can have extensions, which provides a way to add application specific functionality to the relation. They are defined by providing a block to the relation definition.

class Person
  include Mongoid::Document
  embeds_many :addresses do
    def find_by_country(country)
      where(country: country).first
    end
    def chinese
      @target.select { |address| address.country == "China" }
    end
  end
end

person.addresses.find_by_country("Mongolia") # returns address
person.addresses.chinese # returns [ address ]

Custom Relation Names

You can name your relations whatever you like, but if the class cannot be inferred by Mongoid from the name, and neither can the opposite side you’ll want to provide the macro with some additional options to tell Mongoid how to hook them up.

class Lush
  include Mongoid::Document
  embeds_one :whiskey, class_name: "Drink", inverse_of: :alcoholic
end

class Drink
  include Mongoid::Document
  embedded_in :alcoholic, class_name: "Lush", inverse_of: :whiskey
end

Validations

It is important to note that by default, Mongoid will validate the children of any relation that are loaded into memory via a validates_associated. The relations that this applies to are:

  • embeds_many
  • embeds_one
  • has_many
  • has_one
  • has_and_belongs_to_many

If you do not want this behavior, you may turn it off when defining the relation.

class Person
  include Mongoid::Document

  embeds_many :addresses, validate: false
  has_many :posts, validate: false
end

Polymorphism

When a child embedded document can belong to more than one type of parent document, you can tell Mongoid to support this by adding the as option to the definition on the parents, and the polymorphic option on the child. On the child object, an additional field will be stored that indicates the type of the parent. Polymorphic behavior is allowed on all relations with the exception of has_and_belongs_to_many.

class Band
  include Mongoid::Document
  embeds_many :photos, as: :photographic
  has_one :address, as: :addressable
end

class Photo
  include Mongoid::Document
  embedded_in :photographic, polymorphic: true
end

class Address
  include Mongoid::Document
  belongs_to :addressable, polymorphic: true
end

Cascading Callbacks

If you want the embedded document callbacks to fire when calling a persistence operation on its parent, you will need to provide the cascade callbacks option to the relation.

class Band
  include Mongoid::Document
  embeds_many :albums, cascade_callbacks: true
  embeds_one :label, cascade_callbacks: true
end

band.save # Fires all save callbacks on the band, albums, and label.

Dependent Behaviour

You can provide dependent options to referenced associations to instruct Mongoid how to handle situations where one side of the relation is deleted, or is attempted to be deleted. The options are as follows:

  • :delete: Delete the child document without running any of the model callbacks.
  • :destroy: Destroy the child document and run all of the model callbacks.
  • :nullify: Orphan the child document.
  • :restrict: Raise an error if the child is not empty.

The default behavior of each association when no dependent option is provided is to nullify.

class Band
  include Mongoid::Document
  has_many :albums, dependent: :delete
  belongs_to :label, dependent: :nullify
end

class Album
  include Mongoid::Document
  belongs_to :band
end

class Label
  include Mongoid::Document
  has_many :bands, dependent: :restrict
end

label = Label.first
label.bands.push(Band.first)
label.delete # Raises an error since bands is not empty.

Band.first.delete # Will delete all associated albums.

Autosaving

One core difference between Mongoid and Active Record from a behavior standpoint is that Mongoid does not automatically save child relations for relational associations. This is for performance reasons.

To enable an autosave on a relational association (embedded associations do not need this since they are actually part of the parent in the database) add the autosave option to the relation.

Note that autosave functionality will automatically be added to a relation when using accepts_nested_attributes_for or validating presence of the relation.

class Band
  include Mongoid::Document
  has_many :albums, autosave: true
end

band = Band.first
band.albums.build(name: "101")
band.save #=> Will save the album as well.

Recursive Embedding

A document can recursively embed itself using recursively_embeds_one or recursively_embeds_many, which provides accessors for the parent and children via parent_ and child_ methods.

class Tag
  include Mongoid::Document
  recursively_embeds_many
end

root = Tag.new(name: "programming")
child_one = root.child_tags.build
child_two = root.child_tags.build

root.child_tags # [ child_one, child_two ]
child_one.parent_tag # [ root ]
child_two.parent_tag # [ root ]

class Node
  include Mongoid::Document
  recursively_embeds_one
end

root = Node.new
child = Node.new
root.child_node = child

root.child # child
child.parent_node # root

Existence Predicates

All relations have existence predicates on them in the form of name? and has_name? to check if the relation is blank.

class Band
  include Mongoid::Document
  embeds_one :label
  embeds_many :albums
end

band.label?
band.has_label?
band.albums?
band.has_albums?

Autobuilding

One to one relations (embeds_one, has_one) have an autobuild option which tells Mongoid to instantiate a new document when the relation is accessed and it is nil.

class Band
  include Mongoid::Document
  embeds_one :label, autobuild: true
  has_one :producer, autobuild: true
end

band = Band.new
band.label # Returns a new empty label.
band.producer # Returns a new empty producer.

Touching

Any belongs_to relation, no matter where it hangs off from, can take an optional :touch option which will call the touch method on it and any parent relations with the option defined when the base document calls #touch.

class Band
  include Mongoid::Document
  belongs_to :label, touch: true
end

band = Band.first
band.touch #=> Calls touch on the parent label.

Metadata

All relations in Mongoid contain metadata that holds information about the relation in question, and is a valuable tool for third party developers to use to extend Mongoid.

You can access the metadata of the relation in a few different ways.

# Get the metadata for a named relation from the class or document.
Model.reflect_on_association(:relation_name)
model.reflect_on_association(:relation_name)

# Get the metadata that the current object has in its relation.
model.metadata

# Get the metadata with a specific relation itself on a specific
# document.
person.addresses.metadata

The Metadata Object

The metadata object itself contains more information than one might know what to do with, and is useful for developers of extensions to Mongoid.

Method Description
Metadata#as Returns the name of the parent to a polymorphic child.
Metadata#as? Returns whether or not an as option exists.
Metadata#autobuilding? Returns whether or not the relation is autobuilding.
Metadata#autosaving? Returns whether or not the relation is autosaving.
Metadata#cascading_callbacks? Returns whether the relation has callbacks cascaded down from the parent.
Metadata#class_name Returns the class name of the proxied document.
Metadata#cyclic? Returns whether the relation is a cyclic relation.
Metadata#dependent Returns the relation’s dependent option.
Metadata#dependent? Returns whether the relation is a dependent relation.
Metadata#destructive? Returns true if the relation has a dependent delete or destroy.
Metadata#embedded? Returns whether the relation is embedded in another document.
Metadata#forced_nil_inverse? Returns whether the relation has a nil inverse defined.
Metadata#foreign_key Returns the name of the foreign key field.
Metadata#foreign_key_check Returns the name of the foreign key field dirty check method.
Metadata#foreign_key_setter Returns the name of the foreign key field setter.
Metadata#indexed? Returns whether the foreign key is auto indexed.
Metadata#inverses Returns the names of all inverse relation.
Metadata#inverse Returns the name of a single inverse relation.
Metadata#inverse_class_name Returns the class name of the relation on the inverse side.
Metadata#inverse_foreign_key Returns the name of the foreign key field on the inverse side.
Metadata#inverse_klass Returns the class of the relation on the inverse side.
Metadata#inverse_metadata Returns the metadata of the relation on the inverse side.
Metadata#inverse_of Returns the explicitly defined name of the inverse relation.
Metadata#inverse_of? Returns whether an inverse_of option is defined.
Metadata#inverse_setter Returns the name of the method used to set the inverse.
Metadata#inverse_type Returns the name for the polymorphic type field of the inverse.
Metadata#inverse_type_setter Returns the name for the polymorphic type field setter of the inverse.
Metadata#key Returns the name of the field in the attributes hash to use to get the relation.
Metadata#klass Returns the class of the proxied documents in the relation.
Metadata#macro Returns the relation’s macro.
Metadata#name Returns the relation name.
Metadata#options Returns self, for API compatibility with Active Record.
Metadata#order Returns the custom sorting options on the relation.
Metadata#order? Returns whether custom sorting options are set.
Metadata#polymorphic? Returns whether the relation is polymorphic.
Metadata#setter Returns the name of the field to set the relation.
Metadata#store_as Returns the name of the attribute to store an embedded relation in.
Metadata#touchable? Returns whether or not the relation has a touch option.
Metadata#type Returns the name of the field to get the polymorphic type.
Metadata#type_setter Returns the name of the field to set the polymorphic type.
Metadata#validate? Returns whether the relation has an associated validation.

Embeds One

One to one relationships where the children are embedded in the parent document are defined using Mongoid’s embeds_one and embedded_in macros.

Defining

The parent document of the relation should use the embeds_one macro to indicate is has 1 embedded child, where the document that is embedded uses embedded_in. Definitions are required on both sides to the relation in order for it to work properly.

class Band
  include Mongoid::Document
  embeds_one :label
end

class Label
  include Mongoid::Document
  field :name, type: String
  embedded_in :band
end

Storage

Documents that are embedded using the embeds_one macro are stored as a hash inside the parent in the parent’s database collection.

{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "label" : {
    "_id" : ObjectId("4d3ed089fb60ab534684b7e0"),
    "name" : "Mute",
  }
}

You can optionally tell Mongoid to store the embedded document in a different attribute other than the name, by providing a :store_as option.

class Band
  include Mongoid::Document
  embeds_one :label, store_as: "lab"
end

Embeds Many

One to many relationships where the children are embedded in the parent document are defined using Mongoid’s embeds_many and embedded_in macros.

Defining

The parent document of the relation should use the embeds_many macro to indicate it has n number of embedded children, where the document that is embedded uses embedded_in. Definitions are required on both sides to the relation in order for it to work properly.

class Band
  include Mongoid::Document
  embeds_many :albums
end

class Album
  include Mongoid::Document
  field :name, type: String
  embedded_in :band
end

Storage

Documents that are embedded using the embeds_many macro are stored as an array of hashes inside the parent in the parent’s database collection.

{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "albums" : [
    {
      "_id" : ObjectId("4d3ed089fb60ab534684b7e0"),
      "name" : "Violator",
    }
  ]
}

You can optionally tell Mongoid to store the embedded document in a different attribute other than the name, by providing a :store_as option.

class Band
  include Mongoid::Document
  embeds_many :albums, store_as: "albs"
end

Has One

One to one relationships where the children are referenced in the parent document are defined using Mongoid’s has_one and belongs_to macros.

Defining

The parent document of the relation should use the has_one macro to indicate is has 1 referenced child, where the document that is referenced in it uses belongs_to.

class Band
  include Mongoid::Document
  has_one :studio
end

class Studio
  include Mongoid::Document
  field :name, type: String
  belongs_to :band
end

Definitions are required on both sides to the relation in order for it to work properly, unless one of the models is embedded.

Storage

When defining a relation of this nature, each document is stored in its respective collection, but the child document contains a “foreign key” reference to the parent.

# The parent band document.
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9") }

# The child studio document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f1"),
  "band_id" : ObjectId("4d3ed089fb60ab534684b7e9")
}

Has Many

One to many relationships where the children are stored in a separate collection from the parent document are defined using Mongoid’s has_many and belongs_to macros. This exhibits similar behavior to Active Record.

Defining

The parent document of the relation should use the has_many macro to indicate is has n number of referenced children, where the document that is referenced uses belongs_to.

class Band
  include Mongoid::Document
  has_many :members
end

class Member
  include Mongoid::Document
  field :name, type: String
  belongs_to :band
end

Definitions are required on both sides to the relation in order for it to work properly, unless one of the models is embedded.

Storage

When defining a relation of this nature, each document is stored in its respective collection, but the child document contains a “foreign key” reference to the parent.

# The parent band document.
{ "_id" : ObjectId("4d3ed089fb60ab534684b7e9") }

# A child member document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f1"),
  "band_id" : ObjectId("4d3ed089fb60ab534684b7e9")
}

Has And Belongs To Many

Many to many relationships where the inverse documents are stored in a separate collection from the base document are defined using Mongoid’s has_and_belongs_to_many macro. This exhibits similar behavior to Active Record with the exception that no join collection is needed, the foreign key ids are stored as arrays on either side of the relation.

Defining

Both sides of the relation use the same macro.

class Band
  include Mongoid::Document
  has_and_belongs_to_many :tags
end

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

You can create a one sided many to many if you want to mimic a has_many that stores the keys as an array on the parent.

class Band
  include Mongoid::Document
  has_and_belongs_to_many :tags, inverse_of: nil
end

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

Storage

When defining a relation of this nature, each document is stored in its respective collection, and each document contains a “foreign key” reference to the other in the form of an array.

# The band document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7e9"),
  "tag_ids" : [ ObjectId("4d3ed089fb60ab534684b7f2") ]
}

# The tag document.
{
  "_id" : ObjectId("4d3ed089fb60ab534684b7f2"),
  "band_ids" : [ ObjectId("4d3ed089fb60ab534684b7e9") ]
}

Nested Attributes

Nested attributes provide a mechanism for updating documents and their relations in a single operation, by nesting attributes in a single parameters hash. This is extremely useful when wanting to edit multiple documents within a single web form.

Common Behaviour

Nested attributes can be enabled for any relation, embedded or referenced. To enable this for the relation, simply provide the relation name to the accepts_nested_attributes_for macro.

class Band
  include Mongoid::Document
  embeds_many :albums
  belongs_to :producer
  accepts_nested_attributes_for :albums, :producer
end

Note that when you add nested attributes functionality to a referenced relation, Mongoid will automatically enable autosave for that relation.

When a relation gains nested attributes behavior, an additional method is added to the base model, which should be used to update the attributes with the new functionality. This method is the relation name plus _attributes=. You can use this method directly, or more commonly the name of the method can be an attribute in the updates for the base class, in which case Mongoid will call the appropriate setter under the covers.

band = Band.first
band.producer_attributes = { name: "Flood" }
band.attributes = { producer_attributes: { name: "Flood" }}

Note that this will work with any attribute based setter method in Mongoid. This includes: update_attributes, update_attributes! and attributes=.

Callbacks

Document Callbacks

Mongoid supports the following callbacks:

  • after_initialize
  • after_build
  • before_validation
  • after_validation
  • before_create
  • around_create
  • after_create
  • after_find
  • before_update
  • around_update
  • after_update
  • before_upsert
  • around_upsert
  • after_upsert
  • before_save
  • around_save
  • after_save
  • before_destroy
  • around_destroy
  • after_destroy

Callbacks are available on any document, whether it is embedded within another document or not. Note that to be efficient, Mongoid only fires the callback of the document that the persistence action was executed on. This is that Mongoid aims to support large hierarchies and to handle optimized atomic updates callbacks can’t be firing all over the document hierarchy.

Note that using callbacks for domain logic is a bad design practice, and can lead to unexpected errors that are hard to debug when callbacks in the chain halt execution. It is our recommendation to only use them for cross-cutting concerns, like queueing up background jobs.

class Article
  include Mongoid::Document
  field :name, type: String
  field :body, type: String
  field :slug, type: String

  before_create :send_message

  after_save do |document|
    # Handle callback here.
  end

  protected
  def send_message
    # Message sending code here.
  end
end

Callbacks are coming from Active Support, so you can use the new syntax as well:

class Article
  include Mongoid::Document
  field :name, type: String

  set_callback(:create, :before) do |document|
    # Message sending code here.
  end
end

Relation Callbacks

Mongoid has a set of callbacks that are specific to collection based relations - these are:

  • after_add
  • after_remove
  • before_add
  • before_remove

Each time a document is added or removed from any of the following relations, the respective callbacks are fired: embeds_many, has_many, and has_and_belongs_to_many.

Relation Callbacks are specified as an option on the relation. The element added/removed is the parameter to the method you call via the callback. Example:

class Person
  include Mongoid::Document

  has_many :posts, after_add: :send_email_to_subscribers
end

def send_email_to_subscribers(post)
  Notifications.new_post(post).deliver
end

Validation

Mongoid includes ActiveModel::Validations to supply the basic validation plus an additional associated and uniqueness validator.

See ActiveModel::Validations documentation for more information.

Mongoid behaves slightly different to Active Record when using #valid? on already persisted data. Active Record’s #valid? will run all validations whereas Mongoid’s #valid? will only run validations on documents that are in memory as an optimization.

Indexing

You can define indexes on documents using the index macro. Provide the key for the index along with a direction. For additional options, supply them in a second options hash parameter.

class Person
  include Mongoid::Document
  field :ssn

  index({ ssn: 1 }, { unique: true, name: "ssn_index" })
end

You can define indexes on embedded document fields as well.

class Person
  include Mongoid::Document
  embeds_many :addresses
  index "addresses.street" => 1
end

You can index on multiple fields and provide direction.

class Person
  include Mongoid::Document
  field :first_name
  field :last_name

  index({ first_name: 1, last_name: 1 }, { unique: true })
end

Indexes can be sparse:

class Person
  include Mongoid::Document
  field :ssn

  index({ ssn: -1 }, { sparse: true })
end

Indexes can be run in the background in cases where they may take some time:

class Person
  include Mongoid::Document
  field :ssn
  index({ ssn: 1 }, { unique: true, background: true })
end

For unique indexes that are defined for a column that might already have duplicate values, you can drop the duplicate entries:

class Person
  include Mongoid::Document
  field :ssn
  index({ ssn: 1 }, { unique: true, drop_dups: true })
end

For geospatial indexes, make sure the field you are indexing is an Array.

class Person
  include Mongoid::Document
  field :location, type: Array

  index({ location: "2d" }, { min: -200, max: 200 })
end

Indexes can be scoped to a specific database.

class Person
  include Mongoid::Document
  field :ssn
  index({ ssn: 1 }, { database: "users", unique: true, background: true })
end

You can have Mongoid define indexes for you on “foreign key” fields for relational associations. This only works on the relation macro that the foreign key is stored on.

class Comment
  include Mongoid::Document
  belongs_to :post, index: true
  has_and_belongs_to_many :preferences, index: true
end

When you want to create the indexes in the database, use the provided rake task.

$ rake db:mongoid:create_indexes

Mongoid also provides a rake task to delete all secondary indexes.

$ rake db:mongoid:remove_indexes

Rails

Mongoid was built and targeted towards Rails applications, even though it will work in any environment. However if you are using Rails consult the next sections on how Mongoid hooks into a Rails application.

Railties

Mongoid provides some railties and initializers that one should be aware of when writing a Rails application with Mongoid.

Configuration

You can set Mongoid configuration options in your application.rb along with other Rails environment specific options by accessing config.mongoid. Options set here will override those set in your config/mongoid.yml.

module MyApplication
  class Application < Rails::Application
    config.mongoid.logger = Logger.new($stdout, :warn)
    config.mongoid.persist_in_safe_mode = true
  end
end

Model Preloading

In order to properly set up single collection inheritance, Mongoid needs to preload all models before every request in development mode. This can get slow, so if you are not using any inheritance it is recommended you turn this feature off.

config.mongoid.preload_models = false

Exceptions

Similar to Active Record, Mongoid tells raise to return specific http codes when some errors are raised.

Mongoid::Errors::DocumentNotFound : 404
Mongoid::Errors::Validations : 422

Rake Tasks

Mongoid provides the following rake tasks when used in a Rails 3 environment:

  • db:create: Exists only for dependency purposes, does not actually do anything.
  • db:create_indexes: Reads all index definitions from the models and attempts to create them in the database.
  • db:remove_indexes: Reads all secondary index definitions from the models.
  • db:drop: Drops all collections in the database with the exception of the system collections.
  • db:migrate: Exists only for dependency purposes, does not actually do anything.
  • db:purge: Deletes all data, including indexes, from the database. Since 3.1.0
  • db:schema:load: Exists only for framework dependency purposes, does not actually do anything.
  • db:seed: Seeds the database from db/seeds.rb
  • db:setup: Creates indexes and seeds the database.
  • db:test:prepare: Exists only for framework dependency purposes, does not actually do anything.

Upgrading

Upgrading to 5.0.0

The underlying driver has changed from Moped to the official MongoDB Ruby driver. For all users dropping down to the driver level for operations, please see the driver documentation for help with that API.

All Mongoid configuration options have changed, as the underlying driver has changed from Moped to the core MongoDB Ruby driver. Please see the configuration section for a detailed description of all new options.

All references to session are now replaced with client. This includes the mongoid.yml configuration, store_in options, and all exceptions and modules with Session in the name.

find_and_modify has been removed and replaced with 3 options: find_one_and_update, find_one_and_delete and find_one_and_replace.

text_search has been removed as it is now a $text option in a query from 2.6 on.

Mongoid no longer supports MongoDB 2.2 - support is now for only 2.4 and higher.

first and last no longer add an _id sort when no sorting options have been provided. In order to guarantee that a document is the first or last, it needs to now contain an explicit sort.