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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +3 -3
  4. data/README.md +3 -3
  5. data/Rakefile +18 -67
  6. data/lib/config/locales/en.yml +46 -14
  7. data/lib/mongoid/association/accessors.rb +3 -7
  8. data/lib/mongoid/association/builders.rb +1 -1
  9. data/lib/mongoid/association/eager_loadable.rb +0 -3
  10. data/lib/mongoid/association/embedded/batchable.rb +2 -2
  11. data/lib/mongoid/association/embedded/embedded_in/buildable.rb +2 -2
  12. data/lib/mongoid/association/embedded/embedded_in/proxy.rb +2 -1
  13. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +3 -2
  14. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +6 -6
  15. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +1 -1
  16. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +1 -1
  17. data/lib/mongoid/association/macros.rb +0 -6
  18. data/lib/mongoid/association/nested/one.rb +40 -2
  19. data/lib/mongoid/association/proxy.rb +1 -1
  20. data/lib/mongoid/association/referenced/counter_cache.rb +2 -2
  21. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +1 -1
  22. data/lib/mongoid/association/referenced/has_many/enumerable.rb +6 -23
  23. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -3
  24. data/lib/mongoid/association/reflections.rb +2 -2
  25. data/lib/mongoid/atomic.rb +7 -16
  26. data/lib/mongoid/attributes/dynamic.rb +1 -1
  27. data/lib/mongoid/attributes/nested.rb +2 -2
  28. data/lib/mongoid/attributes/processing.rb +5 -29
  29. data/lib/mongoid/attributes/projector.rb +1 -1
  30. data/lib/mongoid/attributes/readonly.rb +1 -1
  31. data/lib/mongoid/attributes.rb +8 -2
  32. data/lib/mongoid/changeable.rb +107 -5
  33. data/lib/mongoid/clients/storage_options.rb +2 -5
  34. data/lib/mongoid/clients/validators/storage.rb +1 -13
  35. data/lib/mongoid/collection_configurable.rb +58 -0
  36. data/lib/mongoid/composable.rb +2 -0
  37. data/lib/mongoid/config/defaults.rb +60 -0
  38. data/lib/mongoid/config/options.rb +0 -3
  39. data/lib/mongoid/config/validators/async_query_executor.rb +24 -0
  40. data/lib/mongoid/config/validators.rb +1 -0
  41. data/lib/mongoid/config.rb +88 -27
  42. data/lib/mongoid/contextual/atomic.rb +1 -1
  43. data/lib/mongoid/contextual/memory.rb +233 -33
  44. data/lib/mongoid/contextual/mongo/documents_loader.rb +177 -0
  45. data/lib/mongoid/contextual/mongo.rb +370 -133
  46. data/lib/mongoid/contextual/none.rb +162 -7
  47. data/lib/mongoid/contextual.rb +12 -0
  48. data/lib/mongoid/criteria/findable.rb +2 -2
  49. data/lib/mongoid/criteria/includable.rb +4 -3
  50. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -15
  51. data/lib/mongoid/criteria/queryable/key.rb +1 -1
  52. data/lib/mongoid/criteria/queryable/mergeable.rb +1 -1
  53. data/lib/mongoid/criteria/queryable/optional.rb +8 -8
  54. data/lib/mongoid/criteria/queryable/selectable.rb +43 -12
  55. data/lib/mongoid/criteria/queryable/selector.rb +1 -1
  56. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  57. data/lib/mongoid/criteria.rb +6 -5
  58. data/lib/mongoid/deprecable.rb +1 -2
  59. data/lib/mongoid/deprecation.rb +3 -3
  60. data/lib/mongoid/document.rb +1 -8
  61. data/lib/mongoid/errors/create_collection_failure.rb +33 -0
  62. data/lib/mongoid/errors/drop_collection_failure.rb +27 -0
  63. data/lib/mongoid/errors/immutable_attribute.rb +26 -0
  64. data/lib/mongoid/errors/invalid_async_query_executor.rb +25 -0
  65. data/lib/mongoid/errors/invalid_global_executor_concurrency.rb +22 -0
  66. data/lib/mongoid/errors/invalid_storage_parent.rb +2 -0
  67. data/lib/mongoid/errors.rb +4 -1
  68. data/lib/mongoid/extensions/hash.rb +2 -24
  69. data/lib/mongoid/extensions/object.rb +2 -2
  70. data/lib/mongoid/extensions/time.rb +2 -0
  71. data/lib/mongoid/fields/localized.rb +10 -0
  72. data/lib/mongoid/fields/standard.rb +10 -0
  73. data/lib/mongoid/fields.rb +59 -35
  74. data/lib/mongoid/findable.rb +27 -3
  75. data/lib/mongoid/interceptable.rb +6 -116
  76. data/lib/mongoid/matcher/eq_impl.rb +1 -1
  77. data/lib/mongoid/matcher/type.rb +1 -1
  78. data/lib/mongoid/persistable/creatable.rb +1 -0
  79. data/lib/mongoid/persistable/deletable.rb +1 -1
  80. data/lib/mongoid/persistable/savable.rb +13 -1
  81. data/lib/mongoid/persistable/unsettable.rb +2 -2
  82. data/lib/mongoid/persistable/updatable.rb +51 -1
  83. data/lib/mongoid/persistable/upsertable.rb +20 -1
  84. data/lib/mongoid/persistable.rb +3 -0
  85. data/lib/mongoid/query_cache.rb +5 -1
  86. data/lib/mongoid/railties/database.rake +7 -2
  87. data/lib/mongoid/reloadable.rb +5 -3
  88. data/lib/mongoid/stateful.rb +22 -1
  89. data/lib/mongoid/tasks/database.rake +12 -0
  90. data/lib/mongoid/tasks/database.rb +20 -0
  91. data/lib/mongoid/timestamps/created.rb +1 -8
  92. data/lib/mongoid/traversable.rb +0 -12
  93. data/lib/mongoid/utils.rb +22 -0
  94. data/lib/mongoid/validatable/associated.rb +17 -98
  95. data/lib/mongoid/validatable/macros.rb +5 -5
  96. data/lib/mongoid/validatable.rb +4 -9
  97. data/lib/mongoid/version.rb +1 -1
  98. data/lib/mongoid/warnings.rb +17 -1
  99. data/lib/mongoid.rb +16 -3
  100. data/spec/integration/app_spec.rb +2 -6
  101. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +0 -40
  102. data/spec/integration/callbacks_spec.rb +99 -12
  103. data/spec/integration/discriminator_key_spec.rb +4 -5
  104. data/spec/integration/i18n_fallbacks_spec.rb +3 -2
  105. data/spec/mongoid/association/eager_spec.rb +2 -24
  106. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +27 -0
  107. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +20 -25
  108. data/spec/mongoid/association/embedded/embeds_many_models.rb +1 -0
  109. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +0 -4
  110. data/spec/mongoid/association/embedded/embeds_one/proxy_spec.rb +15 -2
  111. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -18
  112. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +42 -55
  113. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +9 -50
  114. data/spec/mongoid/association/syncable_spec.rb +1 -1
  115. data/spec/mongoid/association_spec.rb +0 -60
  116. data/spec/mongoid/attributes_spec.rb +3 -33
  117. data/spec/mongoid/changeable_spec.rb +299 -24
  118. data/spec/mongoid/clients_spec.rb +122 -13
  119. data/spec/mongoid/collection_configurable_spec.rb +158 -0
  120. data/spec/mongoid/config/defaults_spec.rb +160 -0
  121. data/spec/mongoid/config_spec.rb +154 -27
  122. data/spec/mongoid/contextual/memory_spec.rb +332 -76
  123. data/spec/mongoid/contextual/mongo/documents_loader_spec.rb +187 -0
  124. data/spec/mongoid/contextual/mongo_spec.rb +1009 -125
  125. data/spec/mongoid/contextual/none_spec.rb +49 -2
  126. data/spec/mongoid/copyable_spec.rb +2 -10
  127. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +4 -10
  128. data/spec/mongoid/criteria/queryable/options_spec.rb +1 -1
  129. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +419 -0
  130. data/spec/mongoid/criteria/queryable/selectable_spec.rb +1 -1
  131. data/spec/mongoid/criteria/queryable/selector_spec.rb +3 -76
  132. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  133. data/spec/mongoid/criteria_projection_spec.rb +1 -4
  134. data/spec/mongoid/criteria_spec.rb +5 -9
  135. data/spec/mongoid/document_spec.rb +0 -27
  136. data/spec/mongoid/errors/readonly_document_spec.rb +2 -2
  137. data/spec/mongoid/extensions/hash_spec.rb +3 -3
  138. data/spec/mongoid/extensions/time_spec.rb +8 -43
  139. data/spec/mongoid/extensions/time_with_zone_spec.rb +7 -52
  140. data/spec/mongoid/fields/localized_spec.rb +46 -28
  141. data/spec/mongoid/fields_spec.rb +136 -77
  142. data/spec/mongoid/findable_spec.rb +391 -34
  143. data/spec/mongoid/indexable_spec.rb +16 -10
  144. data/spec/mongoid/interceptable_spec.rb +153 -442
  145. data/spec/mongoid/interceptable_spec_models.rb +111 -51
  146. data/spec/mongoid/persistable/deletable_spec.rb +26 -6
  147. data/spec/mongoid/persistable/destroyable_spec.rb +26 -6
  148. data/spec/mongoid/persistable/incrementable_spec.rb +37 -0
  149. data/spec/mongoid/persistable/logical_spec.rb +37 -0
  150. data/spec/mongoid/persistable/poppable_spec.rb +36 -0
  151. data/spec/mongoid/persistable/pullable_spec.rb +72 -0
  152. data/spec/mongoid/persistable/pushable_spec.rb +72 -0
  153. data/spec/mongoid/persistable/renamable_spec.rb +36 -0
  154. data/spec/mongoid/persistable/savable_spec.rb +96 -0
  155. data/spec/mongoid/persistable/settable_spec.rb +37 -0
  156. data/spec/mongoid/persistable/unsettable_spec.rb +36 -0
  157. data/spec/mongoid/persistable/updatable_spec.rb +20 -28
  158. data/spec/mongoid/persistable/upsertable_spec.rb +80 -6
  159. data/spec/mongoid/persistence_context_spec.rb +7 -57
  160. data/spec/mongoid/query_cache_spec.rb +56 -61
  161. data/spec/mongoid/reloadable_spec.rb +24 -28
  162. data/spec/mongoid/scopable_spec.rb +70 -0
  163. data/spec/mongoid/serializable_spec.rb +23 -44
  164. data/spec/mongoid/stateful_spec.rb +122 -8
  165. data/spec/mongoid/tasks/database_rake_spec.rb +74 -0
  166. data/spec/mongoid/tasks/database_spec.rb +127 -0
  167. data/spec/mongoid/timestamps/created_spec.rb +0 -23
  168. data/spec/mongoid/timestamps_spec.rb +9 -11
  169. data/spec/mongoid/touchable_spec.rb +277 -5
  170. data/spec/mongoid/touchable_spec_models.rb +3 -1
  171. data/spec/mongoid/traversable_spec.rb +9 -24
  172. data/spec/mongoid/validatable/associated_spec.rb +34 -27
  173. data/spec/mongoid/validatable/uniqueness_spec.rb +2 -3
  174. data/spec/mongoid_spec.rb +36 -10
  175. data/spec/shared/LICENSE +20 -0
  176. data/spec/shared/bin/get-mongodb-download-url +17 -0
  177. data/spec/shared/bin/s3-copy +45 -0
  178. data/spec/shared/bin/s3-upload +69 -0
  179. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  180. data/spec/shared/lib/mrss/cluster_config.rb +231 -0
  181. data/spec/shared/lib/mrss/constraints.rb +378 -0
  182. data/spec/shared/lib/mrss/docker_runner.rb +298 -0
  183. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  184. data/spec/shared/lib/mrss/event_subscriber.rb +210 -0
  185. data/spec/shared/lib/mrss/lite_constraints.rb +238 -0
  186. data/spec/shared/lib/mrss/server_version_registry.rb +113 -0
  187. data/spec/shared/lib/mrss/session_registry.rb +69 -0
  188. data/spec/shared/lib/mrss/session_registry_legacy.rb +60 -0
  189. data/spec/shared/lib/mrss/spec_organizer.rb +179 -0
  190. data/spec/shared/lib/mrss/utils.rb +37 -0
  191. data/spec/shared/share/Dockerfile.erb +321 -0
  192. data/spec/shared/share/haproxy-1.conf +16 -0
  193. data/spec/shared/share/haproxy-2.conf +17 -0
  194. data/spec/shared/shlib/config.sh +27 -0
  195. data/spec/shared/shlib/distro.sh +74 -0
  196. data/spec/shared/shlib/server.sh +416 -0
  197. data/spec/shared/shlib/set_env.sh +169 -0
  198. data/spec/spec_helper.rb +5 -0
  199. data/spec/support/immutable_ids.rb +118 -0
  200. data/spec/support/macros.rb +47 -15
  201. data/spec/support/models/artist.rb +0 -1
  202. data/spec/support/models/band.rb +1 -0
  203. data/spec/support/models/building.rb +2 -0
  204. data/spec/support/models/name.rb +0 -10
  205. data/spec/support/models/person.rb +0 -1
  206. data/spec/support/models/product.rb +1 -0
  207. data.tar.gz.sig +0 -0
  208. metadata +745 -637
  209. metadata.gz.sig +2 -0
  210. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +0 -60
  211. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +0 -60
  212. data/spec/support/models/purse.rb +0 -9
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "mongoid/contextual/mongo/documents_loader"
3
4
  require "mongoid/contextual/atomic"
4
5
  require "mongoid/contextual/aggregable/mongo"
5
6
  require "mongoid/contextual/command"
@@ -37,6 +38,18 @@ module Mongoid
37
38
  # @attribute [r] view The Mongo collection view.
38
39
  attr_reader :view
39
40
 
41
+ # Run an explain on the criteria.
42
+ #
43
+ # @example Explain the criteria.
44
+ # Band.where(name: "Depeche Mode").explain
45
+ #
46
+ # @param [ Hash ] options customizable options (See Mongo::Collection::View::Explainable)
47
+ #
48
+ # @return [ Hash ] The explain result.
49
+ def_delegator :view, :explain
50
+
51
+ attr_reader :documents_loader
52
+
40
53
  # Get the number of documents matching the query.
41
54
  #
42
55
  # @example Get the number of matching documents.
@@ -56,12 +69,7 @@ module Mongoid
56
69
  # @return [ Integer ] The number of matches.
57
70
  def count(options = {}, &block)
58
71
  return super(&block) if block_given?
59
-
60
- if valid_for_count_documents?
61
- view.count_documents(options)
62
- else
63
- view.count(options)
64
- end
72
+ view.count_documents(options)
65
73
  end
66
74
 
67
75
  # Get the estimated number of documents matching the query.
@@ -159,22 +167,28 @@ module Mongoid
159
167
  # @example Do any documents exist for the context.
160
168
  # context.exists?
161
169
  #
170
+ # @example Do any documents exist for given _id.
171
+ # context.exists?(BSON::ObjectId(...))
172
+ #
173
+ # @example Do any documents exist for given conditions.
174
+ # context.exists?(name: "...")
175
+ #
162
176
  # @note We don't use count here since Mongo does not use counted
163
177
  # b-tree indexes.
164
178
  #
165
- # @return [ true | false ] If the count is more than zero.
166
- def exists?
167
- !!(view.projection(_id: 1).limit(1).first)
168
- end
169
-
170
- # Run an explain on the criteria.
171
- #
172
- # @example Explain the criteria.
173
- # Band.where(name: "Depeche Mode").explain
179
+ # @param [ Hash | Object | false ] id_or_conditions an _id to
180
+ # search for, a hash of conditions, nil or false.
174
181
  #
175
- # @return [ Hash ] The explain result.
176
- def explain
177
- view.explain
182
+ # @return [ true | false ] If the count is more than zero.
183
+ # Always false if passed nil or false.
184
+ def exists?(id_or_conditions = :none)
185
+ return false if self.view.limit == 0
186
+ case id_or_conditions
187
+ when :none then !!(view.projection(_id: 1).limit(1).first)
188
+ when nil, false then false
189
+ when Hash then Mongo.new(criteria.where(id_or_conditions)).exists?
190
+ else Mongo.new(criteria.where(_id: id_or_conditions)).exists?
191
+ end
178
192
  end
179
193
 
180
194
  # Execute the find and modify command, used for MongoDB's
@@ -230,28 +244,6 @@ module Mongoid
230
244
  end
231
245
  end
232
246
 
233
- # Get the first document in the database for the criteria's selector.
234
- #
235
- # @example Get the first document.
236
- # context.first
237
- #
238
- # @note Automatically adding a sort on _id when no other sort is
239
- # defined on the criteria has the potential to cause bad performance issues.
240
- # If you experience unexpected poor performance when using #first or #last
241
- # and have no sort defined on the criteria, use #take instead.
242
- # Be aware that #take won't guarantee order.
243
- #
244
- # @param [ Integer ] limit The number of documents to return.
245
- #
246
- # @return [ Document ] The first document.
247
- def first(limit = nil)
248
- sort = view.sort || { _id: 1 }
249
- if raw_docs = view.sort(sort).limit(limit || 1).to_a
250
- process_raw_docs(raw_docs, limit)
251
- end
252
- end
253
- alias :one :first
254
-
255
247
  # Return the first result without applying sort
256
248
  #
257
249
  # @api private
@@ -302,25 +294,6 @@ module Mongoid
302
294
 
303
295
  def_delegator :@klass, :database_field_name
304
296
 
305
- # Get the last document in the database for the criteria's selector.
306
- #
307
- # @example Get the last document.
308
- # context.last
309
- #
310
- # @note Automatically adding a sort on _id when no other sort is
311
- # defined on the criteria has the potential to cause bad performance issues.
312
- # If you experience unexpected poor performance when using #first or #last
313
- # and have no sort defined on the criteria, use #take instead.
314
- # Be aware that #take won't guarantee order.
315
- #
316
- # @param [ Integer ] limit The number of documents to return.
317
- #
318
- # @return [ Document ] The last document.
319
- def last(limit = nil)
320
- raw_docs = view.sort(inverse_sorting).limit(limit || 1).to_a.reverse
321
- process_raw_docs(raw_docs, limit)
322
- end
323
-
324
297
  # Returns the number of documents in the database matching
325
298
  # the query selector.
326
299
  #
@@ -345,44 +318,6 @@ module Mongoid
345
318
  @view = view.limit(value) and self
346
319
  end
347
320
 
348
- # Take the given number of documents from the database.
349
- #
350
- # @example Take 10 documents
351
- # context.take(10)
352
- #
353
- # @param [ Integer | nil ] limit The number of documents to return or nil.
354
- #
355
- # @return [ Document | Array<Document> ] The list of documents, or one
356
- # document if no value was given.
357
- def take(limit = nil)
358
- if limit
359
- limit(limit).to_a
360
- else
361
- # Do to_a first so that the Mongo#first method is not used and the
362
- # result is not sorted.
363
- limit(1).to_a.first
364
- end
365
- end
366
-
367
- # Take one document from the database and raise an error if there are none.
368
- #
369
- # @example Take a document
370
- # context.take!
371
- #
372
- # @return [ Document ] The document.
373
- #
374
- # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
375
- # documents to take.
376
- def take!
377
- # Do to_a first so that the Mongo#first method is not used and the
378
- # result is not sorted.
379
- if fst = limit(1).to_a.first
380
- fst
381
- else
382
- raise Errors::DocumentNotFound.new(klass, nil, nil)
383
- end
384
- end
385
-
386
321
  # Initiate a map/reduce operation from the context.
387
322
  #
388
323
  # @example Initiate a map/reduce.
@@ -396,15 +331,22 @@ module Mongoid
396
331
  MapReduce.new(collection, criteria, map, reduce)
397
332
  end
398
333
 
399
- # Pluck the single field values from the database. Will return duplicates
400
- # if they exist and only works for top level fields.
334
+ # Pluck the field value(s) from the database. Returns one
335
+ # result for each document found in the database for
336
+ # the context. The results are normalized according to their
337
+ # Mongoid field types. Note that the results may include
338
+ # duplicates and nil values.
401
339
  #
402
340
  # @example Pluck a field.
403
341
  # context.pluck(:_id)
404
342
  #
405
- # @param [ String | Symbol ] *fields Field(s) to pluck.
343
+ # @param [ [ String | Symbol ]... ] *fields Field(s) to pluck,
344
+ # which may include nested fields using dot-notation.
406
345
  #
407
346
  # @return [ Array<Object> | Array<Array<Object>> ] The plucked values.
347
+ # If the *fields arg contains a single value, each result
348
+ # in the array will be a single value. Otherwise, each
349
+ # result in the array will be an array of values.
408
350
  def pluck(*fields)
409
351
  # Multiple fields can map to the same field name. For example, plucking
410
352
  # a field and its _translations field map to the same field in the database.
@@ -439,13 +381,51 @@ module Mongoid
439
381
  # @example Pick a field.
440
382
  # context.pick(:_id)
441
383
  #
442
- # @param [ String | Symbol ] *fields Field(s) to pick.
384
+ # @param [ [ String | Symbol ]... ] *fields Field(s) to pick.
443
385
  #
444
386
  # @return [ Object | Array<Object> ] The picked values.
445
387
  def pick(*fields)
446
388
  limit(1).pluck(*fields).first
447
389
  end
448
390
 
391
+ # Take the given number of documents from the database.
392
+ #
393
+ # @example Take 10 documents
394
+ # context.take(10)
395
+ #
396
+ # @param [ Integer | nil ] limit The number of documents to return or nil.
397
+ #
398
+ # @return [ Document | Array<Document> ] The list of documents, or one
399
+ # document if no value was given.
400
+ def take(limit = nil)
401
+ if limit
402
+ limit(limit).to_a
403
+ else
404
+ # Do to_a first so that the Mongo#first method is not used and the
405
+ # result is not sorted.
406
+ limit(1).to_a.first
407
+ end
408
+ end
409
+
410
+ # Take one document from the database and raise an error if there are none.
411
+ #
412
+ # @example Take a document
413
+ # context.take!
414
+ #
415
+ # @return [ Document ] The document.
416
+ #
417
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
418
+ # documents to take.
419
+ def take!
420
+ # Do to_a first so that the Mongo#first method is not used and the
421
+ # result is not sorted.
422
+ if fst = limit(1).to_a.first
423
+ fst
424
+ else
425
+ raise Errors::DocumentNotFound.new(klass, nil, nil)
426
+ end
427
+ end
428
+
449
429
  # Get a hash of counts for the values of a single field. For example,
450
430
  # if the following documents were in the database:
451
431
  #
@@ -558,7 +538,7 @@ module Mongoid
558
538
  # @option opts [ Array ] :array_filters A set of filters specifying to which array elements
559
539
  # an update should apply.
560
540
  #
561
- # @return [ nil, false ] False if no attributes were provided.
541
+ # @return [ nil | false ] False if no attributes were provided.
562
542
  def update(attributes = nil, opts = {})
563
543
  update_documents(attributes, :update_one, opts)
564
544
  end
@@ -574,11 +554,255 @@ module Mongoid
574
554
  # @option opts [ Array ] :array_filters A set of filters specifying to which array elements
575
555
  # an update should apply.
576
556
  #
577
- # @return [ nil, false ] False if no attributes were provided.
557
+ # @return [ nil | false ] False if no attributes were provided.
578
558
  def update_all(attributes = nil, opts = {})
579
559
  update_documents(attributes, :update_many, opts)
580
560
  end
581
561
 
562
+ # Get the first document in the database for the criteria's selector.
563
+ #
564
+ # @example Get the first document.
565
+ # context.first
566
+ #
567
+ # @note Automatically adding a sort on _id when no other sort is
568
+ # defined on the criteria has the potential to cause bad performance issues.
569
+ # If you experience unexpected poor performance when using #first or #last
570
+ # and have no sort defined on the criteria, use #take instead.
571
+ # Be aware that #take won't guarantee order.
572
+ #
573
+ # @param [ Integer ] limit The number of documents to return.
574
+ #
575
+ # @return [ Document | nil ] The first document or nil if none is found.
576
+ def first(limit = nil)
577
+ if limit.nil?
578
+ retrieve_nth(0)
579
+ else
580
+ retrieve_nth_with_limit(0, limit)
581
+ end
582
+ end
583
+ alias :one :first
584
+
585
+ # Get the first document in the database for the criteria's selector or
586
+ # raise an error if none is found.
587
+ #
588
+ # @example Get the first document.
589
+ # context.first!
590
+ #
591
+ # @note Automatically adding a sort on _id when no other sort is
592
+ # defined on the criteria has the potential to cause bad performance issues.
593
+ # If you experience unexpected poor performance when using #first! or #last!
594
+ # and have no sort defined on the criteria, use #take! instead.
595
+ # Be aware that #take! won't guarantee order.
596
+ #
597
+ # @return [ Document ] The first document.
598
+ #
599
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
600
+ # documents available.
601
+ def first!
602
+ first || raise_document_not_found_error
603
+ end
604
+
605
+ # Get the last document in the database for the criteria's selector.
606
+ #
607
+ # @example Get the last document.
608
+ # context.last
609
+ #
610
+ # @note Automatically adding a sort on _id when no other sort is
611
+ # defined on the criteria has the potential to cause bad performance issues.
612
+ # If you experience unexpected poor performance when using #first or #last
613
+ # and have no sort defined on the criteria, use #take instead.
614
+ # Be aware that #take won't guarantee order.
615
+ #
616
+ # @param [ Integer ] limit The number of documents to return.
617
+ #
618
+ # @return [ Document | nil ] The last document or nil if none is found.
619
+ def last(limit = nil)
620
+ if limit.nil?
621
+ retrieve_nth_to_last(0)
622
+ else
623
+ retrieve_nth_to_last_with_limit(0, limit)
624
+ end
625
+ end
626
+
627
+ # Get the last document in the database for the criteria's selector or
628
+ # raise an error if none is found.
629
+ #
630
+ # @example Get the last document.
631
+ # context.last!
632
+ #
633
+ # @note Automatically adding a sort on _id when no other sort is
634
+ # defined on the criteria has the potential to cause bad performance issues.
635
+ # If you experience unexpected poor performance when using #first! or #last!
636
+ # and have no sort defined on the criteria, use #take! instead.
637
+ # Be aware that #take! won't guarantee order.
638
+ #
639
+ # @return [ Document ] The last document.
640
+ #
641
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
642
+ # documents available.
643
+ def last!
644
+ last || raise_document_not_found_error
645
+ end
646
+
647
+ # Get the second document in the database for the criteria's selector.
648
+ #
649
+ # @example Get the second document.
650
+ # context.second
651
+ #
652
+ # @return [ Document | nil ] The second document or nil if none is found.
653
+ def second
654
+ retrieve_nth(1)
655
+ end
656
+
657
+ # Get the second document in the database for the criteria's selector or
658
+ # raise an error if none is found.
659
+ #
660
+ # @example Get the second document.
661
+ # context.second!
662
+ #
663
+ # @return [ Document ] The second document.
664
+ #
665
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
666
+ # documents available.
667
+ def second!
668
+ second || raise_document_not_found_error
669
+ end
670
+
671
+ # Get the third document in the database for the criteria's selector.
672
+ #
673
+ # @example Get the third document.
674
+ # context.third
675
+ #
676
+ # @return [ Document | nil ] The third document or nil if none is found.
677
+ def third
678
+ retrieve_nth(2)
679
+ end
680
+
681
+ # Get the third document in the database for the criteria's selector or
682
+ # raise an error if none is found.
683
+ #
684
+ # @example Get the third document.
685
+ # context.third!
686
+ #
687
+ # @return [ Document ] The third document.
688
+ #
689
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
690
+ # documents available.
691
+ def third!
692
+ third || raise_document_not_found_error
693
+ end
694
+
695
+ # Get the fourth document in the database for the criteria's selector.
696
+ #
697
+ # @example Get the fourth document.
698
+ # context.fourth
699
+ #
700
+ # @return [ Document | nil ] The fourth document or nil if none is found.
701
+ def fourth
702
+ retrieve_nth(3)
703
+ end
704
+
705
+ # Get the fourth document in the database for the criteria's selector or
706
+ # raise an error if none is found.
707
+ #
708
+ # @example Get the fourth document.
709
+ # context.fourth!
710
+ #
711
+ # @return [ Document ] The fourth document.
712
+ #
713
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
714
+ # documents available.
715
+ def fourth!
716
+ fourth || raise_document_not_found_error
717
+ end
718
+
719
+ # Get the fifth document in the database for the criteria's selector.
720
+ #
721
+ # @example Get the fifth document.
722
+ # context.fifth
723
+ #
724
+ # @return [ Document | nil ] The fifth document or nil if none is found.
725
+ def fifth
726
+ retrieve_nth(4)
727
+ end
728
+
729
+ # Get the fifth document in the database for the criteria's selector or
730
+ # raise an error if none is found.
731
+ #
732
+ # @example Get the fifth document.
733
+ # context.fifth!
734
+ #
735
+ # @return [ Document ] The fifth document.
736
+ #
737
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
738
+ # documents available.
739
+ def fifth!
740
+ fifth || raise_document_not_found_error
741
+ end
742
+
743
+ # Get the second to last document in the database for the criteria's
744
+ # selector.
745
+ #
746
+ # @example Get the second to last document.
747
+ # context.second_to_last
748
+ #
749
+ # @return [ Document | nil ] The second to last document or nil if none
750
+ # is found.
751
+ def second_to_last
752
+ retrieve_nth_to_last(1)
753
+ end
754
+
755
+ # Get the second to last document in the database for the criteria's
756
+ # selector or raise an error if none is found.
757
+ #
758
+ # @example Get the second to last document.
759
+ # context.second_to_last!
760
+ #
761
+ # @return [ Document ] The second to last document.
762
+ #
763
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
764
+ # documents available.
765
+ def second_to_last!
766
+ second_to_last || raise_document_not_found_error
767
+ end
768
+
769
+ # Get the third to last document in the database for the criteria's
770
+ # selector.
771
+ #
772
+ # @example Get the third to last document.
773
+ # context.third_to_last
774
+ #
775
+ # @return [ Document | nil ] The third to last document or nil if none
776
+ # is found.
777
+ def third_to_last
778
+ retrieve_nth_to_last(2)
779
+ end
780
+
781
+ # Get the third to last document in the database for the criteria's
782
+ # selector or raise an error if none is found.
783
+ #
784
+ # @example Get the third to last document.
785
+ # context.third_to_last!
786
+ #
787
+ # @return [ Document ] The third to last document.
788
+ #
789
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
790
+ # documents available.
791
+ def third_to_last!
792
+ third_to_last || raise_document_not_found_error
793
+ end
794
+
795
+ # Schedule a task to load documents for the context.
796
+ #
797
+ # Depending on the Mongoid configuration, the scheduled task can be executed
798
+ # immediately on the caller's thread, or can be scheduled for an
799
+ # asynchronous execution.
800
+ #
801
+ # @api private
802
+ def load_async
803
+ @documents_loader ||= DocumentsLoader.new(view, klass, criteria)
804
+ end
805
+
582
806
  private
583
807
 
584
808
  # Update the documents for the provided method.
@@ -646,24 +870,29 @@ module Mongoid
646
870
  Hash[sort.map{|k, v| [k, -1*v]}]
647
871
  end
648
872
 
649
- # Get the documents the context should iterate. This follows 3 rules:
873
+ # Get the documents the context should iterate.
650
874
  #
651
- # 1. If the query is cached, and we already have documents loaded, use
652
- # them.
653
- # 2. If we are eager loading, then eager load the documents and use
654
- # those.
655
- # 3. Use the query.
656
- #
657
- # @api private
658
- #
659
- # @example Get the documents for iteration.
660
- # context.documents_for_iteration
875
+ # If the documents have been already preloaded by `Document::Loader`
876
+ # instance, they will be used.
661
877
  #
662
878
  # @return [ Array<Document> | Mongo::Collection::View ] The docs to iterate.
879
+ #
880
+ # @api private
663
881
  def documents_for_iteration
664
- return view unless eager_loadable?
665
- docs = view.map{ |doc| Factory.from_db(klass, doc, criteria) }
666
- eager_load(docs)
882
+ if @documents_loader
883
+ if @documents_loader.started?
884
+ @documents_loader.value!
885
+ else
886
+ @documents_loader.unschedule
887
+ @documents_loader.execute
888
+ end
889
+ else
890
+ return view unless eager_loadable?
891
+ docs = view.map do |doc|
892
+ Factory.from_db(klass, doc, criteria)
893
+ end
894
+ eager_load(docs)
895
+ end
667
896
  end
668
897
 
669
898
  # Yield to the document.
@@ -682,8 +911,6 @@ module Mongoid
682
911
  yield(doc)
683
912
  end
684
913
 
685
- private
686
-
687
914
  def _session
688
915
  @criteria.send(:_session)
689
916
  end
@@ -819,22 +1046,32 @@ module Mongoid
819
1046
  limit ? docs : docs.first
820
1047
  end
821
1048
 
822
- # Queries whether the current context is valid for use with
823
- # the #count_documents? predicate. A context is valid if it
824
- # does not include a `$where` operator.
825
- #
826
- # @return [ true | false ] whether or not the current context
827
- # excludes a `$where` operator.
828
- def valid_for_count_documents?(hash = view.filter)
829
- # Note that `view.filter` is a BSON::Document, and all keys in a
830
- # BSON::Document are strings; we don't need to worry about symbol
831
- # representations of `$where`.
832
- hash.keys.each do |key|
833
- return false if key == '$where'
834
- return false if hash[key].is_a?(Hash) && !valid_for_count_documents?(hash[key])
1049
+ def raise_document_not_found_error
1050
+ raise Errors::DocumentNotFound.new(klass, nil, nil)
1051
+ end
1052
+
1053
+ def retrieve_nth(n)
1054
+ retrieve_nth_with_limit(n, 1).first
1055
+ end
1056
+
1057
+ def retrieve_nth_with_limit(n, limit)
1058
+ sort = view.sort || { _id: 1 }
1059
+ v = view.sort(sort).limit(limit || 1)
1060
+ v = v.skip(n) if n > 0
1061
+ if raw_docs = v.to_a
1062
+ process_raw_docs(raw_docs, limit)
835
1063
  end
1064
+ end
1065
+
1066
+ def retrieve_nth_to_last(n)
1067
+ retrieve_nth_to_last_with_limit(n, 1).first
1068
+ end
836
1069
 
837
- true
1070
+ def retrieve_nth_to_last_with_limit(n, limit)
1071
+ v = view.sort(inverse_sorting).skip(n).limit(limit || 1)
1072
+ v = v.skip(n) if n > 0
1073
+ raw_docs = v.to_a.reverse
1074
+ process_raw_docs(raw_docs, limit)
838
1075
  end
839
1076
  end
840
1077
  end