mongoid 7.4.3 → 7.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/config/locales/en.yml +7 -0
- data/lib/mongoid/association/embedded/batchable.rb +3 -20
- data/lib/mongoid/association/macros.rb +20 -0
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +12 -8
- data/lib/mongoid/association/referenced/has_many/proxy.rb +2 -2
- data/lib/mongoid/atomic/paths/embedded/many.rb +0 -19
- data/lib/mongoid/config.rb +6 -1
- data/lib/mongoid/contextual/memory.rb +144 -12
- data/lib/mongoid/contextual/mongo.rb +118 -26
- data/lib/mongoid/contextual/none.rb +45 -1
- data/lib/mongoid/criteria/queryable/extensions/array.rb +2 -0
- data/lib/mongoid/criteria/queryable/extensions/hash.rb +2 -0
- data/lib/mongoid/criteria/queryable/mergeable.rb +21 -0
- data/lib/mongoid/criteria/queryable/selectable.rb +26 -10
- data/lib/mongoid/criteria.rb +2 -0
- data/lib/mongoid/document.rb +2 -0
- data/lib/mongoid/equality.rb +4 -4
- data/lib/mongoid/errors/document_not_found.rb +23 -6
- data/lib/mongoid/fields.rb +145 -21
- data/lib/mongoid/findable.rb +20 -5
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/warnings.rb +29 -0
- data/lib/mongoid.rb +1 -0
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -3
- data/spec/integration/i18n_fallbacks_spec.rb +15 -1
- data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +0 -21
- data/spec/mongoid/association/embedded/embeds_many_models.rb +0 -121
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +0 -8
- data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +54 -0
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +8 -24
- data/spec/mongoid/clients/options_spec.rb +1 -0
- data/spec/mongoid/config_spec.rb +10 -4
- data/spec/mongoid/contextual/memory_spec.rb +826 -65
- data/spec/mongoid/contextual/mongo_spec.rb +781 -18
- data/spec/mongoid/contextual/none_spec.rb +46 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +212 -39
- data/spec/mongoid/criteria_spec.rb +8 -0
- data/spec/mongoid/equality_spec.rb +12 -12
- data/spec/mongoid/errors/document_not_found_spec.rb +49 -0
- data/spec/mongoid/findable_spec.rb +30 -0
- data/spec/support/models/code.rb +2 -0
- data.tar.gz.sig +0 -0
- metadata +3 -2
- metadata.gz.sig +0 -0
@@ -379,36 +379,77 @@ describe Mongoid::Contextual::Memory do
|
|
379
379
|
|
380
380
|
describe "#distinct" do
|
381
381
|
|
382
|
-
|
383
|
-
|
384
|
-
end
|
382
|
+
context "when legacy_pluck_distinct is true" do
|
383
|
+
config_override :legacy_pluck_distinct, true
|
385
384
|
|
386
|
-
|
387
|
-
|
388
|
-
|
385
|
+
let(:hobrecht) do
|
386
|
+
Address.new(street: "hobrecht")
|
387
|
+
end
|
389
388
|
|
390
|
-
|
389
|
+
let(:friedel) do
|
390
|
+
Address.new(street: "friedel")
|
391
|
+
end
|
391
392
|
|
392
|
-
|
393
|
-
|
394
|
-
|
393
|
+
context "when limiting the result set" do
|
394
|
+
|
395
|
+
let(:criteria) do
|
396
|
+
Address.where(street: "hobrecht").tap do |crit|
|
397
|
+
crit.documents = [ hobrecht, hobrecht, friedel ]
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
let(:context) do
|
402
|
+
described_class.new(criteria)
|
403
|
+
end
|
404
|
+
|
405
|
+
it "returns the distinct field values" do
|
406
|
+
expect(context.distinct(:street)).to eq([ "hobrecht" ])
|
395
407
|
end
|
396
408
|
end
|
397
409
|
|
398
|
-
|
399
|
-
|
410
|
+
context "when not limiting the result set" do
|
411
|
+
|
412
|
+
let(:criteria) do
|
413
|
+
Address.all.tap do |crit|
|
414
|
+
crit.documents = [ hobrecht, friedel, friedel ]
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
let(:context) do
|
419
|
+
described_class.new(criteria)
|
420
|
+
end
|
421
|
+
|
422
|
+
it "returns the distinct field values" do
|
423
|
+
expect(context.distinct(:street)).to eq([ "hobrecht", "friedel" ])
|
424
|
+
end
|
400
425
|
end
|
401
426
|
|
402
|
-
|
403
|
-
|
427
|
+
context 'when there is a collation on the criteria' do
|
428
|
+
|
429
|
+
let(:criteria) do
|
430
|
+
Address.where(street: "hobrecht").tap do |crit|
|
431
|
+
crit.documents = [ hobrecht, hobrecht, friedel ]
|
432
|
+
end.collation(locale: 'en_US', strength: 2)
|
433
|
+
end
|
434
|
+
|
435
|
+
it "raises an exception" do
|
436
|
+
expect {
|
437
|
+
context.distinct(:street)
|
438
|
+
}.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
|
439
|
+
end
|
404
440
|
end
|
405
441
|
end
|
406
442
|
|
407
|
-
context "when
|
443
|
+
context "when legacy_pluck_distinct is false" do
|
444
|
+
config_override :legacy_pluck_distinct, false
|
445
|
+
|
446
|
+
let(:depeche) { Band.create!(name: "Depeche Mode", years: 30, sales: "1E2") }
|
447
|
+
let(:new_order) { Band.create!(name: "New Order", years: 25, sales: "2E3") }
|
448
|
+
let(:maniacs) { Band.create!(name: "10,000 Maniacs", years: 20, sales: "1E2") }
|
408
449
|
|
409
450
|
let(:criteria) do
|
410
|
-
|
411
|
-
crit.documents = [
|
451
|
+
Band.all.tap do |crit|
|
452
|
+
crit.documents = [ depeche, new_order, maniacs ]
|
412
453
|
end
|
413
454
|
end
|
414
455
|
|
@@ -416,23 +457,180 @@ describe Mongoid::Contextual::Memory do
|
|
416
457
|
described_class.new(criteria)
|
417
458
|
end
|
418
459
|
|
419
|
-
|
420
|
-
|
460
|
+
context "when limiting the result set" do
|
461
|
+
|
462
|
+
let(:criteria) do
|
463
|
+
Band.where(name: "Depeche Mode").tap do |crit|
|
464
|
+
crit.documents = [ depeche ]
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
it "returns the distinct matching fields" do
|
469
|
+
expect(context.distinct(:name)).to eq([ "Depeche Mode" ])
|
470
|
+
end
|
421
471
|
end
|
422
|
-
end
|
423
472
|
|
424
|
-
|
473
|
+
context "when not limiting the result set" do
|
425
474
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
end.collation(locale: 'en_US', strength: 2)
|
475
|
+
it "returns the distinct field values" do
|
476
|
+
expect(context.distinct(:name).sort).to eq([ "10,000 Maniacs", "Depeche Mode", "New Order" ].sort)
|
477
|
+
end
|
430
478
|
end
|
431
479
|
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
480
|
+
context "when providing an aliased field" do
|
481
|
+
|
482
|
+
it "returns the distinct field values" do
|
483
|
+
expect(context.distinct(:years).sort).to eq([ 20, 25, 30 ])
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
context "when providing a demongoizable field" do
|
488
|
+
|
489
|
+
it "returns the non-demongoized distinct field values" do
|
490
|
+
expect(context.distinct(:sales).sort).to eq([ BigDecimal("1E2"), BigDecimal("2E3") ])
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
context "when getting a localized field" do
|
495
|
+
before do
|
496
|
+
I18n.locale = :en
|
497
|
+
d = Dictionary.create!(description: 'english-text')
|
498
|
+
I18n.locale = :de
|
499
|
+
d.description = 'deutsch-text'
|
500
|
+
d.save!
|
501
|
+
end
|
502
|
+
|
503
|
+
after do
|
504
|
+
I18n.locale = :en
|
505
|
+
end
|
506
|
+
|
507
|
+
let(:criteria) do
|
508
|
+
Dictionary.all.tap do |crit|
|
509
|
+
crit.documents = [ Dictionary.first ]
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
context "when getting the field without _translations" do
|
514
|
+
it "gets the demongoized localized field" do
|
515
|
+
expect(context.distinct(:description)).to eq([ 'deutsch-text' ])
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
context "when getting the field with _translations" do
|
520
|
+
|
521
|
+
it "gets the full hash" do
|
522
|
+
expect(context.distinct(:description_translations)).to eq([ { "de" => "deutsch-text", "en" => "english-text" } ])
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
context 'when plucking a specific locale' do
|
527
|
+
|
528
|
+
let(:distinct) do
|
529
|
+
context.distinct(:'description.de')
|
530
|
+
end
|
531
|
+
|
532
|
+
it 'returns the specific translation' do
|
533
|
+
expect(distinct).to eq([ "deutsch-text" ])
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
context 'when plucking a specific locale from _translations field' do
|
538
|
+
|
539
|
+
let(:distinct) do
|
540
|
+
context.distinct(:'description_translations.de')
|
541
|
+
end
|
542
|
+
|
543
|
+
it 'returns the specific translations' do
|
544
|
+
expect(distinct).to eq(['deutsch-text'])
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
context 'when fallbacks are enabled with a locale list' do
|
549
|
+
require_fallbacks
|
550
|
+
|
551
|
+
around(:all) do |example|
|
552
|
+
prev_fallbacks = I18n.fallbacks.dup
|
553
|
+
I18n.fallbacks[:he] = [ :en ]
|
554
|
+
example.run
|
555
|
+
I18n.fallbacks = prev_fallbacks
|
556
|
+
end
|
557
|
+
|
558
|
+
after do
|
559
|
+
I18n.locale = :en
|
560
|
+
end
|
561
|
+
|
562
|
+
let(:distinct) do
|
563
|
+
context.distinct(:description).first
|
564
|
+
end
|
565
|
+
|
566
|
+
it "correctly uses the fallback" do
|
567
|
+
I18n.locale = :en
|
568
|
+
d = Dictionary.create!(description: 'english-text')
|
569
|
+
I18n.locale = :he
|
570
|
+
distinct.should == "english-text"
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
574
|
+
context "when the localized field is embedded" do
|
575
|
+
let(:person) do
|
576
|
+
p = Passport.new
|
577
|
+
I18n.locale = :en
|
578
|
+
p.name = "Neil"
|
579
|
+
I18n.locale = :he
|
580
|
+
p.name = "Nissim"
|
581
|
+
|
582
|
+
Person.create!(passport: p, employer_id: 12345)
|
583
|
+
end
|
584
|
+
|
585
|
+
after do
|
586
|
+
I18n.locale = :en
|
587
|
+
end
|
588
|
+
|
589
|
+
let(:criteria) do
|
590
|
+
Person.where(employer_id: 12345).tap do |crit|
|
591
|
+
crit.documents = [ person ]
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
let(:distinct) do
|
596
|
+
context.distinct("pass.name").first
|
597
|
+
end
|
598
|
+
|
599
|
+
let(:distinct_translations) do
|
600
|
+
context.distinct("pass.name_translations").first
|
601
|
+
end
|
602
|
+
|
603
|
+
let(:distinct_translations_field) do
|
604
|
+
context.distinct("pass.name_translations.en").first
|
605
|
+
end
|
606
|
+
|
607
|
+
it "returns the translation for the current locale" do
|
608
|
+
expect(distinct).to eq("Nissim")
|
609
|
+
end
|
610
|
+
|
611
|
+
it "returns the full _translation hash" do
|
612
|
+
expect(distinct_translations).to eq({ "en" => "Neil", "he" => "Nissim" })
|
613
|
+
end
|
614
|
+
|
615
|
+
it "returns the translation for the requested locale" do
|
616
|
+
expect(distinct_translations_field).to eq("Neil")
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
context "when getting an embedded field" do
|
622
|
+
|
623
|
+
let(:label) { Label.new(sales: "1E2") }
|
624
|
+
let!(:band) { Band.create!(label: label) }
|
625
|
+
let(:criteria) do
|
626
|
+
Band.where(_id: band.id).tap do |crit|
|
627
|
+
crit.documents = [ band ]
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
it "returns the distinct matching fields" do
|
632
|
+
expect(context.distinct("label.sales")).to eq([ BigDecimal("1E2") ])
|
633
|
+
end
|
436
634
|
end
|
437
635
|
end
|
438
636
|
end
|
@@ -624,6 +822,18 @@ describe Mongoid::Contextual::Memory do
|
|
624
822
|
expect(context.send(method)).to eq(hobrecht)
|
625
823
|
end
|
626
824
|
|
825
|
+
it "returns a list when passing a limit" do
|
826
|
+
expect(context.send(method, 2)).to eq([ hobrecht, friedel ])
|
827
|
+
end
|
828
|
+
|
829
|
+
it "returns a list when passing 1" do
|
830
|
+
expect(context.send(method, 1)).to eq([ hobrecht ])
|
831
|
+
end
|
832
|
+
|
833
|
+
it "returns the matching document when passing deprecated options" do
|
834
|
+
expect(context.send(method, id_sort: :none)).to eq(hobrecht)
|
835
|
+
end
|
836
|
+
|
627
837
|
context 'when there is a collation on the criteria' do
|
628
838
|
|
629
839
|
let(:criteria) do
|
@@ -641,6 +851,108 @@ describe Mongoid::Contextual::Memory do
|
|
641
851
|
end
|
642
852
|
end
|
643
853
|
|
854
|
+
describe "#take" do
|
855
|
+
|
856
|
+
let(:hobrecht) do
|
857
|
+
Address.new(street: "hobrecht")
|
858
|
+
end
|
859
|
+
|
860
|
+
let(:friedel) do
|
861
|
+
Address.new(street: "friedel")
|
862
|
+
end
|
863
|
+
|
864
|
+
let(:criteria) do
|
865
|
+
Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
|
866
|
+
crit.documents = [ hobrecht, friedel ]
|
867
|
+
end
|
868
|
+
end
|
869
|
+
|
870
|
+
let(:context) do
|
871
|
+
described_class.new(criteria)
|
872
|
+
end
|
873
|
+
|
874
|
+
it "returns the first matching document" do
|
875
|
+
expect(context.take).to eq(hobrecht)
|
876
|
+
end
|
877
|
+
|
878
|
+
it "returns an array when passing a limit" do
|
879
|
+
expect(context.take(2)).to eq([ hobrecht, friedel ])
|
880
|
+
end
|
881
|
+
|
882
|
+
it "returns an array when passing a limit as 1" do
|
883
|
+
expect(context.take(1)).to eq([ hobrecht ])
|
884
|
+
end
|
885
|
+
|
886
|
+
context 'when there is a collation on the criteria' do
|
887
|
+
|
888
|
+
let(:criteria) do
|
889
|
+
Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
|
890
|
+
crit.documents = [ hobrecht, friedel ]
|
891
|
+
end.collation(locale: 'en_US', strength: 2)
|
892
|
+
end
|
893
|
+
|
894
|
+
it "raises an exception" do
|
895
|
+
expect {
|
896
|
+
context.take
|
897
|
+
}.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
|
898
|
+
end
|
899
|
+
end
|
900
|
+
end
|
901
|
+
|
902
|
+
describe "#take!" do
|
903
|
+
|
904
|
+
let(:hobrecht) do
|
905
|
+
Address.new(street: "hobrecht")
|
906
|
+
end
|
907
|
+
|
908
|
+
let(:friedel) do
|
909
|
+
Address.new(street: "friedel")
|
910
|
+
end
|
911
|
+
|
912
|
+
let(:criteria) do
|
913
|
+
Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
|
914
|
+
crit.documents = [ hobrecht, friedel ]
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
let(:context) do
|
919
|
+
described_class.new(criteria)
|
920
|
+
end
|
921
|
+
|
922
|
+
it "returns the first matching document" do
|
923
|
+
expect(context.take!).to eq(hobrecht)
|
924
|
+
end
|
925
|
+
|
926
|
+
context "when the criteria is empty" do
|
927
|
+
let(:criteria) do
|
928
|
+
Address.where(street: "bogus").tap do |crit|
|
929
|
+
crit.documents = []
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
it "raise an error" do
|
934
|
+
expect do
|
935
|
+
context.take!
|
936
|
+
end.to raise_error(Mongoid::Errors::DocumentNotFound, /Could not find a document of class Address./)
|
937
|
+
end
|
938
|
+
end
|
939
|
+
|
940
|
+
context 'when there is a collation on the criteria' do
|
941
|
+
|
942
|
+
let(:criteria) do
|
943
|
+
Address.where(:street.in => [ "hobrecht", "friedel" ]).tap do |crit|
|
944
|
+
crit.documents = [ hobrecht, friedel ]
|
945
|
+
end.collation(locale: 'en_US', strength: 2)
|
946
|
+
end
|
947
|
+
|
948
|
+
it "raises an exception" do
|
949
|
+
expect {
|
950
|
+
context.take
|
951
|
+
}.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
|
952
|
+
end
|
953
|
+
end
|
954
|
+
end
|
955
|
+
|
644
956
|
describe "#initialize" do
|
645
957
|
|
646
958
|
context "when the criteria has no options" do
|
@@ -766,6 +1078,18 @@ describe Mongoid::Contextual::Memory do
|
|
766
1078
|
expect(context.last).to eq(friedel)
|
767
1079
|
end
|
768
1080
|
|
1081
|
+
it "returns a list when a limit is passed" do
|
1082
|
+
expect(context.last(2)).to eq([ hobrecht, friedel ])
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
it "returns a list when the limit is 1" do
|
1086
|
+
expect(context.last(1)).to eq([ friedel ])
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
it "returns the matching document when passing deprecated options" do
|
1090
|
+
expect(context.last(id_sort: :none)).to eq(friedel)
|
1091
|
+
end
|
1092
|
+
|
769
1093
|
context 'when there is a collation on the criteria' do
|
770
1094
|
|
771
1095
|
let(:criteria) do
|
@@ -919,72 +1243,509 @@ describe Mongoid::Contextual::Memory do
|
|
919
1243
|
|
920
1244
|
describe "#pluck" do
|
921
1245
|
|
922
|
-
let(:
|
923
|
-
|
1246
|
+
let(:context) do
|
1247
|
+
described_class.new(criteria)
|
924
1248
|
end
|
925
1249
|
|
926
|
-
|
927
|
-
|
928
|
-
end
|
1250
|
+
context "when legacy_pluck_distinct is true" do
|
1251
|
+
config_override :legacy_pluck_distinct, true
|
929
1252
|
|
930
|
-
|
931
|
-
|
932
|
-
crit.documents = [ hobrecht, friedel ]
|
1253
|
+
let(:hobrecht) do
|
1254
|
+
Address.new(street: "hobrecht")
|
933
1255
|
end
|
934
|
-
end
|
935
1256
|
|
936
|
-
|
937
|
-
|
938
|
-
|
1257
|
+
let(:friedel) do
|
1258
|
+
Address.new(street: "friedel")
|
1259
|
+
end
|
1260
|
+
|
1261
|
+
let(:criteria) do
|
1262
|
+
Address.all.tap do |crit|
|
1263
|
+
crit.documents = [ hobrecht, friedel ]
|
1264
|
+
end
|
1265
|
+
end
|
1266
|
+
|
1267
|
+
context "when plucking" do
|
1268
|
+
|
1269
|
+
let!(:plucked) do
|
1270
|
+
context.pluck(:street)
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
it "returns the values" do
|
1274
|
+
expect(plucked).to eq([ "hobrecht", "friedel" ])
|
1275
|
+
end
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
context "when plucking a mix of empty and non-empty values" do
|
1279
|
+
|
1280
|
+
let(:empty_doc) do
|
1281
|
+
Address.new(street: nil)
|
1282
|
+
end
|
1283
|
+
|
1284
|
+
let(:criteria) do
|
1285
|
+
Address.all.tap do |crit|
|
1286
|
+
crit.documents = [ hobrecht, friedel, empty_doc ]
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
let!(:plucked) do
|
1291
|
+
context.pluck(:street)
|
1292
|
+
end
|
1293
|
+
|
1294
|
+
it "returns the values" do
|
1295
|
+
expect(plucked).to eq([ "hobrecht", "friedel", nil ])
|
1296
|
+
end
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
context "when plucking a field that doesnt exist" do
|
1300
|
+
|
1301
|
+
context "when pluck one field" do
|
1302
|
+
|
1303
|
+
let(:plucked) do
|
1304
|
+
context.pluck(:foo)
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
it "returns a empty array" do
|
1308
|
+
expect(plucked).to eq([nil, nil])
|
1309
|
+
end
|
1310
|
+
end
|
939
1311
|
|
940
|
-
|
1312
|
+
context "when pluck multiple fields" do
|
941
1313
|
|
942
|
-
|
943
|
-
|
1314
|
+
let(:plucked) do
|
1315
|
+
context.pluck(:foo, :bar)
|
1316
|
+
end
|
1317
|
+
|
1318
|
+
it "returns a empty array" do
|
1319
|
+
expect(plucked).to eq([[nil, nil], [nil, nil]])
|
1320
|
+
end
|
1321
|
+
end
|
944
1322
|
end
|
945
1323
|
|
946
|
-
|
947
|
-
|
1324
|
+
context 'when there is a collation on the criteria' do
|
1325
|
+
|
1326
|
+
let(:criteria) do
|
1327
|
+
Address.all.tap do |crit|
|
1328
|
+
crit.documents = [ hobrecht, friedel ]
|
1329
|
+
end.collation(locale: 'en_US', strength: 2)
|
1330
|
+
end
|
1331
|
+
|
1332
|
+
it "raises an exception" do
|
1333
|
+
expect {
|
1334
|
+
context.pluck(:foo, :bar)
|
1335
|
+
}.to raise_exception(Mongoid::Errors::InMemoryCollationNotSupported)
|
1336
|
+
end
|
948
1337
|
end
|
949
1338
|
end
|
950
1339
|
|
951
|
-
context "when
|
1340
|
+
context "when legacy_pluck_distinct is false" do
|
1341
|
+
config_override :legacy_pluck_distinct, false
|
952
1342
|
|
953
|
-
|
1343
|
+
let!(:depeche) do
|
1344
|
+
Band.create!(name: "Depeche Mode", likes: 3)
|
1345
|
+
end
|
1346
|
+
|
1347
|
+
let!(:tool) do
|
1348
|
+
Band.create!(name: "Tool", likes: 3)
|
1349
|
+
end
|
1350
|
+
|
1351
|
+
let!(:photek) do
|
1352
|
+
Band.create!(name: "Photek", likes: 1)
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
let(:maniacs) do
|
1356
|
+
Band.create!(name: "10,000 Maniacs", likes: 1, sales: "1E2")
|
1357
|
+
end
|
1358
|
+
|
1359
|
+
let(:criteria) do
|
1360
|
+
Band.all.tap do |crit|
|
1361
|
+
crit.documents = [ depeche, tool, photek, maniacs ]
|
1362
|
+
end
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
context "when the field is aliased" do
|
1366
|
+
|
1367
|
+
let!(:expensive) do
|
1368
|
+
Product.create!(price: 100000)
|
1369
|
+
end
|
1370
|
+
|
1371
|
+
let!(:cheap) do
|
1372
|
+
Product.create!(price: 1)
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
let(:criteria) do
|
1376
|
+
Product.all.tap do |crit|
|
1377
|
+
crit.documents = [ expensive, cheap ]
|
1378
|
+
end
|
1379
|
+
end
|
1380
|
+
|
1381
|
+
context "when using alias_attribute" do
|
1382
|
+
|
1383
|
+
let(:plucked) do
|
1384
|
+
context.pluck(:price)
|
1385
|
+
end
|
1386
|
+
|
1387
|
+
it "uses the aliases" do
|
1388
|
+
expect(plucked).to eq([ 100000, 1 ])
|
1389
|
+
end
|
1390
|
+
end
|
1391
|
+
end
|
1392
|
+
|
1393
|
+
context "when the criteria matches" do
|
1394
|
+
|
1395
|
+
context "when there are no duplicate values" do
|
1396
|
+
|
1397
|
+
let!(:plucked) do
|
1398
|
+
context.pluck(:name)
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
it "returns the values" do
|
1402
|
+
expect(plucked).to contain_exactly("10,000 Maniacs", "Depeche Mode", "Tool", "Photek")
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
context "when subsequently executing the criteria without a pluck" do
|
1406
|
+
|
1407
|
+
it "does not limit the fields" do
|
1408
|
+
expect(context.first.likes).to eq(3)
|
1409
|
+
end
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
context 'when the field is a subdocument' do
|
1413
|
+
|
1414
|
+
context 'when a top-level field and a subdocument field are plucked' do
|
1415
|
+
let(:criteria) do
|
1416
|
+
Band.where(name: 'FKA Twigs').tap do |crit|
|
1417
|
+
crit.documents = [
|
1418
|
+
Band.create!(name: 'FKA Twigs'),
|
1419
|
+
Band.create!(name: 'FKA Twigs', records: [ Record.new(name: 'LP1') ])
|
1420
|
+
]
|
1421
|
+
end
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
let(:embedded_pluck) do
|
1425
|
+
context.pluck(:name, 'records.name')
|
1426
|
+
end
|
1427
|
+
|
1428
|
+
let(:expected) do
|
1429
|
+
[
|
1430
|
+
["FKA Twigs", []],
|
1431
|
+
['FKA Twigs', ["LP1"]]
|
1432
|
+
]
|
1433
|
+
end
|
1434
|
+
|
1435
|
+
it 'returns the list of top-level field and subdocument values' do
|
1436
|
+
expect(embedded_pluck).to eq(expected)
|
1437
|
+
end
|
1438
|
+
end
|
1439
|
+
|
1440
|
+
context 'when only a subdocument field is plucked' do
|
1441
|
+
|
1442
|
+
let(:criteria) do
|
1443
|
+
Band.where(name: 'FKA Twigs').tap do |crit|
|
1444
|
+
crit.documents = [
|
1445
|
+
Band.create!(name: 'FKA Twigs'),
|
1446
|
+
Band.create!(name: 'FKA Twigs', records: [ Record.new(name: 'LP1') ])
|
1447
|
+
]
|
1448
|
+
end
|
1449
|
+
end
|
1450
|
+
|
1451
|
+
let(:embedded_pluck) do
|
1452
|
+
context.pluck('records.name')
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
let(:expected) do
|
1456
|
+
[
|
1457
|
+
[],
|
1458
|
+
["LP1"]
|
1459
|
+
]
|
1460
|
+
end
|
1461
|
+
|
1462
|
+
it 'returns the list of subdocument values' do
|
1463
|
+
expect(embedded_pluck).to eq(expected)
|
1464
|
+
end
|
1465
|
+
end
|
1466
|
+
end
|
1467
|
+
end
|
1468
|
+
|
1469
|
+
context "when plucking multi-fields" do
|
1470
|
+
|
1471
|
+
let(:plucked) do
|
1472
|
+
context.pluck(:name, :likes)
|
1473
|
+
end
|
1474
|
+
|
1475
|
+
it "returns the values" do
|
1476
|
+
expect(plucked).to contain_exactly(["10,000 Maniacs", 1], ["Depeche Mode", 3], ["Tool", 3], ["Photek", 1])
|
1477
|
+
end
|
1478
|
+
end
|
1479
|
+
|
1480
|
+
context "when there are duplicate values" do
|
1481
|
+
|
1482
|
+
let(:plucked) do
|
1483
|
+
context.pluck(:likes)
|
1484
|
+
end
|
1485
|
+
|
1486
|
+
it "returns the duplicates" do
|
1487
|
+
expect(plucked).to contain_exactly(1, 3, 3, 1)
|
1488
|
+
end
|
1489
|
+
end
|
1490
|
+
end
|
1491
|
+
|
1492
|
+
context "when plucking an aliased field" do
|
954
1493
|
|
955
1494
|
let(:plucked) do
|
956
|
-
context.pluck(:
|
1495
|
+
context.pluck(:id)
|
957
1496
|
end
|
958
1497
|
|
959
|
-
it "returns
|
960
|
-
expect(plucked).to eq([])
|
1498
|
+
it "returns the field values" do
|
1499
|
+
expect(plucked).to eq([ depeche.id, tool.id, photek.id, maniacs.id ])
|
961
1500
|
end
|
962
1501
|
end
|
963
1502
|
|
964
|
-
context "when
|
1503
|
+
context "when plucking existent and non-existent fields" do
|
965
1504
|
|
966
1505
|
let(:plucked) do
|
967
|
-
context.pluck(:
|
1506
|
+
context.pluck(:id, :fooz)
|
968
1507
|
end
|
969
1508
|
|
970
|
-
it "returns
|
971
|
-
expect(plucked).to eq([[], []])
|
1509
|
+
it "returns nil for the field that doesnt exist" do
|
1510
|
+
expect(plucked).to eq([[depeche.id, nil], [tool.id, nil], [photek.id, nil], [maniacs.id, nil] ])
|
972
1511
|
end
|
973
1512
|
end
|
974
|
-
end
|
975
1513
|
|
976
|
-
|
1514
|
+
context "when plucking a field that doesnt exist" do
|
977
1515
|
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
1516
|
+
context "when pluck one field" do
|
1517
|
+
|
1518
|
+
let(:plucked) do
|
1519
|
+
context.pluck(:foo)
|
1520
|
+
end
|
1521
|
+
|
1522
|
+
it "returns an array with nil values" do
|
1523
|
+
expect(plucked).to eq([nil, nil, nil, nil])
|
1524
|
+
end
|
1525
|
+
end
|
1526
|
+
|
1527
|
+
context "when pluck multiple fields" do
|
1528
|
+
|
1529
|
+
let(:plucked) do
|
1530
|
+
context.pluck(:foo, :bar)
|
1531
|
+
end
|
1532
|
+
|
1533
|
+
it "returns an array of arrays with nil values" do
|
1534
|
+
expect(plucked).to eq([[nil, nil], [nil, nil], [nil, nil], [nil, nil]])
|
1535
|
+
end
|
1536
|
+
end
|
982
1537
|
end
|
983
1538
|
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
1539
|
+
context 'when plucking a localized field' do
|
1540
|
+
|
1541
|
+
before do
|
1542
|
+
I18n.locale = :en
|
1543
|
+
d = Dictionary.create!(description: 'english-text')
|
1544
|
+
I18n.locale = :de
|
1545
|
+
d.description = 'deutsch-text'
|
1546
|
+
d.save!
|
1547
|
+
end
|
1548
|
+
|
1549
|
+
after do
|
1550
|
+
I18n.locale = :en
|
1551
|
+
end
|
1552
|
+
|
1553
|
+
let(:criteria) do
|
1554
|
+
Dictionary.all.tap do |crit|
|
1555
|
+
crit.documents = [ Dictionary.first ]
|
1556
|
+
end
|
1557
|
+
end
|
1558
|
+
|
1559
|
+
context 'when plucking the entire field' do
|
1560
|
+
|
1561
|
+
let(:plucked) do
|
1562
|
+
context.pluck(:description)
|
1563
|
+
end
|
1564
|
+
|
1565
|
+
let(:plucked_translations) do
|
1566
|
+
context.pluck(:description_translations)
|
1567
|
+
end
|
1568
|
+
|
1569
|
+
let(:plucked_translations_both) do
|
1570
|
+
context.pluck(:description_translations, :description)
|
1571
|
+
end
|
1572
|
+
|
1573
|
+
it 'returns the demongoized translations' do
|
1574
|
+
expect(plucked.first).to eq('deutsch-text')
|
1575
|
+
end
|
1576
|
+
|
1577
|
+
it 'returns the full translations hash to _translations' do
|
1578
|
+
expect(plucked_translations.first).to eq({"de"=>"deutsch-text", "en"=>"english-text"})
|
1579
|
+
end
|
1580
|
+
|
1581
|
+
it 'returns both' do
|
1582
|
+
expect(plucked_translations_both.first).to eq([{"de"=>"deutsch-text", "en"=>"english-text"}, "deutsch-text"])
|
1583
|
+
end
|
1584
|
+
end
|
1585
|
+
|
1586
|
+
context 'when plucking a specific locale' do
|
1587
|
+
|
1588
|
+
let(:plucked) do
|
1589
|
+
context.pluck(:'description.de')
|
1590
|
+
end
|
1591
|
+
|
1592
|
+
it 'returns the specific translations' do
|
1593
|
+
expect(plucked.first).to eq('deutsch-text')
|
1594
|
+
end
|
1595
|
+
end
|
1596
|
+
|
1597
|
+
context 'when plucking a specific locale from _translations field' do
|
1598
|
+
|
1599
|
+
let(:plucked) do
|
1600
|
+
context.pluck(:'description_translations.de')
|
1601
|
+
end
|
1602
|
+
|
1603
|
+
it 'returns the specific translations' do
|
1604
|
+
expect(plucked.first).to eq('deutsch-text')
|
1605
|
+
end
|
1606
|
+
end
|
1607
|
+
|
1608
|
+
context 'when fallbacks are enabled with a locale list' do
|
1609
|
+
require_fallbacks
|
1610
|
+
|
1611
|
+
around(:all) do |example|
|
1612
|
+
prev_fallbacks = I18n.fallbacks.dup
|
1613
|
+
I18n.fallbacks[:he] = [ :en ]
|
1614
|
+
example.run
|
1615
|
+
I18n.fallbacks = prev_fallbacks
|
1616
|
+
end
|
1617
|
+
|
1618
|
+
after do
|
1619
|
+
I18n.locale = :en
|
1620
|
+
end
|
1621
|
+
|
1622
|
+
let(:plucked) do
|
1623
|
+
context.pluck(:description).first
|
1624
|
+
end
|
1625
|
+
|
1626
|
+
it "correctly uses the fallback" do
|
1627
|
+
I18n.locale = :en
|
1628
|
+
d = Dictionary.create!(description: 'english-text')
|
1629
|
+
I18n.locale = :he
|
1630
|
+
plucked.should == "english-text"
|
1631
|
+
end
|
1632
|
+
end
|
1633
|
+
|
1634
|
+
context "when the localized field is embedded" do
|
1635
|
+
before do
|
1636
|
+
p = Passport.new
|
1637
|
+
I18n.locale = :en
|
1638
|
+
p.name = "Neil"
|
1639
|
+
I18n.locale = :he
|
1640
|
+
p.name = "Nissim"
|
1641
|
+
|
1642
|
+
Person.create!(passport: p, employer_id: 12345)
|
1643
|
+
end
|
1644
|
+
|
1645
|
+
after do
|
1646
|
+
I18n.locale = :en
|
1647
|
+
end
|
1648
|
+
|
1649
|
+
let(:plucked) do
|
1650
|
+
Person.where(employer_id: 12345).pluck("pass.name").first
|
1651
|
+
end
|
1652
|
+
|
1653
|
+
let(:plucked_translations) do
|
1654
|
+
Person.where(employer_id: 12345).pluck("pass.name_translations").first
|
1655
|
+
end
|
1656
|
+
|
1657
|
+
let(:plucked_translations_field) do
|
1658
|
+
Person.where(employer_id: 12345).pluck("pass.name_translations.en").first
|
1659
|
+
end
|
1660
|
+
|
1661
|
+
it "returns the translation for the current locale" do
|
1662
|
+
expect(plucked).to eq("Nissim")
|
1663
|
+
end
|
1664
|
+
|
1665
|
+
it "returns the full _translation hash" do
|
1666
|
+
expect(plucked_translations).to eq({ "en" => "Neil", "he" => "Nissim" })
|
1667
|
+
end
|
1668
|
+
|
1669
|
+
it "returns the translation for the requested locale" do
|
1670
|
+
expect(plucked_translations_field).to eq("Neil")
|
1671
|
+
end
|
1672
|
+
end
|
1673
|
+
end
|
1674
|
+
|
1675
|
+
context 'when plucking a field to be demongoized' do
|
1676
|
+
|
1677
|
+
let(:plucked) do
|
1678
|
+
Band.where(name: maniacs.name).pluck(:sales)
|
1679
|
+
end
|
1680
|
+
|
1681
|
+
it "demongoizes the field" do
|
1682
|
+
expect(plucked.first).to be_a(BigDecimal)
|
1683
|
+
expect(plucked.first).to eq(BigDecimal("1E2"))
|
1684
|
+
end
|
1685
|
+
end
|
1686
|
+
|
1687
|
+
context "when plucking an embedded field" do
|
1688
|
+
let(:label) { Label.new(sales: "1E2") }
|
1689
|
+
let!(:band) { Band.create!(label: label) }
|
1690
|
+
|
1691
|
+
let(:plucked) do
|
1692
|
+
Band.where(_id: band.id).tap do |crit|
|
1693
|
+
crit.documents = [band]
|
1694
|
+
end.pluck("label.sales")
|
1695
|
+
end
|
1696
|
+
|
1697
|
+
it "demongoizes the field" do
|
1698
|
+
expect(plucked).to eq([ BigDecimal("1E2") ])
|
1699
|
+
end
|
1700
|
+
end
|
1701
|
+
|
1702
|
+
context "when plucking an embeds_many field" do
|
1703
|
+
let(:label) { Label.new(sales: "1E2") }
|
1704
|
+
let!(:band) { Band.create!(labels: [label]) }
|
1705
|
+
|
1706
|
+
let(:plucked) { Band.where(_id: band.id).pluck("labels.sales") }
|
1707
|
+
|
1708
|
+
it "demongoizes the field" do
|
1709
|
+
expect(plucked.first).to eq([ BigDecimal("1E2") ])
|
1710
|
+
end
|
1711
|
+
end
|
1712
|
+
|
1713
|
+
context "when plucking a nonexistent embedded field" do
|
1714
|
+
let(:label) { Label.new(sales: "1E2") }
|
1715
|
+
let!(:band) { Band.create!(label: label) }
|
1716
|
+
|
1717
|
+
let(:plucked) do
|
1718
|
+
Band.where(_id: band.id).tap do |crit|
|
1719
|
+
crit.documents = band
|
1720
|
+
end.pluck("label.qwerty")
|
1721
|
+
end
|
1722
|
+
|
1723
|
+
it "returns nil" do
|
1724
|
+
expect(plucked.first).to eq(nil)
|
1725
|
+
end
|
1726
|
+
end
|
1727
|
+
|
1728
|
+
context "when tallying deeply nested arrays/embedded associations" do
|
1729
|
+
|
1730
|
+
let(:criteria) do
|
1731
|
+
Person.all.tap do |crit|
|
1732
|
+
crit.documents = [
|
1733
|
+
Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ]),
|
1734
|
+
Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 2 } } ]))) ]),
|
1735
|
+
Person.create!(addresses: [ Address.new(code: Code.new(deepest: Deepest.new(array: [ { y: { z: 1 } }, { y: { z: 3 } } ]))) ]),
|
1736
|
+
]
|
1737
|
+
end
|
1738
|
+
end
|
1739
|
+
|
1740
|
+
let(:plucked) do
|
1741
|
+
context.pluck("addresses.code.deepest.array.y.z")
|
1742
|
+
end
|
1743
|
+
|
1744
|
+
it "returns the correct hash" do
|
1745
|
+
expect(plucked).to eq([
|
1746
|
+
[ [ 1, 2 ] ], [ [ 1, 2 ] ], [ [ 1, 3 ] ]
|
1747
|
+
])
|
1748
|
+
end
|
988
1749
|
end
|
989
1750
|
end
|
990
1751
|
end
|