Navigation

Persistence Methods

On this page

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 and passing a block.

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

Persisting using with is a one-time switch in the persistence context - it creates a new client under the covers which will get closed and 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") do |band_object|
  band_object.save!
end
band.update_attribute(likes: 1000) # This will not save - queries the collection "bands"
band.with(collection: "artists") do |band_object|
  band_object.update_attribute(likes: 1000) # This will update the document.
end

The Mongoid API was changed in version 6.0 to always require that a block be passed to #with. This is because a new client is created under the covers with the options passed to #with. A new client may spin up new monitor threads for keeping track of the MongoDB cluster, depending on what options are specified. This may in turn cause the number of connections to continually grow with each use of #with. Now that a block is required, Mongoid can properly stop the monitor threads and close the client after the block is called.

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

Set a Default Collation on a Collection

Mongoid does not provide a mechanism for creating a collection with a default collation. Like capped collections, you will need to create the collection yourself one time, up-front, either with the driver or via the Mongo console.

To create a collection with a default collation with the driver:

client["name", :collation => { :locale => 'fr'}].create

To create a collection with a default collation from the Mongo console:

db.createCollection("name", { collation: { locale: 'fr' } });
←   Documents Queries  →