mongoid 8.0.10 → 8.1.0
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/CHANGELOG.md +3 -3
- data/README.md +3 -3
- data/Rakefile +18 -67
- data/lib/config/locales/en.yml +46 -14
- data/lib/mongoid/association/accessors.rb +3 -7
- data/lib/mongoid/association/builders.rb +1 -1
- data/lib/mongoid/association/eager_loadable.rb +0 -3
- data/lib/mongoid/association/embedded/batchable.rb +2 -2
- data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
- data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
- data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
- data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
- data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
- data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
- data/lib/mongoid/association/macros.rb +0 -6
- data/lib/mongoid/association/nested/one.rb +40 -2
- data/lib/mongoid/association/proxy.rb +1 -1
- data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
- data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
- data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
- data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
- data/lib/mongoid/association/reflections.rb +2 -2
- data/lib/mongoid/atomic.rb +7 -16
- data/lib/mongoid/attributes/dynamic.rb +1 -1
- data/lib/mongoid/attributes/nested.rb +2 -2
- data/lib/mongoid/attributes/processing.rb +5 -29
- data/lib/mongoid/attributes/projector.rb +1 -1
- data/lib/mongoid/attributes/readonly.rb +1 -1
- data/lib/mongoid/attributes.rb +8 -2
- data/lib/mongoid/changeable.rb +107 -5
- data/lib/mongoid/clients/storage_options.rb +2 -5
- data/lib/mongoid/clients/validators/storage.rb +1 -13
- data/lib/mongoid/collection_configurable.rb +58 -0
- data/lib/mongoid/composable.rb +2 -0
- data/lib/mongoid/config/defaults.rb +60 -0
- data/lib/mongoid/config/options.rb +0 -3
- data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
- data/lib/mongoid/config/validators.rb +1 -0
- data/lib/mongoid/config.rb +88 -27
- data/lib/mongoid/contextual/atomic.rb +1 -1
- data/lib/mongoid/contextual/memory.rb +233 -33
- data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
- data/lib/mongoid/contextual/mongo.rb +370 -133
- data/lib/mongoid/contextual/none.rb +162 -7
- data/lib/mongoid/contextual.rb +12 -0
- data/lib/mongoid/criteria/findable.rb +2 -2
- data/lib/mongoid/criteria/includable.rb +4 -3
- data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
- data/lib/mongoid/criteria/queryable/key.rb +1 -1
- data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
- data/lib/mongoid/criteria/queryable/optional.rb +8 -8
- data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
- data/lib/mongoid/criteria/queryable/selector.rb +1 -1
- data/lib/mongoid/criteria/queryable/storable.rb +1 -1
- data/lib/mongoid/criteria.rb +6 -5
- data/lib/mongoid/deprecable.rb +1 -2
- data/lib/mongoid/deprecation.rb +3 -3
- data/lib/mongoid/document.rb +1 -8
- data/lib/mongoid/errors/create_collection_failure.rb +33 -0
- data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
- data/lib/mongoid/errors/immutable_attribute.rb +26 -0
- data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
- data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
- data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
- data/lib/mongoid/errors.rb +4 -1
- data/lib/mongoid/extensions/hash.rb +2 -24
- data/lib/mongoid/extensions/object.rb +2 -2
- data/lib/mongoid/extensions/time.rb +2 -0
- data/lib/mongoid/fields/localized.rb +10 -0
- data/lib/mongoid/fields/standard.rb +10 -0
- data/lib/mongoid/fields.rb +59 -35
- data/lib/mongoid/findable.rb +27 -3
- data/lib/mongoid/interceptable.rb +6 -116
- data/lib/mongoid/matcher/eq_impl.rb +1 -1
- data/lib/mongoid/matcher/type.rb +1 -1
- data/lib/mongoid/persistable/creatable.rb +1 -0
- data/lib/mongoid/persistable/deletable.rb +1 -1
- data/lib/mongoid/persistable/savable.rb +13 -1
- data/lib/mongoid/persistable/unsettable.rb +2 -2
- data/lib/mongoid/persistable/updatable.rb +51 -1
- data/lib/mongoid/persistable/upsertable.rb +20 -1
- data/lib/mongoid/persistable.rb +3 -0
- data/lib/mongoid/query_cache.rb +5 -1
- data/lib/mongoid/railties/database.rake +7 -2
- data/lib/mongoid/reloadable.rb +5 -3
- data/lib/mongoid/stateful.rb +22 -1
- data/lib/mongoid/tasks/database.rake +12 -0
- data/lib/mongoid/tasks/database.rb +20 -0
- data/lib/mongoid/timestamps/created.rb +1 -8
- data/lib/mongoid/traversable.rb +0 -12
- data/lib/mongoid/utils.rb +22 -0
- data/lib/mongoid/validatable/associated.rb +17 -98
- data/lib/mongoid/validatable/macros.rb +5 -5
- data/lib/mongoid/validatable.rb +4 -9
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/warnings.rb +17 -1
- data/lib/mongoid.rb +16 -3
- data/spec/integration/app_spec.rb +2 -6
- data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -40
- data/spec/integration/callbacks_spec.rb +99 -12
- data/spec/integration/discriminator_key_spec.rb +4 -5
- data/spec/integration/i18n_fallbacks_spec.rb +3 -2
- data/spec/mongoid/association/eager_spec.rb +2 -24
- data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
- data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
- data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
- data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
- data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
- data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
- data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -55
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
- data/spec/mongoid/association/syncable_spec.rb +1 -1
- data/spec/mongoid/association_spec.rb +0 -60
- data/spec/mongoid/attributes_spec.rb +3 -33
- data/spec/mongoid/changeable_spec.rb +299 -24
- data/spec/mongoid/clients_spec.rb +122 -13
- data/spec/mongoid/collection_configurable_spec.rb +158 -0
- data/spec/mongoid/config/defaults_spec.rb +160 -0
- data/spec/mongoid/config_spec.rb +154 -27
- data/spec/mongoid/contextual/memory_spec.rb +332 -76
- data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
- data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
- data/spec/mongoid/contextual/none_spec.rb +49 -2
- data/spec/mongoid/copyable_spec.rb +2 -10
- data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
- data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
- data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
- data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
- data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
- data/spec/mongoid/criteria_projection_spec.rb +1 -4
- data/spec/mongoid/criteria_spec.rb +5 -9
- data/spec/mongoid/document_spec.rb +0 -27
- data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
- data/spec/mongoid/extensions/hash_spec.rb +3 -3
- data/spec/mongoid/extensions/time_spec.rb +8 -43
- data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
- data/spec/mongoid/fields/localized_spec.rb +46 -28
- data/spec/mongoid/fields_spec.rb +136 -77
- data/spec/mongoid/findable_spec.rb +391 -34
- data/spec/mongoid/indexable_spec.rb +16 -10
- data/spec/mongoid/interceptable_spec.rb +153 -442
- data/spec/mongoid/interceptable_spec_models.rb +111 -51
- data/spec/mongoid/persistable/deletable_spec.rb +26 -6
- data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
- data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
- data/spec/mongoid/persistable/logical_spec.rb +37 -0
- data/spec/mongoid/persistable/poppable_spec.rb +36 -0
- data/spec/mongoid/persistable/pullable_spec.rb +72 -0
- data/spec/mongoid/persistable/pushable_spec.rb +72 -0
- data/spec/mongoid/persistable/renamable_spec.rb +36 -0
- data/spec/mongoid/persistable/savable_spec.rb +96 -0
- data/spec/mongoid/persistable/settable_spec.rb +37 -0
- data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
- data/spec/mongoid/persistable/updatable_spec.rb +20 -28
- data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
- data/spec/mongoid/persistence_context_spec.rb +7 -57
- data/spec/mongoid/query_cache_spec.rb +56 -61
- data/spec/mongoid/reloadable_spec.rb +24 -28
- data/spec/mongoid/scopable_spec.rb +70 -0
- data/spec/mongoid/serializable_spec.rb +23 -44
- data/spec/mongoid/stateful_spec.rb +122 -8
- data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
- data/spec/mongoid/tasks/database_spec.rb +127 -0
- data/spec/mongoid/timestamps/created_spec.rb +0 -23
- data/spec/mongoid/timestamps_spec.rb +9 -11
- data/spec/mongoid/touchable_spec.rb +277 -5
- data/spec/mongoid/touchable_spec_models.rb +3 -1
- data/spec/mongoid/traversable_spec.rb +9 -24
- data/spec/mongoid/validatable/associated_spec.rb +34 -27
- data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
- data/spec/mongoid_spec.rb +36 -10
- data/spec/shared/LICENSE +20 -0
- data/spec/shared/bin/get-mongodb-download-url +17 -0
- data/spec/shared/bin/s3-copy +45 -0
- data/spec/shared/bin/s3-upload +69 -0
- data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
- data/spec/shared/lib/mrss/cluster_config.rb +231 -0
- data/spec/shared/lib/mrss/constraints.rb +378 -0
- data/spec/shared/lib/mrss/docker_runner.rb +298 -0
- data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
- data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
- data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
- data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
- data/spec/shared/lib/mrss/session_registry.rb +69 -0
- data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
- data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
- data/spec/shared/lib/mrss/utils.rb +37 -0
- data/spec/shared/share/Dockerfile.erb +321 -0
- data/spec/shared/share/haproxy-1.conf +16 -0
- data/spec/shared/share/haproxy-2.conf +17 -0
- data/spec/shared/shlib/config.sh +27 -0
- data/spec/shared/shlib/distro.sh +74 -0
- data/spec/shared/shlib/server.sh +416 -0
- data/spec/shared/shlib/set_env.sh +169 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/immutable_ids.rb +118 -0
- data/spec/support/macros.rb +47 -15
- data/spec/support/models/artist.rb +0 -1
- data/spec/support/models/band.rb +1 -0
- data/spec/support/models/building.rb +2 -0
- data/spec/support/models/name.rb +0 -10
- data/spec/support/models/person.rb +0 -1
- data/spec/support/models/product.rb +1 -0
- data.tar.gz.sig +0 -0
- metadata +745 -637
- metadata.gz.sig +2 -0
- data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
- data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
- data/spec/support/models/purse.rb +0 -9
data/lib/mongoid/config.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "mongoid/config/defaults"
|
3
4
|
require "mongoid/config/environment"
|
4
5
|
require "mongoid/config/options"
|
5
6
|
require "mongoid/config/validators"
|
@@ -11,6 +12,7 @@ module Mongoid
|
|
11
12
|
module Config
|
12
13
|
extend Forwardable
|
13
14
|
extend Options
|
15
|
+
extend Defaults
|
14
16
|
extend self
|
15
17
|
|
16
18
|
def_delegators ::Mongoid, :logger, :logger=
|
@@ -70,6 +72,7 @@ module Mongoid
|
|
70
72
|
|
71
73
|
# Use ActiveSupport's time zone in time operations instead of the
|
72
74
|
# Ruby default time zone.
|
75
|
+
# @deprecated
|
73
76
|
option :use_activesupport_time_zone, default: true
|
74
77
|
|
75
78
|
# Return stored times as UTC.
|
@@ -125,22 +128,35 @@ module Mongoid
|
|
125
128
|
# always return a Hash.
|
126
129
|
option :legacy_attributes, default: false
|
127
130
|
|
128
|
-
#
|
129
|
-
#
|
130
|
-
#
|
131
|
-
#
|
132
|
-
#
|
133
|
-
#
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
131
|
+
# Sets the async_query_executor for the application. By default the thread pool executor
|
132
|
+
# is set to `:immediate. Options are:
|
133
|
+
#
|
134
|
+
# - :immediate - Initializes a single +Concurrent::ImmediateExecutor+
|
135
|
+
# - :global_thread_pool - Initializes a single +Concurrent::ThreadPoolExecutor+
|
136
|
+
# that uses the +async_query_concurrency+ for the +max_threads+ value.
|
137
|
+
option :async_query_executor, default: :immediate
|
138
|
+
|
139
|
+
# Defines how many asynchronous queries can be executed concurrently.
|
140
|
+
# This option should be set only if `async_query_executor` is set
|
141
|
+
# to `:global_thread_pool`.
|
142
|
+
option :global_executor_concurrency, default: nil
|
143
|
+
|
144
|
+
# When this flag is false, a document will become read-only only once the
|
145
|
+
# #readonly! method is called, and an error will be raised on attempting
|
146
|
+
# to save or update such documents, instead of just on delete. When this
|
147
|
+
# flag is true, a document is only read-only if it has been projected
|
148
|
+
# using #only or #without, and read-only documents will not be
|
149
|
+
# deletable/destroyable, but they will be savable/updatable.
|
150
|
+
# When this feature flag is turned on, the read-only state will be reset on
|
151
|
+
# reload, but when it is turned off, it won't be.
|
152
|
+
option :legacy_readonly, default: true
|
153
|
+
|
154
|
+
# When this flag is true, any attempt to change the _id of a persisted
|
155
|
+
# document will raise an exception (`Errors::ImmutableAttribute`).
|
156
|
+
# This will be the default in 9.0. When this flag is false (the default
|
157
|
+
# in 8.x), changing the _id of a persisted document might be ignored,
|
158
|
+
# or it might work, depending on the situation.
|
159
|
+
option :immutable_ids, default: false
|
144
160
|
|
145
161
|
# When this flag is true, callbacks for every embedded document will be
|
146
162
|
# called only once, even if the embedded document is embedded in multiple
|
@@ -152,18 +168,12 @@ module Mongoid
|
|
152
168
|
# See https://jira.mongodb.org/browse/MONGOID-5542
|
153
169
|
option :prevent_multiple_calls_of_embedded_callbacks, default: false
|
154
170
|
|
155
|
-
#
|
156
|
-
# called. This is the default in 8.x, but will be changed to false in 9.0.
|
157
|
-
#
|
158
|
-
# Setting this flag to true (as it is in 8.x) may lead to stack
|
159
|
-
# overflow errors if there are more than cicrca 1000 embedded
|
160
|
-
# documents in the root document's dependencies graph.
|
171
|
+
# Returns the Config singleton, for use in the configure DSL.
|
161
172
|
#
|
162
|
-
#
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
option :around_callbacks_for_embeds, default: true
|
173
|
+
# @return [ self ] The Config singleton.
|
174
|
+
def config
|
175
|
+
self
|
176
|
+
end
|
167
177
|
|
168
178
|
# Has Mongoid been configured? This is checking that at least a valid
|
169
179
|
# client config exists.
|
@@ -246,6 +256,17 @@ module Mongoid
|
|
246
256
|
end
|
247
257
|
end
|
248
258
|
|
259
|
+
# Deregister a model in the application with Mongoid.
|
260
|
+
#
|
261
|
+
# @param [ Class ] klass The model to deregister.
|
262
|
+
#
|
263
|
+
# @api private
|
264
|
+
def deregister_model(klass)
|
265
|
+
LOCK.synchronize do
|
266
|
+
models.delete(klass)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
249
270
|
# From a hash of settings, load all the configuration.
|
250
271
|
#
|
251
272
|
# @example Load the configuration.
|
@@ -318,6 +339,7 @@ module Mongoid
|
|
318
339
|
# @param [ Hash ] options The configuration options.
|
319
340
|
def options=(options)
|
320
341
|
if options
|
342
|
+
Validators::AsyncQueryExecutor.validate(options)
|
321
343
|
options.each_pair do |option, value|
|
322
344
|
Validators::Option.validate(option)
|
323
345
|
send("#{option}=", value)
|
@@ -385,5 +407,44 @@ module Mongoid
|
|
385
407
|
client
|
386
408
|
end
|
387
409
|
end
|
410
|
+
|
411
|
+
module DeprecatedOptions
|
412
|
+
OPTIONS = %i[ use_activesupport_time_zone
|
413
|
+
broken_aggregables
|
414
|
+
broken_alias_handling
|
415
|
+
broken_and
|
416
|
+
broken_scoping
|
417
|
+
broken_updates
|
418
|
+
compare_time_by_ms
|
419
|
+
legacy_attributes
|
420
|
+
legacy_pluck_distinct
|
421
|
+
legacy_triple_equals
|
422
|
+
object_id_as_json_oid
|
423
|
+
overwrite_chained_operators ]
|
424
|
+
|
425
|
+
if RUBY_VERSION < '3.0'
|
426
|
+
def self.prepended(klass)
|
427
|
+
klass.class_eval do
|
428
|
+
OPTIONS.each do |option|
|
429
|
+
alias_method :"#{option}_without_deprecation=", :"#{option}="
|
430
|
+
|
431
|
+
define_method(:"#{option}=") do |value|
|
432
|
+
Mongoid::Warnings.send(:"warn_#{option}_deprecated")
|
433
|
+
send(:"#{option}_without_deprecation=", value)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
else
|
439
|
+
OPTIONS.each do |option|
|
440
|
+
define_method(:"#{option}=") do |value|
|
441
|
+
Mongoid::Warnings.send(:"warn_#{option}_deprecated")
|
442
|
+
super(value)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
prepend DeprecatedOptions
|
388
449
|
end
|
389
450
|
end
|
@@ -150,7 +150,7 @@ module Mongoid
|
|
150
150
|
# @example Unset the field on the matches.
|
151
151
|
# context.unset(:name)
|
152
152
|
#
|
153
|
-
# @param [ String | Symbol | Array<String | Symbol> | Hash ] args
|
153
|
+
# @param [ [ String | Symbol | Array<String | Symbol> | Hash ]... ] *args
|
154
154
|
# The name(s) of the field(s) to unset.
|
155
155
|
# If a Hash is specified, its keys will be used irrespective of what
|
156
156
|
# each key's value is, even if the value is nil or false.
|
@@ -110,9 +110,24 @@ module Mongoid
|
|
110
110
|
# @example Do any documents exist for the context.
|
111
111
|
# context.exists?
|
112
112
|
#
|
113
|
+
# @example Do any documents exist for given _id.
|
114
|
+
# context.exists?(BSON::ObjectId(...))
|
115
|
+
#
|
116
|
+
# @example Do any documents exist for given conditions.
|
117
|
+
# context.exists?(name: "...")
|
118
|
+
#
|
119
|
+
# @param [ Hash | Object | false ] id_or_conditions an _id to
|
120
|
+
# search for, a hash of conditions, nil or false.
|
121
|
+
#
|
113
122
|
# @return [ true | false ] If the count is more than zero.
|
114
|
-
|
115
|
-
|
123
|
+
# Always false if passed nil or false.
|
124
|
+
def exists?(id_or_conditions = :none)
|
125
|
+
case id_or_conditions
|
126
|
+
when :none then any?
|
127
|
+
when nil, false then false
|
128
|
+
when Hash then Memory.new(criteria.where(id_or_conditions)).exists?
|
129
|
+
else Memory.new(criteria.where(_id: id_or_conditions)).exists?
|
130
|
+
end
|
116
131
|
end
|
117
132
|
|
118
133
|
# Get the first document in the database for the criteria's selector.
|
@@ -133,6 +148,20 @@ module Mongoid
|
|
133
148
|
alias :one :first
|
134
149
|
alias :find_first :first
|
135
150
|
|
151
|
+
# Get the first document in the database for the criteria's selector or
|
152
|
+
# raise an error if none is found.
|
153
|
+
#
|
154
|
+
# @example Get the first document.
|
155
|
+
# context.first!
|
156
|
+
#
|
157
|
+
# @return [ Document ] The first document.
|
158
|
+
#
|
159
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
160
|
+
# documents to take.
|
161
|
+
def first!
|
162
|
+
first || raise_document_not_found_error
|
163
|
+
end
|
164
|
+
|
136
165
|
# Create the new in memory context.
|
137
166
|
#
|
138
167
|
# @example Create the new context.
|
@@ -180,37 +209,18 @@ module Mongoid
|
|
180
209
|
end
|
181
210
|
end
|
182
211
|
|
183
|
-
#
|
184
|
-
#
|
185
|
-
# @example Take a document.
|
186
|
-
# context.take
|
187
|
-
#
|
188
|
-
# @param [ Integer | nil ] limit The number of documents to take or nil.
|
212
|
+
# Get the last document in the database for the criteria's selector or
|
213
|
+
# raise an error if none is found.
|
189
214
|
#
|
190
|
-
# @
|
191
|
-
|
192
|
-
if limit
|
193
|
-
eager_load(documents.take(limit))
|
194
|
-
else
|
195
|
-
eager_load([documents.first]).first
|
196
|
-
end
|
197
|
-
end
|
198
|
-
|
199
|
-
# Take the given number of documents from the database.
|
200
|
-
#
|
201
|
-
# @example Take a document.
|
202
|
-
# context.take
|
215
|
+
# @example Get the last document.
|
216
|
+
# context.last!
|
203
217
|
#
|
204
|
-
# @return [ Document ] The document.
|
218
|
+
# @return [ Document ] The last document.
|
205
219
|
#
|
206
220
|
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
207
221
|
# documents to take.
|
208
|
-
def
|
209
|
-
|
210
|
-
raise Errors::DocumentNotFound.new(klass, nil, nil)
|
211
|
-
else
|
212
|
-
eager_load([documents.first]).first
|
213
|
-
end
|
222
|
+
def last!
|
223
|
+
last || raise_document_not_found_error
|
214
224
|
end
|
215
225
|
|
216
226
|
# Get the length of matching documents in the context.
|
@@ -242,7 +252,7 @@ module Mongoid
|
|
242
252
|
# @example Get the values in memory.
|
243
253
|
# context.pluck(:name)
|
244
254
|
#
|
245
|
-
# @param [ String | Symbol ] *fields Field(s) to pluck.
|
255
|
+
# @param [ [ String | Symbol ]... ] *fields Field(s) to pluck.
|
246
256
|
#
|
247
257
|
# @return [ Array<Object> | Array<Array<Object>> ] The plucked values.
|
248
258
|
def pluck(*fields)
|
@@ -260,9 +270,9 @@ module Mongoid
|
|
260
270
|
# @example Get the values in memory.
|
261
271
|
# context.pick(:name)
|
262
272
|
#
|
263
|
-
# @param [ String | Symbol ] *fields Field(s) to pick.
|
273
|
+
# @param [ [ String | Symbol ]... ] *fields Field(s) to pick.
|
264
274
|
#
|
265
|
-
# @return [ Object
|
275
|
+
# @return [ Object | Array<Object> ] The picked values.
|
266
276
|
def pick(*fields)
|
267
277
|
if doc = documents.first
|
268
278
|
pluck_from_doc(doc, *fields)
|
@@ -285,6 +295,36 @@ module Mongoid
|
|
285
295
|
end
|
286
296
|
end
|
287
297
|
|
298
|
+
# Take the given number of documents from the database.
|
299
|
+
#
|
300
|
+
# @example Take a document.
|
301
|
+
# context.take
|
302
|
+
#
|
303
|
+
# @param [ Integer | nil ] limit The number of documents to take or nil.
|
304
|
+
#
|
305
|
+
# @return [ Document ] The document.
|
306
|
+
def take(limit = nil)
|
307
|
+
if limit
|
308
|
+
eager_load(documents.take(limit))
|
309
|
+
else
|
310
|
+
eager_load([documents.first]).first
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
# Take the given number of documents from the database or raise an error
|
315
|
+
# if none are found.
|
316
|
+
#
|
317
|
+
# @example Take a document.
|
318
|
+
# context.take
|
319
|
+
#
|
320
|
+
# @return [ Document ] The document.
|
321
|
+
#
|
322
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
323
|
+
# documents to take.
|
324
|
+
def take!
|
325
|
+
take || raise_document_not_found_error
|
326
|
+
end
|
327
|
+
|
288
328
|
# Skips the provided number of documents.
|
289
329
|
#
|
290
330
|
# @example Skip the documents.
|
@@ -335,6 +375,162 @@ module Mongoid
|
|
335
375
|
update_documents(attributes, entries)
|
336
376
|
end
|
337
377
|
|
378
|
+
# Get the second document in the database for the criteria's selector.
|
379
|
+
#
|
380
|
+
# @example Get the second document.
|
381
|
+
# context.second
|
382
|
+
#
|
383
|
+
# @param [ Integer ] limit The number of documents to return.
|
384
|
+
#
|
385
|
+
# @return [ Document ] The second document.
|
386
|
+
def second
|
387
|
+
eager_load([documents.second]).first
|
388
|
+
end
|
389
|
+
|
390
|
+
# Get the second document in the database for the criteria's selector or
|
391
|
+
# raise an error if none is found.
|
392
|
+
#
|
393
|
+
# @example Get the second document.
|
394
|
+
# context.second!
|
395
|
+
#
|
396
|
+
# @return [ Document ] The second document.
|
397
|
+
#
|
398
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
399
|
+
# documents to take.
|
400
|
+
def second!
|
401
|
+
second || raise_document_not_found_error
|
402
|
+
end
|
403
|
+
|
404
|
+
# Get the third document in the database for the criteria's selector.
|
405
|
+
#
|
406
|
+
# @example Get the third document.
|
407
|
+
# context.third
|
408
|
+
#
|
409
|
+
# @param [ Integer ] limit The number of documents to return.
|
410
|
+
#
|
411
|
+
# @return [ Document ] The third document.
|
412
|
+
def third
|
413
|
+
eager_load([documents.third]).first
|
414
|
+
end
|
415
|
+
|
416
|
+
# Get the third document in the database for the criteria's selector or
|
417
|
+
# raise an error if none is found.
|
418
|
+
#
|
419
|
+
# @example Get the third document.
|
420
|
+
# context.third!
|
421
|
+
#
|
422
|
+
# @return [ Document ] The third document.
|
423
|
+
#
|
424
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
425
|
+
# documents to take.
|
426
|
+
def third!
|
427
|
+
third || raise_document_not_found_error
|
428
|
+
end
|
429
|
+
|
430
|
+
# Get the fourth document in the database for the criteria's selector.
|
431
|
+
#
|
432
|
+
# @example Get the fourth document.
|
433
|
+
# context.fourth
|
434
|
+
#
|
435
|
+
# @param [ Integer ] limit The number of documents to return.
|
436
|
+
#
|
437
|
+
# @return [ Document ] The fourth document.
|
438
|
+
def fourth
|
439
|
+
eager_load([documents.fourth]).first
|
440
|
+
end
|
441
|
+
|
442
|
+
# Get the fourth document in the database for the criteria's selector or
|
443
|
+
# raise an error if none is found.
|
444
|
+
#
|
445
|
+
# @example Get the fourth document.
|
446
|
+
# context.fourth!
|
447
|
+
#
|
448
|
+
# @return [ Document ] The fourth document.
|
449
|
+
#
|
450
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
451
|
+
# documents to take.
|
452
|
+
def fourth!
|
453
|
+
fourth || raise_document_not_found_error
|
454
|
+
end
|
455
|
+
|
456
|
+
# Get the fifth document in the database for the criteria's selector.
|
457
|
+
#
|
458
|
+
# @example Get the fifth document.
|
459
|
+
# context.fifth
|
460
|
+
#
|
461
|
+
# @param [ Integer ] limit The number of documents to return.
|
462
|
+
#
|
463
|
+
# @return [ Document ] The fifth document.
|
464
|
+
def fifth
|
465
|
+
eager_load([documents.fifth]).first
|
466
|
+
end
|
467
|
+
|
468
|
+
# Get the fifth document in the database for the criteria's selector or
|
469
|
+
# raise an error if none is found.
|
470
|
+
#
|
471
|
+
# @example Get the fifth document.
|
472
|
+
# context.fifth!
|
473
|
+
#
|
474
|
+
# @return [ Document ] The fifth document.
|
475
|
+
#
|
476
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
477
|
+
# documents to take.
|
478
|
+
def fifth!
|
479
|
+
fifth || raise_document_not_found_error
|
480
|
+
end
|
481
|
+
|
482
|
+
# Get the second to last document in the database for the criteria's selector.
|
483
|
+
#
|
484
|
+
# @example Get the second to last document.
|
485
|
+
# context.second_to_last
|
486
|
+
#
|
487
|
+
# @param [ Integer ] limit The number of documents to return.
|
488
|
+
#
|
489
|
+
# @return [ Document ] The second to last document.
|
490
|
+
def second_to_last
|
491
|
+
eager_load([documents.second_to_last]).first
|
492
|
+
end
|
493
|
+
|
494
|
+
# Get the second to last document in the database for the criteria's selector or
|
495
|
+
# raise an error if none is found.
|
496
|
+
#
|
497
|
+
# @example Get the second to last document.
|
498
|
+
# context.second_to_last!
|
499
|
+
#
|
500
|
+
# @return [ Document ] The second to last document.
|
501
|
+
#
|
502
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
503
|
+
# documents to take.
|
504
|
+
def second_to_last!
|
505
|
+
second_to_last || raise_document_not_found_error
|
506
|
+
end
|
507
|
+
|
508
|
+
# Get the third to last document in the database for the criteria's selector.
|
509
|
+
#
|
510
|
+
# @example Get the third to last document.
|
511
|
+
# context.third_to_last
|
512
|
+
#
|
513
|
+
# @param [ Integer ] limit The number of documents to return.
|
514
|
+
#
|
515
|
+
# @return [ Document ] The third to last document.
|
516
|
+
def third_to_last
|
517
|
+
eager_load([documents.third_to_last]).first
|
518
|
+
end
|
519
|
+
|
520
|
+
# Get the third to last document in the database for the criteria's selector or
|
521
|
+
# raise an error if none is found.
|
522
|
+
#
|
523
|
+
# @example Get the third to last document.
|
524
|
+
# context.third_to_last!
|
525
|
+
#
|
526
|
+
# @return [ Document ] The third to last document.
|
527
|
+
#
|
528
|
+
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
529
|
+
# documents to take.
|
530
|
+
def third_to_last!
|
531
|
+
third_to_last || raise_document_not_found_error
|
532
|
+
end
|
533
|
+
|
338
534
|
private
|
339
535
|
|
340
536
|
# Get the documents the context should iterate. This follows 3 rules:
|
@@ -561,9 +757,9 @@ module Mongoid
|
|
561
757
|
# Pluck the field values from the given document.
|
562
758
|
#
|
563
759
|
# @param [ Document ] doc The document to pluck from.
|
564
|
-
# @param [ String | Symbol ] *fields Field(s) to pluck.
|
760
|
+
# @param [ [ String | Symbol ]... ] *fields Field(s) to pluck.
|
565
761
|
#
|
566
|
-
# @return [ Object
|
762
|
+
# @return [ Object | Array<Object> ] The plucked values.
|
567
763
|
def pluck_from_doc(doc, *fields)
|
568
764
|
if fields.length == 1
|
569
765
|
retrieve_value_at_path(doc, fields.first)
|
@@ -573,6 +769,10 @@ module Mongoid
|
|
573
769
|
end
|
574
770
|
end
|
575
771
|
end
|
772
|
+
|
773
|
+
def raise_document_not_found_error
|
774
|
+
raise Errors::DocumentNotFound.new(klass, nil, nil)
|
775
|
+
end
|
576
776
|
end
|
577
777
|
end
|
578
778
|
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require "mongoid/association/eager_loadable"
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Contextual
|
5
|
+
class Mongo
|
6
|
+
# Loads documents for the provided criteria.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class DocumentsLoader
|
10
|
+
extend Forwardable
|
11
|
+
include Association::EagerLoadable
|
12
|
+
|
13
|
+
def_delegators :@future, :value!, :value, :wait!, :wait
|
14
|
+
|
15
|
+
# Returns synchronous executor to be used when async_query_executor config option
|
16
|
+
# is set to :immediate. This executor runs all operations on the current
|
17
|
+
# thread, blocking as necessary.
|
18
|
+
#
|
19
|
+
# @return [ Concurrent::ImmediateExecutor ] The executor
|
20
|
+
# to be used to execute document loading tasks.
|
21
|
+
def self.immediate_executor
|
22
|
+
@@immediate_executor ||= Concurrent::ImmediateExecutor.new
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns asynchronous executor to be used when async_query_executor config option
|
26
|
+
# is set to :global_thread_pool. This executor runs operations on background threads
|
27
|
+
# using a thread pool.
|
28
|
+
#
|
29
|
+
# @return [ Concurrent::ThreadPoolExecutor ] The executor
|
30
|
+
# to be used to execute document loading tasks.
|
31
|
+
def self.global_thread_pool_async_query_executor
|
32
|
+
create_pool = Proc.new do |concurrency|
|
33
|
+
Concurrent::ThreadPoolExecutor.new(
|
34
|
+
min_threads: 0,
|
35
|
+
max_threads: concurrency,
|
36
|
+
max_queue: concurrency * 4,
|
37
|
+
fallback_policy: :caller_runs
|
38
|
+
)
|
39
|
+
end
|
40
|
+
concurrency = Mongoid.global_executor_concurrency || 4
|
41
|
+
@@global_thread_pool_async_query_executor ||= create_pool.call(concurrency)
|
42
|
+
if @@global_thread_pool_async_query_executor.max_length != concurrency
|
43
|
+
old_pool = @@global_thread_pool_async_query_executor
|
44
|
+
@@global_thread_pool_async_query_executor = create_pool.call(concurrency)
|
45
|
+
old_pool.shutdown
|
46
|
+
end
|
47
|
+
@@global_thread_pool_async_query_executor
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns suitable executor according to Mongoid config options.
|
51
|
+
#
|
52
|
+
# @param [ String | Symbol] name The query executor name, can be either
|
53
|
+
# :immediate or :global_thread_pool. Defaulted to `async_query_executor`
|
54
|
+
# config option.
|
55
|
+
#
|
56
|
+
# @return [ Concurrent::ImmediateExecutor | Concurrent::ThreadPoolExecutor ] The executor
|
57
|
+
# to be used to execute document loading tasks.
|
58
|
+
#
|
59
|
+
# @raise [ Errors::InvalidQueryExecutor ] If an unknown name is provided.
|
60
|
+
def self.executor(name = Mongoid.async_query_executor)
|
61
|
+
case name.to_sym
|
62
|
+
when :immediate
|
63
|
+
immediate_executor
|
64
|
+
when :global_thread_pool
|
65
|
+
global_thread_pool_async_query_executor
|
66
|
+
else
|
67
|
+
raise Errors::InvalidQueryExecutor.new(name)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [ Mongoid::Criteria ] Criteria that specifies which documents should
|
72
|
+
# be loaded. Exposed here because `eager_loadable?` method from
|
73
|
+
# `Association::EagerLoadable` expects this to be available.
|
74
|
+
attr_accessor :criteria
|
75
|
+
|
76
|
+
# Instantiates the document loader instance and immediately schedules
|
77
|
+
# its execution using the provided executor.
|
78
|
+
#
|
79
|
+
# @param [ Mongo::Collection::View ] view The collection view to get
|
80
|
+
# records from the database.
|
81
|
+
# @param [ Class ] klass Mongoid model class to instantiate documents.
|
82
|
+
# All records obtained from the database will be converted to an
|
83
|
+
# instance of this class, if possible.
|
84
|
+
# @param [ Mongoid::Criteria ] criteria. Criteria that specifies which
|
85
|
+
# documents should be loaded.
|
86
|
+
# @param [ Concurrent::AbstractExecutorService ] executor. Executor that
|
87
|
+
# is capable of running `Concurrent::Promises::Future` instances.
|
88
|
+
def initialize(view, klass, criteria, executor: self.class.executor)
|
89
|
+
@view = view
|
90
|
+
@klass = klass
|
91
|
+
@criteria = criteria
|
92
|
+
@mutex = Mutex.new
|
93
|
+
@state = :pending
|
94
|
+
@future = Concurrent::Promises.future_on(executor) do
|
95
|
+
start && execute
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns false or true whether the loader is in pending state.
|
100
|
+
#
|
101
|
+
# Pending state means that the loader execution has been scheduled,
|
102
|
+
# but has not been started yet.
|
103
|
+
#
|
104
|
+
# @return [ true | false ] true if the loader is in pending state,
|
105
|
+
# otherwise false.
|
106
|
+
def pending?
|
107
|
+
@mutex.synchronize do
|
108
|
+
@state == :pending
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns false or true whether the loader is in started state.
|
113
|
+
#
|
114
|
+
# Started state means that the loader execution has been started.
|
115
|
+
# Note that the loader stays in this state even after the execution
|
116
|
+
# completed (successfully or failed).
|
117
|
+
#
|
118
|
+
# @return [ true | false ] true if the loader is in started state,
|
119
|
+
# otherwise false.
|
120
|
+
def started?
|
121
|
+
@mutex.synchronize do
|
122
|
+
@state == :started
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Mark the loader as unscheduled.
|
127
|
+
#
|
128
|
+
# If the loader is marked unscheduled, it will not be executed. The only
|
129
|
+
# option to load the documents is to call `execute` method directly.
|
130
|
+
#
|
131
|
+
# Please note that if execution of a task has been already started,
|
132
|
+
# unscheduling does not have any effect.
|
133
|
+
def unschedule
|
134
|
+
@mutex.synchronize do
|
135
|
+
@state = :cancelled unless @state == :started
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Loads records specified by `@criteria` from the database, and convert
|
140
|
+
# them to Mongoid documents of `@klass` type.
|
141
|
+
#
|
142
|
+
# This method is called by the task (possibly asynchronous) scheduled
|
143
|
+
# when creating an instance of the loader. However, this method can be
|
144
|
+
# called directly, if it is desired to execute loading on the caller
|
145
|
+
# thread immediately.
|
146
|
+
#
|
147
|
+
# Calling this method does not change the state of the loader.
|
148
|
+
#
|
149
|
+
# @return [ Array<Mongoid::Document> ] Array of document loaded from
|
150
|
+
# the database.
|
151
|
+
def execute
|
152
|
+
documents = @view.map do |doc|
|
153
|
+
Factory.from_db(@klass, doc, @criteria)
|
154
|
+
end
|
155
|
+
eager_load(documents) if eager_loadable?
|
156
|
+
documents
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
# Mark the loader as started if possible.
|
162
|
+
#
|
163
|
+
# @return [ true | false ] Whether the state was changed to :started.
|
164
|
+
def start
|
165
|
+
@mutex.synchronize do
|
166
|
+
if @state == :pending
|
167
|
+
@state = :started
|
168
|
+
true
|
169
|
+
else
|
170
|
+
false
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|