Mongoid 7.4
Contents
Mongoid 7.4#
On this page
This page describes significant changes and improvements in Mongoid 7.4. 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.
All behavior changes in Mongoid 7.4 must be explicitly requested by changing the value of configuration options as detailed below. By default, Mongoid 7.4 behaves the same as Mongoid 7.3.
Ruby Version Support#
As of version 7.4, Mongoid supports Ruby 2.5+. Support for Ruby 2.4 and earlier has been dropped.
Support for MongoDB 3.4 and Earlier Servers Deprecated#
Mongoid 7.4 deprecates support for MongoDB 3.4 and earlier. Mongoid 8 will require MongoDB 3.6 or newer.
Feature Flags Summary#
To ensure a stable upgrade path from Mongoid 7.3, Mongoid 7.4 introduces feature flags which are further explained in the sections below.
To enable all new behavior in Mongoid 7.4, please use the following configuration options in your mongoid.yml file. We recommend newly created apps to do this as well.
development:
...
options:
# Enable all new behavior in Mongoid 7.4
legacy_triple_equals: false
object_id_as_json_oid: false
compare_time_by_ms: true
broken_aggregables: false
broken_updates: false
broken_and: false
broken_scoping: false
broken_alias_handling: false
legacy_pluck_distinct: false
Change ===
Operator To Match Ruby Semantics#
In Mongoid 7.4, the ===
operator on Mongoid::Document
classes and
instances can be configured to behave the same way as it does in Ruby,
and is equivalent to calling is_a?
on the right hand
side with the left hand side as the argument:
ModelClass === instance
# equivalent to:
instance.is_a?(ModelClass)
In order to get this functionality, the Mongoid.legacy_triple_equals
option must be set to false. If it is set to true, which is the default for
Mongoid 7.4, the ===
operator will function as it did in Mongoid 7.3:
===
returned true
for some cases when the equivalent Ruby
===
implementation returned false, as per the examples below.
Mongoid 7.4 with Mongoid.legacy_triple_equals
set to false
behavior:
class Band
include Mongoid::Document
has_many :members
end
class CoverBand < Band
end
class Member
include Mongoid::Document
belongs_to :band
end
band = Band.new
cover_band = CoverBand.new
band === Band
# => false
cover_band === Band
# => false
Band === Band
# => false
CoverBand === Band
# => false
band.members === Array
# => false
band.members === Mongoid::Association::Referenced::HasMany::Enumerable
# => false
Mongoid 7.3 and 7.4 with Mongoid.legacy_triple_equals
set to true
behavior:
band === Band
# => true
cover_band === Band
# => true
Band === Band
# => true
CoverBand === Band
# => true
band.members === Array
# => true
band.members === Mongoid::Association::Referenced::HasMany::Enumerable
# => true
The standard invocation of ===
, that is having the class on the left and
the instance on the right, works the same in Mongoid 7.4 as it did previously
and matches the core Ruby behavior:
Band === band
# => true
Band === cover_band
# => true
Note
In Mongoid 8.0, the default value of the Mongoid.legacy_triple_equals
option will
change to false
.
Return String _id
Value (Hexadecimal) from BSON::ObjectId#as_json
#
Mongoid 7.4 permits configuring the BSON::ObjectId#as_json
method
to return the _id
value as a hexadecimal string instead of the
{"$oid" => "..."}
hash it has returned in Mongoid 7.3 and previous
versions.
When Mongoid.object_id_as_json_oid
is set to false
, Mongoid will
delegate to bson-ruby
implementation of BSON::ObjectId#as_json
.
In bson-ruby
4 the BSON::ObjectId#as_json
method will continue
to return the hash {"$oid" => "..."}
for backwards compatibility, but
in bson-ruby
5 the BSON::ObjectId#as_json
method will return only
the hexadecimal ObjectId string.
When Mongoid.object_id_as_json_oid
is set to true
, Mongoid will
install an implementation of BSON::ObjectId#as_json
which returns
the hash {"$oid" => "..."}
as it did in Mongoid 7.3 and earlier.
The behavior of as_json
is summarized in the following table:
|
true |
false |
---|---|---|
|
|
|
|
|
|
Note
In Mongoid 8.0, the default value of the Mongoid.object_id_as_json_oid
option will
change to false
.
Scoped Associations#
Associations now support the :scope
argument, yielding
scoped associations.
Compare Times With Millisecond Precision When Embedded Matching#
Mongoid 7.4 with the Mongoid.compare_time_by_ms
option set to true
will truncate the times to millisecond precision when comparing them while
performing embedded matching.
Time objects in Ruby have nanosecond precision, whereas MongoDB server
can only store times with millisecond precision. Set the
Mongoid.compare_time_by_ms
option to true
to truncate times to
millisecond precision when performing queries on already loaded embedded
associations (this is also called “embedded matching” and is done completely
in Ruby), to obtain the same query results when performing time comparisons
regardless of which documents are being queried. Setting this option to
false
will produce different results for queries on embedded associations
that are already loaded into memory vs queries on unloaded associations and
top-level models.
The Mongoid.compare_time_by_ms
option is set to false
by default
in Mongoid 7.4 for backwards compatibility.
Note
In Mongoid 8.0, the default value of the Mongoid.compare_time_by_ms
option will
change to true
.
count
, sum
, avg
, min
, max
Ignore Sort If Not Limiting/Skipping#
The count
, sum
, avg
, min
and max
methods now omit the
sort stage from the generated aggregation pipeline if no skip or limit
is specified, because the results aren’t affected by the sort order.
Example call that will now omit the sort stage and would potentially use
an index where it wouldn’t before:
Band.desc(:name).count
Return 0
When Aggregating Empty Result Sets#
Mongoid 7.4 with the Mongoid.broken_aggregables
option set to false
will return 0
from the sum
method over an empty result set, for example:
Product.where(impossible_condition: true).sum(:price)
# => 0
Mongoid 7.3 and Mongoid 7.4 with the Mongoid.broken_aggregables
option
set to true
(the default) returns nil
in this case.
Note
In Mongoid 8.0, the default value of the Mongoid.broken_aggregables
option will
change to false
.
Correct Update Behavior When Replacing Association#
Mongoid 7.4 with the Mongoid.broken_updates
option set to false
will correctly persist an embeds_one
association target that is set to nil
and then to a non-nil value, for example:
class Canvas
include Mongoid::Document
embeds_one :palette
end
canvas.palette = palette
canvas.palette = nil
canvas.palette = palette
In Mongoid 7.3 and earlier, and in 7.4 with the Mongoid.broken_aggregables
option set to true
(the default), canvas.palette
would be nil
when
we would expect it to be palette
.
Note
In Mongoid 8.0, the default value of the Mongoid.broken_updates
option will
change to false
Correct Logical and
Query Generation#
Mongoid 7.4 with the Mongoid.broken_and
option set to false
will preserve existing conditions when using and
to add new conditions
to a query when the same operator is used on the same field multiple times.
For example, in the following query:
Band.where(id: 1).and({year: {'$in' => [2020]}}, {year: {'$in' => [2021]}}).where(id: 2)
Mongoid 7.4 with the Mongoid.broken_and
option set to false
will
generate the following criteria:
#<Mongoid::Criteria
selector: {"_id"=>1, "year"=>{"$in"=>[2020]}, "$and"=>[{"year"=>{"$in"=>[2021]}}, {"_id"=>2}]}
options: {}
class: Band
embedded: false>
In Mongoid 7.3 and earlier, and in 7.4 with the Mongoid.broken_and
option set to true
(the default), the following criteria would be
generated instead which omit the {“$in” => [2021]} condition:
<Mongoid::Criteria
selector: {"_id"=>1, "year"=>{"$in"=>[2020]}, "$and"=>[{"_id"=>2}]}
options: {}
class: Band
embedded: false>
Note
In Mongoid 8.0, the default value of the Mongoid.broken_and
option will
change to false
.
Restore Parent Scope When Exiting with_scope
Block#
Mongoid 7.4 with the Mongoid.broken_scoping
option set to false
will restore the parent scope when exiting a with_scope
block.
For example:
Band.with_scope(year: 2020) do
Band.with_scope(active: true) do
# ...
end
# {year: 2020} condition is applied here
end
In Mongoid 7.3 and earlier, and in 7.4 with the Mongoid.broken_scoping
option set to true
(the default), once any with_scope
block finishes,
all scopes are cleared:
Band.with_scope(year: 2020) do
Band.with_scope(active: true) do
# ...
end
# No scope is applied here
end
Note
In Mongoid 8.0, the default value of the Mongoid.broken_scoping
option will
change to false
.
Changes to distinct
and pluck
#
Respect Field Aliases In Embedded Documents When Using distinct
and pluck
#
When distinct
and pluck
are used with aliased fields in embedded
documents, the aliases can be expanded if the Mongoid.broken_alias_handling
option is set to false
. By default, for backwards compatibility, in
Mongoid 7.4 this option is set to true, yielding Mongoid 7.3 and earlier
behavior. Given the following definitions:
class Band
include Mongoid::Document
embeds_many :managers
end
class Manager
include Mongoid::Document
embedded_in :band
field :name, as: :n
end
Mongoid 7.4 behavior with Mongoid.broken_alias_handling
set to false
:
# Expands out to "managers.n" in the query:
Band.distinct('managers.name')
Band.pluck('managers.name')
Mongoid 7.3 and 7.4 with Mongoid.broken_alias_handling
set to true
behavior:
# Sends "managers.name" without expanding the alias:
Band.distinct('managers.name')
Band.pluck('managers.name')
Note
The alias expansion for top-level fields has already been done by Mongoid 7.3.
Note
In Mongoid 8.0, the default value of the Mongoid.broken_alias_handling
option will
change to false
.
Demongoize Values Returned from pluck
and distinct
#
Mongoid 7.4 with the Mongoid.legacy_pluck_distinct
option set to false
will demongoize values returned from the pluck
and distinct
methods.
Given the following definitions:
class Band
include Mongoid::Document
field :sales, type: BigDecimal
end
Band.create!(sales: "1E2")
Band.create!(sales: "2E2")
Mongoid 7.4 behavior with Mongoid.legacy_pluck_distinct
set to false
:
Band.pluck(:sales)
# => [0.1e3, 0.2e3]
Band.distinct(:sales)
# => [0.1e3, 0.2e3]
In Mongoid 7.3 and earlier, and in 7.4 with the Mongoid.legacy_pluck_distinct
option set to true
(the default), the value returned from the pluck and
distinct methods will not be demongoized. For example:
Band.pluck(:sales)
# => ["1E2", "2E2"]
Band.distinct(:sales)
# => ["1E2", "2E2"]
Note
In Mongoid 8.0, the default value of the Mongoid.legacy_pluck_distinct
option will
change to false
.
Localized Fields with pluck
and distinct
#
Mongoid 7.4 with the Mongoid.legacy_pluck_distinct
option set to false
changes the behavior of using pluck
and distinct
with localized fields.
Now, when retrieving a localized field using these methods, the translation for
the current locale will be returned. To get the full translations hash the
_translations
field can be used. Given the following definitions:
class Band
include Mongoid::Document
field :name, localize: true
end
I18n.locale = :en
band = Band.create!(name: 'english-name')
I18n.locale = :de
band.name = 'deutsch-name'
band.save!
Mongoid 7.4 behavior with Mongoid.legacy_pluck_distinct
set to false
:
Band.pluck(:name)
# => ["deutsch-name"]
Band.pluck(:name_translations)
# => [{"en"=>"english-name", "de"=>"deutsch-name"}, {"en"=>"english-name", "de"=>"deutsch-name"}]
In Mongoid 7.3 and earlier, and in 7.4 with the Mongoid.legacy_pluck_distinct
option set to true
(the default), inputting a localized field returns the
full translations hash. Inputting the _translations
field will return nil
.
For example:
Band.pluck(:name)
# => [{"en"=>"english-name", "de"=>"deutsch-name"}, {"en"=>"english-name", "de"=>"deutsch-name"}]
Band.pluck(:name_translations)
# => [nil, nil]
Note
In Mongoid 8.0, the default value of the Mongoid.legacy_pluck_distinct
option will
change to false
.
Embedded Fields with pluck
#
Mongoid 7.4 with the Mongoid.legacy_pluck_distinct
option set to false
returns the embedded values themselves, i.e. not inside a hash. Given the
following definitions:
class Band
include Mongoid::Document
embeds_one :label
end
class Label
include Mongoid::Document
embedded_in :band
field :sales, type: BigDecimal
end
Mongoid 7.4 behavior with Mongoid.legacy_pluck_distinct
set to false
:
Band.pluck("label.sales")
# => [0.1e3]
In Mongoid 7.3 and earlier, and in 7.4 with the Mongoid.legacy_pluck_distinct
option set to true
(the default), plucking embedded attributes returns them
inside a hash. For example:
Band.pluck("label.sales")
# => [{"sales"=>"1E2"}]
Note
In Mongoid 8.0, the default value of the Mongoid.legacy_pluck_distinct
option will
change to false
.
update_one
Warnings in upsert
#
Mongoid 7.4.1 fixes incorrect usage of the driver’s update_one
method from
Mongoid’s upsert
method. Mongoid’s upsert
actually performs a
replacing upsert, and Mongoid 7.4.1 and later correctly call replace_one
.