mongoid 6.4.2 → 6.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4c5d349e3de6af570dd7b1347fd76b6056848149180dc355e116f67f8ac2c3c6
4
- data.tar.gz: e9be28598f701bb124ba6626bb777da07d05bca692224110078700557f2c0aa9
3
+ metadata.gz: ccf32387a001233a1704c7c20281e25dfbb8262134114bbdfedef5dcd888032c
4
+ data.tar.gz: 25fecceb3036b5feb86d572e8994c60a8cbac0c47daabd1307788723bc8090f3
5
5
  SHA512:
6
- metadata.gz: 1bf33975f8969cc06ac717cef1a443429dd6ad8e8a7cd755c8b74ed9b7bc0e1a113413dac81098f54f9c181b91d03fc5958adfaaddb03d7fb91caa9f2e11dc84
7
- data.tar.gz: c5ef0576ed9941a5ba0f0865fdaff885766e4d6fe6901d1037ce408a0acdc0e00fdbfe503a5a7d721d8178a83c39c6e5893aaf0c258e871b3dca17939d0e7d97
6
+ metadata.gz: 03be9dcb886f2bc83a7b57a93fecf2bf3e0732668ee173c30d810fb0459cc4ec4eae277717e4bd0695d969ec378d58e4767f5e99ea73b036eb764637da1cb8a6
7
+ data.tar.gz: fe0e129f059daabb83fd638455ea22fe0da7800b5095400210ee4d71acf6b02d78847f60e8393ae15815fbb57d4753bfbb2be0db76ca2351f75f075d3a65b5c7
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -134,13 +134,21 @@ module Mongoid
134
134
  ::Boolean.evolve(value)
135
135
  end
136
136
 
137
- # Add a $geoIntersects or $geoWithin selection. Symbol operators must be used as shown in
138
- # the examples to expand the criteria.
137
+ # Add a $geoIntersects or $geoWithin selection. Symbol operators must
138
+ # be used as shown in the examples to expand the criteria.
139
139
  #
140
140
  # @note The only valid geometry shapes for a $geoIntersects are:
141
141
  # :intersects_line, :intersects_point, and :intersects_polygon.
142
142
  #
143
- # @note The only valid geometry shape for a $geoWithin is :within_polygon
143
+ # @note The only valid options for a $geoWithin query are the geometry
144
+ # shape :within_polygon and the operator :within_box.
145
+ #
146
+ # @note The :within_box operator for the $geoWithin query expects the
147
+ # lower left (south west) coordinate pair as the first argument and
148
+ # the upper right (north east) as the second argument.
149
+ # Important: When latitude and longitude are passed, longitude is
150
+ # expected as the first element of the coordinate pair.
151
+ # Source: https://docs.mongodb.com/manual/reference/operator/query/box/
144
152
  #
145
153
  # @example Add a geo intersect criterion for a line.
146
154
  # query.geo_spacial(:location.intersects_line => [[ 1, 10 ], [ 2, 10 ]])
@@ -154,6 +162,9 @@ module Mongoid
154
162
  # @example Add a geo within criterion for a polygon.
155
163
  # query.geo_spacial(:location.within_polygon => [[ 1, 10 ], [ 2, 10 ], [ 1, 10 ]])
156
164
  #
165
+ # @example Add a geo within criterion for a box.
166
+ # query.geo_spacial(:location.within_box => [[ 1, 10 ], [ 2, 10 ])
167
+ #
157
168
  # @param [ Hash ] criterion The criterion.
158
169
  #
159
170
  # @return [ Selectable ] The cloned selectable.
@@ -174,6 +185,7 @@ module Mongoid
174
185
  key :within_polygon, :override, "$geoWithin", "$geometry" do |value|
175
186
  { "type" => POLYGON, "coordinates" => value }
176
187
  end
188
+ key :within_box, :override, "$geoWithin", "$box"
177
189
 
178
190
  # Add the $gt criterion to the selector.
179
191
  #
@@ -501,6 +513,11 @@ module Mongoid
501
513
  # @example Construct a text search selector with options.
502
514
  # selectable.text_search("testing", :$language => "fr")
503
515
  #
516
+ # @note Per https://docs.mongodb.com/manual/reference/operator/query/text/
517
+ # it is not currently possible to supply multiple text search
518
+ # conditions in a query. Mongoid will build such a query but the
519
+ # server will return an error when trying to execute it.
520
+ #
504
521
  # @param [ String, Symbol ] terms A string of terms that MongoDB parses
505
522
  # and uses to query the text index.
506
523
  # @param [ Hash ] opts Text search options. See MongoDB documentation
@@ -512,9 +529,19 @@ module Mongoid
512
529
  def text_search(terms, opts = nil)
513
530
  clone.tap do |query|
514
531
  if terms
515
- criterion = { :$text => { :$search => terms } }
516
- criterion[:$text].merge!(opts) if opts
517
- query.selector = criterion
532
+ criterion = {'$text' => { '$search' => terms }}
533
+ criterion['$text'].merge!(opts) if opts
534
+ if query.selector['$text']
535
+ # Per https://docs.mongodb.com/manual/reference/operator/query/text/
536
+ # multiple $text expressions are not currently supported by
537
+ # MongoDB server, but build the query correctly instead of
538
+ # overwriting previous text search condition with the currently
539
+ # given one.
540
+ Mongoid.logger.warn('Multiple $text expressions per query are not currently supported by the server')
541
+ query.selector = {'$and' => [query.selector]}.merge(criterion)
542
+ else
543
+ query.selector = query.selector.merge(criterion)
544
+ end
518
545
  end
519
546
  end
520
547
  end
@@ -190,11 +190,11 @@ module Mongoid
190
190
  #
191
191
  # @since 5.1.0
192
192
  def as_json(options = nil)
193
- if options && (options[:compact] == true)
194
- super(options).reject! { |k,v| v.nil? }
195
- else
196
- super(options)
193
+ rv = super
194
+ if options && options[:compact]
195
+ rv = rv.compact
197
196
  end
197
+ rv
198
198
  end
199
199
 
200
200
  # Returns an instance of the specified class with the attributes,
@@ -53,7 +53,7 @@ module Mongoid
53
53
  #
54
54
  # @since 3.0.0
55
55
  def demongoize(object)
56
- object && object.numeric? ? ::BigDecimal.new(object.to_s) : nil
56
+ object && object.numeric? ? BigDecimal(object.to_s) : nil
57
57
  end
58
58
 
59
59
  # Mongoize an object of any type to how it's stored in the db as a String.
@@ -17,6 +17,7 @@ module Mongoid
17
17
  #
18
18
  # @since 3.0.0
19
19
  def mongoize(object)
20
+ return nil if object.nil?
20
21
  ::Regexp.new(object)
21
22
  end
22
23
  end
@@ -102,6 +102,23 @@ module Rails
102
102
  puts "There is a configuration error with the current mongoid.yml."
103
103
  puts e.message
104
104
  end
105
+
106
+ # Include Controller extension that measures Mongoid runtime
107
+ # during request processing. The value then appears in Rails'
108
+ # instrumentation event `process_action.action_controller`.
109
+ #
110
+ # The measurement is made via internal Mongo monitoring subscription
111
+ initializer "mongoid.runtime-metric" do
112
+ require "mongoid/railties/controller_runtime"
113
+
114
+ ActiveSupport.on_load :action_controller do
115
+ include ::Mongoid::Railties::ControllerRuntime::ControllerExtension
116
+ end
117
+
118
+ Mongo::Monitoring::Global.subscribe Mongo::Monitoring::COMMAND,
119
+ ::Mongoid::Railties::ControllerRuntime::Collector.new
120
+ end
121
+
105
122
  end
106
123
  end
107
124
  end
@@ -0,0 +1,86 @@
1
+ module Mongoid
2
+ module Railties
3
+ module ControllerRuntime
4
+
5
+ # This extension mimics the Rails' internal method to
6
+ # measure ActiveRecord runtime during request processing.
7
+ # It appends MongoDB runtime value (`mongoid_runtime`) into payload
8
+ # of instrumentation event `process_action.action_controller`.
9
+ module ControllerExtension
10
+ extend ActiveSupport::Concern
11
+
12
+ protected
13
+
14
+ attr_internal :mongoid_runtime
15
+
16
+ # Reset the runtime before each action.
17
+ def process_action(action, *args)
18
+ Collector.reset_runtime
19
+ super
20
+ end
21
+
22
+ # Override to collect the measurements.
23
+ def cleanup_view_runtime
24
+ mongo_rt_before_render = Collector.reset_runtime
25
+ runtime = super
26
+ mongo_rt_after_render = Collector.reset_runtime
27
+ self.mongoid_runtime = mongo_rt_before_render + mongo_rt_after_render
28
+ runtime - mongo_rt_after_render
29
+ end
30
+
31
+ # Add the measurement to the instrumentation event payload.
32
+ def append_info_to_payload(payload)
33
+ super
34
+ payload[:mongoid_runtime] = (mongoid_runtime || 0) + Collector.reset_runtime
35
+ end
36
+
37
+ module ClassMethods
38
+
39
+ # Append MongoDB runtime information to ActionController runtime
40
+ # log message.
41
+ def log_process_action(payload)
42
+ messages = super
43
+ mongoid_runtime = payload[:mongoid_runtime]
44
+ messages << ("MongoDB: %.1fms" % mongoid_runtime.to_f) if mongoid_runtime
45
+ messages
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ # The Collector of MongoDB runtime metric, that subscribes to Mongo
53
+ # driver command monitoring. Stores the value within a thread-local
54
+ # variable to provide correct accounting when an application issues
55
+ # MongoDB operations from background threads.
56
+ class Collector
57
+
58
+ VARIABLE_NAME = "Mongoid.controller_runtime".freeze
59
+
60
+ def started _; end
61
+
62
+ def _completed e
63
+ Collector.runtime += e.duration
64
+ end
65
+ alias :succeeded :_completed
66
+ alias :failed :_completed
67
+
68
+ def self.runtime
69
+ Thread.current[VARIABLE_NAME] ||= 0
70
+ end
71
+
72
+ def self.runtime= value
73
+ Thread.current[VARIABLE_NAME] = value
74
+ end
75
+
76
+ def self.reset_runtime
77
+ to_now = runtime
78
+ self.runtime = 0
79
+ to_now
80
+ end
81
+
82
+ end
83
+
84
+ end
85
+ end
86
+ end
@@ -102,7 +102,7 @@ module Mongoid
102
102
  #
103
103
  # @since 3.0.0
104
104
  def default_scopable?
105
- default_scoping? && !Threaded.executing?(:without_default_scope)
105
+ default_scoping? && !Threaded.without_default_scope?(self)
106
106
  end
107
107
 
108
108
  # Get a queryable, either the last one on the scope stack or a fresh one.
@@ -244,10 +244,10 @@ module Mongoid
244
244
  #
245
245
  # @since 3.0.0
246
246
  def without_default_scope
247
- Threaded.begin_execution("without_default_scope")
247
+ Threaded.begin_without_default_scope(self)
248
248
  yield
249
249
  ensure
250
- Threaded.exit_execution("without_default_scope")
250
+ Threaded.exit_without_default_scope(self)
251
251
  end
252
252
 
253
253
  private
@@ -163,6 +163,30 @@ module Mongoid
163
163
  validations_for(document.class).delete_one(document._id)
164
164
  end
165
165
 
166
+ # Begin suppressing default scopes for given model on the current thread.
167
+ #
168
+ # @example Begin without default scope stack.
169
+ # Threaded.begin_without_default_scope(klass)
170
+ #
171
+ # @param [ Class ] klass The model to suppress default scoping on.
172
+ #
173
+ # @api private
174
+ def begin_without_default_scope(klass)
175
+ stack(:without_default_scope).push(klass)
176
+ end
177
+
178
+ # Exit suppressing default scopes for given model on the current thread.
179
+ #
180
+ # @example Exit without default scope stack.
181
+ # Threaded.exit_without_default_scope(klass)
182
+ #
183
+ # @param [ Class ] klass The model to unsuppress default scoping on.
184
+ #
185
+ # @api private
186
+ def exit_without_default_scope(klass)
187
+ stack(:without_default_scope).delete(klass)
188
+ end
189
+
166
190
  # Get the global client override.
167
191
  #
168
192
  # @example Get the global client override.
@@ -247,6 +271,18 @@ module Mongoid
247
271
  end
248
272
  end
249
273
 
274
+ # Is the given klass' default scope suppressed on the current thread?
275
+ #
276
+ # @example Is the given klass' default scope suppressed?
277
+ # Threaded.without_default_scope?(klass)
278
+ #
279
+ # @param [ Class ] klass The model to check for default scope suppression.
280
+ #
281
+ # @api private
282
+ def without_default_scope?(klass)
283
+ stack(:without_default_scope).include?(klass)
284
+ end
285
+
250
286
  # Is the document autosaved on the current thread?
251
287
  #
252
288
  # @example Is the document autosaved?
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "6.4.2"
3
+ VERSION = "6.4.4"
4
4
  end
@@ -2363,7 +2363,7 @@ describe Mongoid::Contextual::Mongo do
2363
2363
  end
2364
2364
 
2365
2365
  it 'creates a pipeline with the selector as one of the $match criteria' do
2366
- expect(pipeline_match).to include({ :'$text' => { :'$search' => "New Order" } })
2366
+ expect(pipeline_match).to include({ '$text' => { '$search' => "New Order" } })
2367
2367
  end
2368
2368
 
2369
2369
  it 'creates a pipeline with the $exists operator as one of the $match criteria' do
@@ -7,7 +7,7 @@ describe BigDecimal do
7
7
  context "when provided a big decimal" do
8
8
 
9
9
  let(:big_decimal) do
10
- BigDecimal.new("123456.789")
10
+ BigDecimal("123456.789")
11
11
  end
12
12
 
13
13
  it "returns the decimal as a string" do
@@ -25,11 +25,11 @@ describe BigDecimal do
25
25
  context "when provided an array of big decimals" do
26
26
 
27
27
  let(:bd_one) do
28
- BigDecimal.new("123456.789")
28
+ BigDecimal("123456.789")
29
29
  end
30
30
 
31
31
  let(:bd_two) do
32
- BigDecimal.new("123333.789")
32
+ BigDecimal("123333.789")
33
33
  end
34
34
 
35
35
  let(:array) do
@@ -1073,6 +1073,26 @@ describe Mongoid::Criteria::Queryable::Selectable do
1073
1073
  })
1074
1074
  end
1075
1075
 
1076
+ context "when used with the $box operator ($geoWithin query) " do
1077
+ let(:selection) do
1078
+ query.geo_spacial(
1079
+ :location.within_box => [[ 1, 10 ], [ 2, 10 ]]
1080
+ )
1081
+ end
1082
+
1083
+ it "adds the $geoIntersects expression" do
1084
+ expect(selection.selector).to eq({
1085
+ "location" => {
1086
+ "$geoWithin" => {
1087
+ "$box" => [
1088
+ [ 1, 10 ], [ 2, 10 ]
1089
+ ]
1090
+ }
1091
+ }
1092
+ })
1093
+ end
1094
+ end
1095
+
1076
1096
  it_behaves_like "a cloning selection"
1077
1097
  end
1078
1098
  end
@@ -3369,7 +3389,7 @@ describe Mongoid::Criteria::Queryable::Selectable do
3369
3389
  end
3370
3390
 
3371
3391
  it "constructs a text search document" do
3372
- expect(selection.selector).to eq({ :$text => { :$search => "testing" }})
3392
+ expect(selection.selector).to eq({ '$text' => { '$search' => "testing" }})
3373
3393
  end
3374
3394
 
3375
3395
  it "returns the cloned selectable" do
@@ -3383,16 +3403,35 @@ describe Mongoid::Criteria::Queryable::Selectable do
3383
3403
  end
3384
3404
 
3385
3405
  it "constructs a text search document" do
3386
- expect(selection.selector[:$text][:$search]).to eq("essais")
3406
+ expect(selection.selector['$text']['$search']).to eq("essais")
3387
3407
  end
3388
3408
 
3389
3409
  it "add the options to the text search document" do
3390
- expect(selection.selector[:$text][:$language]).to eq("fr")
3410
+ expect(selection.selector['$text'][:$language]).to eq("fr")
3391
3411
  end
3392
3412
 
3393
3413
  it_behaves_like "a cloning selection"
3394
3414
  end
3395
3415
  end
3416
+
3417
+ context 'when given more than once' do
3418
+ let(:selection) do
3419
+ query.text_search("one").text_search('two')
3420
+ end
3421
+
3422
+ # MongoDB server can only handle one text expression at a time,
3423
+ # per https://docs.mongodb.com/manual/reference/operator/query/text/.
3424
+ # Nonetheless we test that the query is built correctly when
3425
+ # a user supplies more than one text condition.
3426
+ it 'merges conditions' do
3427
+ expect(Mongoid.logger).to receive(:warn)
3428
+ expect(selection.selector).to eq('$and' => [
3429
+ {'$text' => {'$search' => 'one'}}
3430
+ ],
3431
+ '$text' => {'$search' => 'two'},
3432
+ )
3433
+ end
3434
+ end
3396
3435
  end
3397
3436
 
3398
3437
  describe "#where" do
@@ -370,11 +370,11 @@ describe Mongoid::Criteria::Queryable::Selector do
370
370
  context "when providing an array" do
371
371
 
372
372
  let(:big_one) do
373
- BigDecimal.new("1.2321")
373
+ BigDecimal("1.2321")
374
374
  end
375
375
 
376
376
  let(:big_two) do
377
- BigDecimal.new("4.2222")
377
+ BigDecimal("4.2222")
378
378
  end
379
379
 
380
380
  let(:array) do
@@ -388,4 +388,85 @@ describe Mongoid::Criteria::Scopable do
388
388
  end
389
389
  end
390
390
  end
391
+
392
+ describe 'scope and where' do
393
+ class ScopeWhereModel
394
+ include Mongoid::Document
395
+
396
+ scope :foo, -> { where(foo: true) }
397
+ end
398
+
399
+ shared_examples_for 'restricts to both' do
400
+ it 'restricts to both' do
401
+ expect(result.selector['foo']).to eq(true)
402
+ expect(result.selector['hello']).to eq('world')
403
+ end
404
+ end
405
+
406
+ context 'scope is given first' do
407
+ let(:result) { ScopeWhereModel.foo.where(hello: 'world') }
408
+
409
+ it_behaves_like 'restricts to both'
410
+ end
411
+
412
+ context 'where is given first' do
413
+ let(:result) { ScopeWhereModel.where(hello: 'world').foo }
414
+
415
+ it_behaves_like 'restricts to both'
416
+ end
417
+ end
418
+
419
+ describe 'scope with argument and where' do
420
+ class ArgumentScopeWhereModel
421
+ include Mongoid::Document
422
+
423
+ scope :my_text_search, ->(search) { where('$text' => {'$search' => search}) }
424
+ end
425
+
426
+ shared_examples_for 'restricts to both' do
427
+ it 'restricts to both' do
428
+ expect(result.selector['$text']).to eq({'$search' => 'bar'})
429
+ expect(result.selector['hello']).to eq('world')
430
+ end
431
+ end
432
+
433
+ context 'scope is given first' do
434
+ let(:result) { ArgumentScopeWhereModel.my_text_search('bar').where(hello: 'world') }
435
+
436
+ it_behaves_like 'restricts to both'
437
+ end
438
+
439
+ context 'where is given first' do
440
+ let(:result) { ArgumentScopeWhereModel.where(hello: 'world').my_text_search('bar') }
441
+
442
+ it_behaves_like 'restricts to both'
443
+ end
444
+ end
445
+
446
+ describe 'built in text search scope and where' do
447
+ class TextSearchScopeWhereModel
448
+ include Mongoid::Document
449
+
450
+ # using the default text_search scope
451
+ end
452
+
453
+ shared_examples_for 'restricts to both' do
454
+ it 'restricts to both' do
455
+ expect(result.selector['$text']).to eq({'$search' => 'bar'})
456
+ expect(result.selector['hello']).to eq('world')
457
+ end
458
+ end
459
+
460
+ context 'scope is given first' do
461
+ let(:result) { TextSearchScopeWhereModel.text_search('bar').where(hello: 'world') }
462
+
463
+ it_behaves_like 'restricts to both'
464
+ end
465
+
466
+ context 'where is given first' do
467
+ let(:result) { TextSearchScopeWhereModel.where(hello: 'world').text_search('bar') }
468
+
469
+ it_behaves_like 'restricts to both'
470
+ end
471
+ end
391
472
  end