Skip to content

Commit 59fa493

Browse files
authored
MONGOID-5028 demongoize on pluck (#5176)
* MONGOID-5028 demongoize on pluck * MONGOID-5028 add demongoize to distinct * MONGOID-5028 add feature flag and tests. Revamped config tests * MONGOID-5028 remove byebug * MONGOID-5028 update docs and release notes * MONGOID-5028 add back broken scoping * MONGOID-5028 change language * MONGOID-5028 update tests * MONGOID-5028 sort test results * MONGOID-5028 revert config spec * MONGOID-5028 make tests more explicit * MONGOID-5028 expand more tests
1 parent 083ecfb commit 59fa493

File tree

8 files changed

+158
-21
lines changed

8 files changed

+158
-21
lines changed

docs/reference/configuration.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ for details on driver options.
182182
# determined by the driver will be used. Please refer to:
183183
# https://docs.mongodb.com/ruby-driver/current/reference/authentication/#auth-source
184184
auth_source: admin
185-
185+
186186
# Connect directly to and perform all operations on the specified
187187
# server, bypassing replica set node discovery and monitoring.
188188
# Exactly one host address must be specified. (default: false)
@@ -367,6 +367,12 @@ for details on driver options.
367367
# to parent contexts by default. (default: false)
368368
join_contexts: false
369369

370+
# Maintain legacy behavior of pluck and distinct, which does not demongoize
371+
# values on returning them. Setting this option to false will cause
372+
# pluck and distinct to return demongoized values.
373+
# (default: true in Mongoid 7, will change to false in Mongoid 8)
374+
legacy_pluck_distinct: true
375+
370376
# Maintain legacy behavior of === on Mongoid document classes, which
371377
# returns true in a number of cases where Ruby's === implementation would
372378
# return false. Note that the behavior of === on Mongoid document

docs/release-notes/mongoid-7.4.txt

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ in Mongoid 7.4 for backwards compatibility. In Mongoid 8 the default value
179179
will change to ``true``.
180180

181181

182-
Respect Field Aliases In Embedded Documents When Using ``distinct`` And ``pluck``
182+
Respect Field Aliases In Embedded Documents When Using ``distinct`` And ``pluck``
183183
---------------------------------------------------------------------------------
184184

185185
When ``distinct`` and ``pluck`` are used with aliased fields in embedded
@@ -273,10 +273,10 @@ and then to a non-nil value, for example:
273273

274274
class Canvas
275275
include Mongoid::Document
276-
276+
277277
embeds_one :palette
278278
end
279-
279+
280280
canvas.palette = palette
281281
canvas.palette = nil
282282
canvas.palette = palette
@@ -345,7 +345,7 @@ For example:
345345
Band.with_scope(active: true) do
346346
# ...
347347
end
348-
348+
349349
# {year: 2020} condition is applied here
350350
end
351351

@@ -359,11 +359,51 @@ all scopes are cleared:
359359
Band.with_scope(active: true) do
360360
# ...
361361
end
362-
362+
363363
# No scope is applied here
364364
end
365365

366366
.. note::
367367

368368
The value of ``Mongoid.broken_scoping`` option will change to ``false``
369369
by default in Mongoid 8.0.
370+
371+
372+
Demongoize Values Returned from Pluck and Distinct
373+
--------------------------------------------------
374+
375+
Mongoid 7.4 with the ``Mongoid.legacy_pluck_distinct`` option set to ``false``
376+
will demongoize values returned from the pluck and distinct methods.
377+
For example:
378+
379+
.. code-block:: ruby
380+
381+
class Band
382+
include Mongoid::Document
383+
384+
field :sales, type: BigDecimal
385+
end
386+
387+
Band.create!(sales: "1E2")
388+
Band.create!(sales: "2E2")
389+
Band.pluck(:sales)
390+
# => [0.1e3, 0.2e3]
391+
Band.distinct(:sales)
392+
# => [0.1e3, 0.2e3]
393+
394+
395+
In Mongoid 7.3 and earlier, and in 7.4 with the ``Mongoid.legacy_pluck_distinct``
396+
option set to ``true`` (the default), the value returned from the pluck and
397+
distinct methods will not be demongoized. For example:
398+
399+
.. code-block:: ruby
400+
401+
Band.pluck(:sales)
402+
# => ["1E2", "2E2"]
403+
Band.distinct(:sales)
404+
# => ["1E2", "2E2"]
405+
406+
.. note::
407+
408+
The value of ``Mongoid.legacy_pluck_distinct`` option will change to ``false``
409+
by default in Mongoid 8.0.

lib/mongoid/config.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ module Config
110110
# the one monkey-patched into Mongoid.
111111
option :object_id_as_json_oid, default: true
112112

113+
# Maintain legacy behavior of pluck and distinct, which does not
114+
# demongoize the values on returning them.
115+
option :legacy_pluck_distinct, default: true
116+
113117
# Has Mongoid been configured? This is checking that at least a valid
114118
# client config exists.
115119
#

lib/mongoid/contextual/mongo.rb

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,13 @@ def destroy
123123
#
124124
# @return [ Array<Object> ] The distinct values for the field.
125125
def distinct(field)
126-
view.distinct(klass.database_field_name(field)).map do |value|
127-
value.class.demongoize(value)
126+
field_name = klass.database_field_name(field)
127+
view.distinct(field_name).map do |value|
128+
if !Mongoid.legacy_pluck_distinct && field = klass.fields[field_name]
129+
field.demongoize(value)
130+
else
131+
value.class.demongoize(value)
132+
end
128133
end
129134
end
130135

@@ -422,7 +427,14 @@ def pluck(*fields)
422427

423428
view.projection(normalized_select).reduce([]) do |plucked, doc|
424429
values = normalized_select.keys.map do |n|
425-
n =~ /\./ ? doc[n.partition('.')[0]] : doc[n]
430+
res = n =~ /\./ ? doc[n.partition('.')[0]] : doc[n]
431+
if Mongoid.legacy_pluck_distinct
432+
res
433+
elsif field = klass.fields[n]
434+
field.demongoize(res)
435+
else
436+
res.class.demongoize(res)
437+
end
426438
end
427439
plucked << (values.size == 1 ? values.first : values)
428440
end

spec/mongoid/config_spec.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,14 @@
328328
it_behaves_like "a config option"
329329
end
330330

331+
context 'when setting the legacy_pluck_distinct option in the config' do
332+
let(:option) { :legacy_pluck_distinct }
333+
let(:default) { true }
334+
335+
it_behaves_like "a config option"
336+
end
337+
338+
331339
describe "#load!" do
332340

333341
before(:all) do

spec/mongoid/contextual/mongo_spec.rb

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -473,8 +473,9 @@
473473
describe "#distinct" do
474474

475475
before do
476-
Band.create!(name: "Depeche Mode", years: 30)
477-
Band.create!(name: "New Order", years: 25)
476+
Band.create!(name: "Depeche Mode", years: 30, sales: "1E2")
477+
Band.create!(name: "New Order", years: 25, sales: "2E3")
478+
Band.create!(name: "10,000 Maniacs", years: 20, sales: "1E2")
478479
end
479480

480481
context "when limiting the result set" do
@@ -503,7 +504,7 @@
503504
end
504505

505506
it "returns the distinct field values" do
506-
expect(context.distinct(:name)).to eq([ "Depeche Mode", "New Order" ])
507+
expect(context.distinct(:name).sort).to eq([ "10,000 Maniacs", "Depeche Mode", "New Order" ].sort)
507508
end
508509
end
509510

@@ -518,7 +519,7 @@
518519
end
519520

520521
it "returns the distinct field values" do
521-
expect(context.distinct(:years).sort).to eq([ 25, 30 ])
522+
expect(context.distinct(:years).sort).to eq([ 20, 25, 30 ])
522523
end
523524
end
524525

@@ -534,15 +535,41 @@
534535
end
535536

536537
let(:expected_results) do
537-
["Depeche Mode", "New Order"]
538+
["10,000 Maniacs", "Depeche Mode", "New Order"]
538539
end
539540

540541
let(:criteria) do
541542
Band.where({}).collation(locale: 'en_US', strength: 2)
542543
end
543544

544545
it 'applies the collation' do
545-
expect(context.distinct(:name)).to eq(expected_results)
546+
expect(context.distinct(:name).sort).to eq(expected_results.sort)
547+
end
548+
end
549+
550+
context "when providing a demongoizable field" do
551+
let(:criteria) do
552+
Band.criteria
553+
end
554+
555+
let(:context) do
556+
described_class.new(criteria)
557+
end
558+
559+
context "when legacy_pluck_distinct is set" do
560+
config_override :legacy_pluck_distinct, true
561+
562+
it "returns the non-demongoized distinct field values" do
563+
expect(context.distinct(:sales).sort).to eq([ "1E2", "2E3" ])
564+
end
565+
end
566+
567+
context "when legacy_pluck_distinct is not set" do
568+
config_override :legacy_pluck_distinct, false
569+
570+
it "returns the non-demongoized distinct field values" do
571+
expect(context.distinct(:sales).sort).to eq([ BigDecimal("1E2"), BigDecimal("2E3") ])
572+
end
546573
end
547574
end
548575
end

spec/mongoid/criteria_spec.rb

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2830,6 +2830,10 @@ class D
28302830
Band.create!(name: "Photek", likes: 1)
28312831
end
28322832

2833+
let(:maniacs) do
2834+
Band.create!(name: "10,000 Maniacs", likes: 1, sales: "1E2")
2835+
end
2836+
28332837
context "when the field is aliased" do
28342838

28352839
let!(:expensive) do
@@ -3026,13 +3030,24 @@ class D
30263030
end
30273031

30283032
context 'when plucking the entire field' do
3029-
30303033
let(:plucked) do
30313034
Dictionary.all.pluck(:description)
30323035
end
30333036

3034-
it 'returns all translations' do
3035-
expect(plucked.first).to eq({'en' => 'english-text', 'de' => 'deutsch-text'})
3037+
context "when legacy_pluck_distinct is set" do
3038+
config_override :legacy_pluck_distinct, true
3039+
3040+
it 'returns the non-demongoized translations' do
3041+
expect(plucked.first).to eq({"de"=>"deutsch-text", "en"=>"english-text"})
3042+
end
3043+
end
3044+
3045+
context "when legacy_pluck_distinct is not set" do
3046+
config_override :legacy_pluck_distinct, false
3047+
3048+
it 'returns the demongoized translations' do
3049+
expect(plucked.first).to eq('deutsch-text')
3050+
end
30363051
end
30373052
end
30383053

@@ -3047,6 +3062,31 @@ class D
30473062
end
30483063
end
30493064
end
3065+
3066+
context 'when plucking a field to be demongoized' do
3067+
3068+
let(:plucked) do
3069+
Band.where(name: maniacs.name).pluck(:sales)
3070+
end
3071+
3072+
context "when legacy_pluck_distinct is set" do
3073+
config_override :legacy_pluck_distinct, true
3074+
3075+
it "does not demongoize the field" do
3076+
expect(plucked.first).to be_a(String)
3077+
expect(plucked.first).to eq("1E2")
3078+
end
3079+
end
3080+
3081+
context "when legacy_pluck_distinct is not set" do
3082+
config_override :legacy_pluck_distinct, false
3083+
3084+
it "demongoizes the field" do
3085+
expect(plucked.first).to be_a(BigDecimal)
3086+
expect(plucked.first).to eq(BigDecimal("1E2"))
3087+
end
3088+
end
3089+
end
30503090
end
30513091

30523092
describe "#respond_to?" do
@@ -3648,7 +3688,7 @@ def self.ages; self; end
36483688
# Used to test MONGOID-5251 where the find command was adding unnecessary
36493689
# and clauses. Since the find command creates the criteria and executes it,
36503690
# it is difficult to analyze the criteria used. For this reason, I have
3651-
# extracted the crux of the issue, adding an _id to the the criteria twice,
3691+
# extracted the crux of the issue, adding an _id to the the criteria twice,
36523692
# and used that for the test case.
36533693
context "when searching by _id twice" do
36543694
let(:_id) { BSON::ObjectId.new }

spec/support/macros.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ def config_override(key, value)
2929
end
3030
end
3131

32-
def with_config_values(key, *values)
32+
def with_config_values(key, *values, &block)
3333
values.each do |value|
3434
context "when #{key} is #{value}" do
3535
config_override key, value
3636

37-
yield
37+
class_exec(value, &block)
3838
end
3939
end
4040
end

0 commit comments

Comments
 (0)