Docs Menu
Docs Home
/ / /
Mongoid
/

Mongoid 9.0

On this page

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.

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.

Mongoid 9 requires Ruby 2.7 or newer or JRuby 9.4. Earlier Ruby and JRuby versions are not supported.

Mongoid 9 requires Rails 6.0 or newer. Earlier Rails versions are not supported.

The deprecated class Mongoid::Errors::InvalidStorageParent has been removed.

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.

The for_js method is deprecated and will be removed in Mongoid 10.0.

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.)

Breaking change: The following deprecated functionality is now removed:

  • The Mongoid::QueryCache module has been removed. Please replace any usages 1-for-1 with Mongo::QueryCache. The method Mongoid::QueryCache#clear_cache should be replaced with Mongo::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 returned Hash object instead.

  • The deprecated class Mongoid::Errors::InvalidStorageParent has been removed.

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 # => {}

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.

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.

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.

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"))

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.

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.

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.

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.

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.

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

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.

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 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'

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.

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 a BigDecimal (i.e. when querying a BigDecimal field with a String object), if the map_big_decimal_to_decimal128 flag set to true, the conversion will return a BSON::Decimal128 and not a String MONGOID-5484.

  • Created new error Mongoid::Errors::InvalidEstimatedCountCriteria for when calling estimated_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 a BigDecimal MONGOID-5507.

  • Added support for serializing and deserializing BSON::ObjectId values when passed as ActiveJob arguments MONGOID-5611.

Back

Upgrading Mongoid