Persistence Configuration
On this page
Document 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: "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" } ] }
Persistence Context Attributes
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
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 "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.
Runtime Persistence Options
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.
Global Override
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
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(...)