You are looking at the docs for v4.x. You can check out this page for Mongoid v3.x if you haven't upgraded yet.
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 is a cheat sheet with the method in Mongoid on the left, and the Moped driver operation on the right.
Mongoid never persists the entire document at once unless it is new. It figures out what has changed, and only ever updates the changed items atomically. |
Operation | Mongoid | Moped |
---|---|---|
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 |
collections[:people].insert({ first_name: "Heinrich", last_name: "Heine" }) collections[:people].insert([ { first_name: "Heinrich", last_name: "Heine" }, { first_name: "Willy", last_name: "Brandt" } ]) |
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") do |doc| doc.last_name = "Heine" end |
collections[:people].insert({ first_name: "Heinrich", last_name: "Heine" }) |
Model#save
Saves the changed attributes to the database
atomically, or insert the document if flagged as a new_record via
|
person = Person.new( first_name: "Heinrich", last_name: "Heine" ) person.save person.save(validate: false) person.first_name = "Christian Johan" person.save |
collections[:people].insert({ first_name: "Heinrich", last_name: "Heine" }) collections[:people].find(...). update("$set" => { first_name: "Christian Johan" }) |
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! |
collections[:people].insert({ first_name: "Heinrich", last_name: "Heine" }) collections[:people].find(...). update("$set" => { first_name: "Christian Johan" }) |
Model#update_attributes
Update the provided attributes (and any other dirty fields). |
person.update_attributes( first_name: "Jean", last_name: "Zorg" ) |
collections[:people].find(...). update("$set" => { first_name: "Jean", last_name: "Zorg" }) |
Model#update_attributes!
Update the attributes and raise an error if validation fails. |
person.update_attributes!( first_name: "Jean", last_name: "Zorg" ) |
collections[:people].find(...). update("$set" => { first_name: "Jean", last_name: "Zorg" }) |
Model#update_attribute
Update a single attribute, bypassing validations. |
person.update_attribute(:first_name, "Jean") |
collections[:people].find(...). update("$set" => { first_name: "Jean" }) |
Model#upsert
Performs a MongoDB |
person = Person.new( first_name: "Heinrich", last_name: "Heine" ) person.upsert |
collections[:people].upsert({ first_name: "Heinrich", last_name: "Heine" }) |
Model#touch
Update the document's updated_at timestamp, optionally
with one extra provided time field. This will cascade the
touch to all |
person.touch
person.touch(:audited_at) |
collections[:people].find(...). update("$set" => { updated_at: Time.now }) collections[:people].find(...). update({ "$set" => { updated_at: Time.now, audited_at: Time.now } }) |
Model#delete
Deletes the document from the database without running callbacks. |
person.delete |
collections[:people].find(...).remove |
Model#destroy
Deletes the document from the database while running destroy callbacks. |
person.destroy |
collections[:people].find(...).remove |
Model.delete_all
Deletes all documents from the database that match the provided attributes. Does not run any callbacks. |
Person.delete_all Person.delete_all(first_name: "Heinrich") |
collections[:people].find.remove_all collections[:people].find(first_name: "Heinrich"). remove_all |
Model.destroy_all
Deletes all documents from the database that match the provided attributes. Runs each document's destroy callbacks |
Person.destroy_all Person.destroy_all(first_name: "Heinrich") |
collections[:people].find.remove_all collections[:people].find(first_name: "Heinrich"). remove_all |
Atomic Persistence
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 | Mongoid | Moped |
---|---|---|
Model#add_to_set Performs an atomic $addToSet on the field. |
person.add_to_set(aliases: "Bond") |
collections[:people].find(...). update("$addToSet" => { aliases: "Bond" }) |
Model#bit Performs an atomic $bit on the field. |
person.bit(age: { and: 10, or: 12 }) |
collections[:people].find(...). update("$bit" => { age: { and: 10, or: 12 }}) |
Model#inc Performs an atomic $inc on the field. |
person.inc(age: 1) |
collections[:people].find(...). update("$inc" => { age: 1 }) |
Model#pop Performs an atomic $pop on the field. |
person.pop(aliases: 1) |
collections[:people].find(...). update("$pop" => { aliases: 1 }) |
Model#pull Performs an atomic $pull on the field. |
person.pull(aliases: "Bond") |
collections[:people].find(...). update("$pull" => { aliases: "Bond" }) |
Model#pull_all Performs an atomic $pullAll on the field. |
person.pull_all(aliases: [ "Bond", "James" ]) |
collections[:people].find(...). update("$pullAll" => { aliases: [ "Bond", "James" ]}) |
Model#push Performs an atomic $push on the field. |
person.push(aliases: ["007","008"]) |
collections[:people].find(...). update("$push" => { aliases: ["007","008'] }) |
Model#rename Performs an atomic $rename on the field. |
person.rename(bday: :dob) |
collections[:people].find(...). update("$rename" => { "bday" => "dob" }) |
Model#set Performs an atomic $set on the field. |
person.set(name: "Tyler Durden") |
collections[:people].find(...). update("$set" => { name: "Tyler Durden" }) |
Model#unset Performs an atomic $unset on the field. |
person.unset(:name) |
collections[:people].find(...). update("$unset" => { name: 1 }) |
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 session. The following example would store the Band class by default into a collection named "artists" in the database named "music", with the session "secondary".
Note that the value supplied to the session option must be
configured under sessions in your mongoid.yml.
|
class Band include Mongoid::Document store_in collection: "artists", database: "music", session: "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 session.
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(session: :tertiary).save!
Persisting using with
is a one time switch in the persistence
context - it changes back to its defaults immediately after. 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.
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 session and database level globally. The methods
for this are Mongoid.override_session
and
Mongoid.override_database
. A useful case for this are internationalized
applications that store information for different locales in different databases
or sessions, 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.
#with
is also used for changing write mode options temporarily
at runtime.
Band.with(write: { w: "majority" }).create Band.with(write: { w: 3 }).create!
The values that can be passed with :safe
are:
true
: Persist in safe mode, no extra options.false
: Don't persist in safe mode.fsync: true|false
: Whether to perform an fsync.w: n
: The number of nodes to write to before returning.wtimeout: n
: The timeout value for writing to multiple nodes.
Session and Collection Access
If you want to drop down to the driver level to perform operations, you can grab the Moped session or collection from the model or document instance.
Band.mongo_session band.mongo_session Band.collection band.collection
From here you also have the same runtime persistence options using Moped's
#with
. The Moped documentation will go into more detail
about this.
Band.mongo_session.with(write: {w: 0}, database: "musik") do |session| session[:artists].find(...) end
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 Moped or via the Mongo console.
To create a capped collection with Moped:
session.command(create: "name", capped: true, size: 10000000, max: 1000)
To create a capped collection from the Mongo console:
db.createCollection("name", { capped: true, size: 10000000, max: 1000 });