Mongoid 9.0
On this page
- Railsmdb for Mongoid
- Support for Ruby 2.6 and JRuby 9.3 Dropped
- Support for Rails 5 Dropped
- Deprecated class
Mongoid::Errors::InvalidStorageParent
removed around_*
callbacks for embedded documents are now ignoredfor_js
method is deprecated- Deprecated options removed
- Deprecated functionality removed
touch
method now clears changed state- Sandbox Mode for Rails Console
- New Transactions API
- Embedded Documents Always Use Parent Persistence Context
- Support for Passing Raw Values into Queries
- Raise AttributeNotLoaded error when accessing fields omitted from query projection
- Use configured time zone to typecast Date to Time in queries
`#touch
method on embedded documents correctly handlestouch: false
optionembedded_in
associations now default totouch: true
- Flipped default for
:replace
option in#upsert
- The immutability of the
_id
field is now enforced - Support Field Aliases on Index Options
- BSON 5 and BSON::Decimal128 Fields
- Search Index Management with MongoDB Atlas
Time.configured
has been removed- Records now remember the persistence context in which they were loaded/created
- Bug Fixes and Improvements
This page describes significant changes and improvements in Mongoid 9.0. 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.
Railsmdb for Mongoid
To coincide with the release of Mongoid 9.0 a new command-line utility for creating, updating, managing, and maintaining Rails applications has also been made generally available!
railsmdb
makes it easier to work with MongoDB from the command line through
common generators that Ruby on Rails developers are already familiar with.
For example, you can use railsmdb
to generate stubs for new Mongoid models:
bin/railsmdb generate model person
This will create a new model at app/models/person.rb
:
class Person include Mongoid::Document include Mongoid::Timestamp end
You can specify the fields of the model as well:
# bin/railsmdb generate model person name:string birth:date class Person include Mongoid::Document include Mongoid::Timestamp field :name, type: String field :birth, type: Date end
You can instruct the generator to make the new model a subclass of another,
by passing the --parent
option:
# bin/railsmdb generate model student --parent=person class Student < Person include Mongoid::Timestamp end
And if you need to store your models in a different collection than can be
inferred from the model name, you can specify --collection
:
# bin/railsmdb generate model course --collection=classes class Course include Mongoid::Document include Mongoid::Timestamp store_in collection: 'classes' end
For more information see the GitHub Repository.
Support for Ruby 2.6 and JRuby 9.3 Dropped
Mongoid 9 requires Ruby 2.7 or newer or JRuby 9.4. Earlier Ruby and JRuby versions are not supported.
Support for Rails 5 Dropped
Mongoid 9 requires Rails 6.0 or newer. Earlier Rails versions are not supported.
Deprecated class Mongoid::Errors::InvalidStorageParent
removed
The deprecated class Mongoid::Errors::InvalidStorageParent
has been removed.
around_*
callbacks for embedded documents are now ignored
Mongoid 8.x and older allows user to define around_*
callbacks for embedded
documents. Starting from 9.0 these callbacks are ignored and will not be executed.
A warning will be printed to the console if such callbacks are defined.
If you want to restore the old behavior, you can set
Mongoid.around_embedded_document_callbacks
to true in your application.
Note
Enabling around_*
callbacks for embedded documents is not recommended
as it may cause SystemStackError
exceptions when a document has many
embedded documents. See MONGOID-5658
for more details.
for_js
method is deprecated
The for_js
method is deprecated and will be removed in Mongoid 10.0.
Deprecated options removed
Breaking change: The following config options are removed in Mongoid 9.0.
Please ensure you have removed all references to these from your app.
If you were using config.load_defaults 8.1
prior to upgrading, you will not
experience any behavior change. Refer to earlier release notes for the meaning
of each option.
:use_activesupport_time_zone
:broken_aggregables
:broken_alias_handling
:broken_and
:broken_scoping
:broken_updates
:compare_time_by_ms
:legacy_attributes
:legacy_pluck_distinct
:legacy_triple_equals
:object_id_as_json_oid
:overwrite_chained_operators
In addition, support for config.load_defaults
versions 7.5 and
prior has been dropped (you must use a minimum of version 8.0.)
Deprecated functionality removed
Breaking change: The following deprecated functionality is now removed:
The
Mongoid::QueryCache
module has been removed. Please replace any usages 1-for-1 withMongo::QueryCache
. The methodMongoid::QueryCache#clear_cache
should be replaced withMongo::QueryCache#clear
. All other methods and submodules are identically named. Refer to the driver query cache documentation for more details.Object#blank_criteria?
method is removed (was previously deprecated.)Document#as_json :compact
option is removed. Please call`#compact
on the returnedHash
object instead.The deprecated class
Mongoid::Errors::InvalidStorageParent
has been removed.
touch
method now clears changed state
In Mongoid 8.x and older touch
method leaves models in the changed state:
# Mongoid 8.x behaviour band = Band.create! band.touch band.changed? # => true band.changes # => {"updated_at"=>[2023-01-30 13:12:57.477191135 UTC, 2023-01-30 13:13:11.482975646 UTC]}
Starting from 9.0 Mongoid now correctly clears changed state after using touch
method.
# Mongoid 9.0 behaviour band = Band.create! band.touch band.changed? # => false band.changes # => {}
Sandbox Mode for Rails Console
Mongoid now supports Rails console sandbox mode. If the Rails console started
with --sandbox
flag, Mongoid starts a transaction on the :default
client
before opening the console. This transaction won't be committed; therefore, all
the commands executed in the console using the :default
client won't
be persisted in the database.
Note
If you execute commands in the sandbox mode using any other client than default, these changes will be persisted as usual.
New Transactions API
Mongoid 9.0 introduces new transactions API that is inspired by ActiveRecord:
Band.transaction do Band.create(title: 'Led Zeppelin') end band = Band.create(title: 'Deep Purple') band.transaction do band.active = false band.save! end
Please consult transactions documentation for more details.
Embedded Documents Always Use Parent Persistence Context
Mongoid 8.x and older allows user to specify persistence context for an
embedded document (using store_in
macro). In Mongoid 9.0 these settings are
ignored for embedded documents; an embedded document now always uses the persistence
context of its parent.
Support for Passing Raw Values into Queries
When performing queries, it is now possible skip Mongoid's type coercion logic
using the Mongoid::RawValue
wrapper class. This can be useful when legacy
data in the database is of a different type than the field definition.
class Person include Mongoid::Document field :age, type: Integer end # Query for the string "42", not the integer 42 Person.where(age: Mongoid::RawValue("42"))
Raise AttributeNotLoaded error when accessing fields omitted from query projection
When attempting to access a field on a model instance which was
excluded with the .only
or .without
query projections methods
when the instance was loaded, Mongoid will now raise a
Mongoid::Errors::AttributeNotLoaded
error.
Band.only(:name).first.label #=> raises Mongoid::Errors::AttributeNotLoaded Band.without(:label).first.label = 'Sub Pop Records' #=> raises Mongoid::Errors::AttributeNotLoaded
In earlier Mongoid versions, the same conditions would raise an
ActiveModel::MissingAttributeError
. Please check your code for
any Mongoid-specific usages of this class, and change them to
Mongoid::Errors::AttributeNotLoaded
. Note additionally that
AttributeNotLoaded
inherits from Mongoid::Errors::MongoidError
,
while ActiveModel::MissingAttributeError
does not.
Use configured time zone to typecast Date to Time in queries
When querying for a Time field using a Date value, Mongoid now correctly
considers Time.zone
to perform type conversion.
class Magazine include Mongoid::Document field :published_at, type: Time end Time.zone = 'Asia/Tokyo' Magazine.gte(published_at: Date.parse('2022-09-26')) #=> will return all results on or after Sept 26th, 2022 # at 0:00 in Asia/Tokyo time zone.
In prior Mongoid versions, the above code would ignore the Time.zone
(irrespective of the now-removed :use_activesupport_time_zone
setting) and always using the system time zone to perform the type conversion.
Note that in prior Mongoid versions, typecasting Date to Time during persistence operations was already correctly using time zone.
`#touch
method on embedded documents correctly handles touch: false
option
When the touch: false
option is set on an embedded_in
relation,
calling the #touch
method on an embedded child document will not
invoke #touch
on its parent document.
class Address include Mongoid::Document include Mongoid::Timestamps embedded_in :mall, touch: false end class Mall include Mongoid::Document include Mongoid::Timestamps embeds_many :addresses end mall = Mall.create! address = mall.addresses.create! address.touch #=> updates address.updated_at but not mall.updated_at
In addition, the #touch
method has been optimized to perform one
persistence operation per parent document, even when using multiple
levels of nested embedded documents.
embedded_in
associations now default to touch: true
Updating an embedded subdocument will now automatically touch the parent,
unless you explicitly set touch: false
on the relation:
class Address include Mongoid::Document include Mongoid::Timestamps embedded_in :mall, touch: false end
For all other associations, the default remains touch: false
.
Flipped default for :replace
option in #upsert
Mongoid 8.1 added the :replace
option to the #upsert
method. This
option was used to specify whether or not the existing document should be
updated or replaced.
Mongoid 9.0 flips the default of this flag from true
=> false
.
This means that, by default, Mongoid 9 will update the existing document and will not replace it.
The immutability of the _id
field is now enforced
Prior to Mongoid 9.0, mutating the _id
field behaved inconsistently
depending on whether the document was top-level or embedded, and depending on
how the update was performed. As of 9.0, changing the _id
field will now
raise an exception when the document is saved, if the document had been
previously persisted.
Mongoid 9.0 also introduces a new feature flag, immutable_ids
, which
defaults to true
.
Mongoid::Config.immutable_ids = true
When set to false, the older, inconsistent behavior is restored.
Support Field Aliases on Index Options
Support has been added to use aliased field names in the following options
of the index
macro: partial_filter_expression
, weights
,
wildcard_projection
.
class Person include Mongoid::Document field :a, as: :age index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) end
Note
The expansion of field name aliases in index options such as
partial_filter_expression
is performed according to the behavior of MongoDB
server 6.0. Future server versions may change how they interpret these options,
and Mongoid's functionality may not support such changes.
BSON 5 and BSON::Decimal128 Fields
When BSON 4 or earlier is present, any field declared as BSON::Decimal128 will return a BSON::Decimal128 value. When BSON 5 is present, however, any field declared as BSON::Decimal128 will return a BigDecimal value by default.
class Model include Mongoid::Document field :decimal_field, type: BSON::Decimal128 end # under BSON <= 4 Model.first.decimal_field.class #=> BSON::Decimal128 # under BSON >= 5 Model.first.decimal_field.class #=> BigDecimal
If you need literal BSON::Decimal128 values with BSON 5, you may instruct Mongoid to allow literal BSON::Decimal128 fields:
Model.first.decimal_field.class #=> BigDecimal Mongoid.allow_bson5_decimal128 = true Model.first.decimal_field.class #=> BSON::Decimal128
Note
The allow_bson5_decimal128
option only has any effect under
BSON 5 and later. BSON 4 and earlier ignore the setting entirely.
Search Index Management with MongoDB Atlas
When connected to MongoDB Atlas, Mongoid now supports creating and removing search indexes. You may do so programmatically, via the Mongoid::SearchIndexable API:
class SearchablePerson include Mongoid::Document search_index { ... } # define the search index here end # create the declared search indexes; this returns immediately, but the # search indexes may take several minutes before they are available. SearchablePerson.create_search_indexes # query the available search indexes SearchablePerson.search_indexes.each do |index| # ... end # remove all search indexes from the model's collection SearchablePerson.remove_search_indexes
If you are not connected to MongoDB Atlas, the search index definitions are ignored. Trying to create, enumerate, or remove search indexes will result in an error.
There are also rake tasks available, for convenience:
# create search indexes for all models; waits for indexes to be created # and shows progress on the terminal. $ rake mongoid:db:create_search_indexes # as above, but returns immediately and lets the indexes be created in the # background $ rake WAIT_FOR_SEARCH_INDEXES=0 mongoid:db:create_search_indexes # removes search indexes from all models $ rake mongoid:db:remove_search_indexes
Time.configured
has been removed
Time.configured
returned either the time object wrapping the configured
time zone, or the standard Ruby Time
class. This allowed you to query
a time value even if no time zone had been configured.
Mongoid now requires that you set a time zone if you intend to do
anything with time values (including using timestamps in your documents).
Any uses of Time.configured
must be replaced with Time.zone
.
# before: puts Time.configured.now # after: puts Time.zone.now # or, better for finding the current Time specifically: puts Time.current
If you do not set a time zone, you will see errors in your code related
to nil
values. If you are using Rails, the default time zone is already
set to UTC. If you are not using Rails, you may set a time zone at the start
of your program like this:
Time.zone = 'UTC'
This will set the time zone to UTC. You can see all available time zone names by running the following command:
$ ruby -ractive_support/values/time_zone \ -e 'puts ActiveSupport::TimeZone::MAPPING.keys'
Records now remember the persistence context in which they were loaded/created
Consider the following code:
record = Model.with(collection: 'other_collection') { Model.first } record.update(field: 'value')
Prior to Mongoid 9.0, this could would silently fail to execute the update, because the storage options (here, the specification of an alternate collection for the model) would not be remembered by the record. Thus, the record would be loaded from "other_collection", but when updated, would attempt to look for and update the document in the default collection for Model. To make this work, you would have had to specify the collection explicitly for every update.
As of Mongoid 9.0, records that are created or loaded under explicit storage options, will remember those options (including a named client, a different database, or a different collection).
If you need the legacy (pre-9.0) behavior, you can enable it with the following flag:
Mongoid.legacy_persistence_context_behavior = true
This flag defaults to false in Mongoid 9.
Bug Fixes and Improvements
This section will be for smaller bug fixes and improvements:
The
.unscoped
method now also clears scopes declared using.with_scope
MONGOID-5214.When evolving a
String
to aBigDecimal
(i.e. when querying aBigDecimal
field with aString
object), if themap_big_decimal_to_decimal128
flag set to true, the conversion will return aBSON::Decimal128
and not aString
MONGOID-5484.Created new error
Mongoid::Errors::InvalidEstimatedCountCriteria
for when callingestimated_document_count
on a document class with a default scope MONGOID-4960.Mongoid now uses primary reads for validations in all cases MONGOID-5150.
Added support for symbol keys in localized field translation hashes MONGOID-5334.
Added index wildcard option MONGOID-5388.
With the
map_big_decimal_to_decimal128
flag set to false,demongoizing
a non-numeric, non-string value that implements:to_d
will return a string rather than aBigDecimal
MONGOID-5507.Added support for serializing and deserializing BSON::ObjectId values when passed as ActiveJob arguments MONGOID-5611.