mongoid 6.4.0 → 6.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Rakefile +26 -0
- data/lib/mongoid.rb +1 -1
- data/lib/mongoid/clients/sessions.rb +2 -2
- data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
- data/lib/mongoid/contextual/map_reduce.rb +4 -4
- data/lib/mongoid/contextual/memory.rb +4 -4
- data/lib/mongoid/contextual/mongo.rb +3 -3
- data/lib/mongoid/criteria/modifiable.rb +12 -2
- data/lib/mongoid/criteria/queryable/selectable.rb +34 -7
- data/lib/mongoid/document.rb +4 -4
- data/lib/mongoid/extensions/big_decimal.rb +1 -1
- data/lib/mongoid/extensions/regexp.rb +1 -0
- data/lib/mongoid/extensions/string.rb +3 -1
- data/lib/mongoid/indexable.rb +4 -4
- data/lib/mongoid/matchable.rb +3 -0
- data/lib/mongoid/matchable/nor.rb +37 -0
- data/lib/mongoid/persistable.rb +1 -1
- data/lib/mongoid/persistable/creatable.rb +2 -2
- data/lib/mongoid/persistable/deletable.rb +2 -2
- data/lib/mongoid/persistable/settable.rb +5 -5
- data/lib/mongoid/persistable/updatable.rb +2 -2
- data/lib/mongoid/persistable/upsertable.rb +1 -1
- data/lib/mongoid/persistence_context.rb +4 -0
- data/lib/mongoid/query_cache.rb +21 -10
- data/lib/mongoid/railtie.rb +17 -0
- data/lib/mongoid/railties/controller_runtime.rb +86 -0
- data/lib/mongoid/relations/embedded/batchable.rb +4 -4
- data/lib/mongoid/relations/embedded/many.rb +23 -0
- data/lib/mongoid/relations/many.rb +2 -2
- data/lib/mongoid/relations/referenced/many.rb +1 -1
- data/lib/mongoid/relations/touchable.rb +1 -1
- data/lib/mongoid/reloadable.rb +1 -1
- data/lib/mongoid/scopable.rb +3 -3
- data/lib/mongoid/tasks/database.rb +2 -2
- data/lib/mongoid/threaded.rb +36 -0
- data/lib/mongoid/version.rb +1 -1
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -0
- data/spec/app/models/array_field.rb +7 -0
- data/spec/app/models/delegating_patient.rb +16 -0
- data/spec/integration/document_spec.rb +22 -0
- data/spec/mongoid/clients/factory_spec.rb +52 -28
- data/spec/mongoid/clients/options_spec.rb +30 -15
- data/spec/mongoid/clients/sessions_spec.rb +12 -3
- data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
- data/spec/mongoid/contextual/mongo_spec.rb +2 -2
- data/spec/mongoid/criteria/modifiable_spec.rb +59 -10
- data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +3 -3
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +42 -3
- data/spec/mongoid/criteria/queryable/selector_spec.rb +2 -2
- data/spec/mongoid/criteria/scopable_spec.rb +81 -0
- data/spec/mongoid/criteria_spec.rb +4 -1
- data/spec/mongoid/document_spec.rb +54 -0
- data/spec/mongoid/extensions/big_decimal_spec.rb +9 -9
- data/spec/mongoid/extensions/regexp_spec.rb +23 -0
- data/spec/mongoid/extensions/string_spec.rb +35 -7
- data/spec/mongoid/fields_spec.rb +1 -1
- data/spec/mongoid/findable_spec.rb +1 -1
- data/spec/mongoid/matchable/nor_spec.rb +209 -0
- data/spec/mongoid/matchable_spec.rb +26 -1
- data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
- data/spec/mongoid/persistable/settable_spec.rb +35 -1
- data/spec/mongoid/query_cache_spec.rb +73 -18
- data/spec/mongoid/relations/embedded/many_spec.rb +246 -16
- data/spec/mongoid/scopable_spec.rb +13 -0
- data/spec/mongoid/threaded_spec.rb +68 -0
- data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/cluster_config.rb +158 -0
- data/spec/support/constraints.rb +101 -0
- data/spec/support/macros.rb +20 -0
- data/spec/support/spec_config.rb +42 -0
- metadata +41 -23
- metadata.gz.sig +0 -0
@@ -64,7 +64,7 @@ module Mongoid
|
|
64
64
|
selector = _parent.atomic_selector
|
65
65
|
_root.collection.find(selector).update_one(
|
66
66
|
positionally(selector, atomic_deletes),
|
67
|
-
session:
|
67
|
+
session: _session)
|
68
68
|
end
|
69
69
|
true
|
70
70
|
end
|
@@ -80,7 +80,7 @@ module Mongoid
|
|
80
80
|
#
|
81
81
|
# @since 4.0.0
|
82
82
|
def delete_as_root
|
83
|
-
collection.find(atomic_selector).delete_one(session:
|
83
|
+
collection.find(atomic_selector).delete_one(session: _session)
|
84
84
|
true
|
85
85
|
end
|
86
86
|
|
@@ -25,15 +25,15 @@ module Mongoid
|
|
25
25
|
|
26
26
|
field_and_value_hash = hasherizer(field.split('.'), value)
|
27
27
|
field = field_and_value_hash.keys.first.to_s
|
28
|
+
value = field_and_value_hash[field]
|
28
29
|
|
29
|
-
if fields[field] && fields[field].type == Hash && attributes.key?(field)
|
30
|
+
if fields[field] && fields[field].type == Hash && attributes.key?(field) && Hash === value && !value.empty?
|
30
31
|
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
31
|
-
value = (attributes[field] || {}).merge(
|
32
|
-
process_attribute(field.to_s, value)
|
33
|
-
else
|
34
|
-
process_attribute(field.to_s, field_and_value_hash[field])
|
32
|
+
value = (attributes[field] || {}).merge(value, &merger)
|
35
33
|
end
|
36
34
|
|
35
|
+
process_attribute(field.to_s, value)
|
36
|
+
|
37
37
|
unless relations.include?(field.to_s)
|
38
38
|
ops[atomic_attribute_name(field)] = attributes[field]
|
39
39
|
end
|
@@ -134,9 +134,9 @@ module Mongoid
|
|
134
134
|
unless updates.empty?
|
135
135
|
coll = collection(_root)
|
136
136
|
selector = atomic_selector
|
137
|
-
coll.find(selector).update_one(positionally(selector, updates), session:
|
137
|
+
coll.find(selector).update_one(positionally(selector, updates), session: _session)
|
138
138
|
conflicts.each_pair do |key, value|
|
139
|
-
coll.find(selector).update_one(positionally(selector, { key => value }), session:
|
139
|
+
coll.find(selector).update_one(positionally(selector, { key => value }), session: _session)
|
140
140
|
end
|
141
141
|
end
|
142
142
|
end
|
@@ -107,6 +107,10 @@ module Mongoid
|
|
107
107
|
#
|
108
108
|
# @since 6.0.0
|
109
109
|
def client
|
110
|
+
client_options = send(:client_options)
|
111
|
+
if client_options[:read].is_a?(Symbol)
|
112
|
+
client_options = client_options.merge(read: {mode: client_options[:read]})
|
113
|
+
end
|
110
114
|
@client ||= (client = Clients.with_name(client_name)
|
111
115
|
client = client.use(database_name) if database_name_option
|
112
116
|
client.with(client_options))
|
data/lib/mongoid/query_cache.rb
CHANGED
@@ -224,14 +224,29 @@ module Mongoid
|
|
224
224
|
unless cursor = cached_cursor
|
225
225
|
read_with_retry do
|
226
226
|
server = server_selector.select_server(cluster)
|
227
|
-
|
228
|
-
|
227
|
+
result = send_initial_query(server)
|
228
|
+
if result.cursor_id == 0 || result.cursor_id.nil?
|
229
|
+
cursor = CachedCursor.new(view, result, server)
|
230
|
+
QueryCache.cache_table[cache_key] = cursor
|
231
|
+
else
|
232
|
+
cursor = Mongo::Cursor.new(view, result, server)
|
233
|
+
end
|
229
234
|
end
|
230
235
|
end
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
236
|
+
|
237
|
+
if block_given?
|
238
|
+
if limit && limit != -1
|
239
|
+
cursor.to_a[0...limit].each do |doc|
|
240
|
+
yield doc
|
241
|
+
end
|
242
|
+
else
|
243
|
+
cursor.each do |doc|
|
244
|
+
yield doc
|
245
|
+
end
|
246
|
+
end
|
247
|
+
else
|
248
|
+
cursor
|
249
|
+
end
|
235
250
|
end
|
236
251
|
end
|
237
252
|
|
@@ -241,10 +256,6 @@ module Mongoid
|
|
241
256
|
if limit
|
242
257
|
key = [ collection.namespace, selector, nil, skip, sort, projection, collation ]
|
243
258
|
cursor = QueryCache.cache_table[key]
|
244
|
-
if cursor
|
245
|
-
limited_docs = cursor.to_a[0...limit.abs]
|
246
|
-
cursor.instance_variable_set(:@cached_documents, limited_docs)
|
247
|
-
end
|
248
259
|
end
|
249
260
|
cursor || QueryCache.cache_table[cache_key]
|
250
261
|
end
|
data/lib/mongoid/railtie.rb
CHANGED
@@ -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
|
@@ -39,7 +39,7 @@ module Mongoid
|
|
39
39
|
unless docs.empty?
|
40
40
|
collection.find(selector).update_one(
|
41
41
|
positionally(selector, "$unset" => { path => true }),
|
42
|
-
session:
|
42
|
+
session: _session
|
43
43
|
)
|
44
44
|
post_process_batch_remove(docs, :delete)
|
45
45
|
end
|
@@ -60,7 +60,7 @@ module Mongoid
|
|
60
60
|
if !docs.empty?
|
61
61
|
collection.find(selector).update_one(
|
62
62
|
positionally(selector, "$pullAll" => { path => removals }),
|
63
|
-
session:
|
63
|
+
session: _session
|
64
64
|
)
|
65
65
|
post_process_batch_remove(docs, method)
|
66
66
|
end
|
@@ -136,7 +136,7 @@ module Mongoid
|
|
136
136
|
if insertable?
|
137
137
|
collection.find(selector).update_one(
|
138
138
|
positionally(selector, '$set' => { path => inserts }),
|
139
|
-
session:
|
139
|
+
session: _session
|
140
140
|
)
|
141
141
|
post_process_batch_insert(docs)
|
142
142
|
end
|
@@ -161,7 +161,7 @@ module Mongoid
|
|
161
161
|
if insertable?
|
162
162
|
collection.find(selector).update_one(
|
163
163
|
positionally(selector, '$push' => { path => { '$each' => pushes } }),
|
164
|
-
session:
|
164
|
+
session: _session
|
165
165
|
)
|
166
166
|
post_process_batch_insert(docs)
|
167
167
|
end
|
@@ -294,6 +294,29 @@ module Mongoid
|
|
294
294
|
end
|
295
295
|
end
|
296
296
|
|
297
|
+
# Shift documents off the relation. This can be a single document or
|
298
|
+
# multiples, and will automatically persist the changes.
|
299
|
+
#
|
300
|
+
# @example Shift a single document.
|
301
|
+
# relation.shift
|
302
|
+
#
|
303
|
+
# @example Shift multiple documents.
|
304
|
+
# relation.shift(3)
|
305
|
+
#
|
306
|
+
# @param [ Integer ] count The number of documents to shift, or 1 if not
|
307
|
+
# provided.
|
308
|
+
#
|
309
|
+
# @return [ Document, Array<Document> ] The shifted document(s).
|
310
|
+
def shift(count = nil)
|
311
|
+
if count
|
312
|
+
if target.size > 0 && docs = target[0, count]
|
313
|
+
docs.each { |doc| delete(doc) }
|
314
|
+
end
|
315
|
+
else
|
316
|
+
delete(target[0])
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
297
320
|
# Substitutes the supplied target documents for the existing documents
|
298
321
|
# in the relation.
|
299
322
|
#
|
@@ -477,7 +477,7 @@ module Mongoid
|
|
477
477
|
# @since 3.0.0
|
478
478
|
def persist_delayed(docs, inserts)
|
479
479
|
unless docs.empty?
|
480
|
-
collection.insert_many(inserts, session:
|
480
|
+
collection.insert_many(inserts, session: _session)
|
481
481
|
docs.each do |doc|
|
482
482
|
doc.new_record = false
|
483
483
|
doc.run_after_callbacks(:create, :save)
|
@@ -31,7 +31,7 @@ module Mongoid
|
|
31
31
|
touches = touch_atomic_updates(field)
|
32
32
|
unless touches["$set"].blank?
|
33
33
|
selector = atomic_selector
|
34
|
-
_root.collection.find(selector).update_one(positionally(selector, touches), session:
|
34
|
+
_root.collection.find(selector).update_one(positionally(selector, touches), session: _session)
|
35
35
|
end
|
36
36
|
run_callbacks(:touch)
|
37
37
|
true
|
data/lib/mongoid/reloadable.rb
CHANGED
@@ -58,7 +58,7 @@ module Mongoid
|
|
58
58
|
#
|
59
59
|
# @since 2.3.2
|
60
60
|
def reload_root_document
|
61
|
-
{}.merge(collection.find({ _id: _id }, session:
|
61
|
+
{}.merge(collection.find({ _id: _id }, session: _session).read(mode: :primary).first || {})
|
62
62
|
end
|
63
63
|
|
64
64
|
# Reload the embedded document.
|
data/lib/mongoid/scopable.rb
CHANGED
@@ -102,7 +102,7 @@ module Mongoid
|
|
102
102
|
#
|
103
103
|
# @since 3.0.0
|
104
104
|
def default_scopable?
|
105
|
-
default_scoping? && !Threaded.
|
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.
|
247
|
+
Threaded.begin_without_default_scope(self)
|
248
248
|
yield
|
249
249
|
ensure
|
250
|
-
Threaded.
|
250
|
+
Threaded.exit_without_default_scope(self)
|
251
251
|
end
|
252
252
|
|
253
253
|
private
|
@@ -44,7 +44,7 @@ module Mongoid
|
|
44
44
|
models.each do |model|
|
45
45
|
unless model.embedded?
|
46
46
|
begin
|
47
|
-
model.collection.indexes(session: model.send(:
|
47
|
+
model.collection.indexes(session: model.send(:_session)).each do |index|
|
48
48
|
# ignore default index
|
49
49
|
unless index['name'] == '_id_'
|
50
50
|
key = index['key'].symbolize_keys
|
@@ -77,7 +77,7 @@ module Mongoid
|
|
77
77
|
indexes.each do |index|
|
78
78
|
key = index['key'].symbolize_keys
|
79
79
|
collection = model.collection
|
80
|
-
collection.indexes(session: model.send(:
|
80
|
+
collection.indexes(session: model.send(:_session)).drop_one(key)
|
81
81
|
logger.info(
|
82
82
|
"MONGOID: Removed index '#{index['name']}' on collection " +
|
83
83
|
"'#{collection.name}' in database '#{collection.database.name}'."
|
data/lib/mongoid/threaded.rb
CHANGED
@@ -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?
|
data/lib/mongoid/version.rb
CHANGED
@@ -11,6 +11,10 @@ development:
|
|
11
11
|
hosts:
|
12
12
|
- localhost:27017
|
13
13
|
options:
|
14
|
+
# Note that all options listed below are Ruby driver client options (the mongo gem).
|
15
|
+
# Please refer to the driver documentation of the version of the mongo gem you are using
|
16
|
+
# for the most up-to-date list of options.
|
17
|
+
#
|
14
18
|
# Change the default write concern. (default = { w: 1 })
|
15
19
|
# write:
|
16
20
|
# w: 1
|