mongoid 7.1.1 → 7.1.2

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  5. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  6. data/lib/mongoid/association/proxy.rb +1 -1
  7. data/lib/mongoid/atomic.rb +13 -3
  8. data/lib/mongoid/criteria.rb +7 -1
  9. data/lib/mongoid/criteria/modifiable.rb +2 -1
  10. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  11. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +3 -3
  12. data/lib/mongoid/criteria/queryable/mergeable.rb +75 -8
  13. data/lib/mongoid/criteria/queryable/selectable.rb +28 -8
  14. data/lib/mongoid/extensions/hash.rb +4 -2
  15. data/lib/mongoid/extensions/regexp.rb +1 -1
  16. data/lib/mongoid/fields.rb +2 -1
  17. data/lib/mongoid/matchable/regexp.rb +2 -2
  18. data/lib/mongoid/persistable/pushable.rb +4 -1
  19. data/lib/mongoid/persistence_context.rb +6 -6
  20. data/lib/mongoid/query_cache.rb +2 -1
  21. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  22. data/lib/mongoid/version.rb +1 -1
  23. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  24. data/spec/integration/app_spec.rb +192 -0
  25. data/spec/integration/associations/embedded_spec.rb +54 -0
  26. data/spec/integration/criteria/logical_spec.rb +13 -0
  27. data/spec/lite_spec_helper.rb +11 -4
  28. data/spec/mongoid/association/embedded/embeds_many_models.rb +19 -0
  29. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  30. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  31. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +2 -1
  32. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  33. data/spec/mongoid/clients/options_spec.rb +2 -2
  34. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  35. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  36. data/spec/mongoid/clients_spec.rb +2 -2
  37. data/spec/mongoid/contextual/atomic_spec.rb +22 -11
  38. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  39. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  40. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  41. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  42. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +45 -12
  43. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +480 -198
  44. data/spec/mongoid/criteria_spec.rb +4 -2
  45. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  46. data/spec/mongoid/indexable_spec.rb +6 -4
  47. data/spec/mongoid/matchable/default_spec.rb +1 -1
  48. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  49. data/spec/mongoid/matchable_spec.rb +2 -2
  50. data/spec/mongoid/query_cache_spec.rb +2 -1
  51. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  52. data/spec/mongoid/scopable_spec.rb +2 -1
  53. data/spec/mongoid/shardable_models.rb +1 -1
  54. data/spec/mongoid/shardable_spec.rb +2 -2
  55. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  56. data/spec/mongoid/tasks/database_spec.rb +1 -1
  57. data/spec/spec_helper.rb +0 -31
  58. data/spec/support/child_process_helper.rb +76 -0
  59. data/spec/support/cluster_config.rb +3 -3
  60. data/spec/support/constraints.rb +26 -10
  61. data/spec/support/spec_config.rb +12 -4
  62. metadata +8 -2
  63. metadata.gz.sig +0 -0
@@ -11,7 +11,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
11
11
  context "when provided a regexp" do
12
12
 
13
13
  let(:regexp) do
14
- /^[123]/
14
+ /\A[123]/
15
15
  end
16
16
 
17
17
  let(:evolved) do
@@ -26,7 +26,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
26
26
  context "when provided a string" do
27
27
 
28
28
  let(:regexp) do
29
- "^[123]"
29
+ "\\A[123]"
30
30
  end
31
31
 
32
32
  let(:evolved) do
@@ -34,7 +34,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
34
34
  end
35
35
 
36
36
  it "returns the converted regexp" do
37
- expect(evolved).to eq(/^[123]/)
37
+ expect(evolved).to eq(/\A[123]/)
38
38
  end
39
39
  end
40
40
 
@@ -43,7 +43,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
43
43
  context "when the elements are regexps" do
44
44
 
45
45
  let(:regexp) do
46
- /^[123]/
46
+ /\A[123]/
47
47
  end
48
48
 
49
49
  let(:array) do
@@ -66,7 +66,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
66
66
  context "when the elements are strings" do
67
67
 
68
68
  let(:regexp) do
69
- "^[123]"
69
+ "\\A[123]"
70
70
  end
71
71
 
72
72
  let(:evolved) do
@@ -74,7 +74,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
74
74
  end
75
75
 
76
76
  it "returns the regexps" do
77
- expect(evolved).to eq([ /^[123]/ ])
77
+ expect(evolved).to eq([ /\A[123]/ ])
78
78
  end
79
79
  end
80
80
  end
@@ -83,7 +83,7 @@ describe Mongoid::Criteria::Queryable::Extensions::Regexp do
83
83
  describe "#regexp?" do
84
84
 
85
85
  let(:regexp) do
86
- /^[123]/
86
+ /\A[123]/
87
87
  end
88
88
 
89
89
  it "returns true" do
@@ -318,7 +318,7 @@ describe String do
318
318
  context "when provided a regex" do
319
319
 
320
320
  let(:regex) do
321
- /^[123]/
321
+ /\A[123]/.freeze
322
322
  end
323
323
 
324
324
  let(:evolved) do
@@ -5,11 +5,11 @@ require "spec_helper"
5
5
 
6
6
  describe Mongoid::Criteria::Queryable::Mergeable do
7
7
 
8
- describe "#intersect" do
8
+ let(:query) do
9
+ Mongoid::Query.new
10
+ end
9
11
 
10
- let(:query) do
11
- Mongoid::Query.new
12
- end
12
+ describe "#intersect" do
13
13
 
14
14
  before do
15
15
  query.intersect
@@ -22,10 +22,6 @@ describe Mongoid::Criteria::Queryable::Mergeable do
22
22
 
23
23
  describe "#override" do
24
24
 
25
- let(:query) do
26
- Mongoid::Query.new
27
- end
28
-
29
25
  before do
30
26
  query.override
31
27
  end
@@ -37,10 +33,6 @@ describe Mongoid::Criteria::Queryable::Mergeable do
37
33
 
38
34
  describe "#union" do
39
35
 
40
- let(:query) do
41
- Mongoid::Query.new
42
- end
43
-
44
36
  before do
45
37
  query.union
46
38
  end
@@ -49,4 +41,45 @@ describe Mongoid::Criteria::Queryable::Mergeable do
49
41
  expect(query.strategy).to eq(:__union__)
50
42
  end
51
43
  end
44
+
45
+ describe '#_mongoid_expand_keys' do
46
+ it 'expands simple keys' do
47
+ query.send(:_mongoid_expand_keys, {a: 1}).should == {a: 1}
48
+ end
49
+
50
+ let(:gt) do
51
+ Mongoid::Criteria::Queryable::Key.new("age", :__override__, "$gt")
52
+ end
53
+
54
+ let(:gtp) do
55
+ Mongoid::Criteria::Queryable::Key.new("age", :__override__, "$gt")
56
+ end
57
+
58
+ let(:lt) do
59
+ Mongoid::Criteria::Queryable::Key.new("age", :__override__, "$lt")
60
+ end
61
+
62
+ it 'expands Key instances' do
63
+ query.send(:_mongoid_expand_keys, {gt => 42}).should == {'age' => {'$gt' => 42}}
64
+ end
65
+
66
+ it 'expands multiple Key instances on the same field' do
67
+ query.send(:_mongoid_expand_keys, {gt => 42, lt => 50}).should == {
68
+ 'age' => {'$gt' => 42, '$lt' => 50}}
69
+ end
70
+
71
+ it 'expands simple and Key instances on the same field' do
72
+ query.send(:_mongoid_expand_keys, {'age' => 42, lt => 50}).should == {
73
+ 'age' => {'$eq' => 42, '$lt' => 50}}
74
+ end
75
+
76
+ it 'expands Key and simple instances on the same field' do
77
+ query.send(:_mongoid_expand_keys, {gt => 42, 'age' => 50}).should == {
78
+ 'age' => {'$gt' => 42, '$eq' => 50}}
79
+ end
80
+
81
+ it 'Ruby does not allow same symbol operator with different values' do
82
+ {gt => 42, gtp => 50}.should == {gtp => 50}
83
+ end
84
+ end
52
85
  end
@@ -85,7 +85,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
85
85
 
86
86
  it "adds the conditions to top level" do
87
87
  expect(selection.selector).to eq({
88
- "field" => {'$gt' => 3 },
88
+ "field" => {'$gt' => 3},
89
89
  })
90
90
  end
91
91
 
@@ -113,6 +113,54 @@ describe Mongoid::Criteria::Queryable::Selectable do
113
113
 
114
114
  it_behaves_like 'adds the conditions to top level'
115
115
  end
116
+
117
+ context 'when the criterion is a time' do
118
+ let(:selection) do
119
+ query.send(tested_method, :field.gte => Time.new(2020, 1, 1))
120
+ end
121
+
122
+ it 'adds the conditions' do
123
+ expect(selection.selector).to eq({
124
+ "field" => {'$gte' => Time.new(2020, 1, 1)},
125
+ })
126
+ end
127
+
128
+ it 'keeps argument type' do
129
+ selection.selector['field']['$gte'].should be_a(Time)
130
+ end
131
+ end
132
+
133
+ context 'when the criterion is a datetime' do
134
+ let(:selection) do
135
+ query.send(tested_method, :field.gte => DateTime.new(2020, 1, 1))
136
+ end
137
+
138
+ it 'adds the conditions' do
139
+ expect(selection.selector).to eq({
140
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
141
+ })
142
+ end
143
+
144
+ it 'converts argument to a time' do
145
+ selection.selector['field']['$gte'].should be_a(Time)
146
+ end
147
+ end
148
+
149
+ context 'when the criterion is a date' do
150
+ let(:selection) do
151
+ query.send(tested_method, :field.gte => Date.new(2020, 1, 1))
152
+ end
153
+
154
+ it 'adds the conditions' do
155
+ expect(selection.selector).to eq({
156
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
157
+ })
158
+ end
159
+
160
+ it 'converts argument to a time' do
161
+ selection.selector['field']['$gte'].should be_a(Time)
162
+ end
163
+ end
116
164
  end
117
165
 
118
166
  context "when provided a nested criterion" do
@@ -283,25 +331,58 @@ describe Mongoid::Criteria::Queryable::Selectable do
283
331
 
284
332
  context "when provided multiple criteria" do
285
333
 
286
- context "when the criteria is already included" do
334
+ context "when the criterion is already included" do
287
335
 
288
- let(:selection) do
289
- query.and({ first: [ 1, 2 ] }).and({ first: [ 1, 2 ] })
336
+ context 'simple criterion' do
337
+ let(:selection) do
338
+ query.and({ first: [ 1, 2 ] }).and({ first: [ 1, 2 ] })
339
+ end
340
+
341
+ it "adds all conditions" do
342
+ expect(selection.selector).to eq({
343
+ 'first' => [1, 2],
344
+ "$and" => [
345
+ { "first" => [ 1, 2 ] }
346
+ ]
347
+ })
348
+ end
349
+
350
+ it_behaves_like 'returns a cloned query'
290
351
  end
291
352
 
292
- it "adds all conditions" do
293
- expect(selection.selector).to eq({
294
- 'first' => [1, 2],
295
- "$and" => [
296
- { "first" => [ 1, 2 ] }
297
- ]
298
- })
353
+ context 'Key criterion' do
354
+ let(:selection) do
355
+ query.and({ first: [ 1, 2 ] }).and(:first.gt => 3)
356
+ end
357
+
358
+ it "adds all conditions" do
359
+ expect(selection.selector).to eq({
360
+ 'first' => [1, 2],
361
+ "$and" => [
362
+ { "first" => {'$gt' => 3} }
363
+ ]
364
+ })
365
+ end
366
+
367
+ it_behaves_like 'returns a cloned query'
299
368
  end
300
369
 
301
- it_behaves_like 'returns a cloned query'
370
+ context 'Key criterion when existing criterion is an operator' do
371
+ let(:selection) do
372
+ query.and(:first.lt => 5).and(:first.gt => 3)
373
+ end
374
+
375
+ it "adds all conditions" do
376
+ expect(selection.selector).to eq({
377
+ 'first' => {'$lt' => 5, '$gt' => 3},
378
+ })
379
+ end
380
+
381
+ it_behaves_like 'returns a cloned query'
382
+ end
302
383
  end
303
384
 
304
- context "when the new criterion is for different fields" do
385
+ context "when the new criteria are for different fields" do
305
386
 
306
387
  let(:selection) do
307
388
  query.and({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
@@ -317,22 +398,137 @@ describe Mongoid::Criteria::Queryable::Selectable do
317
398
  it_behaves_like 'returns a cloned query'
318
399
  end
319
400
 
320
- context "when the new criterion is for the same field" do
401
+ context "when the new criteria are for the same field" do
321
402
 
322
- let(:selection) do
323
- query.and({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
403
+ context 'when criteria are simple' do
404
+ let(:selection) do
405
+ query.and({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
406
+ end
407
+
408
+ it "combines via $and operator" do
409
+ expect(selection.selector).to eq({
410
+ "first" => [ 1, 2 ],
411
+ "$and" => [
412
+ { "first" => [ 3, 4 ] }
413
+ ]
414
+ })
415
+ end
416
+
417
+ it_behaves_like 'returns a cloned query'
324
418
  end
325
419
 
326
- it "combines via $and operator" do
327
- expect(selection.selector).to eq({
328
- "first" => [ 1, 2 ],
329
- "$and" => [
330
- { "first" => [ 3, 4 ] }
331
- ]
332
- })
420
+ context 'when criteria are handled via Key' do
421
+ shared_examples_for 'adds the conditions to top level' do
422
+
423
+ it "adds the conditions to top level" do
424
+ expect(selection.selector).to eq({
425
+ "field" => {'$gt' => 3, '$lt' => 5},
426
+ })
427
+ end
428
+
429
+ it_behaves_like 'returns a cloned query'
430
+ end
431
+
432
+ context 'criteria are provided in the same hash' do
433
+ let(:selection) do
434
+ query.send(tested_method, :field.gt => 3, :field.lt => 5)
435
+ end
436
+
437
+ it_behaves_like 'adds the conditions to top level'
438
+ end
439
+
440
+ context 'criteria are provided in separate hashes' do
441
+ let(:selection) do
442
+ query.send(tested_method, {:field.gt => 3}, {:field.lt => 5})
443
+ end
444
+
445
+ it_behaves_like 'adds the conditions to top level'
446
+ end
447
+
448
+ context 'when the criterion is wrapped in an array' do
449
+ let(:selection) do
450
+ query.send(tested_method, [:field.gt => 3], [:field.lt => 5])
451
+ end
452
+
453
+ it_behaves_like 'adds the conditions to top level'
454
+ end
333
455
  end
334
456
 
335
- it_behaves_like 'returns a cloned query'
457
+ context 'when criteria are simple and handled via Key' do
458
+ shared_examples_for 'combines conditions with $and' do
459
+
460
+ it "combines conditions with $and" do
461
+ expect(selection.selector).to eq({
462
+ "field" => 3,
463
+ '$and' => ['field' => {'$lt' => 5}],
464
+ })
465
+ end
466
+
467
+ it_behaves_like 'returns a cloned query'
468
+ end
469
+
470
+ context 'criteria are provided in the same hash' do
471
+ let(:selection) do
472
+ query.send(tested_method, :field => 3, :field.lt => 5)
473
+ end
474
+
475
+ it_behaves_like 'combines conditions with $and'
476
+ end
477
+
478
+ context 'criteria are provided in separate hashes' do
479
+ let(:selection) do
480
+ query.send(tested_method, {:field => 3}, {:field.lt => 5})
481
+ end
482
+
483
+ it_behaves_like 'combines conditions with $and'
484
+ end
485
+
486
+ context 'when the criterion is wrapped in an array' do
487
+ let(:selection) do
488
+ query.send(tested_method, [:field => 3], [:field.lt => 5])
489
+ end
490
+
491
+ it_behaves_like 'combines conditions with $and'
492
+ end
493
+ end
494
+
495
+ context 'when criteria are handled via Key and simple' do
496
+ shared_examples_for 'combines conditions with $and' do
497
+
498
+ it "combines conditions with $and" do
499
+ expect(selection.selector).to eq({
500
+ "field" => {'$gt' => 3},
501
+ '$and' => ['field' => 5],
502
+ })
503
+ end
504
+
505
+ it_behaves_like 'returns a cloned query'
506
+ end
507
+
508
+ context 'criteria are provided in the same hash' do
509
+ let(:selection) do
510
+ query.send(tested_method, :field.gt => 3, :field => 5)
511
+ end
512
+
513
+ it_behaves_like 'combines conditions with $and'
514
+ end
515
+
516
+ context 'criteria are provided in separate hashes' do
517
+ let(:selection) do
518
+ query.send(tested_method, {:field.gt => 3}, {:field => 5})
519
+ end
520
+
521
+ it_behaves_like 'combines conditions with $and'
522
+ end
523
+
524
+ context 'when the criterion is wrapped in an array' do
525
+ let(:selection) do
526
+ query.send(tested_method, [:field.gt => 3], [:field => 5])
527
+ end
528
+
529
+ it_behaves_like 'combines conditions with $and'
530
+ end
531
+ end
336
532
  end
337
533
  end
338
534
 
@@ -509,17 +705,14 @@ describe Mongoid::Criteria::Queryable::Selectable do
509
705
  end
510
706
  end
511
707
 
512
- describe "#or" do
513
-
514
- let(:tested_method) { :or }
515
- let(:expected_operator) { '$or' }
708
+ shared_examples '$or/$nor' do
516
709
 
517
710
  it_behaves_like 'a non-hoisting logical operation'
518
711
 
519
712
  context "when provided no arguments" do
520
713
 
521
714
  let(:selection) do
522
- query.or
715
+ query.send(tested_method)
523
716
  end
524
717
 
525
718
  it_behaves_like 'returns a cloned query'
@@ -536,7 +729,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
536
729
  context "when provided nil" do
537
730
 
538
731
  let(:selection) do
539
- query.or(nil)
732
+ query.send(tested_method, nil)
540
733
  end
541
734
 
542
735
  it_behaves_like 'returns a cloned query'
@@ -553,42 +746,42 @@ describe Mongoid::Criteria::Queryable::Selectable do
553
746
  context "when provided a single criterion" do
554
747
 
555
748
  let(:selection) do
556
- query.or(field: [ 1, 2 ])
749
+ query.send(tested_method, field: [ 1, 2 ])
557
750
  end
558
751
 
559
752
  it_behaves_like 'returns a cloned query'
560
753
 
561
- it "adds the $or selector" do
754
+ it "adds the $or/$nor selector" do
562
755
  expect(selection.selector).to eq({
563
- "$or" => [{ "field" => [ 1, 2 ] }]
756
+ expected_operator => [{ "field" => [ 1, 2 ] }]
564
757
  })
565
758
  end
566
759
 
567
760
  context 'when the criterion is wrapped in array' do
568
761
 
569
762
  let(:selection) do
570
- query.or([{ field: [ 1, 2 ] }])
763
+ query.send(tested_method, [{ field: [ 1, 2 ] }])
571
764
  end
572
765
 
573
766
  it_behaves_like 'returns a cloned query'
574
767
 
575
- it "adds the $or selector" do
768
+ it "adds the $or/$nor selector" do
576
769
  expect(selection.selector).to eq({
577
- "$or" => [{ "field" => [ 1, 2 ] }]
770
+ expected_operator => [{ "field" => [ 1, 2 ] }]
578
771
  })
579
772
  end
580
773
 
581
774
  context 'when the array has nil as one of the elements' do
582
775
 
583
776
  let(:selection) do
584
- query.or([{ field: [ 1, 2 ] }, nil])
777
+ query.send(tested_method, [{ field: [ 1, 2 ] }, nil])
585
778
  end
586
779
 
587
780
  it_behaves_like 'returns a cloned query'
588
781
 
589
- it "adds the $or selector ignoring the nil element" do
782
+ it "adds the $or/$nor selector ignoring the nil element" do
590
783
  expect(selection.selector).to eq({
591
- "$or" => [{ "field" => [ 1, 2 ] }]
784
+ expected_operator => [{ "field" => [ 1, 2 ] }]
592
785
  })
593
786
  end
594
787
  end
@@ -597,27 +790,27 @@ describe Mongoid::Criteria::Queryable::Selectable do
597
790
  context 'when query already has a condition on another field' do
598
791
 
599
792
  let(:selection) do
600
- query.where(foo: 'bar').or(field: [ 1, 2 ])
793
+ query.where(foo: 'bar').send(tested_method, field: [ 1, 2 ])
601
794
  end
602
795
 
603
- it 'moves original conditions under $or' do
796
+ it 'moves original conditions under $or/$nor' do
604
797
  expect(selection.selector).to eq({
605
- "$or" => [{'foo' => 'bar'}, { "field" => [ 1, 2 ] }]
798
+ expected_operator => [{'foo' => 'bar'}, { "field" => [ 1, 2 ] }]
606
799
  })
607
800
  end
608
801
  end
609
802
 
610
- context 'when query already has an $or condition and another condition' do
803
+ context 'when query already has an $or/$nor condition and another condition' do
611
804
 
612
805
  let(:selection) do
613
- query.or(field: [ 1, 2 ]).where(foo: 'bar').or(test: 1)
806
+ query.send(tested_method, field: [ 1, 2 ]).where(foo: 'bar').send(tested_method, test: 1)
614
807
  end
615
808
 
616
809
  it 'unions existing conditions' do
617
810
  expect(selection.selector).to eq(
618
- '$or' => [
811
+ expected_operator => [
619
812
  {
620
- "$or" => [{ "field" => [ 1, 2 ] }],
813
+ expected_operator => [{ "field" => [ 1, 2 ] }],
621
814
  'foo' => 'bar',
622
815
  },
623
816
  {'test' => 1},
@@ -632,14 +825,14 @@ describe Mongoid::Criteria::Queryable::Selectable do
632
825
  context "when the criteria are for different fields" do
633
826
 
634
827
  let(:selection) do
635
- query.or({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
828
+ query.send(tested_method, { first: [ 1, 2 ] }, { second: [ 3, 4 ] })
636
829
  end
637
830
 
638
831
  it_behaves_like 'returns a cloned query'
639
832
 
640
- it "adds the $or selector" do
833
+ it "adds the $or/$nor selector" do
641
834
  expect(selection.selector).to eq({
642
- "$or" => [
835
+ expected_operator => [
643
836
  { "first" => [ 1, 2 ] },
644
837
  { "second" => [ 3, 4 ] }
645
838
  ]
@@ -650,12 +843,12 @@ describe Mongoid::Criteria::Queryable::Selectable do
650
843
  context "when the criteria uses a Key instance" do
651
844
 
652
845
  let(:selection) do
653
- query.or({ first: [ 1, 2 ] }, { :second.gt => 3 })
846
+ query.send(tested_method, { first: [ 1, 2 ] }, { :second.gt => 3 })
654
847
  end
655
848
 
656
- it "adds the $or selector" do
849
+ it "adds the $or/$nor selector" do
657
850
  expect(selection.selector).to eq({
658
- "$or" => [
851
+ expected_operator => [
659
852
  { "first" => [ 1, 2 ] },
660
853
  { "second" => { "$gt" => 3 }}
661
854
  ]
@@ -663,233 +856,197 @@ describe Mongoid::Criteria::Queryable::Selectable do
663
856
  end
664
857
 
665
858
  it_behaves_like 'returns a cloned query'
666
- end
667
859
 
668
- context "when a criterion has an aliased field" do
860
+ context 'when the criterion is a time' do
861
+ let(:selection) do
862
+ query.send(tested_method, :field.gte => Time.new(2020, 1, 1))
863
+ end
669
864
 
670
- let(:selection) do
671
- query.or({ id: 1 })
672
- end
865
+ it 'adds the conditions' do
866
+ expect(selection.selector).to eq(expected_operator => [
867
+ "field" => {'$gte' => Time.new(2020, 1, 1)},
868
+ ])
869
+ end
673
870
 
674
- it "adds the $or selector and aliases the field" do
675
- expect(selection.selector).to eq({
676
- "$or" => [ { "_id" => 1 } ]
677
- })
871
+ it 'keeps the type' do
872
+ selection.selector[expected_operator].first['field']['$gte'].should be_a(Time)
873
+ end
678
874
  end
679
875
 
680
- it_behaves_like 'returns a cloned query'
681
- end
876
+ context 'when the criterion is a datetime' do
877
+ let(:selection) do
878
+ query.send(tested_method, :field.gte => DateTime.new(2020, 1, 1))
879
+ end
682
880
 
683
- context "when a criterion is wrapped in an array" do
881
+ it 'adds the conditions' do
882
+ expect(selection.selector).to eq(expected_operator => [
883
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
884
+ ])
885
+ end
684
886
 
685
- let(:selection) do
686
- query.or([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
887
+ it 'converts argument to a time' do
888
+ selection.selector[expected_operator].first['field']['$gte'].should be_a(Time)
889
+ end
687
890
  end
688
891
 
689
- it_behaves_like 'returns a cloned query'
892
+ context 'when the criterion is a date' do
893
+ let(:selection) do
894
+ query.send(tested_method, :field.gte => Date.new(2020, 1, 1))
895
+ end
690
896
 
691
- it "adds the $or selector" do
692
- expect(selection.selector).to eq({
693
- "$or" => [
694
- { "first" => [ 1, 2 ] },
695
- { "second" => { "$gt" => 3 }}
696
- ]
697
- })
897
+ it 'adds the conditions' do
898
+ expect(selection.selector).to eq(expected_operator => [
899
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
900
+ ])
901
+ end
902
+
903
+ it 'converts argument to a time' do
904
+ selection.selector[expected_operator].first['field']['$gte'].should be_a(Time)
905
+ end
698
906
  end
699
907
  end
700
908
 
701
- context "when the criteria are on the same field" do
909
+ context "when a criterion has an aliased field" do
702
910
 
703
911
  let(:selection) do
704
- query.or({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
912
+ query.send(tested_method, { id: 1 })
705
913
  end
706
914
 
707
- it_behaves_like 'returns a cloned query'
708
-
709
- it "appends both $or expressions" do
915
+ it "adds the $or/$nor selector and aliases the field" do
710
916
  expect(selection.selector).to eq({
711
- "$or" => [
712
- { "first" => [ 1, 2 ] },
713
- { "first" => [ 3, 4 ] }
714
- ]
917
+ expected_operator => [ { "_id" => 1 } ]
715
918
  })
716
919
  end
717
- end
718
- end
719
-
720
- context "when chaining the criterion" do
721
-
722
- context "when the criterion are for different fields" do
723
-
724
- let(:selection) do
725
- query.or(first: [ 1, 2 ]).or(second: [ 3, 4 ])
726
- end
727
920
 
728
921
  it_behaves_like 'returns a cloned query'
729
-
730
- it "adds the $or selectors" do
731
- expect(selection.selector).to eq({
732
- "$or" => [
733
- { "first" => [ 1, 2 ] },
734
- { "second" => [ 3, 4 ] }
735
- ]
736
- })
737
- end
738
922
  end
739
923
 
740
- context "when the criterion are on the same field" do
924
+ context "when a criterion is wrapped in an array" do
741
925
 
742
926
  let(:selection) do
743
- query.or(first: [ 1, 2 ]).or(first: [ 3, 4 ])
927
+ query.send(tested_method, [{ first: [ 1, 2 ] }, { :second.gt => 3 }])
744
928
  end
745
929
 
746
930
  it_behaves_like 'returns a cloned query'
747
931
 
748
- it "appends both $or expressions" do
932
+ it "adds the $or/$nor selector" do
749
933
  expect(selection.selector).to eq({
750
- "$or" => [
934
+ expected_operator => [
751
935
  { "first" => [ 1, 2 ] },
752
- { "first" => [ 3, 4 ] }
936
+ { "second" => { "$gt" => 3 }}
753
937
  ]
754
938
  })
755
939
  end
756
940
  end
757
- end
758
- end
759
941
 
760
- describe "#nor" do
761
-
762
- let(:tested_method) { :nor }
763
- let(:expected_operator) { '$nor' }
764
-
765
- it_behaves_like 'a non-hoisting logical operation'
766
-
767
- context "when provided no criterion" do
768
-
769
- let(:selection) do
770
- query.nor
771
- end
772
-
773
- it "does not add any criterion" do
774
- expect(selection.selector).to eq({})
775
- end
776
-
777
- it "returns the query" do
778
- expect(selection).to eq(query)
779
- end
942
+ context "when the criteria are on the same field" do
780
943
 
781
- it_behaves_like 'returns a cloned query'
782
- end
944
+ context 'simple criteria' do
945
+ let(:selection) do
946
+ query.send(tested_method, { first: [ 1, 2 ] }, { first: [ 3, 4 ] })
947
+ end
783
948
 
784
- context "when provided nil" do
949
+ it_behaves_like 'returns a cloned query'
785
950
 
786
- let(:selection) do
787
- query.nor(nil)
788
- end
951
+ it "appends both $or/$nor expressions" do
952
+ expect(selection.selector).to eq({
953
+ expected_operator => [
954
+ { "first" => [ 1, 2 ] },
955
+ { "first" => [ 3, 4 ] }
956
+ ]
957
+ })
958
+ end
959
+ end
789
960
 
790
- it "does not add any criterion" do
791
- expect(selection.selector).to eq({})
792
- end
961
+ context 'Key criteria as one argument' do
962
+ let(:selection) do
963
+ query.send(tested_method, :first.gt => 3, :first.lt => 5)
964
+ end
793
965
 
794
- it "returns the query" do
795
- expect(selection).to eq(query)
796
- end
966
+ it_behaves_like 'returns a cloned query'
797
967
 
798
- it_behaves_like 'returns a cloned query'
799
- end
968
+ it "adds all criteria" do
969
+ expect(selection.selector).to eq({
970
+ expected_operator => [
971
+ { "first" => {'$gt' => 3, '$lt' => 5} },
972
+ ]
973
+ })
974
+ end
975
+ end
800
976
 
801
- context "when provided a single criterion" do
977
+ context 'Key criteria as multiple arguments' do
978
+ let(:selection) do
979
+ query.send(tested_method, {:first.gt => 3}, {:first.lt => 5})
980
+ end
802
981
 
803
- let(:selection) do
804
- query.nor(field: [ 1, 2 ])
805
- end
982
+ it_behaves_like 'returns a cloned query'
806
983
 
807
- it "adds the $nor selector" do
808
- expect(selection.selector).to eq({
809
- "$nor" => [{"field" => [ 1, 2 ] }]
810
- })
984
+ it "adds all criteria" do
985
+ expect(selection.selector).to eq({
986
+ expected_operator => [
987
+ { "first" => {'$gt' => 3} },
988
+ { "first" => {'$lt' => 5} },
989
+ ]
990
+ })
991
+ end
992
+ end
811
993
  end
812
-
813
- it_behaves_like 'returns a cloned query'
814
994
  end
815
995
 
816
- context "when provided multiple criterion" do
996
+ context "when chaining the criterion" do
817
997
 
818
- context "when the criterion are fnor different fields" do
998
+ context "when the criterion are for different fields" do
819
999
 
820
1000
  let(:selection) do
821
- query.nor({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
1001
+ query.send(tested_method, first: [ 1, 2 ]).send(tested_method, second: [ 3, 4 ])
822
1002
  end
823
1003
 
824
- it "adds the $nor selector" do
1004
+ it_behaves_like 'returns a cloned query'
1005
+
1006
+ it "adds the $or/$nor selectors" do
825
1007
  expect(selection.selector).to eq({
826
- "$nor" => [
1008
+ expected_operator => [
827
1009
  { "first" => [ 1, 2 ] },
828
1010
  { "second" => [ 3, 4 ] }
829
1011
  ]
830
1012
  })
831
1013
  end
832
-
833
- it_behaves_like 'returns a cloned query'
834
1014
  end
835
1015
 
836
1016
  context "when the criterion are on the same field" do
837
1017
 
838
1018
  let(:selection) do
839
- query.nor({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
1019
+ query.send(tested_method, first: [ 1, 2 ]).send(tested_method, first: [ 3, 4 ])
840
1020
  end
841
1021
 
842
- it "appends both $nor expressions" do
1022
+ it_behaves_like 'returns a cloned query'
1023
+
1024
+ it "appends both $or/$nor expressions" do
843
1025
  expect(selection.selector).to eq({
844
- "$nor" => [
1026
+ expected_operator => [
845
1027
  { "first" => [ 1, 2 ] },
846
1028
  { "first" => [ 3, 4 ] }
847
1029
  ]
848
1030
  })
849
1031
  end
850
-
851
- it_behaves_like 'returns a cloned query'
852
1032
  end
853
1033
  end
1034
+ end
854
1035
 
855
- context "when chaining the criterion" do
856
-
857
- context "when the criterion are fnor different fields" do
858
-
859
- let(:selection) do
860
- query.nor(first: [ 1, 2 ]).nor(second: [ 3, 4 ])
861
- end
862
-
863
- it "adds the $nor selectors" do
864
- expect(selection.selector).to eq({
865
- "$nor" => [
866
- { "first" => [ 1, 2 ] },
867
- { "second" => [ 3, 4 ] }
868
- ]
869
- })
870
- end
1036
+ describe "#or" do
871
1037
 
872
- it_behaves_like 'returns a cloned query'
873
- end
1038
+ let(:tested_method) { :or }
1039
+ let(:expected_operator) { '$or' }
874
1040
 
875
- context "when the criterion are on the same field" do
1041
+ it_behaves_like '$or/$nor'
1042
+ end
876
1043
 
877
- let(:selection) do
878
- query.nor(first: [ 1, 2 ]).nor(first: [ 3, 4 ])
879
- end
1044
+ describe "#nor" do
880
1045
 
881
- it "appends both $nor expressions" do
882
- expect(selection.selector).to eq({
883
- "$nor" => [
884
- { "first" => [ 1, 2 ] },
885
- { "first" => [ 3, 4 ] }
886
- ]
887
- })
888
- end
1046
+ let(:tested_method) { :nor }
1047
+ let(:expected_operator) { '$nor' }
889
1048
 
890
- it_behaves_like 'returns a cloned query'
891
- end
892
- end
1049
+ it_behaves_like '$or/$nor'
893
1050
  end
894
1051
 
895
1052
  describe "#any_of" do
@@ -1098,6 +1255,112 @@ describe Mongoid::Criteria::Queryable::Selectable do
1098
1255
  it_behaves_like 'returns a cloned query'
1099
1256
  end
1100
1257
 
1258
+ context 'when criteria are simple and handled via Key' do
1259
+ shared_examples_for 'adds conditions with $or' do
1260
+
1261
+ it "adds conditions with $or" do
1262
+ expect(selection.selector).to eq({
1263
+ '$or' => [
1264
+ {'field' => 3},
1265
+ {'field' => {'$lt' => 5}},
1266
+ ],
1267
+ })
1268
+ end
1269
+
1270
+ it_behaves_like 'returns a cloned query'
1271
+ end
1272
+
1273
+ shared_examples_for 'adds one condition' do
1274
+
1275
+ it "adds one condition" do
1276
+ expect(selection.selector).to eq({
1277
+ 'field' => 3,
1278
+ '$and' => [
1279
+ {'field' => {'$lt' => 5}},
1280
+ ],
1281
+ })
1282
+ end
1283
+
1284
+ it_behaves_like 'returns a cloned query'
1285
+ end
1286
+
1287
+ context 'criteria are provided in the same hash' do
1288
+ let(:selection) do
1289
+ query.send(tested_method, :field => 3, :field.lt => 5)
1290
+ end
1291
+
1292
+ it_behaves_like 'adds one condition'
1293
+ end
1294
+
1295
+ context 'criteria are provided in separate hashes' do
1296
+ let(:selection) do
1297
+ query.send(tested_method, {:field => 3}, {:field.lt => 5})
1298
+ end
1299
+
1300
+ it_behaves_like 'adds conditions with $or'
1301
+ end
1302
+
1303
+ context 'when the criterion is wrapped in an array' do
1304
+ let(:selection) do
1305
+ query.send(tested_method, [:field => 3], [:field.lt => 5])
1306
+ end
1307
+
1308
+ it_behaves_like 'adds conditions with $or'
1309
+ end
1310
+ end
1311
+
1312
+ context 'when criteria are handled via Key and simple' do
1313
+ shared_examples_for 'adds conditions with $or' do
1314
+
1315
+ it "adds conditions with $or" do
1316
+ expect(selection.selector).to eq({
1317
+ '$or' => [
1318
+ {'field' => {'$gt' => 3}},
1319
+ {'field' => 5},
1320
+ ],
1321
+ })
1322
+ end
1323
+
1324
+ it_behaves_like 'returns a cloned query'
1325
+ end
1326
+
1327
+ shared_examples_for 'adds one condition' do
1328
+
1329
+ it "adds one condition" do
1330
+ expect(selection.selector).to eq({
1331
+ 'field' => {'$gt' => 3},
1332
+ '$and' => ['field' => 5],
1333
+ })
1334
+ end
1335
+
1336
+ it_behaves_like 'returns a cloned query'
1337
+ end
1338
+
1339
+ context 'criteria are provided in the same hash' do
1340
+ let(:selection) do
1341
+ query.send(tested_method, :field.gt => 3, :field => 5)
1342
+ end
1343
+
1344
+ it_behaves_like 'adds one condition'
1345
+ end
1346
+
1347
+ context 'criteria are provided in separate hashes' do
1348
+ let(:selection) do
1349
+ query.send(tested_method, {:field.gt => 3}, {:field => 5})
1350
+ end
1351
+
1352
+ it_behaves_like 'adds conditions with $or'
1353
+ end
1354
+
1355
+ context 'when the criterion is wrapped in an array' do
1356
+ let(:selection) do
1357
+ query.send(tested_method, [:field.gt => 3], [:field => 5])
1358
+ end
1359
+
1360
+ it_behaves_like 'adds conditions with $or'
1361
+ end
1362
+ end
1363
+
1101
1364
  context "when a criterion has an aliased field" do
1102
1365
 
1103
1366
  let(:selection) do
@@ -1271,6 +1534,25 @@ describe Mongoid::Criteria::Queryable::Selectable do
1271
1534
  end
1272
1535
  end
1273
1536
 
1537
+ context "when the criteria uses Key" do
1538
+
1539
+ let(:selection) do
1540
+ query.not(:age.gt => 50)
1541
+ end
1542
+
1543
+ it "negates the gt selection" do
1544
+ expect(selection.selector).to eq(
1545
+ '$and' => ['$nor' => ['age' => {'$gt' => 50}]]
1546
+ )
1547
+ end
1548
+
1549
+ it_behaves_like 'returns a cloned query'
1550
+
1551
+ it "removes the negation on the clone" do
1552
+ expect(selection).to_not be_negating
1553
+ end
1554
+ end
1555
+
1274
1556
  context "when the following criteria is a where" do
1275
1557
 
1276
1558
  let(:selection) do