mongoid 7.1.0.rc0 → 7.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +6 -6
  5. data/LICENSE +1 -1
  6. data/README.md +5 -5
  7. data/Rakefile +14 -0
  8. data/lib/config/locales/en.yml +5 -5
  9. data/lib/mongoid.rb +3 -2
  10. data/lib/mongoid/association/accessors.rb +37 -2
  11. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  13. data/lib/mongoid/association/many.rb +3 -2
  14. data/lib/mongoid/association/proxy.rb +6 -4
  15. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -1
  16. data/lib/mongoid/association/referenced/belongs_to/eager.rb +38 -2
  17. data/lib/mongoid/association/referenced/eager.rb +29 -9
  18. data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -22
  19. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -2
  20. data/lib/mongoid/atomic.rb +13 -3
  21. data/lib/mongoid/attributes.rb +28 -20
  22. data/lib/mongoid/clients/factory.rb +2 -2
  23. data/lib/mongoid/clients/options.rb +8 -8
  24. data/lib/mongoid/clients/sessions.rb +20 -4
  25. data/lib/mongoid/clients/storage_options.rb +5 -5
  26. data/lib/mongoid/config.rb +42 -12
  27. data/lib/mongoid/config/options.rb +5 -2
  28. data/lib/mongoid/contextual.rb +5 -4
  29. data/lib/mongoid/contextual/geo_near.rb +3 -2
  30. data/lib/mongoid/contextual/map_reduce.rb +3 -2
  31. data/lib/mongoid/contextual/mongo.rb +2 -1
  32. data/lib/mongoid/criteria.rb +23 -4
  33. data/lib/mongoid/criteria/modifiable.rb +2 -1
  34. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  35. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +6 -6
  36. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  37. data/lib/mongoid/criteria/queryable/mergeable.rb +75 -8
  38. data/lib/mongoid/criteria/queryable/pipeline.rb +3 -2
  39. data/lib/mongoid/criteria/queryable/selectable.rb +120 -13
  40. data/lib/mongoid/criteria/queryable/storable.rb +104 -99
  41. data/lib/mongoid/errors/eager_load.rb +2 -0
  42. data/lib/mongoid/errors/no_client_config.rb +2 -2
  43. data/lib/mongoid/errors/no_default_client.rb +1 -1
  44. data/lib/mongoid/extensions/hash.rb +4 -2
  45. data/lib/mongoid/extensions/regexp.rb +1 -1
  46. data/lib/mongoid/fields.rb +2 -1
  47. data/lib/mongoid/fields/standard.rb +2 -1
  48. data/lib/mongoid/fields/validators/macro.rb +4 -1
  49. data/lib/mongoid/findable.rb +5 -4
  50. data/lib/mongoid/interceptable.rb +5 -1
  51. data/lib/mongoid/matchable/regexp.rb +2 -2
  52. data/lib/mongoid/persistable/pushable.rb +11 -2
  53. data/lib/mongoid/persistence_context.rb +6 -6
  54. data/lib/mongoid/query_cache.rb +61 -18
  55. data/lib/mongoid/railties/database.rake +7 -0
  56. data/lib/mongoid/serializable.rb +10 -2
  57. data/lib/mongoid/shardable.rb +56 -4
  58. data/lib/mongoid/tasks/database.rake +10 -5
  59. data/lib/mongoid/tasks/database.rb +83 -0
  60. data/lib/mongoid/timestamps/timeless.rb +3 -1
  61. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  62. data/lib/mongoid/version.rb +1 -1
  63. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +32 -23
  64. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  65. data/spec/app/models/coding.rb +4 -0
  66. data/spec/app/models/coding/pull_request.rb +12 -0
  67. data/spec/app/models/delegating_patient.rb +16 -0
  68. data/spec/app/models/passport.rb +1 -0
  69. data/spec/app/models/phone.rb +1 -0
  70. data/spec/app/models/publication.rb +5 -0
  71. data/spec/app/models/publication/encyclopedia.rb +12 -0
  72. data/spec/app/models/publication/review.rb +14 -0
  73. data/spec/integration/app_spec.rb +254 -0
  74. data/spec/integration/associations/embedded_spec.rb +54 -0
  75. data/spec/integration/associations/has_many_spec.rb +34 -0
  76. data/spec/integration/associations/has_one_spec.rb +34 -0
  77. data/spec/integration/bson_regexp_raw_spec.rb +20 -0
  78. data/spec/integration/criteria/date_field_spec.rb +41 -0
  79. data/spec/integration/criteria/logical_spec.rb +13 -0
  80. data/spec/integration/document_spec.rb +22 -0
  81. data/spec/integration/shardable_spec.rb +149 -0
  82. data/spec/lite_spec_helper.rb +15 -4
  83. data/spec/mongoid/association/accessors_spec.rb +238 -63
  84. data/spec/mongoid/association/embedded/embeds_many_models.rb +19 -0
  85. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  86. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  87. data/spec/mongoid/association/referenced/belongs_to/eager_spec.rb +193 -10
  88. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  89. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +146 -68
  90. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  91. data/spec/mongoid/attributes_spec.rb +19 -7
  92. data/spec/mongoid/changeable_spec.rb +23 -0
  93. data/spec/mongoid/clients/factory_spec.rb +8 -8
  94. data/spec/mongoid/clients/options_spec.rb +11 -11
  95. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  96. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  97. data/spec/mongoid/clients_spec.rb +2 -2
  98. data/spec/mongoid/contextual/atomic_spec.rb +22 -11
  99. data/spec/mongoid/contextual/geo_near_spec.rb +11 -2
  100. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  101. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  102. data/spec/mongoid/criteria/queryable/extensions/regexp_raw_spec.rb +1 -1
  103. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  104. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  105. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  106. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  107. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +45 -12
  108. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +1051 -392
  109. data/spec/mongoid/criteria/queryable/selectable_spec.rb +52 -0
  110. data/spec/mongoid/criteria/queryable/storable_spec.rb +80 -2
  111. data/spec/mongoid/criteria_spec.rb +36 -2
  112. data/spec/mongoid/document_fields_spec.rb +29 -0
  113. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  114. data/spec/mongoid/errors/no_client_config_spec.rb +2 -2
  115. data/spec/mongoid/errors/no_client_database_spec.rb +3 -3
  116. data/spec/mongoid/errors/no_client_hosts_spec.rb +3 -3
  117. data/spec/mongoid/fields_spec.rb +24 -1
  118. data/spec/mongoid/indexable_spec.rb +6 -4
  119. data/spec/mongoid/interceptable_spec.rb +62 -0
  120. data/spec/mongoid/interceptable_spec_models.rb +76 -0
  121. data/spec/mongoid/matchable/default_spec.rb +1 -1
  122. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  123. data/spec/mongoid/matchable_spec.rb +2 -2
  124. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  125. data/spec/mongoid/query_cache_spec.rb +77 -9
  126. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  127. data/spec/mongoid/scopable_spec.rb +2 -1
  128. data/spec/mongoid/serializable_spec.rb +129 -18
  129. data/spec/mongoid/shardable_models.rb +61 -0
  130. data/spec/mongoid/shardable_spec.rb +69 -16
  131. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  132. data/spec/mongoid/tasks/database_spec.rb +1 -1
  133. data/spec/spec_helper.rb +2 -31
  134. data/spec/support/child_process_helper.rb +76 -0
  135. data/spec/support/cluster_config.rb +3 -3
  136. data/spec/support/constraints.rb +26 -10
  137. data/spec/support/expectations.rb +3 -1
  138. data/spec/support/helpers.rb +11 -0
  139. data/spec/support/lite_constraints.rb +22 -0
  140. data/spec/support/session_registry.rb +50 -0
  141. data/spec/support/spec_config.rb +12 -4
  142. metadata +518 -480
  143. metadata.gz.sig +0 -0
@@ -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
@@ -354,23 +354,35 @@ describe Time do
354
354
 
355
355
  describe "#__evolve_date__" do
356
356
 
357
- let(:time) do
358
- Time.new(2010, 1, 1, 12, 0, 0)
359
- end
360
-
361
357
  let(:evolved) do
362
358
  time.__evolve_date__
363
359
  end
364
360
 
365
- it "returns midnight utc" do
366
- expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
361
+ context 'beginning of day' do
362
+ let(:time) do
363
+ Time.new(2010, 1, 1, 0, 0, 1).freeze
364
+ end
365
+
366
+ it "returns midnight utc" do
367
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
368
+ end
369
+ end
370
+
371
+ context 'end of day' do
372
+ let(:time) do
373
+ Time.new(2010, 1, 1, 23, 59, 59).freeze
374
+ end
375
+
376
+ it "returns midnight utc" do
377
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
378
+ end
367
379
  end
368
380
  end
369
381
 
370
382
  describe "#__evolve_time__" do
371
383
 
372
384
  let(:time) do
373
- Time.new(2010, 1, 1, 12, 0, 0)
385
+ Time.new(2010, 1, 1, 12, 0, 0).freeze
374
386
  end
375
387
 
376
388
  let(:evolved) do
@@ -325,10 +325,37 @@ describe ActiveSupport::TimeWithZone do
325
325
  end
326
326
  end
327
327
 
328
+ describe "#__evolve_date__" do
329
+
330
+ let(:evolved) do
331
+ time.__evolve_date__
332
+ end
333
+
334
+ context 'beginning of day' do
335
+ let(:time) do
336
+ time_zone.local(2010, 1, 1, 0, 0, 1).freeze
337
+ end
338
+
339
+ it "returns midnight utc" do
340
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
341
+ end
342
+ end
343
+
344
+ context 'end of day' do
345
+ let(:time) do
346
+ time_zone.local(2010, 1, 1, 23, 59, 59).freeze
347
+ end
348
+
349
+ it "returns midnight utc" do
350
+ expect(evolved).to eq(Time.utc(2010, 1, 1, 0, 0, 0))
351
+ end
352
+ end
353
+ end
354
+
328
355
  describe "#__evolve_time__" do
329
356
 
330
357
  let(:date) do
331
- time_zone.local(2010, 1, 1, 12, 0, 0)
358
+ time_zone.local(2010, 1, 1, 12, 0, 0).freeze
332
359
  end
333
360
 
334
361
  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
@@ -16,110 +16,12 @@ describe Mongoid::Criteria::Queryable::Selectable do
16
16
  end
17
17
  end
18
18
 
19
- shared_examples_for 'a non-combining logical operation' do
19
+ # Hoisting means the operator can be elided, for example
20
+ # Foo.and(a: 1) produces simply {'a' => 1}.
21
+ shared_examples_for 'a hoisting logical operation' do
20
22
 
21
- context 'when there is a single predicate' do
22
- let(:query) do
23
- Mongoid::Query.new.send(tested_method, hello: 'world')
24
- end
25
-
26
- it 'adds the predicate' do
27
- expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
28
- end
29
- end
30
-
31
- context 'when the single predicate is wrapped in an array' do
32
- let(:query) do
33
- Mongoid::Query.new.send(tested_method, [{hello: 'world'}])
34
- end
35
-
36
- it 'adds the predicate' do
37
- expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
38
- end
39
- end
40
-
41
- context 'when argument is a Criteria' do
42
- let(:query) do
43
- Mongoid::Query.new.where(hello: 'world')
44
- end
45
-
46
- let(:other) do
47
- Mongoid::Query.new.where(foo: 'bar')
48
- end
49
-
50
- let(:result) { query.send(tested_method, other) }
51
-
52
- it 'combines' do
53
- # This is used for $or / $nor, the two conditions should remain
54
- # as separate hashes
55
- expect(result.selector).to eq(expected_operator => [{'hello' => 'world'}, {'foo' => 'bar'}])
56
- end
57
- end
58
-
59
- context 'when argument is a mix of Criteria and hashes' do
60
- let(:query) do
61
- Mongoid::Query.new.where(hello: 'world')
62
- end
63
-
64
- let(:other1) do
65
- Mongoid::Query.new.where(foo: 'bar')
66
- end
67
-
68
- let(:other2) do
69
- {bar: 42}
70
- end
71
-
72
- let(:other3) do
73
- Mongoid::Query.new.where(a: 2)
74
- end
75
-
76
- let(:result) { query.send(tested_method, other1, other2, other3) }
77
-
78
- it 'combines' do
79
- expect(result.selector).to eq(expected_operator => [
80
- {'hello' => 'world'},
81
- {'foo' => 'bar'},
82
- {'bar' => 42},
83
- {'a' => 2},
84
- ])
85
- end
86
- end
87
- end
88
-
89
- describe "#and" do
90
-
91
- context "when provided no criterion" do
92
-
93
- let(:selection) do
94
- query.and
95
- end
96
-
97
- it "does not add any criterion" do
98
- expect(selection.selector).to eq({})
99
- end
100
-
101
- it "returns the query" do
102
- expect(selection).to eq(query)
103
- end
104
-
105
- it_behaves_like 'returns a cloned query'
106
- end
107
-
108
- context "when provided nil" do
109
-
110
- let(:selection) do
111
- query.and(nil)
112
- end
113
-
114
- it "does not add any criterion" do
115
- expect(selection.selector).to eq({})
116
- end
117
-
118
- it "returns the query" do
119
- expect(selection).to eq(query)
120
- end
121
-
122
- it_behaves_like 'returns a cloned query'
23
+ let(:query) do
24
+ Mongoid::Query.new
123
25
  end
124
26
 
125
27
  context "when provided a single criterion" do
@@ -127,23 +29,23 @@ describe Mongoid::Criteria::Queryable::Selectable do
127
29
  shared_examples_for 'adds the conditions to top level' do
128
30
 
129
31
  it "adds the conditions to top level" do
130
- expect(selection.selector).to eq({
32
+ expect(selection.selector).to eq(
131
33
  "field" => [ 1, 2 ]
132
- })
34
+ )
133
35
  end
134
36
 
135
37
  it_behaves_like 'returns a cloned query'
136
38
  end
137
39
 
138
40
  let(:selection) do
139
- query.and(field: [ 1, 2 ])
41
+ query.send(tested_method, field: [ 1, 2 ])
140
42
  end
141
43
 
142
44
  it_behaves_like 'adds the conditions to top level'
143
45
 
144
46
  context 'when the criterion is wrapped in an array' do
145
47
  let(:selection) do
146
- query.and([{field: [ 1, 2 ] }])
48
+ query.send(tested_method, [{field: [ 1, 2 ] }])
147
49
  end
148
50
 
149
51
  it_behaves_like 'adds the conditions to top level'
@@ -151,20 +53,39 @@ describe Mongoid::Criteria::Queryable::Selectable do
151
53
 
152
54
  context 'when the criterion is wrapped in a deep array with nil elements' do
153
55
  let(:selection) do
154
- query.and([[[{field: [ 1, 2 ] }]], [nil]])
56
+ query.send(tested_method, [[[{field: [ 1, 2 ] }]], [nil]])
155
57
  end
156
58
 
157
59
  it_behaves_like 'adds the conditions to top level'
158
60
  end
159
61
  end
160
62
 
63
+ context 'when argument is a Criteria' do
64
+ let(:base) do
65
+ query.where(hello: 'world')
66
+ end
67
+
68
+ let(:other) do
69
+ query.where(foo: 'bar')
70
+ end
71
+
72
+ let(:result) { base.send(tested_method, other) }
73
+
74
+ it 'combines' do
75
+ expect(result.selector).to eq(
76
+ 'hello' => 'world',
77
+ 'foo' => 'bar',
78
+ )
79
+ end
80
+ end
81
+
161
82
  context "when provided a single criterion that is handled via Key" do
162
83
 
163
84
  shared_examples_for 'adds the conditions to top level' do
164
85
 
165
86
  it "adds the conditions to top level" do
166
87
  expect(selection.selector).to eq({
167
- "field" => {'$gt' => 3 },
88
+ "field" => {'$gt' => 3},
168
89
  })
169
90
  end
170
91
 
@@ -172,14 +93,14 @@ describe Mongoid::Criteria::Queryable::Selectable do
172
93
  end
173
94
 
174
95
  let(:selection) do
175
- query.and(:field.gt => 3)
96
+ query.send(tested_method, :field.gt => 3)
176
97
  end
177
98
 
178
99
  it_behaves_like 'adds the conditions to top level'
179
100
 
180
101
  context 'when the criterion is wrapped in an array' do
181
102
  let(:selection) do
182
- query.and([{ :field.gt => 3 }])
103
+ query.send(tested_method, [{ :field.gt => 3 }])
183
104
  end
184
105
 
185
106
  it_behaves_like 'adds the conditions to top level'
@@ -187,81 +108,74 @@ describe Mongoid::Criteria::Queryable::Selectable do
187
108
 
188
109
  context 'when the criterion is wrapped in a deep array with nil elements' do
189
110
  let(:selection) do
190
- query.and([[[{ :field.gt => 3 }]], [nil]])
111
+ query.send(tested_method, [[[{ :field.gt => 3 }]], [nil]])
191
112
  end
192
113
 
193
114
  it_behaves_like 'adds the conditions to top level'
194
115
  end
195
- end
196
-
197
- context "when provided a nested criterion" do
198
-
199
- let(:selection) do
200
- query.and(:test.elem_match => { :field.in => [ 1, 2 ] })
201
- end
202
-
203
- it "builds the correct selector" do
204
- expect(selection.selector).to eq({
205
- "test" => { "$elemMatch" => { "field" => { "$in" => [ 1, 2 ] }}}
206
- })
207
- end
208
-
209
- it_behaves_like 'returns a cloned query'
210
- end
211
-
212
- context "when provided multiple criteria" do
213
-
214
- context "when the criteria is already included" do
215
116
 
117
+ context 'when the criterion is a time' do
216
118
  let(:selection) do
217
- query.and({ first: [ 1, 2 ] }).and({ first: [ 1, 2 ] })
119
+ query.send(tested_method, :field.gte => Time.new(2020, 1, 1))
218
120
  end
219
121
 
220
- it "adds all conditions" do
122
+ it 'adds the conditions' do
221
123
  expect(selection.selector).to eq({
222
- 'first' => [1, 2],
223
- "$and" => [
224
- { "first" => [ 1, 2 ] }
225
- ]
124
+ "field" => {'$gte' => Time.new(2020, 1, 1)},
226
125
  })
227
126
  end
228
127
 
229
- it_behaves_like 'returns a cloned query'
128
+ it 'keeps argument type' do
129
+ selection.selector['field']['$gte'].should be_a(Time)
130
+ end
230
131
  end
231
132
 
232
- context "when the new criterion is for different fields" do
233
-
133
+ context 'when the criterion is a datetime' do
234
134
  let(:selection) do
235
- query.and({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
135
+ query.send(tested_method, :field.gte => DateTime.new(2020, 1, 1))
236
136
  end
237
137
 
238
- it "adds all conditions to top level" do
138
+ it 'adds the conditions' do
239
139
  expect(selection.selector).to eq({
240
- "first" => [ 1, 2 ],
241
- "second" => [ 3, 4 ],
140
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
242
141
  })
243
142
  end
244
143
 
245
- it_behaves_like 'returns a cloned query'
144
+ it 'converts argument to a time' do
145
+ selection.selector['field']['$gte'].should be_a(Time)
146
+ end
246
147
  end
247
148
 
248
- context "when the new criterion is for the same field" do
249
-
149
+ context 'when the criterion is a date' do
250
150
  let(:selection) do
251
- query.and({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
151
+ query.send(tested_method, :field.gte => Date.new(2020, 1, 1))
252
152
  end
253
153
 
254
- it "combines via $and operator" do
154
+ it 'adds the conditions' do
255
155
  expect(selection.selector).to eq({
256
- "first" => [ 1, 2 ],
257
- "$and" => [
258
- { "first" => [ 3, 4 ] }
259
- ]
156
+ "field" => {'$gte' => Time.utc(2020, 1, 1)},
260
157
  })
261
158
  end
262
159
 
263
- it_behaves_like 'returns a cloned query'
160
+ it 'converts argument to a time' do
161
+ selection.selector['field']['$gte'].should be_a(Time)
162
+ end
163
+ end
164
+ end
165
+
166
+ context "when provided a nested criterion" do
167
+
168
+ let(:selection) do
169
+ query.send(tested_method, :test.elem_match => { :field.in => [ 1, 2 ] })
170
+ end
171
+
172
+ it "builds the correct selector" do
173
+ expect(selection.selector).to eq({
174
+ "test" => { "$elemMatch" => { "field" => { "$in" => [ 1, 2 ] }}}
175
+ })
264
176
  end
177
+
178
+ it_behaves_like 'returns a cloned query'
265
179
  end
266
180
 
267
181
  context "when chaining the criteria" do
@@ -269,7 +183,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
269
183
  context "when the criteria are for different fields" do
270
184
 
271
185
  let(:selection) do
272
- query.and(first: [ 1, 2 ]).and(second: [ 3, 4 ])
186
+ query.and(first: [ 1, 2 ]).send(tested_method, second: [ 3, 4 ])
273
187
  end
274
188
 
275
189
  it "adds the conditions to top level" do
@@ -285,7 +199,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
285
199
  context "when the criteria are on the same field" do
286
200
 
287
201
  let(:selection) do
288
- query.and(first: [ 1, 2 ]).and(first: [ 3, 4 ])
202
+ query.and(first: [ 1, 2 ]).send(tested_method, first: [ 3, 4 ])
289
203
  end
290
204
 
291
205
  it "combines via $and operator" do
@@ -300,34 +214,47 @@ describe Mongoid::Criteria::Queryable::Selectable do
300
214
  it_behaves_like 'returns a cloned query'
301
215
  end
302
216
  end
217
+ end
303
218
 
304
- context 'when argument is a Criteria' do
219
+ # Non-hoisting means the operator is always present, for example
220
+ # Foo.or(a: 1) produces {'$or' => [{'a' => 1}]}.
221
+ shared_examples_for 'a non-hoisting logical operation' do
222
+
223
+ context 'when there is a single predicate' do
305
224
  let(:query) do
306
- Mongoid::Query.new.where(hello: 'world')
225
+ Mongoid::Query.new.send(tested_method, hello: 'world')
307
226
  end
308
227
 
309
- let(:result) { query.and(other) }
228
+ it 'adds the predicate' do
229
+ expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
230
+ end
231
+ end
310
232
 
311
- context 'different fields' do
233
+ context 'when the single predicate is wrapped in an array' do
234
+ let(:query) do
235
+ Mongoid::Query.new.send(tested_method, [{hello: 'world'}])
236
+ end
312
237
 
313
- let(:other) do
314
- Mongoid::Query.new.where(foo: 'bar')
315
- end
238
+ it 'adds the predicate' do
239
+ expect(query.selector).to eq(expected_operator => [{'hello' => 'world'}])
240
+ end
241
+ end
316
242
 
317
- it 'combines both fields at top level' do
318
- expect(result.selector).to eq('hello' => 'world', 'foo' => 'bar')
319
- end
243
+ context 'when argument is a Criteria' do
244
+ let(:query) do
245
+ Mongoid::Query.new.where(hello: 'world')
320
246
  end
321
247
 
322
- context 'same field' do
248
+ let(:other) do
249
+ Mongoid::Query.new.where(foo: 'bar')
250
+ end
323
251
 
324
- let(:other) do
325
- Mongoid::Query.new.where(hello: /bar/)
326
- end
252
+ let(:result) { query.send(tested_method, other) }
327
253
 
328
- it 'combines fields with $and' do
329
- expect(result.selector).to eq('hello' => 'world', '$and' => [{'hello' => /bar/}])
330
- end
254
+ it 'combines' do
255
+ # This is used for $or / $nor, the two conditions should remain
256
+ # as separate hashes
257
+ expect(result.selector).to eq(expected_operator => [{'hello' => 'world'}, {'foo' => 'bar'}])
331
258
  end
332
259
  end
333
260
 
@@ -348,40 +275,344 @@ describe Mongoid::Criteria::Queryable::Selectable do
348
275
  Mongoid::Query.new.where(a: 2)
349
276
  end
350
277
 
351
- let(:result) { query.and(other1, other2, other3) }
278
+ let(:result) { query.send(tested_method, other1, other2, other3) }
352
279
 
353
280
  it 'combines' do
354
- expect(result.selector).to eq('hello' => 'world',
355
- 'foo' => 'bar',
356
- 'bar' => 42,
357
- 'a' => 2,
358
- )
281
+ expect(result.selector).to eq(expected_operator => [
282
+ {'hello' => 'world'},
283
+ {'foo' => 'bar'},
284
+ {'bar' => 42},
285
+ {'a' => 2},
286
+ ])
359
287
  end
360
288
  end
289
+ end
361
290
 
362
- context 'when Key instances are used and types involved have serializers' do
363
- let(:time) { Time.now }
291
+ describe "#and" do
364
292
 
365
- let(:query) do
366
- Band.all.and(:created_at.gt => time)
293
+ let(:tested_method) { :and }
294
+ let(:expected_operator) { '$and' }
295
+
296
+ it_behaves_like 'a hoisting logical operation'
297
+
298
+ context "when provided no criterion" do
299
+
300
+ let(:selection) do
301
+ query.and
367
302
  end
368
303
 
369
- let(:expected) do
370
- {'created_at' => {'$gt' => time.utc}}
304
+ it "does not add any criterion" do
305
+ expect(selection.selector).to eq({})
371
306
  end
372
307
 
373
- it 'combines and evolves' do
374
- expect(query.selector).to eq(expected)
308
+ it "returns the query" do
309
+ expect(selection).to eq(query)
375
310
  end
311
+
312
+ it_behaves_like 'returns a cloned query'
376
313
  end
377
314
 
378
- describe 'query shape' do
379
- shared_examples_for 'adds most recent criterion as $and' do
380
- let(:selector) { scope.selector }
315
+ context "when provided nil" do
381
316
 
382
- it 'adds most recent criterion as $and' do
383
- expect(selector).to eq('foo' => 1, '$and' => [{'foo' => 2}])
384
- end
317
+ let(:selection) do
318
+ query.and(nil)
319
+ end
320
+
321
+ it "does not add any criterion" do
322
+ expect(selection.selector).to eq({})
323
+ end
324
+
325
+ it "returns the query" do
326
+ expect(selection).to eq(query)
327
+ end
328
+
329
+ it_behaves_like 'returns a cloned query'
330
+ end
331
+
332
+ context "when provided multiple criteria" do
333
+
334
+ context "when the criterion is already included" do
335
+
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'
351
+ end
352
+
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'
368
+ end
369
+
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
383
+ end
384
+
385
+ context "when the new criteria are for different fields" do
386
+
387
+ let(:selection) do
388
+ query.and({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
389
+ end
390
+
391
+ it "adds all conditions to top level" do
392
+ expect(selection.selector).to eq({
393
+ "first" => [ 1, 2 ],
394
+ "second" => [ 3, 4 ],
395
+ })
396
+ end
397
+
398
+ it_behaves_like 'returns a cloned query'
399
+ end
400
+
401
+ context "when the new criteria are for the same field" do
402
+
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'
418
+ end
419
+
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
455
+ end
456
+
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
532
+ end
533
+ end
534
+
535
+ context 'when argument is a Criteria' do
536
+ let(:query) do
537
+ Mongoid::Query.new.where(hello: 'world')
538
+ end
539
+
540
+ let(:result) { query.and(other) }
541
+
542
+ context 'different fields' do
543
+
544
+ let(:other) do
545
+ Mongoid::Query.new.where(foo: 'bar')
546
+ end
547
+
548
+ it 'combines both fields at top level' do
549
+ expect(result.selector).to eq('hello' => 'world', 'foo' => 'bar')
550
+ end
551
+ end
552
+
553
+ context 'same field' do
554
+
555
+ let(:other) do
556
+ Mongoid::Query.new.where(hello: /bar/)
557
+ end
558
+
559
+ it 'combines fields with $and' do
560
+ expect(result.selector).to eq('hello' => 'world', '$and' => [{'hello' => /bar/}])
561
+ end
562
+ end
563
+ end
564
+
565
+ context 'when argument is a mix of Criteria and hashes' do
566
+ let(:query) do
567
+ Mongoid::Query.new.where(hello: 'world')
568
+ end
569
+
570
+ let(:other1) do
571
+ Mongoid::Query.new.where(foo: 'bar')
572
+ end
573
+
574
+ let(:other2) do
575
+ {bar: 42}
576
+ end
577
+
578
+ let(:other3) do
579
+ Mongoid::Query.new.where(a: 2)
580
+ end
581
+
582
+ let(:result) { query.and(other1, other2, other3) }
583
+
584
+ it 'combines' do
585
+ expect(result.selector).to eq('hello' => 'world',
586
+ 'foo' => 'bar',
587
+ 'bar' => 42,
588
+ 'a' => 2,
589
+ )
590
+ end
591
+ end
592
+
593
+ context 'when Key instances are used and types involved have serializers' do
594
+ let(:time) { Time.now }
595
+
596
+ let(:query) do
597
+ Band.all.and(:created_at.gt => time)
598
+ end
599
+
600
+ let(:expected) do
601
+ {'created_at' => {'$gt' => time.utc}}
602
+ end
603
+
604
+ it 'combines and evolves' do
605
+ expect(query.selector).to eq(expected)
606
+ end
607
+ end
608
+
609
+ describe 'query shape' do
610
+ shared_examples_for 'adds most recent criterion as $and' do
611
+ let(:selector) { scope.selector }
612
+
613
+ it 'adds most recent criterion as $and' do
614
+ expect(selector).to eq('foo' => 1, '$and' => [{'foo' => 2}])
615
+ end
385
616
  end
386
617
 
387
618
  context 'and/and' do
@@ -405,38 +636,463 @@ describe Mongoid::Criteria::Queryable::Selectable do
405
636
  Band.and(foo: 1).where(foo: 2)
406
637
  end
407
638
 
408
- it_behaves_like 'adds most recent criterion as $and'
639
+ it_behaves_like 'adds most recent criterion as $and'
640
+ end
641
+
642
+ context 'where/and' do
643
+ let(:scope) do
644
+ Band.where(foo: 1).and(foo: 2)
645
+ end
646
+
647
+ it_behaves_like 'adds most recent criterion as $and'
648
+ end
649
+
650
+ context 'where/where' do
651
+ let(:scope) do
652
+ Band.where(foo: 1).where(foo: 2)
653
+ end
654
+
655
+ it_behaves_like 'adds most recent criterion as $and'
656
+ end
657
+ end
658
+
659
+ context 'when conditions already exist in criteria' do
660
+ let(:base_selection) do
661
+ query.where(foo: 'bar')
662
+ end
663
+
664
+ context 'when hash conditions are given' do
665
+ let(:selection) do
666
+ base_selection.and(hello: 'world')
667
+ end
668
+
669
+ it 'adds new conditions to top level' do
670
+ selection.selector.should == {
671
+ 'foo' => 'bar',
672
+ 'hello' => 'world',
673
+ }
674
+ end
675
+ end
676
+
677
+ context 'when criteria conditions are given' do
678
+ let(:selection) do
679
+ base_selection.and(query.where(hello: 'world'))
680
+ end
681
+
682
+ it 'adds new conditions to top level' do
683
+ selection.selector.should == {
684
+ 'foo' => 'bar',
685
+ 'hello' => 'world',
686
+ }
687
+ end
688
+ end
689
+
690
+ context 'when complex criteria conditions are given' do
691
+ let(:selection) do
692
+ base_selection.and(query.or([one: 'one'], [two: 'two']))
693
+ end
694
+
695
+ it 'adds new conditions to top level' do
696
+ selection.selector.should == {
697
+ 'foo' => 'bar',
698
+ '$or' => [
699
+ {'one' => 'one'},
700
+ {'two' => 'two'},
701
+ ],
702
+ }
703
+ end
704
+ end
705
+ end
706
+ end
707
+
708
+ shared_examples '$or/$nor' do
709
+
710
+ it_behaves_like 'a non-hoisting logical operation'
711
+
712
+ context "when provided no arguments" do
713
+
714
+ let(:selection) do
715
+ query.send(tested_method)
716
+ end
717
+
718
+ it_behaves_like 'returns a cloned query'
719
+
720
+ it "does not add any criteria" do
721
+ expect(selection.selector).to eq({})
722
+ end
723
+
724
+ it "returns the query" do
725
+ expect(selection).to eq(query)
726
+ end
727
+ end
728
+
729
+ context "when provided nil" do
730
+
731
+ let(:selection) do
732
+ query.send(tested_method, nil)
733
+ end
734
+
735
+ it_behaves_like 'returns a cloned query'
736
+
737
+ it "does not add any criteria" do
738
+ expect(selection.selector).to eq({})
739
+ end
740
+
741
+ it "returns the query" do
742
+ expect(selection).to eq(query)
743
+ end
744
+ end
745
+
746
+ context "when provided a single criterion" do
747
+
748
+ let(:selection) do
749
+ query.send(tested_method, field: [ 1, 2 ])
750
+ end
751
+
752
+ it_behaves_like 'returns a cloned query'
753
+
754
+ it "adds the $or/$nor selector" do
755
+ expect(selection.selector).to eq({
756
+ expected_operator => [{ "field" => [ 1, 2 ] }]
757
+ })
758
+ end
759
+
760
+ context 'when the criterion is wrapped in array' do
761
+
762
+ let(:selection) do
763
+ query.send(tested_method, [{ field: [ 1, 2 ] }])
764
+ end
765
+
766
+ it_behaves_like 'returns a cloned query'
767
+
768
+ it "adds the $or/$nor selector" do
769
+ expect(selection.selector).to eq({
770
+ expected_operator => [{ "field" => [ 1, 2 ] }]
771
+ })
772
+ end
773
+
774
+ context 'when the array has nil as one of the elements' do
775
+
776
+ let(:selection) do
777
+ query.send(tested_method, [{ field: [ 1, 2 ] }, nil])
778
+ end
779
+
780
+ it_behaves_like 'returns a cloned query'
781
+
782
+ it "adds the $or/$nor selector ignoring the nil element" do
783
+ expect(selection.selector).to eq({
784
+ expected_operator => [{ "field" => [ 1, 2 ] }]
785
+ })
786
+ end
787
+ end
788
+ end
789
+
790
+ context 'when query already has a condition on another field' do
791
+
792
+ let(:selection) do
793
+ query.where(foo: 'bar').send(tested_method, field: [ 1, 2 ])
794
+ end
795
+
796
+ it 'moves original conditions under $or/$nor' do
797
+ expect(selection.selector).to eq({
798
+ expected_operator => [{'foo' => 'bar'}, { "field" => [ 1, 2 ] }]
799
+ })
800
+ end
801
+ end
802
+
803
+ context 'when query already has an $or/$nor condition and another condition' do
804
+
805
+ let(:selection) do
806
+ query.send(tested_method, field: [ 1, 2 ]).where(foo: 'bar').send(tested_method, test: 1)
807
+ end
808
+
809
+ it 'unions existing conditions' do
810
+ expect(selection.selector).to eq(
811
+ expected_operator => [
812
+ {
813
+ expected_operator => [{ "field" => [ 1, 2 ] }],
814
+ 'foo' => 'bar',
815
+ },
816
+ {'test' => 1},
817
+ ]
818
+ )
819
+ end
820
+ end
821
+ end
822
+
823
+ context "when provided multiple criteria" do
824
+
825
+ context "when the criteria are for different fields" do
826
+
827
+ let(:selection) do
828
+ query.send(tested_method, { first: [ 1, 2 ] }, { second: [ 3, 4 ] })
829
+ end
830
+
831
+ it_behaves_like 'returns a cloned query'
832
+
833
+ it "adds the $or/$nor selector" do
834
+ expect(selection.selector).to eq({
835
+ expected_operator => [
836
+ { "first" => [ 1, 2 ] },
837
+ { "second" => [ 3, 4 ] }
838
+ ]
839
+ })
840
+ end
841
+ end
842
+
843
+ context "when the criteria uses a Key instance" do
844
+
845
+ let(:selection) do
846
+ query.send(tested_method, { first: [ 1, 2 ] }, { :second.gt => 3 })
847
+ end
848
+
849
+ it "adds the $or/$nor selector" do
850
+ expect(selection.selector).to eq({
851
+ expected_operator => [
852
+ { "first" => [ 1, 2 ] },
853
+ { "second" => { "$gt" => 3 }}
854
+ ]
855
+ })
856
+ end
857
+
858
+ it_behaves_like 'returns a cloned query'
859
+
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
864
+
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
870
+
871
+ it 'keeps the type' do
872
+ selection.selector[expected_operator].first['field']['$gte'].should be_a(Time)
873
+ end
874
+ end
875
+
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
880
+
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
886
+
887
+ it 'converts argument to a time' do
888
+ selection.selector[expected_operator].first['field']['$gte'].should be_a(Time)
889
+ end
890
+ end
891
+
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
896
+
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
906
+ end
907
+ end
908
+
909
+ context "when a criterion has an aliased field" do
910
+
911
+ let(:selection) do
912
+ query.send(tested_method, { id: 1 })
913
+ end
914
+
915
+ it "adds the $or/$nor selector and aliases the field" do
916
+ expect(selection.selector).to eq({
917
+ expected_operator => [ { "_id" => 1 } ]
918
+ })
919
+ end
920
+
921
+ it_behaves_like 'returns a cloned query'
922
+ end
923
+
924
+ context "when a criterion is wrapped in an array" do
925
+
926
+ let(:selection) do
927
+ query.send(tested_method, [{ first: [ 1, 2 ] }, { :second.gt => 3 }])
928
+ end
929
+
930
+ it_behaves_like 'returns a cloned query'
931
+
932
+ it "adds the $or/$nor selector" do
933
+ expect(selection.selector).to eq({
934
+ expected_operator => [
935
+ { "first" => [ 1, 2 ] },
936
+ { "second" => { "$gt" => 3 }}
937
+ ]
938
+ })
939
+ end
940
+ end
941
+
942
+ context "when the criteria are on the same field" do
943
+
944
+ context 'simple criteria' do
945
+ let(:selection) do
946
+ query.send(tested_method, { first: [ 1, 2 ] }, { first: [ 3, 4 ] })
947
+ end
948
+
949
+ it_behaves_like 'returns a cloned query'
950
+
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
960
+
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
965
+
966
+ it_behaves_like 'returns a cloned query'
967
+
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
976
+
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
981
+
982
+ it_behaves_like 'returns a cloned query'
983
+
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
993
+ end
994
+ end
995
+
996
+ context "when chaining the criterion" do
997
+
998
+ context "when the criterion are for different fields" do
999
+
1000
+ let(:selection) do
1001
+ query.send(tested_method, first: [ 1, 2 ]).send(tested_method, second: [ 3, 4 ])
1002
+ end
1003
+
1004
+ it_behaves_like 'returns a cloned query'
1005
+
1006
+ it "adds the $or/$nor selectors" do
1007
+ expect(selection.selector).to eq({
1008
+ expected_operator => [
1009
+ { "first" => [ 1, 2 ] },
1010
+ { "second" => [ 3, 4 ] }
1011
+ ]
1012
+ })
1013
+ end
1014
+ end
1015
+
1016
+ context "when the criterion are on the same field" do
1017
+
1018
+ let(:selection) do
1019
+ query.send(tested_method, first: [ 1, 2 ]).send(tested_method, first: [ 3, 4 ])
1020
+ end
1021
+
1022
+ it_behaves_like 'returns a cloned query'
1023
+
1024
+ it "appends both $or/$nor expressions" do
1025
+ expect(selection.selector).to eq({
1026
+ expected_operator => [
1027
+ { "first" => [ 1, 2 ] },
1028
+ { "first" => [ 3, 4 ] }
1029
+ ]
1030
+ })
1031
+ end
1032
+ end
1033
+ end
1034
+ end
1035
+
1036
+ describe "#or" do
1037
+
1038
+ let(:tested_method) { :or }
1039
+ let(:expected_operator) { '$or' }
1040
+
1041
+ it_behaves_like '$or/$nor'
1042
+ end
1043
+
1044
+ describe "#nor" do
1045
+
1046
+ let(:tested_method) { :nor }
1047
+ let(:expected_operator) { '$nor' }
1048
+
1049
+ it_behaves_like '$or/$nor'
1050
+ end
1051
+
1052
+ describe "#any_of" do
1053
+
1054
+ let(:tested_method) { :any_of }
1055
+ let(:expected_operator) { '$or' }
1056
+
1057
+ it_behaves_like 'a hoisting logical operation'
1058
+
1059
+ # When multiple arguments are given to any_of, it behaves differently
1060
+ # from and.
1061
+ context 'when argument is a mix of Criteria and hashes' do
1062
+ let(:query) do
1063
+ Mongoid::Query.new.where(hello: 'world')
1064
+ end
1065
+
1066
+ let(:other1) do
1067
+ Mongoid::Query.new.where(foo: 'bar')
409
1068
  end
410
1069
 
411
- context 'where/and' do
412
- let(:scope) do
413
- Band.where(foo: 1).and(foo: 2)
414
- end
1070
+ let(:other2) do
1071
+ {bar: 42}
1072
+ end
415
1073
 
416
- it_behaves_like 'adds most recent criterion as $and'
1074
+ let(:other3) do
1075
+ Mongoid::Query.new.where(a: 2)
417
1076
  end
418
1077
 
419
- context 'where/where' do
420
- let(:scope) do
421
- Band.where(foo: 1).where(foo: 2)
422
- end
1078
+ let(:result) { query.send(tested_method, other1, other2, other3) }
423
1079
 
424
- it_behaves_like 'adds most recent criterion as $and'
1080
+ it 'combines' do
1081
+ expect(result.selector).to eq(
1082
+ 'hello' => 'world',
1083
+ expected_operator => [
1084
+ {'foo' => 'bar'},
1085
+ {'bar' => 42},
1086
+ {'a' => 2},
1087
+ ],
1088
+ )
425
1089
  end
426
1090
  end
427
- end
428
-
429
- describe "#or" do
430
-
431
- let(:tested_method) { :or }
432
- let(:expected_operator) { '$or' }
433
-
434
- it_behaves_like 'a non-combining logical operation'
435
1091
 
436
1092
  context "when provided no arguments" do
437
1093
 
438
1094
  let(:selection) do
439
- query.or
1095
+ query.any_of
440
1096
  end
441
1097
 
442
1098
  it_behaves_like 'returns a cloned query'
@@ -453,7 +1109,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
453
1109
  context "when provided nil" do
454
1110
 
455
1111
  let(:selection) do
456
- query.or(nil)
1112
+ query.any_of(nil)
457
1113
  end
458
1114
 
459
1115
  it_behaves_like 'returns a cloned query'
@@ -470,75 +1126,92 @@ describe Mongoid::Criteria::Queryable::Selectable do
470
1126
  context "when provided a single criterion" do
471
1127
 
472
1128
  let(:selection) do
473
- query.or(field: [ 1, 2 ])
1129
+ query.any_of(field: [ 1, 2 ])
474
1130
  end
475
1131
 
476
1132
  it_behaves_like 'returns a cloned query'
477
1133
 
478
1134
  it "adds the $or selector" do
479
- expect(selection.selector).to eq({
480
- "$or" => [{ "field" => [ 1, 2 ] }]
481
- })
1135
+ expect(selection.selector).to eq(
1136
+ "field" => [ 1, 2 ],
1137
+ )
482
1138
  end
483
1139
 
484
1140
  context 'when the criterion is wrapped in array' do
485
1141
 
486
1142
  let(:selection) do
487
- query.or([{ field: [ 1, 2 ] }])
1143
+ query.any_of([{ field: [ 1, 2 ] }])
488
1144
  end
489
1145
 
490
1146
  it_behaves_like 'returns a cloned query'
491
1147
 
492
- it "adds the $or selector" do
493
- expect(selection.selector).to eq({
494
- "$or" => [{ "field" => [ 1, 2 ] }]
495
- })
1148
+ it "adds the condition" do
1149
+ expect(selection.selector).to eq(
1150
+ "field" => [ 1, 2 ],
1151
+ )
496
1152
  end
497
1153
 
498
1154
  context 'when the array has nil as one of the elements' do
499
1155
 
500
1156
  let(:selection) do
501
- query.or([{ field: [ 1, 2 ] }, nil])
1157
+ query.any_of([{ field: [ 1, 2 ] }, nil])
502
1158
  end
503
1159
 
504
1160
  it_behaves_like 'returns a cloned query'
505
1161
 
506
1162
  it "adds the $or selector ignoring the nil element" do
507
- expect(selection.selector).to eq({
508
- "$or" => [{ "field" => [ 1, 2 ] }]
509
- })
1163
+ expect(selection.selector).to eq(
1164
+ "field" => [ 1, 2 ],
1165
+ )
510
1166
  end
511
1167
  end
512
1168
  end
513
1169
 
514
1170
  context 'when query already has a condition on another field' do
515
1171
 
516
- let(:selection) do
517
- query.where(foo: 'bar').or(field: [ 1, 2 ])
1172
+ context 'when there is one argument' do
1173
+
1174
+ let(:selection) do
1175
+ query.where(foo: 'bar').any_of(field: [ 1, 2 ])
1176
+ end
1177
+
1178
+ it 'adds the new condition' do
1179
+ expect(selection.selector).to eq(
1180
+ 'foo' => 'bar',
1181
+ 'field' => [1, 2],
1182
+ )
1183
+ end
518
1184
  end
519
1185
 
520
- it 'moves original conditions under $or' do
521
- expect(selection.selector).to eq({
522
- "$or" => [{'foo' => 'bar'}, { "field" => [ 1, 2 ] }]
523
- })
1186
+ context 'when there are multiple arguments' do
1187
+
1188
+ let(:selection) do
1189
+ query.where(foo: 'bar').any_of({field: [ 1, 2 ]}, {hello: 'world'})
1190
+ end
1191
+
1192
+ it 'adds the new condition' do
1193
+ expect(selection.selector).to eq(
1194
+ 'foo' => 'bar',
1195
+ '$or' => [
1196
+ {'field' => [1, 2]},
1197
+ {'hello' => 'world'},
1198
+ ],
1199
+ )
1200
+ end
524
1201
  end
525
1202
  end
526
1203
 
527
1204
  context 'when query already has an $or condition and another condition' do
528
1205
 
529
1206
  let(:selection) do
530
- query.or(field: [ 1, 2 ]).where(foo: 'bar').or(test: 1)
1207
+ query.or(field: [ 1, 2 ]).where(foo: 'bar').any_of(test: 1)
531
1208
  end
532
1209
 
533
- it 'unions existing conditions' do
1210
+ it 'adds the new condition' do
534
1211
  expect(selection.selector).to eq(
535
- '$or' => [
536
- {
537
- "$or" => [{ "field" => [ 1, 2 ] }],
538
- 'foo' => 'bar',
539
- },
540
- {'test' => 1},
541
- ]
1212
+ '$or' => [{'field' => [1, 2]}],
1213
+ 'foo' => 'bar',
1214
+ 'test' => 1,
542
1215
  )
543
1216
  end
544
1217
  end
@@ -549,7 +1222,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
549
1222
  context "when the criteria are for different fields" do
550
1223
 
551
1224
  let(:selection) do
552
- query.or({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
1225
+ query.any_of({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
553
1226
  end
554
1227
 
555
1228
  it_behaves_like 'returns a cloned query'
@@ -567,7 +1240,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
567
1240
  context "when the criteria uses a Key instance" do
568
1241
 
569
1242
  let(:selection) do
570
- query.or({ first: [ 1, 2 ] }, { :second.gt => 3 })
1243
+ query.any_of({ first: [ 1, 2 ] }, { :second.gt => 3 })
571
1244
  end
572
1245
 
573
1246
  it "adds the $or selector" do
@@ -582,229 +1255,196 @@ describe Mongoid::Criteria::Queryable::Selectable do
582
1255
  it_behaves_like 'returns a cloned query'
583
1256
  end
584
1257
 
585
- context "when a criterion has an aliased field" do
1258
+ context 'when criteria are simple and handled via Key' do
1259
+ shared_examples_for 'adds conditions with $or' do
586
1260
 
587
- let(:selection) do
588
- query.or({ id: 1 })
589
- end
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
590
1269
 
591
- it "adds the $or selector and aliases the field" do
592
- expect(selection.selector).to eq({
593
- "$or" => [ { "_id" => 1 } ]
594
- })
1270
+ it_behaves_like 'returns a cloned query'
595
1271
  end
596
1272
 
597
- it_behaves_like 'returns a cloned query'
598
- end
1273
+ shared_examples_for 'adds one condition' do
599
1274
 
600
- context "when a criterion is wrapped in an array" do
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
601
1283
 
602
- let(:selection) do
603
- query.or([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
1284
+ it_behaves_like 'returns a cloned query'
604
1285
  end
605
1286
 
606
- it_behaves_like 'returns a cloned query'
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
607
1291
 
608
- it "adds the $or selector" do
609
- expect(selection.selector).to eq({
610
- "$or" => [
611
- { "first" => [ 1, 2 ] },
612
- { "second" => { "$gt" => 3 }}
613
- ]
614
- })
1292
+ it_behaves_like 'adds one condition'
615
1293
  end
616
- end
617
1294
 
618
- context "when the criteria are on the same field" do
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
619
1299
 
620
- let(:selection) do
621
- query.or({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
1300
+ it_behaves_like 'adds conditions with $or'
622
1301
  end
623
1302
 
624
- it_behaves_like 'returns a cloned query'
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
625
1307
 
626
- it "appends both $or expressions" do
627
- expect(selection.selector).to eq({
628
- "$or" => [
629
- { "first" => [ 1, 2 ] },
630
- { "first" => [ 3, 4 ] }
631
- ]
632
- })
1308
+ it_behaves_like 'adds conditions with $or'
633
1309
  end
634
1310
  end
635
- end
636
1311
 
637
- context "when chaining the criterion" do
1312
+ context 'when criteria are handled via Key and simple' do
1313
+ shared_examples_for 'adds conditions with $or' do
638
1314
 
639
- context "when the criterion are for different fields" do
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
640
1323
 
641
- let(:selection) do
642
- query.or(first: [ 1, 2 ]).or(second: [ 3, 4 ])
1324
+ it_behaves_like 'returns a cloned query'
643
1325
  end
644
1326
 
645
- it_behaves_like 'returns a cloned query'
646
-
647
- it "adds the $or selectors" do
648
- expect(selection.selector).to eq({
649
- "$or" => [
650
- { "first" => [ 1, 2 ] },
651
- { "second" => [ 3, 4 ] }
652
- ]
653
- })
654
- end
655
- end
1327
+ shared_examples_for 'adds one condition' do
656
1328
 
657
- context "when the criterion are on the same field" do
1329
+ it "adds one condition" do
1330
+ expect(selection.selector).to eq({
1331
+ 'field' => {'$gt' => 3},
1332
+ '$and' => ['field' => 5],
1333
+ })
1334
+ end
658
1335
 
659
- let(:selection) do
660
- query.or(first: [ 1, 2 ]).or(first: [ 3, 4 ])
1336
+ it_behaves_like 'returns a cloned query'
661
1337
  end
662
1338
 
663
- it_behaves_like 'returns a cloned query'
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
664
1343
 
665
- it "appends both $or expressions" do
666
- expect(selection.selector).to eq({
667
- "$or" => [
668
- { "first" => [ 1, 2 ] },
669
- { "first" => [ 3, 4 ] }
670
- ]
671
- })
1344
+ it_behaves_like 'adds one condition'
672
1345
  end
673
- end
674
- end
675
- end
676
-
677
- describe "#nor" do
678
-
679
- let(:tested_method) { :nor }
680
- let(:expected_operator) { '$nor' }
681
-
682
- it_behaves_like 'a non-combining logical operation'
683
-
684
- context "when provided no criterion" do
685
-
686
- let(:selection) do
687
- query.nor
688
- end
689
-
690
- it "does not add any criterion" do
691
- expect(selection.selector).to eq({})
692
- end
693
-
694
- it "returns the query" do
695
- expect(selection).to eq(query)
696
- end
697
-
698
- it_behaves_like 'returns a cloned query'
699
- end
700
1346
 
701
- context "when provided nil" do
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
702
1351
 
703
- let(:selection) do
704
- query.nor(nil)
705
- end
1352
+ it_behaves_like 'adds conditions with $or'
1353
+ end
706
1354
 
707
- it "does not add any criterion" do
708
- expect(selection.selector).to eq({})
709
- end
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
710
1359
 
711
- it "returns the query" do
712
- expect(selection).to eq(query)
1360
+ it_behaves_like 'adds conditions with $or'
1361
+ end
713
1362
  end
714
1363
 
715
- it_behaves_like 'returns a cloned query'
716
- end
1364
+ context "when a criterion has an aliased field" do
717
1365
 
718
- context "when provided a single criterion" do
1366
+ let(:selection) do
1367
+ query.any_of({ id: 1 })
1368
+ end
719
1369
 
720
- let(:selection) do
721
- query.nor(field: [ 1, 2 ])
722
- end
1370
+ it "adds the $or selector and aliases the field" do
1371
+ expect(selection.selector).to eq(
1372
+ "_id" => 1,
1373
+ )
1374
+ end
723
1375
 
724
- it "adds the $nor selector" do
725
- expect(selection.selector).to eq({
726
- "$nor" => [{"field" => [ 1, 2 ] }]
727
- })
1376
+ it_behaves_like 'returns a cloned query'
728
1377
  end
729
1378
 
730
- it_behaves_like 'returns a cloned query'
731
- end
732
-
733
- context "when provided multiple criterion" do
734
-
735
- context "when the criterion are fnor different fields" do
1379
+ context "when a criterion is wrapped in an array" do
736
1380
 
737
1381
  let(:selection) do
738
- query.nor({ first: [ 1, 2 ] }, { second: [ 3, 4 ] })
1382
+ query.any_of([{ first: [ 1, 2 ] }, { :second.gt => 3 }])
739
1383
  end
740
1384
 
741
- it "adds the $nor selector" do
1385
+ it_behaves_like 'returns a cloned query'
1386
+
1387
+ it "adds the $or selector" do
742
1388
  expect(selection.selector).to eq({
743
- "$nor" => [
1389
+ "$or" => [
744
1390
  { "first" => [ 1, 2 ] },
745
- { "second" => [ 3, 4 ] }
1391
+ { "second" => { "$gt" => 3 }}
746
1392
  ]
747
1393
  })
748
1394
  end
749
-
750
- it_behaves_like 'returns a cloned query'
751
1395
  end
752
1396
 
753
- context "when the criterion are on the same field" do
1397
+ context "when the criteria are on the same field" do
754
1398
 
755
1399
  let(:selection) do
756
- query.nor({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
1400
+ query.any_of({ first: [ 1, 2 ] }, { first: [ 3, 4 ] })
757
1401
  end
758
1402
 
759
- it "appends both $nor expressions" do
1403
+ it_behaves_like 'returns a cloned query'
1404
+
1405
+ it "appends both $or expressions" do
760
1406
  expect(selection.selector).to eq({
761
- "$nor" => [
1407
+ "$or" => [
762
1408
  { "first" => [ 1, 2 ] },
763
1409
  { "first" => [ 3, 4 ] }
764
1410
  ]
765
1411
  })
766
1412
  end
767
-
768
- it_behaves_like 'returns a cloned query'
769
1413
  end
770
1414
  end
771
1415
 
772
- context "when chaining the criterion" do
1416
+ context "when chaining the criteria" do
773
1417
 
774
- context "when the criterion are fnor different fields" do
1418
+ context "when the criteria are for different fields" do
775
1419
 
776
1420
  let(:selection) do
777
- query.nor(first: [ 1, 2 ]).nor(second: [ 3, 4 ])
778
- end
779
-
780
- it "adds the $nor selectors" do
781
- expect(selection.selector).to eq({
782
- "$nor" => [
783
- { "first" => [ 1, 2 ] },
784
- { "second" => [ 3, 4 ] }
785
- ]
786
- })
1421
+ query.any_of(first: [ 1, 2 ]).any_of(second: [ 3, 4 ])
787
1422
  end
788
1423
 
789
1424
  it_behaves_like 'returns a cloned query'
1425
+
1426
+ it "adds the conditions separately" do
1427
+ expect(selection.selector).to eq(
1428
+ "first" => [ 1, 2 ],
1429
+ "second" => [ 3, 4 ],
1430
+ )
1431
+ end
790
1432
  end
791
1433
 
792
- context "when the criterion are on the same field" do
1434
+ context "when the criteria are on the same field" do
793
1435
 
794
1436
  let(:selection) do
795
- query.nor(first: [ 1, 2 ]).nor(first: [ 3, 4 ])
796
- end
797
-
798
- it "appends both $nor expressions" do
799
- expect(selection.selector).to eq({
800
- "$nor" => [
801
- { "first" => [ 1, 2 ] },
802
- { "first" => [ 3, 4 ] }
803
- ]
804
- })
1437
+ query.any_of(first: [ 1, 2 ]).any_of(first: [ 3, 4 ])
805
1438
  end
806
1439
 
807
1440
  it_behaves_like 'returns a cloned query'
1441
+
1442
+ it "adds the conditions separately" do
1443
+ expect(selection.selector).to eq(
1444
+ "first" => [ 1, 2 ],
1445
+ '$and' => [{"first" => [ 3, 4 ]}],
1446
+ )
1447
+ end
808
1448
  end
809
1449
  end
810
1450
  end
@@ -894,6 +1534,25 @@ describe Mongoid::Criteria::Queryable::Selectable do
894
1534
  end
895
1535
  end
896
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
+
897
1556
  context "when the following criteria is a where" do
898
1557
 
899
1558
  let(:selection) do