mongoid 7.1.1 → 7.1.2

Sign up to get free protection for your applications and to get access to all the features.
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