Docs Menu

Docs HomeMongoid

Persistence Configuration

On this page

  • Document Storage
  • Persistence Context Attributes
  • Custom
  • Model-Level Persistence Options
  • Runtime Persistence Options
  • Client and Collection Access

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: "analytics"
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"
}
]
}

Mongoid provides client_name, database_name and collection_name methods on model classes to determine the client, database and collection names used for persistence:

Band.client_name
# => :default
Band.database_name
# => "mongoid"
Band.collection_name
# => :bands

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.

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 "analytics".

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: "analytics"
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.

It is possible to change the client, database and collection, as well as any of the MongoDB client options, used for persistence for a group of operations by using the with method on a model class or instance:

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

The with method creates a temporary persistence context and a MongoDB client to be used for operations in the context. For the duration of the block, the persistence context on the model class or instance that with was called on is changed to the temporary persistence context. For convenience, the model class or instance that with was called on is yielded to the block.

The temporary persistence context applies to both queries and writes.

Care should be taken when performing persistence operations across different persistence contexts. For example, if a document is saved in a temporary persistence context, it may not exist in the default persistence context, failing subsequent updates:

band = Band.new(name: "Scuba")
band.with(collection: "artists") do |band_object|
band_object.save!
end
# This will not save - updates the collection "bands" which does not have
# the Scuba band
band.update_attribute(likes: 1000)
# This will update the document.
band.with(collection: "artists") do |band_object|
band_object.update_attribute(likes: 1000)
end

As of Mongoid 6.0, the with method must always be called with a block, and the temporary persistence context exists only for the duration of the block. This is because a new client is created under the covers with the options passed to with. To ensure that this client is closed and its associated resources are freed, the scope when this client could be used must be well-defined.

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_action :switch_database
after_action :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.

Persistence context applies to both read and write operations. For example, secondary reads can be performed as follows:

Band.with(read: {mode: :secondary}) do
Band.count
end

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(...)
←  Map/ReduceNested Attributes →