mongoid 8.0.13 → 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 +17 -66
- data/lib/config/locales/en.yml +46 -40
- data/lib/mongoid/association/accessors.rb +3 -7
- data/lib/mongoid/association/builders.rb +1 -1
- data/lib/mongoid/association/depending.rb +4 -8
- 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 +2 -2
- data/lib/mongoid/association/macros.rb +0 -6
- data/lib/mongoid/association/nested/many.rb +3 -38
- data/lib/mongoid/association/nested/nested_buildable.rb +0 -14
- 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 +4 -103
- 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/factory.rb +0 -4
- 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 -89
- data/lib/mongoid/contextual/aggregable/memory.rb +2 -5
- data/lib/mongoid/contextual/atomic.rb +1 -1
- data/lib/mongoid/contextual/memory.rb +240 -51
- 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 -13
- data/lib/mongoid/criteria/queryable/optional.rb +8 -8
- data/lib/mongoid/criteria/queryable/selectable.rb +43 -121
- 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 -2
- data/lib/mongoid/extensions/hash.rb +3 -51
- 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/matchable.rb +1 -6
- data/lib/mongoid/matcher/eq_impl.rb +1 -1
- data/lib/mongoid/matcher/eq_impl_with_regexp.rb +5 -3
- data/lib/mongoid/matcher/regex.rb +9 -8
- data/lib/mongoid/matcher/type.rb +1 -1
- data/lib/mongoid/matcher.rb +0 -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/threaded.rb +0 -3
- 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 -20
- data/lib/mongoid/validatable.rb +4 -10
- data/lib/mongoid/version.rb +1 -5
- data/lib/mongoid/warnings.rb +17 -2
- data/lib/mongoid.rb +16 -3
- data/spec/integration/app_spec.rb +2 -20
- data/spec/integration/associations/embeds_one_spec.rb +5 -25
- data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -55
- data/spec/integration/callbacks_spec.rb +99 -12
- data/spec/integration/discriminator_key_spec.rb +4 -5
- data/spec/integration/dots_and_dollars_spec.rb +2 -12
- data/spec/integration/i18n_fallbacks_spec.rb +3 -2
- data/spec/integration/matcher_operator_data/regex.yml +0 -21
- 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 -103
- data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -195
- data/spec/mongoid/association/syncable_spec.rb +1 -1
- data/spec/mongoid/attributes/nested_spec.rb +10 -204
- data/spec/mongoid/attributes_spec.rb +3 -33
- data/spec/mongoid/changeable_spec.rb +299 -24
- data/spec/mongoid/clients/factory_spec.rb +0 -30
- 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/aggregable/memory_spec.rb +0 -91
- data/spec/mongoid/contextual/memory_spec.rb +332 -253
- 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 -2
- data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
- data/spec/mongoid/criteria/queryable/selectable_where_spec.rb +0 -235
- 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 -11
- 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 -239
- 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/numericality_spec.rb +0 -16
- data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
- data/spec/mongoid_spec.rb +36 -10
- data/spec/shared/lib/mrss/docker_runner.rb +1 -1
- data/spec/shared/lib/mrss/server_version_registry.rb +1 -1
- data/spec/shared/lib/mrss/spec_organizer.rb +3 -32
- data/spec/shared/share/Dockerfile.erb +85 -15
- data/spec/shared/shlib/distro.sh +0 -10
- data/spec/shared/shlib/server.sh +27 -34
- data/spec/shared/shlib/set_env.sh +68 -9
- data/spec/spec_helper.rb +5 -0
- data/spec/support/expectations.rb +17 -20
- 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 +715 -671
- metadata.gz.sig +2 -0
- data/lib/mongoid/errors/in_memory_regexp_timeout.rb +0 -26
- data/lib/mongoid/field_readable.rb +0 -70
- data/lib/mongoid/matcher/regexp_budget.rb +0 -389
- data/lib/mongoid/validatable/numericality.rb +0 -19
- data/spec/integration/matcher_regexp_timeout_spec.rb +0 -215
- data/spec/integration/query_operator_guard_spec.rb +0 -89
- data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -61
- data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -61
- data/spec/mongoid/matcher/regexp_budget_spec.rb +0 -570
- data/spec/shared/CANDIDATE.md +0 -28
- data/spec/shared/lib/mrss/release/candidate.rb +0 -281
- data/spec/shared/lib/mrss/release/product_data.rb +0 -144
- data/spec/shared/lib/tasks/candidate.rake +0 -64
- data/spec/support/models/purse.rb +0 -9
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "mongoid/contextual/aggregable/memory"
|
|
4
4
|
require "mongoid/association/eager_loadable"
|
|
5
|
-
require "mongoid/field_readable"
|
|
6
5
|
|
|
7
6
|
module Mongoid
|
|
8
7
|
module Contextual
|
|
@@ -12,7 +11,6 @@ module Mongoid
|
|
|
12
11
|
include Association::EagerLoadable
|
|
13
12
|
include Queryable
|
|
14
13
|
include Positional
|
|
15
|
-
include FieldReadable
|
|
16
14
|
|
|
17
15
|
# @attribute [r] root The root document.
|
|
18
16
|
# @attribute [r] path The atomic path.
|
|
@@ -112,9 +110,24 @@ module Mongoid
|
|
|
112
110
|
# @example Do any documents exist for the context.
|
|
113
111
|
# context.exists?
|
|
114
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
|
+
#
|
|
115
122
|
# @return [ true | false ] If the count is more than zero.
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
118
131
|
end
|
|
119
132
|
|
|
120
133
|
# Get the first document in the database for the criteria's selector.
|
|
@@ -135,6 +148,20 @@ module Mongoid
|
|
|
135
148
|
alias :one :first
|
|
136
149
|
alias :find_first :first
|
|
137
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
|
+
|
|
138
165
|
# Create the new in memory context.
|
|
139
166
|
#
|
|
140
167
|
# @example Create the new context.
|
|
@@ -143,20 +170,10 @@ module Mongoid
|
|
|
143
170
|
# @param [ Criteria ] criteria The criteria.
|
|
144
171
|
def initialize(criteria)
|
|
145
172
|
@criteria, @klass = criteria, criteria.klass
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
# the middle of that would leave a half-built client behind.
|
|
151
|
-
if (first = criteria.documents.first)
|
|
152
|
-
@root = first._root
|
|
153
|
-
@collection = @root.collection
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# One regexp budget covers the whole scan, so that the limit bounds the
|
|
157
|
-
# query rather than each document individually.
|
|
158
|
-
@documents = Matcher::RegexpBudget.open(criteria.selector) do
|
|
159
|
-
criteria.documents.select { |doc| doc._matches?(criteria.selector) }
|
|
173
|
+
@documents = criteria.documents.select do |doc|
|
|
174
|
+
@root ||= doc._root
|
|
175
|
+
@collection ||= root.collection
|
|
176
|
+
doc._matches?(criteria.selector)
|
|
160
177
|
end
|
|
161
178
|
apply_sorting
|
|
162
179
|
apply_options
|
|
@@ -192,37 +209,18 @@ module Mongoid
|
|
|
192
209
|
end
|
|
193
210
|
end
|
|
194
211
|
|
|
195
|
-
#
|
|
196
|
-
#
|
|
197
|
-
# @example Take a document.
|
|
198
|
-
# context.take
|
|
199
|
-
#
|
|
200
|
-
# @param [ Integer | nil ] limit The number of documents to take or nil.
|
|
201
|
-
#
|
|
202
|
-
# @return [ Document ] The document.
|
|
203
|
-
def take(limit = nil)
|
|
204
|
-
if limit
|
|
205
|
-
eager_load(documents.take(limit))
|
|
206
|
-
else
|
|
207
|
-
eager_load([documents.first]).first
|
|
208
|
-
end
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
# Take the given number of documents from the database.
|
|
212
|
+
# Get the last document in the database for the criteria's selector or
|
|
213
|
+
# raise an error if none is found.
|
|
212
214
|
#
|
|
213
|
-
# @example
|
|
214
|
-
# context.
|
|
215
|
+
# @example Get the last document.
|
|
216
|
+
# context.last!
|
|
215
217
|
#
|
|
216
|
-
# @return [ Document ] The document.
|
|
218
|
+
# @return [ Document ] The last document.
|
|
217
219
|
#
|
|
218
220
|
# @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
|
|
219
221
|
# documents to take.
|
|
220
|
-
def
|
|
221
|
-
|
|
222
|
-
raise Errors::DocumentNotFound.new(klass, nil, nil)
|
|
223
|
-
else
|
|
224
|
-
eager_load([documents.first]).first
|
|
225
|
-
end
|
|
222
|
+
def last!
|
|
223
|
+
last || raise_document_not_found_error
|
|
226
224
|
end
|
|
227
225
|
|
|
228
226
|
# Get the length of matching documents in the context.
|
|
@@ -254,7 +252,7 @@ module Mongoid
|
|
|
254
252
|
# @example Get the values in memory.
|
|
255
253
|
# context.pluck(:name)
|
|
256
254
|
#
|
|
257
|
-
# @param [ String | Symbol ] *fields Field(s) to pluck.
|
|
255
|
+
# @param [ [ String | Symbol ]... ] *fields Field(s) to pluck.
|
|
258
256
|
#
|
|
259
257
|
# @return [ Array<Object> | Array<Array<Object>> ] The plucked values.
|
|
260
258
|
def pluck(*fields)
|
|
@@ -272,9 +270,9 @@ module Mongoid
|
|
|
272
270
|
# @example Get the values in memory.
|
|
273
271
|
# context.pick(:name)
|
|
274
272
|
#
|
|
275
|
-
# @param [ String | Symbol ] *fields Field(s) to pick.
|
|
273
|
+
# @param [ [ String | Symbol ]... ] *fields Field(s) to pick.
|
|
276
274
|
#
|
|
277
|
-
# @return [ Object
|
|
275
|
+
# @return [ Object | Array<Object> ] The picked values.
|
|
278
276
|
def pick(*fields)
|
|
279
277
|
if doc = documents.first
|
|
280
278
|
pluck_from_doc(doc, *fields)
|
|
@@ -297,6 +295,36 @@ module Mongoid
|
|
|
297
295
|
end
|
|
298
296
|
end
|
|
299
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
|
+
|
|
300
328
|
# Skips the provided number of documents.
|
|
301
329
|
#
|
|
302
330
|
# @example Skip the documents.
|
|
@@ -347,6 +375,162 @@ module Mongoid
|
|
|
347
375
|
update_documents(attributes, entries)
|
|
348
376
|
end
|
|
349
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
|
+
|
|
350
534
|
private
|
|
351
535
|
|
|
352
536
|
# Get the documents the context should iterate. This follows 3 rules:
|
|
@@ -546,10 +730,11 @@ module Mongoid
|
|
|
546
730
|
# _translations hash so that we can get the specified translation in
|
|
547
731
|
# the remaining
|
|
548
732
|
if field&.localized?
|
|
549
|
-
document.
|
|
733
|
+
document.send("#{segment}_translations")
|
|
550
734
|
end
|
|
551
735
|
end
|
|
552
|
-
|
|
736
|
+
meth = klass.aliased_associations[segment] || segment
|
|
737
|
+
res.nil? ? document.try(meth) : res
|
|
553
738
|
elsif document.is_a?(Hash)
|
|
554
739
|
# TODO: Remove the indifferent access when implementing MONGOID-5410.
|
|
555
740
|
document.key?(segment.to_s) ?
|
|
@@ -572,9 +757,9 @@ module Mongoid
|
|
|
572
757
|
# Pluck the field values from the given document.
|
|
573
758
|
#
|
|
574
759
|
# @param [ Document ] doc The document to pluck from.
|
|
575
|
-
# @param [ String | Symbol ] *fields Field(s) to pluck.
|
|
760
|
+
# @param [ [ String | Symbol ]... ] *fields Field(s) to pluck.
|
|
576
761
|
#
|
|
577
|
-
# @return [ Object
|
|
762
|
+
# @return [ Object | Array<Object> ] The plucked values.
|
|
578
763
|
def pluck_from_doc(doc, *fields)
|
|
579
764
|
if fields.length == 1
|
|
580
765
|
retrieve_value_at_path(doc, fields.first)
|
|
@@ -584,6 +769,10 @@ module Mongoid
|
|
|
584
769
|
end
|
|
585
770
|
end
|
|
586
771
|
end
|
|
772
|
+
|
|
773
|
+
def raise_document_not_found_error
|
|
774
|
+
raise Errors::DocumentNotFound.new(klass, nil, nil)
|
|
775
|
+
end
|
|
587
776
|
end
|
|
588
777
|
end
|
|
589
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
|