Docs Menu

Docs HomeMongoid

Mongoid 8.1

On this page

This page describes significant changes and improvements in Mongoid 8.1. The complete list of releases is available on GitHub and in JIRA; please consult GitHub releases for detailed release notes and JIRA for the complete list of issues fixed in each release, including bug fixes.

The new load_async method on Criteria allows running database queries asynchronously.

These new methods behave identically to corresponding methods from ActiveRecord::AttributeMethods::Dirty. The methods are particularly useful in callbacks:

class Person
include Mongoid::Document
field :name, type: String
before_save do
puts "attribute_was(:name): #{attribute_was(:name)}"
puts "attribute_before_last_save(:name): #{attribute_before_last_save(:name)}"
puts "will_save_change_to_attribute?(:name): #{will_save_change_to_attribute?(:name)}"
end
after_save do
puts "attribute_was(:name): #{attribute_was(:name)}"
puts "attribute_before_last_save(:name): #{attribute_before_last_save(:name)}"
puts "saved_change_to_attribute(:name): #{saved_change_to_attribute(:name)}"
puts "attribute_changed?(:name): #{attribute_changed?(:name)}"
puts "saved_change_to_attribute?(:name): #{saved_change_to_attribute?(:name)}"
end
end
person = Person.create(name: 'John')
#
# before_save
#
## attribute_was(:name): nil
## attribute_before_last_save(:name): nil
## will_save_change_to_attribute?(:name): true
#
# after_save
#
## attribute_was(:name): John => New value
## attribute_before_last_save(:name): nil => Value before save
## saved_change_to_attribute(:name): [nil, "John"] => Both values
## attribute_changed?(:name): false
## saved_change_to_attribute?(:name): true => Correctly indicates that the change for :name was saved
person.name = 'Jane'
person.save
#
# before_save
#
## attribute_was(:name): John => attribute_was not look back before the last save
## attribute_before_last_save(:name): nil => value before the last save
## will_save_change_to_attribute?(:name): true
#
# after_save
#
## attribute_was(:name): Jane => New value
## attribute_before_last_save(:name): John => Value before save
## saved_change_to_attribute(:name): ["John", "Jane"] => Both values
## attribute_changed?(:name): false
## saved_change_to_attribute?(:name): true => Correctly indicates that the change for :name was saved

For all of the new methods there are also shorter forms created dynamically, e.g. attribute_before_last_save(:name) is equivalent to name_before_last_save, saved_change_to_attribute(:name) is equivalent to saved_change_to_name, saved_change_to_attribute?(:name) is equivalent to saved_change_to_name?, and will_save_change_to_attribute?(:name) is equivalent to will_save_change_to_name?.

The config option use_activesupport_time_zone has been deprecated. Beginning in Mongoid 9.0, it will be ignored and always behave as true. Since use_activesupport_time_zone currently defaults to true, it is safe to remove from your config file at this time.

It is now possible to use Mongoid.configure without providing an argument to its block:

Mongoid.configure do
connect_to("mongoid_test")
# Use config method when assigning variables
config.preload_models = true

Note that configure will continue to support a block argument. The following is equivalent to the above:

Mongoid.configure do |config|
config.connect_to("mongoid_test")
config.preload_models = true

Mongoid 8.1 implements several finder methods on Mongoid::Criteria:

  • first!

  • last!

  • second/second!

  • third/third!

  • fourth/fourth!

  • fifth/fifth!

  • second_to_last/second_to_last!

  • third_to_last/third_to_last!

When no documents are found, methods without a bang (!) return nil, and methods with a bang (!) raise an error:

Band.none.first
# => nil
Band.none.first!
# => raise Mongoid::Errors::DocumentNotFound

Support for the :touch option has been added to the #save and #save! methods. When this option is false, the updated_at field on the saved document and all of it's embedded documents will not be updated with the current time. When this option is true or unset, the updated_at field will be updated with the current time.

If the document being saved is a new document (i.e. it has not yet been persisted to the database), then the :touch option will be ignored, and the updated_at (and created_at) fields will be updated with the current time.

Mongoid 8.1 has added the ability to set the default configurations for a specific version:

Mongoid.configure do |config|
config.load_defaults 8.0
end

This is helpful for upgrading between versions. See the section on Version Based Default Configuration for more details on how to use this feature to make upgrading between Mongoid versions easier.

The :present option was added to localized fields for removing blank values from the _translations hash:

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

See the section on Localize :present Field Option for more details on how these are used.

Mongoid 8.1 adds the :to and :from options on the attribute_changed? method. These options can be used to inquire whether the attribute has been changed to or from a certain value:

Starting in Mongoid 8.1, subclasses can now specify which collection its documents should be stored in using the store_in macro:

class Shape
include Mongoid::Document
store_in collection: :shapes
end
class Circle < Shape
store_in collection: :circles
end
class Square < Shape
store_in collection: :squares
end

Previously, an error was raised if this was done. See the section on Inheritance Persistence Context for more details.

Mongoid 8.1 changes the meaning of read-only documents. In Mongoid 8.1 with this feature flag turned off (false), a document becomes read-only when calling the readonly! method:

band = Band.first
band.readonly? # => false
band.readonly!
band.readonly? # => true
band.name = "The Rolling Stones"
band.save # => raises ReadonlyDocument error

With this feature flag turned off, a ReadonlyDocument error will be raised when destroying or deleting, as well as when saving or updating.

Prior to Mongoid 8.1 and in 8.1 with the legacy_readonly feature flag turned on (true), documents become read-only when they are projected (i.e. using #only or #without).

class Band
include Mongoid::Document
field :name, type: String
field :genre, type: String
end
band = Band.only(:name).first
band.readonly? # => true
band.destroy # => raises ReadonlyDocument error

Note that with this feature flag on, a ReadonlyDocument error will only be raised when destroying or deleting, and not on saving or updating. See the section on Read-only Documents for more details.

Mongoid 8.1 introduces method paramters to the Contextual#exists? method. An _id, a hash of conditions, or false/nil can now be included:

Band.exists?
Band.exists?(name: "The Rolling Stones")
Band.exists?(BSON::ObjectId('6320d96a3282a48cfce9e72c'))
Band.exists?(false) # always false
Band.exists?(nil) # always false

Mongoid 8.1 adds the :replace option to the #upsert method. This option is false by default.

In Mongoid 8 and earlier, and in Mongoid 8.1 when passing replace: true (the default) the upserted document will overwrite the current document in the database if it exists. Consider the following example:

existing = Player.create!(name: "Juan Soto", age: 23, team: "WAS")
player = Player.new(name: "Juan Soto", team: "SD")
player.id = existing.id
player.upsert # :replace defaults to true in 8.1
p Player.find(existing.id)
# => <Player _id: 633b42f43282a45fadfaaf9d, name: "Juan Soto", age: nil, team: "SD">

As you can see, the value for the :age field was dropped, because the upsert replaced the entire document instead of just updating it. If we take the same example and set :replace to false, however:

player.upsert(replace: false)
p Player.find(existing.id)
# => <Player _id: 633b42f43282a45fadfaaf9d, name: "Juan Soto", age: 23, team: "SD">

This time, the value for the :age field is maintained.

Note

The default for the :replace option will be changed to false in Mongoid 9.0, therefore it is recommended to explicitly specify this option while using #upsert in 8.1 for easier upgradability.

Mongoid 8.1 allows the assignment of a hash to an embedded_in association. On assignment, the hash will be coerced into a document of the class of the association that it is being assigned to. This functionality already exists for embeds_one and embeds_many associations. Consider the following example:

class Band
include Mongoid::Document
field :name, type: String
embeds_many :albums
end
class Album
include Mongoid::Document
embedded_in :band
end
album = Album.new
album.band = { name: "Death Cab For Cutie" }
p album.band
# => <Band _id: 633c74113282a438a15d2b56, name: "Death Cab For Cutie">

See the section on Hash Assignment on Embedded Associations for more details

With the addition of none_of, Mongoid 8.1 allows queries to exclude conditions in bulk. The emitted query will encapsulate the specified criteria in a $nor operation. For example:

class Building
include Mongoid::Document
field :city, type: String
field :height, type: Integer
field :purpose, type: String
field :occupancy, type: Integer
end
Building.where(city: 'Portland').
none_of(:height.lt => 100,
:purpose => 'apartment',
:occupancy.gt => 2500)

This would query all buildings in Portland, excluding apartments, buildings less than 100 units tall, and buildings with an occupancy greater than 2500 people.

Coming in Mongoid 9.0, the _id field will be immutable in both top-level and embedded documents. This addresses some inconsistency in how mutations to the _id field are treated currently. To prepare for this potentially breaking change, the Mongoid::Config.immutable_ids flag has been added. It defaults to false, preserving the existing behavior, but you may set it to true to prepare your apps for Mongoid 9.0. When this is set to true, attempts to mutate the _id of a document will raise an exception.

# The default in Mongoid 8.1
Mongoid::Config.immutable_ids = false
# The default in Mongoid 9.0
Mongoid::Config.immutable_ids = true
←  Mongoid 9.0Mongoid 8.0 →