mongoid 7.4.0 → 8.0.1

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 (315) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/lib/config/locales/en.yml +51 -28
  5. data/lib/mongoid/association/accessors.rb +32 -3
  6. data/lib/mongoid/association/bindable.rb +48 -0
  7. data/lib/mongoid/association/builders.rb +4 -2
  8. data/lib/mongoid/association/eager_loadable.rb +29 -7
  9. data/lib/mongoid/association/embedded/batchable.rb +48 -8
  10. data/lib/mongoid/association/embedded/embedded_in/binding.rb +24 -2
  11. data/lib/mongoid/association/embedded/embedded_in.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_many/binding.rb +1 -0
  13. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +1 -1
  14. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +40 -18
  15. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +18 -4
  16. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +21 -2
  17. data/lib/mongoid/association/macros.rb +22 -1
  18. data/lib/mongoid/association/many.rb +5 -0
  19. data/lib/mongoid/association/nested/many.rb +2 -1
  20. data/lib/mongoid/association/proxy.rb +12 -0
  21. data/lib/mongoid/association/referenced/auto_save.rb +3 -2
  22. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -0
  23. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  24. data/lib/mongoid/association/referenced/belongs_to.rb +1 -1
  25. data/lib/mongoid/association/referenced/counter_cache.rb +8 -8
  26. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +64 -11
  27. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +4 -1
  28. data/lib/mongoid/association/referenced/has_many/enumerable.rb +10 -14
  29. data/lib/mongoid/association/referenced/has_many/proxy.rb +12 -9
  30. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  31. data/lib/mongoid/association/referenced/has_one/proxy.rb +8 -11
  32. data/lib/mongoid/association/referenced/syncable.rb +2 -2
  33. data/lib/mongoid/association/relatable.rb +38 -4
  34. data/lib/mongoid/atomic/paths/embedded/many.rb +19 -0
  35. data/lib/mongoid/attributes/processing.rb +9 -2
  36. data/lib/mongoid/attributes.rb +30 -27
  37. data/lib/mongoid/changeable.rb +37 -2
  38. data/lib/mongoid/clients/options.rb +4 -0
  39. data/lib/mongoid/clients/sessions.rb +2 -14
  40. data/lib/mongoid/config/environment.rb +20 -4
  41. data/lib/mongoid/config.rb +25 -10
  42. data/lib/mongoid/contextual/aggregable/memory.rb +23 -15
  43. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  44. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  45. data/lib/mongoid/contextual/memory.rb +176 -17
  46. data/lib/mongoid/contextual/mongo.rb +226 -206
  47. data/lib/mongoid/contextual/none.rb +66 -4
  48. data/lib/mongoid/copyable.rb +32 -8
  49. data/lib/mongoid/criteria/includable.rb +24 -20
  50. data/lib/mongoid/criteria/marshalable.rb +10 -2
  51. data/lib/mongoid/criteria/queryable/extensions/array.rb +2 -13
  52. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  53. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  54. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  55. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  56. data/lib/mongoid/criteria/queryable/extensions/hash.rb +0 -14
  57. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  58. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  59. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  60. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +1 -1
  61. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +3 -1
  62. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  63. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  64. data/lib/mongoid/criteria/queryable/mergeable.rb +21 -0
  65. data/lib/mongoid/criteria/queryable/optional.rb +3 -9
  66. data/lib/mongoid/criteria/queryable/options.rb +1 -1
  67. data/lib/mongoid/criteria/queryable/selectable.rb +28 -34
  68. data/lib/mongoid/criteria/queryable/selector.rb +89 -4
  69. data/lib/mongoid/criteria/queryable/smash.rb +39 -6
  70. data/lib/mongoid/criteria/queryable.rb +11 -6
  71. data/lib/mongoid/criteria.rb +1 -26
  72. data/lib/mongoid/deprecable.rb +36 -0
  73. data/lib/mongoid/deprecation.rb +25 -0
  74. data/lib/mongoid/document.rb +96 -32
  75. data/lib/mongoid/errors/document_not_found.rb +29 -8
  76. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  77. data/lib/mongoid/errors/invalid_field.rb +5 -1
  78. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  79. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  80. data/lib/mongoid/errors.rb +2 -2
  81. data/lib/mongoid/extensions/array.rb +8 -6
  82. data/lib/mongoid/extensions/big_decimal.rb +29 -10
  83. data/lib/mongoid/extensions/binary.rb +42 -0
  84. data/lib/mongoid/extensions/boolean.rb +8 -2
  85. data/lib/mongoid/extensions/date.rb +26 -20
  86. data/lib/mongoid/extensions/date_time.rb +1 -1
  87. data/lib/mongoid/extensions/float.rb +4 -5
  88. data/lib/mongoid/extensions/hash.rb +12 -5
  89. data/lib/mongoid/extensions/integer.rb +4 -5
  90. data/lib/mongoid/extensions/object.rb +2 -0
  91. data/lib/mongoid/extensions/range.rb +41 -10
  92. data/lib/mongoid/extensions/regexp.rb +11 -4
  93. data/lib/mongoid/extensions/set.rb +11 -4
  94. data/lib/mongoid/extensions/string.rb +2 -13
  95. data/lib/mongoid/extensions/symbol.rb +3 -14
  96. data/lib/mongoid/extensions/time.rb +27 -16
  97. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  98. data/lib/mongoid/extensions.rb +1 -0
  99. data/lib/mongoid/factory.rb +42 -7
  100. data/lib/mongoid/fields/foreign_key.rb +7 -0
  101. data/lib/mongoid/fields/validators/macro.rb +3 -9
  102. data/lib/mongoid/fields.rb +194 -28
  103. data/lib/mongoid/findable.rb +27 -7
  104. data/lib/mongoid/indexable/specification.rb +1 -1
  105. data/lib/mongoid/indexable/validators/options.rb +4 -1
  106. data/lib/mongoid/interceptable.rb +69 -9
  107. data/lib/mongoid/persistable/creatable.rb +14 -5
  108. data/lib/mongoid/persistable/updatable.rb +12 -5
  109. data/lib/mongoid/persistable/upsertable.rb +1 -1
  110. data/lib/mongoid/persistence_context.rb +19 -2
  111. data/lib/mongoid/query_cache.rb +6 -258
  112. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  113. data/lib/mongoid/reloadable.rb +7 -3
  114. data/lib/mongoid/selectable.rb +1 -2
  115. data/lib/mongoid/stateful.rb +27 -1
  116. data/lib/mongoid/timestamps/created.rb +1 -1
  117. data/lib/mongoid/timestamps/updated.rb +1 -1
  118. data/lib/mongoid/touchable.rb +2 -3
  119. data/lib/mongoid/traversable.rb +5 -1
  120. data/lib/mongoid/validatable/uniqueness.rb +2 -1
  121. data/lib/mongoid/version.rb +1 -1
  122. data/lib/mongoid/warnings.rb +28 -0
  123. data/lib/mongoid.rb +2 -0
  124. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +11 -5
  125. data/spec/config/mongoid.yml +16 -0
  126. data/spec/config/mongoid_with_schema_map_uuid.yml +27 -0
  127. data/spec/integration/app_spec.rb +28 -26
  128. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  129. data/spec/integration/associations/embedded_dirty_spec.rb +28 -0
  130. data/spec/integration/associations/embedded_spec.rb +15 -0
  131. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  132. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  133. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  134. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  135. data/spec/integration/associations/has_one_spec.rb +97 -1
  136. data/spec/integration/associations/scope_option_spec.rb +1 -1
  137. data/spec/integration/callbacks_models.rb +95 -1
  138. data/spec/integration/callbacks_spec.rb +226 -4
  139. data/spec/integration/criteria/range_spec.rb +95 -1
  140. data/spec/integration/discriminator_key_spec.rb +115 -76
  141. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  142. data/spec/integration/matcher_examples_spec.rb +20 -13
  143. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  144. data/spec/integration/matcher_operator_spec.rb +3 -5
  145. data/spec/integration/persistence/range_field_spec.rb +350 -0
  146. data/spec/lite_spec_helper.rb +1 -1
  147. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  148. data/spec/mongoid/association/depending_spec.rb +9 -9
  149. data/spec/mongoid/association/eager_spec.rb +2 -1
  150. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  151. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  152. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +69 -9
  153. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  154. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +219 -8
  155. data/spec/mongoid/association/embedded/embeds_many_models.rb +157 -0
  156. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  157. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  158. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  159. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  160. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  161. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  162. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  163. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  164. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  165. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -2
  166. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +67 -4
  167. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  168. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  169. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  170. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +8 -8
  171. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +82 -13
  172. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  173. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  174. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  175. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  176. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  177. data/spec/mongoid/association/syncable_spec.rb +14 -0
  178. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  179. data/spec/mongoid/atomic_spec.rb +22 -0
  180. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  181. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  182. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  183. data/spec/mongoid/attributes_spec.rb +524 -27
  184. data/spec/mongoid/changeable_spec.rb +130 -13
  185. data/spec/mongoid/clients/factory_spec.rb +34 -42
  186. data/spec/mongoid/clients/options_spec.rb +1 -0
  187. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  188. data/spec/mongoid/clients_spec.rb +32 -2
  189. data/spec/mongoid/config/environment_spec.rb +39 -1
  190. data/spec/mongoid/config_spec.rb +104 -13
  191. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  192. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  193. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  194. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  195. data/spec/mongoid/contextual/memory_spec.rb +1337 -69
  196. data/spec/mongoid/contextual/mongo_spec.rb +1105 -172
  197. data/spec/mongoid/contextual/none_spec.rb +38 -0
  198. data/spec/mongoid/copyable_spec.rb +451 -1
  199. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  200. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  201. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  202. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  203. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  204. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  205. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  206. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  207. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  208. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  209. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  210. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  211. data/spec/mongoid/criteria/queryable/optional_spec.rb +0 -484
  212. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +50 -0
  213. data/spec/mongoid/criteria/queryable/selectable_spec.rb +289 -124
  214. data/spec/mongoid/criteria/queryable/selector_spec.rb +14 -2
  215. data/spec/mongoid/criteria_spec.rb +474 -1198
  216. data/spec/mongoid/document_fields_spec.rb +173 -24
  217. data/spec/mongoid/document_spec.rb +32 -41
  218. data/spec/mongoid/errors/document_not_found_spec.rb +76 -0
  219. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  220. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  221. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  222. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  223. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  224. data/spec/mongoid/extensions/array_spec.rb +16 -2
  225. data/spec/mongoid/extensions/big_decimal_spec.rb +697 -212
  226. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  227. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  228. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  229. data/spec/mongoid/extensions/date_spec.rb +71 -1
  230. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  231. data/spec/mongoid/extensions/float_spec.rb +48 -76
  232. data/spec/mongoid/extensions/hash_spec.rb +30 -0
  233. data/spec/mongoid/extensions/integer_spec.rb +45 -66
  234. data/spec/mongoid/extensions/range_spec.rb +255 -54
  235. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  236. data/spec/mongoid/extensions/set_spec.rb +106 -0
  237. data/spec/mongoid/extensions/string_spec.rb +53 -25
  238. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  239. data/spec/mongoid/extensions/time_spec.rb +634 -66
  240. data/spec/mongoid/extensions/time_with_zone_spec.rb +17 -31
  241. data/spec/mongoid/factory_spec.rb +61 -1
  242. data/spec/mongoid/fields_spec.rb +321 -50
  243. data/spec/mongoid/findable_spec.rb +80 -15
  244. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  245. data/spec/mongoid/indexable_spec.rb +16 -19
  246. data/spec/mongoid/interceptable_spec.rb +584 -5
  247. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  248. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  249. data/spec/mongoid/mongoizable_spec.rb +285 -0
  250. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  251. data/spec/mongoid/persistable/deletable_spec.rb +2 -2
  252. data/spec/mongoid/persistable/destroyable_spec.rb +2 -2
  253. data/spec/mongoid/persistable/upsertable_spec.rb +14 -0
  254. data/spec/mongoid/persistence_context_spec.rb +50 -1
  255. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  256. data/spec/mongoid/query_cache_spec.rb +0 -154
  257. data/spec/mongoid/reloadable_spec.rb +35 -2
  258. data/spec/mongoid/scopable_spec.rb +21 -1
  259. data/spec/mongoid/shardable_spec.rb +14 -0
  260. data/spec/mongoid/stateful_spec.rb +28 -0
  261. data/spec/mongoid/timestamps_spec.rb +390 -0
  262. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  263. data/spec/mongoid/touchable_spec.rb +116 -0
  264. data/spec/mongoid/touchable_spec_models.rb +12 -8
  265. data/spec/mongoid/traversable_spec.rb +4 -11
  266. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  267. data/spec/mongoid/validatable/uniqueness_spec.rb +60 -31
  268. data/spec/mongoid/warnings_spec.rb +35 -0
  269. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  270. data/spec/rails/mongoid_spec.rb +4 -16
  271. data/spec/shared/lib/mrss/constraints.rb +8 -16
  272. data/spec/shared/lib/mrss/docker_runner.rb +23 -3
  273. data/spec/shared/lib/mrss/eg_config_utils.rb +51 -0
  274. data/spec/shared/lib/mrss/lite_constraints.rb +32 -1
  275. data/spec/shared/share/Dockerfile.erb +34 -48
  276. data/spec/shared/shlib/config.sh +27 -0
  277. data/spec/shared/shlib/server.sh +32 -19
  278. data/spec/shared/shlib/set_env.sh +37 -0
  279. data/spec/support/constraints.rb +24 -0
  280. data/spec/support/macros.rb +39 -0
  281. data/spec/support/models/augmentation.rb +12 -0
  282. data/spec/support/models/band.rb +3 -0
  283. data/spec/support/models/catalog.rb +24 -0
  284. data/spec/support/models/circus.rb +3 -0
  285. data/spec/support/models/code.rb +2 -0
  286. data/spec/support/models/fanatic.rb +8 -0
  287. data/spec/support/models/implant.rb +9 -0
  288. data/spec/support/models/label.rb +2 -0
  289. data/spec/support/models/membership.rb +1 -0
  290. data/spec/support/models/passport.rb +9 -0
  291. data/spec/support/models/person.rb +1 -0
  292. data/spec/support/models/player.rb +2 -0
  293. data/spec/support/models/powerup.rb +12 -0
  294. data/spec/support/models/registry.rb +1 -0
  295. data/spec/support/models/school.rb +14 -0
  296. data/spec/support/models/shield.rb +18 -0
  297. data/spec/support/models/student.rb +14 -0
  298. data/spec/support/models/weapon.rb +12 -0
  299. data/spec/support/schema_maps/schema_map_aws.json +17 -0
  300. data/spec/support/schema_maps/schema_map_aws_key_alt_names.json +12 -0
  301. data/spec/support/schema_maps/schema_map_azure.json +17 -0
  302. data/spec/support/schema_maps/schema_map_azure_key_alt_names.json +12 -0
  303. data/spec/support/schema_maps/schema_map_gcp.json +17 -0
  304. data/spec/support/schema_maps/schema_map_gcp_key_alt_names.json +12 -0
  305. data/spec/support/schema_maps/schema_map_kmip.json +17 -0
  306. data/spec/support/schema_maps/schema_map_kmip_key_alt_names.json +12 -0
  307. data/spec/support/schema_maps/schema_map_local.json +18 -0
  308. data/spec/support/schema_maps/schema_map_local_key_alt_names.json +12 -0
  309. data/spec/support/spec_config.rb +4 -0
  310. data.tar.gz.sig +0 -0
  311. metadata +76 -13
  312. metadata.gz.sig +0 -0
  313. data/lib/mongoid/errors/eager_load.rb +0 -23
  314. data/lib/mongoid/errors/invalid_value.rb +0 -17
  315. data/spec/mongoid/errors/eager_load_spec.rb +0 -31
@@ -17,6 +17,8 @@ module Mongoid
17
17
  include Association::EagerLoadable
18
18
  include Queryable
19
19
 
20
+ Mongoid.deprecate(self, :geo_near)
21
+
20
22
  # Options constant.
21
23
  OPTIONS = [ :hint,
22
24
  :limit,
@@ -35,16 +37,6 @@ module Mongoid
35
37
  # @attribute [r] view The Mongo collection view.
36
38
  attr_reader :view
37
39
 
38
- # Is the context cached?
39
- #
40
- # @example Is the context cached?
41
- # context.cached?
42
- #
43
- # @return [ true, false ] If the context is cached.
44
- def cached?
45
- !!@cache
46
- end
47
-
48
40
  # Get the number of documents matching the query.
49
41
  #
50
42
  # @example Get the number of matching documents.
@@ -64,7 +56,7 @@ module Mongoid
64
56
  # @return [ Integer ] The number of matches.
65
57
  def count(options = {}, &block)
66
58
  return super(&block) if block_given?
67
- try_cache(:count) { view.count_documents(options) }
59
+ view.count_documents(options)
68
60
  end
69
61
 
70
62
  # Get the estimated number of documents matching the query.
@@ -83,7 +75,7 @@ module Mongoid
83
75
  unless self.criteria.selector.empty?
84
76
  raise Mongoid::Errors::InvalidEstimatedCountCriteria.new(self.klass)
85
77
  end
86
- try_cache(:estimated_count) { view.estimated_document_count(options) }
78
+ view.estimated_document_count(options)
87
79
  end
88
80
 
89
81
  # Delete all documents in the database that match the selector.
@@ -151,7 +143,6 @@ module Mongoid
151
143
  documents_for_iteration.each do |doc|
152
144
  yield_document(doc, &block)
153
145
  end
154
- @cache_loaded = true
155
146
  self
156
147
  else
157
148
  to_enum
@@ -164,17 +155,11 @@ module Mongoid
164
155
  # context.exists?
165
156
  #
166
157
  # @note We don't use count here since Mongo does not use counted
167
- # b-tree indexes, unless a count is already cached then that is
168
- # used to determine the value.
158
+ # b-tree indexes.
169
159
  #
170
160
  # @return [ true, false ] If the count is more than zero.
171
161
  def exists?
172
- return !documents.empty? if cached? && cache_loaded?
173
- return @count > 0 if instance_variable_defined?(:@count)
174
-
175
- try_cache(:exists) do
176
- !!(view.projection(_id: 1).limit(1).first)
177
- end
162
+ !!(view.projection(_id: 1).limit(1).first)
178
163
  end
179
164
 
180
165
  # Run an explain on the criteria.
@@ -248,29 +233,16 @@ module Mongoid
248
233
  # @note Automatically adding a sort on _id when no other sort is
249
234
  # defined on the criteria has the potential to cause bad performance issues.
250
235
  # If you experience unexpected poor performance when using #first or #last
251
- # and have no sort defined on the criteria, use the option { id_sort: :none }.
252
- # Be aware that #first/#last won't guarantee order in this case.
236
+ # and have no sort defined on the criteria, use #take instead.
237
+ # Be aware that #take won't guarantee order.
253
238
  #
254
- # @param [ Hash ] opts The options for the query returning the first document.
255
- #
256
- # @option opts [ :none ] :id_sort Don't apply a sort on _id if no other sort
257
- # is defined on the criteria.
239
+ # @param [ Integer ] limit The number of documents to return.
258
240
  #
259
241
  # @return [ Document ] The first document.
260
- def first(opts = {})
261
- return documents.first if cached? && cache_loaded?
262
- try_cache(:first) do
263
- if sort = view.sort || ({ _id: 1 } unless opts[:id_sort] == :none)
264
- if raw_doc = view.sort(sort).limit(1).first
265
- doc = Factory.from_db(klass, raw_doc, criteria)
266
- eager_load([doc]).first
267
- end
268
- else
269
- if raw_doc = view.limit(1).first
270
- doc = Factory.from_db(klass, raw_doc, criteria)
271
- eager_load([doc]).first
272
- end
273
- end
242
+ def first(limit = nil)
243
+ sort = view.sort || { _id: 1 }
244
+ if raw_docs = view.sort(sort).limit(limit || 1).to_a
245
+ process_raw_docs(raw_docs, limit)
274
246
  end
275
247
  end
276
248
  alias :one :first
@@ -279,7 +251,6 @@ module Mongoid
279
251
  #
280
252
  # @api private
281
253
  def find_first
282
- return documents.first if cached? && cache_loaded?
283
254
  if raw_doc = view.first
284
255
  doc = Factory.from_db(klass, raw_doc, criteria)
285
256
  eager_load([doc]).first
@@ -309,29 +280,6 @@ module Mongoid
309
280
  GeoNear.new(collection, criteria, coordinates)
310
281
  end
311
282
 
312
- # Invoke the block for each element of Contextual. Create a new array
313
- # containing the values returned by the block.
314
- #
315
- # If the symbol field name is passed instead of the block, additional
316
- # optimizations would be used.
317
- #
318
- # @example Map by some field.
319
- # context.map(:field1)
320
- #
321
- # @example Map with block.
322
- # context.map(&:field1)
323
- #
324
- # @param [ Symbol ] field The field name.
325
- #
326
- # @return [ Array ] The result of mapping.
327
- def map(field = nil, &block)
328
- if block_given?
329
- super(&block)
330
- else
331
- criteria.pluck(field)
332
- end
333
- end
334
-
335
283
  # Create the new Mongo context. This delegates operations to the
336
284
  # underlying driver.
337
285
  #
@@ -340,7 +288,7 @@ module Mongoid
340
288
  #
341
289
  # @param [ Criteria ] criteria The criteria.
342
290
  def initialize(criteria)
343
- @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
291
+ @criteria, @klass = criteria, criteria.klass
344
292
  @collection = @klass.collection
345
293
  criteria.send(:merge_type_selection)
346
294
  @view = collection.find(criteria.selector, session: _session)
@@ -357,32 +305,26 @@ module Mongoid
357
305
  # @note Automatically adding a sort on _id when no other sort is
358
306
  # defined on the criteria has the potential to cause bad performance issues.
359
307
  # If you experience unexpected poor performance when using #first or #last
360
- # and have no sort defined on the criteria, use the option { id_sort: :none }.
361
- # Be aware that #first/#last won't guarantee order in this case.
362
- #
363
- # @param [ Hash ] opts The options for the query returning the first document.
364
- #
365
- # @option opts [ :none ] :id_sort Don't apply a sort on _id if no other sort
366
- # is defined on the criteria.
367
- def last(opts = {})
368
- try_cache(:last) do
369
- with_inverse_sorting(opts) do
370
- if raw_doc = view.limit(1).first
371
- doc = Factory.from_db(klass, raw_doc, criteria)
372
- eager_load([doc]).first
373
- end
374
- end
375
- end
308
+ # and have no sort defined on the criteria, use #take instead.
309
+ # Be aware that #take won't guarantee order.
310
+ #
311
+ # @param [ Integer ] limit The number of documents to return.
312
+ #
313
+ # @return [ Document ] The last document.
314
+ def last(limit = nil)
315
+ raw_docs = view.sort(inverse_sorting).limit(limit || 1).to_a.reverse
316
+ process_raw_docs(raw_docs, limit)
376
317
  end
377
318
 
378
- # Get's the number of documents matching the query selector.
319
+ # Returns the number of documents in the database matching
320
+ # the query selector.
379
321
  #
380
322
  # @example Get the length.
381
323
  # context.length
382
324
  #
383
325
  # @return [ Integer ] The number of documents.
384
326
  def length
385
- @length ||= self.count
327
+ self.count
386
328
  end
387
329
  alias :size :length
388
330
 
@@ -398,6 +340,44 @@ module Mongoid
398
340
  @view = view.limit(value) and self
399
341
  end
400
342
 
343
+ # Take the given number of documents from the database.
344
+ #
345
+ # @example Take 10 documents
346
+ # context.take(10)
347
+ #
348
+ # @param [ Integer | nil ] limit The number of documents to return or nil.
349
+ #
350
+ # @return [ Document | Array<Document> ] The list of documents, or one
351
+ # document if no value was given.
352
+ def take(limit = nil)
353
+ if limit
354
+ limit(limit).to_a
355
+ else
356
+ # Do to_a first so that the Mongo#first method is not used and the
357
+ # result is not sorted.
358
+ limit(1).to_a.first
359
+ end
360
+ end
361
+
362
+ # Take one document from the database and raise an error if there are none.
363
+ #
364
+ # @example Take a document
365
+ # context.take!
366
+ #
367
+ # @return [ Document ] The document.
368
+ #
369
+ # @raises [ Mongoid::Errors::DocumentNotFound ] raises when there are no
370
+ # documents to take.
371
+ def take!
372
+ # Do to_a first so that the Mongo#first method is not used and the
373
+ # result is not sorted.
374
+ if fst = limit(1).to_a.first
375
+ fst
376
+ else
377
+ raise Errors::DocumentNotFound.new(klass, nil, nil)
378
+ end
379
+ end
380
+
401
381
  # Initiate a map/reduce operation from the context.
402
382
  #
403
383
  # @example Initiate a map/reduce.
@@ -417,9 +397,6 @@ module Mongoid
417
397
  # @example Pluck a field.
418
398
  # context.pluck(:_id)
419
399
  #
420
- # @note This method will return the raw db values - it performs no custom
421
- # serialization.
422
- #
423
400
  # @param [ String, Symbol, Array ] fields Fields to pluck.
424
401
  #
425
402
  # @return [ Array<Object, Array> ] The plucked values.
@@ -452,6 +429,87 @@ module Mongoid
452
429
  end
453
430
  end
454
431
 
432
+ # Pick the single field values from the database.
433
+ #
434
+ # @example Pick a field.
435
+ # context.pick(:_id)
436
+ #
437
+ # @param [ String, Symbol, Array ] fields Fields to pick.
438
+ #
439
+ # @return [ Object, Array<Object> ] The picked values.
440
+ def pick(*fields)
441
+ limit(1).pluck(*fields).first
442
+ end
443
+
444
+ # Get a hash of counts for the values of a single field. For example,
445
+ # if the following documents were in the database:
446
+ #
447
+ # { _id: 1, age: 21 }
448
+ # { _id: 2, age: 21 }
449
+ # { _id: 3, age: 22 }
450
+ #
451
+ # Model.tally("age")
452
+ #
453
+ # would yield the following result:
454
+ #
455
+ # { 21 => 2, 22 => 1 }
456
+ #
457
+ # When tallying a field inside an array or embeds_many association:
458
+ #
459
+ # { _id: 1, array: [ { x: 1 }, { x: 2 } ] }
460
+ # { _id: 2, array: [ { x: 1 }, { x: 2 } ] }
461
+ # { _id: 3, array: [ { x: 1 }, { x: 3 } ] }
462
+ #
463
+ # Model.tally("array.x")
464
+ #
465
+ # The keys of the resulting hash are arrays:
466
+ #
467
+ # { [ 1, 2 ] => 2, [ 1, 3 ] => 1 }
468
+ #
469
+ # Note that if tallying an element in an array of hashes, and the key
470
+ # doesn't exist in some of the hashes, tally will not include those
471
+ # nil keys in the resulting hash:
472
+ #
473
+ # { _id: 1, array: [ { x: 1 }, { x: 2 }, { y: 3 } ] }
474
+ #
475
+ # Model.tally("array.x")
476
+ # # => { [ 1, 2 ] => 1 }
477
+ #
478
+ # @param [ String | Symbol ] field The field name.
479
+ #
480
+ # @return [ Hash ] The hash of counts.
481
+ def tally(field)
482
+ name = klass.cleanse_localized_field_names(field)
483
+
484
+ fld = klass.traverse_association_tree(name)
485
+ pipeline = [ { "$group" => { _id: "$#{name}", counts: { "$sum": 1 } } } ]
486
+ pipeline.unshift("$match" => view.filter) unless view.filter.blank?
487
+
488
+ collection.aggregate(pipeline).reduce({}) do |tallies, doc|
489
+ is_translation = "#{name}_translations" == field.to_s
490
+ val = doc["_id"]
491
+
492
+ key = if val.is_a?(Array)
493
+ val.map do |v|
494
+ demongoize_with_field(fld, v, is_translation)
495
+ end
496
+ else
497
+ demongoize_with_field(fld, val, is_translation)
498
+ end
499
+
500
+ # The only time where a key will already exist in the tallies hash
501
+ # is when the values are stored differently in the database, but
502
+ # demongoize to the same value. A good example of when this happens
503
+ # is when using localized fields. While the server query won't group
504
+ # together hashes that have other values in different languages, the
505
+ # demongoized value is just the translation in the current locale,
506
+ # which can be the same across multiple of those unequal hashes.
507
+ tallies[key] ||= 0
508
+ tallies[key] += doc["counts"]
509
+ tallies
510
+ end
511
+ end
512
+
455
513
  # Skips the provided number of documents.
456
514
  #
457
515
  # @example Skip the documents.
@@ -518,22 +576,6 @@ module Mongoid
518
576
 
519
577
  private
520
578
 
521
- # yield the block given or return the cached value
522
- #
523
- # @param [ String, Symbol ] key The instance variable name
524
- #
525
- # @return the result of the block
526
- def try_cache(key, &block)
527
- unless cached?
528
- yield
529
- else
530
- unless ret = instance_variable_get("@#{key}")
531
- instance_variable_set("@#{key}", ret = yield)
532
- end
533
- ret
534
- end
535
- end
536
-
537
579
  # Update the documents for the provided method.
538
580
  #
539
581
  # @api private
@@ -594,55 +636,9 @@ module Mongoid
594
636
  # Map the inverse sort symbols to the correct MongoDB values.
595
637
  #
596
638
  # @api private
597
- #
598
- # @example Apply the inverse sorting params to the given block
599
- # context.with_inverse_sorting
600
- def with_inverse_sorting(opts = {})
601
- begin
602
- if sort = criteria.options[:sort] || ( { _id: 1 } unless opts[:id_sort] == :none )
603
- @view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}])
604
- end
605
- yield
606
- ensure
607
- apply_option(:sort)
608
- end
609
- end
610
-
611
- # Is the cache able to be added to?
612
- #
613
- # @api private
614
- #
615
- # @example Is the context cacheable?
616
- # context.cacheable?
617
- #
618
- # @return [ true, false ] If caching, and the cache isn't loaded.
619
- def cacheable?
620
- cached? && !cache_loaded?
621
- end
622
-
623
- # Is the cache fully loaded? Will be true if caching after one full
624
- # iteration.
625
- #
626
- # @api private
627
- #
628
- # @example Is the cache loaded?
629
- # context.cache_loaded?
630
- #
631
- # @return [ true, false ] If the cache is loaded.
632
- def cache_loaded?
633
- !!@cache_loaded
634
- end
635
-
636
- # Get the documents for cached queries.
637
- #
638
- # @api private
639
- #
640
- # @example Get the cached documents.
641
- # context.documents
642
- #
643
- # @return [ Array<Document> ] The documents.
644
- def documents
645
- @documents ||= []
639
+ def inverse_sorting
640
+ sort = view.sort || { _id: 1 }
641
+ Hash[sort.map{|k, v| [k, -1*v]}]
646
642
  end
647
643
 
648
644
  # Get the documents the context should iterate. This follows 3 rules:
@@ -660,7 +656,6 @@ module Mongoid
660
656
  #
661
657
  # @return [ Array<Document>, Mongo::Collection::View ] The docs to iterate.
662
658
  def documents_for_iteration
663
- return documents if cached? && !documents.empty?
664
659
  return view unless eager_loadable?
665
660
  docs = view.map{ |doc| Factory.from_db(klass, doc, criteria) }
666
661
  eager_load(docs)
@@ -680,7 +675,6 @@ module Mongoid
680
675
  doc = document.respond_to?(:_id) ?
681
676
  document : Factory.from_db(klass, document, criteria)
682
677
  yield(doc)
683
- documents.push(doc) if cacheable?
684
678
  end
685
679
 
686
680
  private
@@ -693,6 +687,26 @@ module Mongoid
693
687
  collection.write_concern.nil? || collection.write_concern.acknowledged?
694
688
  end
695
689
 
690
+ # Fetch the element from the given hash and demongoize it using the
691
+ # given field. If the obj is an array, map over it and call this method
692
+ # on all of its elements.
693
+ #
694
+ # @param [ Hash | Array<Hash> ] obj The hash or array of hashes to fetch from.
695
+ # @param [ String ] meth The key to fetch from the hash.
696
+ # @param [ Field ] field The field to use for demongoization.
697
+ #
698
+ # @return [ Object ] The demongoized value.
699
+ #
700
+ # @api private
701
+ def fetch_and_demongoize(obj, meth, field)
702
+ if obj.is_a?(Array)
703
+ obj.map { |doc| fetch_and_demongoize(doc, meth, field) }
704
+ else
705
+ res = obj.try(:fetch, meth, nil)
706
+ field ? field.demongoize(res) : res.class.demongoize(res)
707
+ end
708
+ end
709
+
696
710
  # Extracts the value for the given field name from the given attribute
697
711
  # hash.
698
712
  #
@@ -701,24 +715,18 @@ module Mongoid
701
715
  #
702
716
  # @param [ Object ] The value for the given field name
703
717
  def extract_value(attrs, field_name)
704
- def fetch_and_demongoize(d, meth, klass)
705
- res = d.try(:fetch, meth, nil)
706
- if field = klass.fields[meth]
707
- field.demongoize(res)
708
- else
709
- res.class.demongoize(res)
710
- end
711
- end
718
+ i = 1
719
+ num_meths = field_name.count('.') + 1
720
+ curr = attrs.dup
712
721
 
713
- k = klass
714
- meths = field_name.split('.')
715
- meths.each_with_index.inject(attrs) do |curr, (meth, i)|
722
+ klass.traverse_association_tree(field_name) do |meth, obj, is_field|
723
+ field = obj if is_field
716
724
  is_translation = false
717
- if !k.fields.key?(meth) && !k.relations.key?(meth)
718
- if tr = meth.match(/(.*)_translations\z/)&.captures&.first
719
- is_translation = true
720
- meth = tr
721
- end
725
+ # If no association or field was found, check if the meth is an
726
+ # _translations field.
727
+ if obj.nil? & tr = meth.match(/(.*)_translations\z/)&.captures&.first
728
+ is_translation = true
729
+ meth = tr
722
730
  end
723
731
 
724
732
  # 1. If curr is an array fetch from all elements in the array.
@@ -731,31 +739,24 @@ module Mongoid
731
739
  # 3. If the meth is an _translations field, do not demongoize the
732
740
  # value so the full hash is returned.
733
741
  # 4. Otherwise, fetch and demongoize the value for the key meth.
734
- if curr.is_a? Array
735
- res = curr.map { |x| fetch_and_demongoize(x, meth, k) }
742
+ curr = if curr.is_a? Array
743
+ res = fetch_and_demongoize(curr, meth, field)
736
744
  res.empty? ? nil : res
737
- elsif !is_translation && k.fields[meth]&.localized?
738
- if i < meths.length-1
745
+ elsif !is_translation && field&.localized?
746
+ if i < num_meths
739
747
  curr.try(:fetch, meth, nil)
740
748
  else
741
- fetch_and_demongoize(curr, meth, k)
749
+ fetch_and_demongoize(curr, meth, field)
742
750
  end
743
751
  elsif is_translation
744
752
  curr.try(:fetch, meth, nil)
745
753
  else
746
- fetch_and_demongoize(curr, meth, k)
747
- end.tap do
748
- if as = k.try(:aliased_associations)
749
- if a = as.fetch(meth, nil)
750
- meth = a
751
- end
752
- end
753
-
754
- if relation = k.relations[meth]
755
- k = relation.klass
756
- end
754
+ fetch_and_demongoize(curr, meth, field)
757
755
  end
756
+
757
+ i += 1
758
758
  end
759
+ curr
759
760
  end
760
761
 
761
762
  # Recursively demongoize the given value. This method recursively traverses
@@ -768,30 +769,49 @@ module Mongoid
768
769
  #
769
770
  # @return [ Object ] The demongoized value.
770
771
  def recursive_demongoize(field_name, value, is_translation)
771
- k = klass
772
- field_name.split('.').each do |meth|
773
- if as = k.try(:aliased_associations)
774
- if a = as.fetch(meth, nil)
775
- meth = a.to_s
776
- end
777
- end
772
+ field = klass.traverse_association_tree(field_name)
773
+ demongoize_with_field(field, value, is_translation)
774
+ end
778
775
 
779
- if relation = k.relations[meth]
780
- k = relation.klass
781
- elsif field = k.fields[meth]
782
- # If it's a localized field that's not a hash, don't demongoize
783
- # again, we already have the translation. If it's an _translation
784
- # field, don't demongoize, we want the full hash not just a
785
- # specific translation.
786
- if field.localized? && (!value.is_a?(Hash) || is_translation)
787
- return value.class.demongoize(value)
788
- else
789
- return field.demongoize(value)
790
- end
776
+ # Demongoize the value for the given field. If the field is nil or the
777
+ # field is a translations field, the value is demongoized using its class.
778
+ #
779
+ # @param [ Field ] field The field to use to demongoize.
780
+ # @param [ Object ] value The value to demongoize.
781
+ # @param [ Boolean ] is_translation The field we are retrieving is an
782
+ # _translations field.
783
+ #
784
+ # @return [ Object ] The demongoized value.
785
+ #
786
+ # @api private
787
+ def demongoize_with_field(field, value, is_translation)
788
+ if field
789
+ # If it's a localized field that's not a hash, don't demongoize
790
+ # again, we already have the translation. If it's an _translations
791
+ # field, don't demongoize, we want the full hash not just a
792
+ # specific translation.
793
+ # If it is a hash, and it's not a translations field, we need to
794
+ # demongoize to get the correct translation.
795
+ if field.localized? && (!value.is_a?(Hash) || is_translation)
796
+ value.class.demongoize(value)
791
797
  else
792
- return value.class.demongoize(value)
798
+ field.demongoize(value)
793
799
  end
800
+ else
801
+ value.class.demongoize(value)
802
+ end
803
+ end
804
+
805
+ # Process the raw documents retrieved for #first/#last.
806
+ #
807
+ # @return [ Array<Document> | Document ] The list of documents or a
808
+ # single document.
809
+ def process_raw_docs(raw_docs, limit)
810
+ docs = raw_docs.map do |d|
811
+ Factory.from_db(klass, d, criteria)
794
812
  end
813
+ docs = eager_load(docs)
814
+ limit ? docs : docs.first
795
815
  end
796
816
  end
797
817
  end