mongoid 5.4.0 → 6.4.8

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 (301) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/Rakefile +26 -0
  5. data/lib/config/locales/en.yml +40 -0
  6. data/lib/mongoid/atomic/modifiers.rb +2 -2
  7. data/lib/mongoid/atomic.rb +5 -5
  8. data/lib/mongoid/attributes/readonly.rb +22 -0
  9. data/lib/mongoid/attributes.rb +22 -21
  10. data/lib/mongoid/cacheable.rb +36 -0
  11. data/lib/mongoid/changeable.rb +36 -0
  12. data/lib/mongoid/clients/options.rb +55 -250
  13. data/lib/mongoid/clients/sessions.rb +113 -0
  14. data/lib/mongoid/clients/storage_options.rb +2 -69
  15. data/lib/mongoid/clients.rb +10 -63
  16. data/lib/mongoid/composable.rb +29 -2
  17. data/lib/mongoid/config.rb +1 -0
  18. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  19. data/lib/mongoid/contextual/atomic.rb +4 -4
  20. data/lib/mongoid/contextual/map_reduce.rb +7 -3
  21. data/lib/mongoid/contextual/memory.rb +9 -4
  22. data/lib/mongoid/contextual/mongo.rb +65 -30
  23. data/lib/mongoid/contextual/none.rb +12 -0
  24. data/lib/mongoid/copyable.rb +13 -6
  25. data/lib/mongoid/criteria/marshalable.rb +2 -2
  26. data/lib/mongoid/criteria/modifiable.rb +29 -3
  27. data/lib/mongoid/criteria/options.rb +25 -0
  28. data/lib/mongoid/criteria/queryable/aggregable.rb +120 -0
  29. data/lib/mongoid/criteria/queryable/extensions/array.rb +185 -0
  30. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +37 -0
  31. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +34 -0
  32. data/lib/mongoid/criteria/queryable/extensions/date.rb +63 -0
  33. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +53 -0
  34. data/lib/mongoid/criteria/queryable/extensions/hash.rb +200 -0
  35. data/lib/mongoid/criteria/queryable/extensions/nil_class.rb +86 -0
  36. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +90 -0
  37. data/lib/mongoid/criteria/queryable/extensions/object.rb +206 -0
  38. data/lib/mongoid/criteria/queryable/extensions/range.rb +70 -0
  39. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +79 -0
  40. data/lib/mongoid/criteria/queryable/extensions/set.rb +34 -0
  41. data/lib/mongoid/criteria/queryable/extensions/string.rb +137 -0
  42. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +79 -0
  43. data/lib/mongoid/criteria/queryable/extensions/time.rb +60 -0
  44. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +54 -0
  45. data/lib/mongoid/criteria/queryable/extensions.rb +28 -0
  46. data/lib/mongoid/criteria/queryable/forwardable.rb +65 -0
  47. data/lib/mongoid/criteria/queryable/key.rb +103 -0
  48. data/lib/mongoid/criteria/queryable/macroable.rb +27 -0
  49. data/lib/mongoid/criteria/queryable/mergeable.rb +273 -0
  50. data/lib/mongoid/criteria/queryable/optional.rb +429 -0
  51. data/lib/mongoid/criteria/queryable/options.rb +153 -0
  52. data/lib/mongoid/criteria/queryable/pipeline.rb +111 -0
  53. data/lib/mongoid/criteria/queryable/selectable.rb +689 -0
  54. data/lib/mongoid/criteria/queryable/selector.rb +212 -0
  55. data/lib/mongoid/criteria/queryable/smash.rb +104 -0
  56. data/lib/mongoid/criteria/queryable.rb +87 -0
  57. data/lib/mongoid/criteria.rb +6 -2
  58. data/lib/mongoid/document.rb +34 -41
  59. data/lib/mongoid/errors/ambiguous_relationship.rb +1 -1
  60. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +1 -1
  61. data/lib/mongoid/errors/invalid_field.rb +2 -2
  62. data/lib/mongoid/errors/invalid_persistence_option.rb +29 -0
  63. data/lib/mongoid/errors/invalid_relation.rb +66 -0
  64. data/lib/mongoid/errors/invalid_session_use.rb +24 -0
  65. data/lib/mongoid/errors.rb +3 -0
  66. data/lib/mongoid/evolvable.rb +1 -1
  67. data/lib/mongoid/extensions/big_decimal.rb +17 -8
  68. data/lib/mongoid/extensions/date.rb +4 -1
  69. data/lib/mongoid/extensions/decimal128.rb +3 -3
  70. data/lib/mongoid/extensions/hash.rb +1 -0
  71. data/lib/mongoid/extensions/regexp.rb +1 -0
  72. data/lib/mongoid/extensions/string.rb +6 -3
  73. data/lib/mongoid/extensions/time.rb +4 -1
  74. data/lib/mongoid/extensions.rb +0 -4
  75. data/lib/mongoid/factory.rb +2 -1
  76. data/lib/mongoid/fields/validators/macro.rb +18 -0
  77. data/lib/mongoid/findable.rb +2 -2
  78. data/lib/mongoid/indexable.rb +16 -14
  79. data/lib/mongoid/interceptable.rb +9 -22
  80. data/lib/mongoid/matchable/all.rb +2 -2
  81. data/lib/mongoid/matchable/and.rb +3 -3
  82. data/lib/mongoid/matchable/default.rb +2 -2
  83. data/lib/mongoid/matchable/elem_match.rb +28 -0
  84. data/lib/mongoid/matchable/exists.rb +2 -2
  85. data/lib/mongoid/matchable/gt.rb +4 -2
  86. data/lib/mongoid/matchable/gte.rb +4 -2
  87. data/lib/mongoid/matchable/in.rb +2 -2
  88. data/lib/mongoid/matchable/lt.rb +4 -2
  89. data/lib/mongoid/matchable/lte.rb +4 -2
  90. data/lib/mongoid/matchable/ne.rb +2 -2
  91. data/lib/mongoid/matchable/nin.rb +2 -2
  92. data/lib/mongoid/matchable/nor.rb +37 -0
  93. data/lib/mongoid/matchable/or.rb +3 -3
  94. data/lib/mongoid/matchable/regexp.rb +3 -3
  95. data/lib/mongoid/matchable/size.rb +2 -2
  96. data/lib/mongoid/matchable.rb +16 -7
  97. data/lib/mongoid/persistable/creatable.rb +5 -3
  98. data/lib/mongoid/persistable/deletable.rb +5 -3
  99. data/lib/mongoid/persistable/destroyable.rb +1 -5
  100. data/lib/mongoid/persistable/settable.rb +5 -5
  101. data/lib/mongoid/persistable/updatable.rb +7 -14
  102. data/lib/mongoid/persistable/upsertable.rb +2 -1
  103. data/lib/mongoid/persistable.rb +4 -6
  104. data/lib/mongoid/persistence_context.rb +220 -0
  105. data/lib/mongoid/query_cache.rb +67 -23
  106. data/lib/mongoid/railtie.rb +17 -1
  107. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  108. data/lib/mongoid/relations/accessors.rb +3 -0
  109. data/lib/mongoid/relations/auto_save.rb +12 -4
  110. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -4
  111. data/lib/mongoid/relations/counter_cache.rb +15 -5
  112. data/lib/mongoid/relations/eager/base.rb +3 -3
  113. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +2 -2
  114. data/lib/mongoid/relations/eager/has_many.rb +1 -1
  115. data/lib/mongoid/relations/eager.rb +6 -11
  116. data/lib/mongoid/relations/embedded/batchable.rb +20 -18
  117. data/lib/mongoid/relations/embedded/in.rb +13 -1
  118. data/lib/mongoid/relations/embedded/many.rb +51 -10
  119. data/lib/mongoid/relations/embedded/one.rb +14 -1
  120. data/lib/mongoid/relations/macros.rb +9 -1
  121. data/lib/mongoid/relations/many.rb +4 -0
  122. data/lib/mongoid/relations/metadata.rb +3 -3
  123. data/lib/mongoid/relations/options.rb +2 -2
  124. data/lib/mongoid/relations/proxy.rb +1 -31
  125. data/lib/mongoid/relations/referenced/in.rb +19 -10
  126. data/lib/mongoid/relations/referenced/many.rb +30 -26
  127. data/lib/mongoid/relations/referenced/many_to_many.rb +20 -13
  128. data/lib/mongoid/relations/referenced/one.rb +15 -1
  129. data/lib/mongoid/relations/synchronization.rb +12 -12
  130. data/lib/mongoid/relations/targets/enumerable.rb +24 -4
  131. data/lib/mongoid/relations/touchable.rb +7 -4
  132. data/lib/mongoid/reloadable.rb +2 -2
  133. data/lib/mongoid/scopable.rb +3 -3
  134. data/lib/mongoid/serializable.rb +1 -1
  135. data/lib/mongoid/stateful.rb +1 -0
  136. data/lib/mongoid/tasks/database.rb +3 -2
  137. data/lib/mongoid/threaded.rb +74 -0
  138. data/lib/mongoid/traversable.rb +1 -1
  139. data/lib/mongoid/validatable/uniqueness.rb +1 -2
  140. data/lib/mongoid/version.rb +1 -1
  141. data/lib/mongoid.rb +6 -6
  142. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +18 -3
  143. data/spec/app/models/agent.rb +2 -0
  144. data/spec/app/models/album.rb +5 -1
  145. data/spec/app/models/array_field.rb +7 -0
  146. data/spec/app/models/artist.rb +21 -0
  147. data/spec/app/models/band.rb +3 -0
  148. data/spec/app/models/book.rb +2 -1
  149. data/spec/app/models/delegating_patient.rb +16 -0
  150. data/spec/app/models/dokument.rb +1 -0
  151. data/spec/app/models/ordered_post.rb +5 -0
  152. data/spec/app/models/oscar.rb +1 -2
  153. data/spec/app/models/page.rb +1 -1
  154. data/spec/app/models/person.rb +3 -3
  155. data/spec/app/models/princess.rb +2 -0
  156. data/spec/app/models/record.rb +1 -0
  157. data/spec/app/models/subscription.rb +1 -0
  158. data/spec/app/models/thing.rb +1 -1
  159. data/spec/config/mongoid.yml +15 -0
  160. data/spec/integration/document_spec.rb +22 -0
  161. data/spec/mongoid/atomic/modifiers_spec.rb +3 -3
  162. data/spec/mongoid/atomic_spec.rb +5 -5
  163. data/spec/mongoid/attributes/nested_spec.rb +18 -14
  164. data/spec/mongoid/attributes/readonly_spec.rb +87 -44
  165. data/spec/mongoid/attributes_spec.rb +90 -5
  166. data/spec/mongoid/cacheable_spec.rb +112 -0
  167. data/spec/mongoid/changeable_spec.rb +58 -0
  168. data/spec/mongoid/clients/factory_spec.rb +80 -28
  169. data/spec/mongoid/clients/options_spec.rb +396 -95
  170. data/spec/mongoid/clients/sessions_spec.rb +334 -0
  171. data/spec/mongoid/clients_spec.rb +243 -101
  172. data/spec/mongoid/composable_spec.rb +7 -0
  173. data/spec/mongoid/config_spec.rb +67 -11
  174. data/spec/mongoid/contextual/atomic_spec.rb +3 -3
  175. data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
  176. data/spec/mongoid/contextual/mongo_spec.rb +275 -22
  177. data/spec/mongoid/contextual/none_spec.rb +15 -0
  178. data/spec/mongoid/copyable_spec.rb +13 -4
  179. data/spec/mongoid/criteria/modifiable_spec.rb +297 -16
  180. data/spec/mongoid/criteria/options_spec.rb +29 -0
  181. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +370 -0
  182. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +523 -0
  183. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +59 -0
  184. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +58 -0
  185. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +213 -0
  186. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +330 -0
  187. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +405 -0
  188. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +58 -0
  189. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +65 -0
  190. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +327 -0
  191. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +65 -0
  192. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +77 -0
  193. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +108 -0
  194. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +309 -0
  195. data/spec/mongoid/{extensions/origin → criteria/queryable/extensions}/regexp_raw_spec.rb +2 -2
  196. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +90 -0
  197. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +39 -0
  198. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +302 -0
  199. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +167 -0
  200. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +376 -0
  201. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +347 -0
  202. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +87 -0
  203. data/spec/mongoid/criteria/queryable/key_spec.rb +52 -0
  204. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +49 -0
  205. data/spec/mongoid/criteria/queryable/optional_spec.rb +1799 -0
  206. data/spec/mongoid/criteria/queryable/options_spec.rb +360 -0
  207. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +200 -0
  208. data/spec/mongoid/criteria/queryable/queryable_spec.rb +137 -0
  209. data/spec/mongoid/criteria/queryable/selectable_spec.rb +4242 -0
  210. data/spec/mongoid/criteria/queryable/selector_spec.rb +844 -0
  211. data/spec/mongoid/criteria/queryable/smash_spec.rb +30 -0
  212. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  213. data/spec/mongoid/criteria_spec.rb +156 -22
  214. data/spec/mongoid/document_spec.rb +100 -90
  215. data/spec/mongoid/errors/invalid_relation_spec.rb +37 -0
  216. data/spec/mongoid/errors/mongoid_error_spec.rb +6 -3
  217. data/spec/mongoid/extensions/big_decimal_spec.rb +321 -19
  218. data/spec/mongoid/extensions/boolean_spec.rb +14 -0
  219. data/spec/mongoid/extensions/date_spec.rb +2 -6
  220. data/spec/mongoid/extensions/date_time_spec.rb +2 -6
  221. data/spec/mongoid/extensions/decimal128_spec.rb +1 -1
  222. data/spec/mongoid/extensions/float_spec.rb +8 -1
  223. data/spec/mongoid/extensions/hash_spec.rb +15 -0
  224. data/spec/mongoid/extensions/integer_spec.rb +8 -1
  225. data/spec/mongoid/extensions/object_spec.rb +11 -0
  226. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  227. data/spec/mongoid/extensions/string_spec.rb +53 -4
  228. data/spec/mongoid/extensions/time_spec.rb +2 -6
  229. data/spec/mongoid/extensions/time_with_zone_spec.rb +2 -6
  230. data/spec/mongoid/factory_spec.rb +11 -0
  231. data/spec/mongoid/fields_spec.rb +1 -1
  232. data/spec/mongoid/findable_spec.rb +47 -2
  233. data/spec/mongoid/indexable_spec.rb +15 -3
  234. data/spec/mongoid/interceptable_spec.rb +85 -19
  235. data/spec/mongoid/matchable/all_spec.rb +4 -4
  236. data/spec/mongoid/matchable/and_spec.rb +10 -10
  237. data/spec/mongoid/matchable/default_spec.rb +12 -12
  238. data/spec/mongoid/matchable/elem_match_spec.rb +86 -0
  239. data/spec/mongoid/matchable/exists_spec.rb +5 -5
  240. data/spec/mongoid/matchable/gt_spec.rb +18 -7
  241. data/spec/mongoid/matchable/gte_spec.rb +17 -7
  242. data/spec/mongoid/matchable/in_spec.rb +5 -5
  243. data/spec/mongoid/matchable/lt_spec.rb +18 -7
  244. data/spec/mongoid/matchable/lte_spec.rb +18 -7
  245. data/spec/mongoid/matchable/ne_spec.rb +5 -5
  246. data/spec/mongoid/matchable/nin_spec.rb +5 -5
  247. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  248. data/spec/mongoid/matchable/or_spec.rb +7 -7
  249. data/spec/mongoid/matchable/regexp_spec.rb +5 -5
  250. data/spec/mongoid/matchable/size_spec.rb +3 -3
  251. data/spec/mongoid/matchable_spec.rb +199 -54
  252. data/spec/mongoid/persistable/creatable_spec.rb +7 -2
  253. data/spec/mongoid/persistable/deletable_spec.rb +35 -1
  254. data/spec/mongoid/persistable/destroyable_spec.rb +25 -2
  255. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  256. data/spec/mongoid/persistable/savable_spec.rb +34 -29
  257. data/spec/mongoid/persistable/settable_spec.rb +77 -27
  258. data/spec/mongoid/persistable/updatable_spec.rb +182 -3
  259. data/spec/mongoid/persistable_spec.rb +16 -16
  260. data/spec/mongoid/persistence_context_spec.rb +694 -0
  261. data/spec/mongoid/positional_spec.rb +1 -1
  262. data/spec/mongoid/query_cache_spec.rb +170 -12
  263. data/spec/mongoid/relations/accessors_spec.rb +1 -1
  264. data/spec/mongoid/relations/auto_save_spec.rb +39 -6
  265. data/spec/mongoid/relations/bindings/referenced/many_to_many_spec.rb +4 -4
  266. data/spec/mongoid/relations/builders_spec.rb +37 -10
  267. data/spec/mongoid/relations/counter_cache_spec.rb +64 -3
  268. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +16 -0
  269. data/spec/mongoid/relations/eager_spec.rb +40 -0
  270. data/spec/mongoid/relations/embedded/many_spec.rb +305 -59
  271. data/spec/mongoid/relations/embedded/one_spec.rb +2 -1
  272. data/spec/mongoid/relations/macros_spec.rb +415 -7
  273. data/spec/mongoid/relations/metadata_spec.rb +15 -1
  274. data/spec/mongoid/relations/proxy_spec.rb +27 -1
  275. data/spec/mongoid/relations/referenced/in_spec.rb +41 -1
  276. data/spec/mongoid/relations/referenced/many_spec.rb +35 -25
  277. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +14 -26
  278. data/spec/mongoid/relations/synchronization_spec.rb +48 -2
  279. data/spec/mongoid/relations/targets/enumerable_spec.rb +108 -0
  280. data/spec/mongoid/relations/touchable_spec.rb +40 -0
  281. data/spec/mongoid/reloadable_spec.rb +51 -0
  282. data/spec/mongoid/scopable_spec.rb +13 -0
  283. data/spec/mongoid/serializable_spec.rb +0 -50
  284. data/spec/mongoid/threaded_spec.rb +68 -0
  285. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  286. data/spec/mongoid/validatable/uniqueness_spec.rb +18 -9
  287. data/spec/mongoid/validatable_spec.rb +16 -0
  288. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  289. data/spec/spec_helper.rb +101 -8
  290. data/spec/support/cluster_config.rb +158 -0
  291. data/spec/support/constraints.rb +101 -0
  292. data/spec/support/macros.rb +20 -0
  293. data/spec/support/session_registry.rb +50 -0
  294. data/spec/support/spec_config.rb +42 -0
  295. data.tar.gz.sig +0 -0
  296. metadata +163 -61
  297. metadata.gz.sig +0 -0
  298. data/lib/mongoid/clients/thread_options.rb +0 -19
  299. data/lib/mongoid/extensions/origin/regexp_raw.rb +0 -43
  300. data/lib/mongoid/railties/document.rb +0 -12
  301. data/spec/mongoid/railties/document_spec.rb +0 -24
@@ -12,6 +12,7 @@ require "mongoid/scopable"
12
12
  require "mongoid/serializable"
13
13
  require "mongoid/shardable"
14
14
  require "mongoid/stateful"
15
+ require "mongoid/cacheable"
15
16
  require "mongoid/traversable"
16
17
  require "mongoid/validatable"
17
18
 
@@ -32,7 +33,6 @@ module Mongoid
32
33
  include ActiveModel::Model
33
34
  include ActiveModel::ForbiddenAttributesProtection
34
35
  include ActiveModel::Serializers::JSON
35
- include ActiveModel::Serializers::Xml
36
36
  include Atomic
37
37
  include Changeable
38
38
  include Clients
@@ -50,6 +50,7 @@ module Mongoid
50
50
  include Serializable
51
51
  include Shardable
52
52
  include Stateful
53
+ include Cacheable
53
54
  include Threaded::Lifecycle
54
55
  include Traversable
55
56
  include Validatable
@@ -74,16 +75,42 @@ module Mongoid
74
75
  Scopable,
75
76
  Serializable,
76
77
  Clients,
78
+ Clients::Options,
77
79
  Shardable,
78
80
  Stateful,
81
+ Cacheable,
79
82
  Threaded::Lifecycle,
80
83
  Traversable,
81
84
  Validatable,
82
85
  Equality,
86
+ Relations::Synchronization,
87
+ Relations::Macros,
83
88
  ActiveModel::Model,
84
89
  ActiveModel::Validations
85
90
  ]
86
91
 
92
+ # These are methods names defined in included blocks that may conflict
93
+ # with user-defined relation or field names.
94
+ # They won't be in the list of Module.instance_methods on which the
95
+ # #prohibited_methods code below is dependent so we must track them
96
+ # separately.
97
+ #
98
+ # @return [ Array<Symbol> ] A list of reserved method names.
99
+ #
100
+ # @since 6.0.0
101
+ RESERVED_METHOD_NAMES = [ :fields,
102
+ :aliased_fields,
103
+ :localized_fields,
104
+ :index_specifications,
105
+ :shard_key_fields,
106
+ :nested_attributes,
107
+ :readonly_attributes,
108
+ :storage_options,
109
+ :cascades,
110
+ :cyclic,
111
+ :cache_timestamp_format
112
+ ]
113
+
87
114
  class << self
88
115
 
89
116
  # Get a list of methods that would be a bad idea to define as field names
@@ -98,7 +125,7 @@ module Mongoid
98
125
  def prohibited_methods
99
126
  @prohibited_methods ||= MODULES.flat_map do |mod|
100
127
  mod.instance_methods.map(&:to_sym)
101
- end
128
+ end + RESERVED_METHOD_NAMES
102
129
  end
103
130
  end
104
131
  end
@@ -25,6 +25,7 @@ module Mongoid
25
25
  option :use_activesupport_time_zone, default: true
26
26
  option :use_utc, default: false
27
27
  option :log_level, default: :info
28
+ option :belongs_to_required_by_default, default: true
28
29
  option :app_name, default: nil
29
30
 
30
31
  # Has Mongoid been configured? This is checking that at least a valid
@@ -23,7 +23,7 @@ module Mongoid
23
23
  #
24
24
  # @since 3.0.0
25
25
  def aggregates(field)
26
- result = collection.find.aggregate(pipeline(field)).to_a
26
+ result = collection.find.aggregate(pipeline(field), session: _session).to_a
27
27
  if result.empty?
28
28
  { "count" => 0, "sum" => nil, "avg" => nil, "min" => nil, "max" => nil }
29
29
  else
@@ -109,16 +109,16 @@ module Mongoid
109
109
  # Perform an atomic $push/$each operation on the matching documents.
110
110
  #
111
111
  # @example Push the values to the matching docs.
112
- # context.push_each(members: [ "Alan", "Fletch" ])
112
+ # context.push_all(members: [ "Alan", "Fletch" ])
113
113
  #
114
114
  # @param [ Hash ] pushes The operations.
115
115
  #
116
116
  # @return [ nil ] Nil.
117
117
  #
118
- # @since 5.4.0
119
- def push_each(pushes)
118
+ # @since 3.0.0
119
+ def push_all(pushes)
120
120
  push_each_updates = collect_operations(pushes).each.inject({}) do |ops, (field, elements)|
121
- ops.merge!(field => { '$each' => elements })
121
+ ops.merge!(field => {'$each' => elements})
122
122
  end
123
123
  view.update_many("$push" => push_each_updates)
124
124
  end
@@ -165,13 +165,13 @@ module Mongoid
165
165
  def raw
166
166
  validate_out!
167
167
  cmd = command
168
- opts = { read: cmd.delete(:read).options } if cmd[:read]
169
- @map_reduce.database.command(cmd, opts || {}).first
168
+ opts = { read: cmd.delete(:read) } if cmd[:read]
169
+ @map_reduce.database.command(cmd, (opts || {}).merge(session: _session)).first
170
170
  end
171
171
  alias :results :raw
172
172
 
173
173
  # Execute the map/reduce, returning the raw output.
174
- # Useful when you don't care about map/reduce's ouptut.
174
+ # Useful when you don't care about map/reduce's output.
175
175
  #
176
176
  # @example Run the map reduce
177
177
  # map_reduce.execute
@@ -249,6 +249,10 @@ module Mongoid
249
249
  def validate_out!
250
250
  raise Errors::NoMapReduceOutput.new({}) unless @map_reduce.out
251
251
  end
252
+
253
+ def _session
254
+ criteria.send(:_session)
255
+ end
252
256
  end
253
257
  end
254
258
  end
@@ -44,11 +44,12 @@ module Mongoid
44
44
  deleted = count
45
45
  removed = map do |doc|
46
46
  prepare_remove(doc)
47
- doc.as_document
47
+ doc.send(:as_attributes)
48
48
  end
49
49
  unless removed.empty?
50
50
  collection.find(selector).update_one(
51
- positionally(selector, "$pullAll" => { path => removed })
51
+ positionally(selector, "$pullAll" => { path => removed }),
52
+ session: _session
52
53
  )
53
54
  end
54
55
  deleted
@@ -148,7 +149,7 @@ module Mongoid
148
149
  @documents = criteria.documents.select do |doc|
149
150
  @root ||= doc._root
150
151
  @collection ||= root.collection
151
- doc.matches?(criteria.selector)
152
+ doc._matches?(criteria.selector)
152
153
  end
153
154
  apply_sorting
154
155
  apply_options
@@ -303,7 +304,7 @@ module Mongoid
303
304
  updates["$set"].merge!(doc.atomic_updates["$set"] || {})
304
305
  doc.move_changes
305
306
  end
306
- collection.find(selector).update_one(updates) unless updates["$set"].empty?
307
+ collection.find(selector).update_one(updates, session: _session) unless updates["$set"].empty?
307
308
  end
308
309
 
309
310
  # Get the limiting value.
@@ -444,6 +445,10 @@ module Mongoid
444
445
  doc._parent.remove_child(doc)
445
446
  doc.destroyed = true
446
447
  end
448
+
449
+ def _session
450
+ @criteria.send(:_session)
451
+ end
447
452
  end
448
453
  end
449
454
  end
@@ -24,6 +24,7 @@ module Mongoid
24
24
  :sort,
25
25
  :batch_size,
26
26
  :max_scan,
27
+ :max_time_ms,
27
28
  :snapshot,
28
29
  :comment,
29
30
  :read,
@@ -94,7 +95,8 @@ module Mongoid
94
95
  def destroy
95
96
  each.inject(0) do |count, doc|
96
97
  doc.destroy
97
- count += 1
98
+ count += 1 if acknowledged_write?
99
+ count
98
100
  end
99
101
  end
100
102
  alias :destroy_all :destroy
@@ -110,7 +112,9 @@ module Mongoid
110
112
  #
111
113
  # @since 3.0.0
112
114
  def distinct(field)
113
- view.distinct(klass.database_field_name(field))
115
+ view.distinct(klass.database_field_name(field)).map do |value|
116
+ value.class.demongoize(value)
117
+ end
114
118
  end
115
119
 
116
120
  # Iterate over the context. If provided a block, yield to a Mongoid
@@ -233,21 +237,33 @@ module Mongoid
233
237
  # @example Get the first document.
234
238
  # context.first
235
239
  #
236
- # @note Mongoid previously added an _id sort when no sort parameters were
237
- # provided explicitly by the user. This caused bad performance issues
238
- # and was not expected, so #first/#last will no longer guarantee order
239
- # if no sorting parameters are provided. For order guarantees - a sort
240
- # must be explicitly provided.
240
+ # @note Automatically adding a sort on _id when no other sort is
241
+ # defined on the criteria has the potential to cause bad performance issues.
242
+ # If you experience unexpected poor performance when using #first or #last
243
+ # and have no sort defined on the criteria, use the option { id_sort: :none }.
244
+ # Be aware that #first/#last won't guarantee order in this case.
245
+ #
246
+ # @param [ Hash ] opts The options for the query returning the first document.
247
+ #
248
+ # @option opts [ :none ] :id_sort Don't apply a sort on _id if no other sort
249
+ # is defined on the criteria.
241
250
  #
242
251
  # @return [ Document ] The first document.
243
252
  #
244
253
  # @since 3.0.0
245
- def first
254
+ def first(opts = {})
246
255
  return documents.first if cached? && cache_loaded?
247
256
  try_cache(:first) do
248
- if raw_doc = view.limit(-1).first
249
- doc = Factory.from_db(klass, raw_doc, criteria.options[:fields])
250
- eager_load([doc]).first
257
+ if sort = view.sort || ({ _id: 1 } unless opts[:id_sort] == :none)
258
+ if raw_doc = view.sort(sort).limit(-1).first
259
+ doc = Factory.from_db(klass, raw_doc, criteria.options[:fields])
260
+ eager_load([doc]).first
261
+ end
262
+ else
263
+ if raw_doc = view.limit(-1).first
264
+ doc = Factory.from_db(klass, raw_doc, criteria.options[:fields])
265
+ eager_load([doc]).first
266
+ end
251
267
  end
252
268
  end
253
269
  end
@@ -323,9 +339,9 @@ module Mongoid
323
339
  # @since 3.0.0
324
340
  def initialize(criteria)
325
341
  @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
326
- @collection = @klass.with(criteria.persistence_options || {}).collection
342
+ @collection = @klass.collection
327
343
  criteria.send(:merge_type_selection)
328
- @view = collection.find(criteria.selector)
344
+ @view = collection.find(criteria.selector, session: _session)
329
345
  apply_options
330
346
  end
331
347
 
@@ -336,18 +352,21 @@ module Mongoid
336
352
  # @example Get the last document.
337
353
  # context.last
338
354
  #
339
- # @note Mongoid previously added an _id sort when no sort parameters were
340
- # provided explicitly by the user. This caused bad performance issues
341
- # and was not expected, so #first/#last will no longer guarantee order
342
- # if no sorting parameters are provided. For order guarantees - a sort
343
- # must be explicitly provided.
355
+ # @note Automatically adding a sort on _id when no other sort is
356
+ # defined on the criteria has the potential to cause bad performance issues.
357
+ # If you experience unexpected poor performance when using #first or #last
358
+ # and have no sort defined on the criteria, use the option { id_sort: :none }.
359
+ # Be aware that #first/#last won't guarantee order in this case.
360
+ #
361
+ # @param [ Hash ] opts The options for the query returning the first document.
344
362
  #
345
- # @return [ Document ] The last document.
363
+ # @option opts [ :none ] :id_sort Don't apply a sort on _id if no other sort
364
+ # is defined on the criteria.
346
365
  #
347
366
  # @since 3.0.0
348
- def last
367
+ def last(opts = {})
349
368
  try_cache(:last) do
350
- with_inverse_sorting do
369
+ with_inverse_sorting(opts) do
351
370
  if raw_doc = view.limit(-1).first
352
371
  doc = Factory.from_db(klass, raw_doc, criteria.options[:fields])
353
372
  eager_load([doc]).first
@@ -468,12 +487,16 @@ module Mongoid
468
487
  # context.update({ "$set" => { name: "Smiths" }})
469
488
  #
470
489
  # @param [ Hash ] attributes The new attributes for the document.
490
+ # @param [ Hash ] opts The update operation options.
491
+ #
492
+ # @option opts [ Array ] :array_filters A set of filters specifying to which array elements
493
+ # an update should apply.
471
494
  #
472
495
  # @return [ nil, false ] False if no attributes were provided.
473
496
  #
474
497
  # @since 3.0.0
475
- def update(attributes = nil)
476
- update_documents(attributes)
498
+ def update(attributes = nil, opts = {})
499
+ update_documents(attributes, :update_one, opts)
477
500
  end
478
501
 
479
502
  # Update all the matching documents atomically.
@@ -482,12 +505,16 @@ module Mongoid
482
505
  # context.update_all({ "$set" => { name: "Smiths" }})
483
506
  #
484
507
  # @param [ Hash ] attributes The new attributes for each document.
508
+ # @param [ Hash ] opts The update operation options.
509
+ #
510
+ # @option opts [ Array ] :array_filters A set of filters specifying to which array elements
511
+ # an update should apply.
485
512
  #
486
513
  # @return [ nil, false ] False if no attributes were provided.
487
514
  #
488
515
  # @since 3.0.0
489
- def update_all(attributes = nil)
490
- update_documents(attributes, :update_many)
516
+ def update_all(attributes = nil, opts = {})
517
+ update_documents(attributes, :update_many, opts)
491
518
  end
492
519
 
493
520
  private
@@ -523,10 +550,10 @@ module Mongoid
523
550
  # @return [ true, false ] If the update succeeded.
524
551
  #
525
552
  # @since 3.0.4
526
- def update_documents(attributes, method = :update_one)
553
+ def update_documents(attributes, method = :update_one, opts = {})
527
554
  return false unless attributes
528
555
  attributes = Hash[attributes.map { |k, v| [klass.database_field_name(k.to_s), v] }]
529
- view.send(method, attributes.__consolidate__(klass))
556
+ view.send(method, attributes.__consolidate__(klass), opts)
530
557
  end
531
558
 
532
559
  # Apply the field limitations.
@@ -583,10 +610,10 @@ module Mongoid
583
610
  # context.with_inverse_sorting
584
611
  #
585
612
  # @since 3.0.0
586
- def with_inverse_sorting
613
+ def with_inverse_sorting(opts = {})
587
614
  begin
588
- if spec = criteria.options[:sort]
589
- @view = view.sort(Hash[spec.map{|k, v| [k, -1*v]}])
615
+ if sort = criteria.options[:sort] || ( { _id: 1 } unless opts[:id_sort] == :none )
616
+ @view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}])
590
617
  end
591
618
  yield
592
619
  ensure
@@ -678,6 +705,14 @@ module Mongoid
678
705
  yield(doc)
679
706
  documents.push(doc) if cacheable?
680
707
  end
708
+
709
+ def _session
710
+ @criteria.send(:_session)
711
+ end
712
+
713
+ def acknowledged_write?
714
+ collection.write_concern.nil? || collection.write_concern.acknowledged?
715
+ end
681
716
  end
682
717
  end
683
718
  end
@@ -21,6 +21,18 @@ module Mongoid
21
21
  other.is_a?(None)
22
22
  end
23
23
 
24
+ # Allow distinct for null context.
25
+ #
26
+ # @example Get the distinct values.
27
+ # context.distinct(:name)
28
+ #
29
+ # @param [ String, Symbol ] field the name of the field.
30
+ #
31
+ # @return [ Array ] Empty Array
32
+ def distinct(field)
33
+ []
34
+ end
35
+
24
36
  # Iterate over the null context. There are no documents to iterate over
25
37
  # in this case.
26
38
  #
@@ -20,11 +20,18 @@ module Mongoid
20
20
  # _id and id field in the document would cause problems with Mongoid
21
21
  # elsewhere.
22
22
  attrs = clone_document.except("_id", "id")
23
- begin
24
- self.class.new(attrs)
25
- rescue Errors::UnknownAttribute
26
- self.class.send(:include, Attributes::Dynamic)
27
- self.class.new(attrs)
23
+ dynamic_attrs = {}
24
+ attrs.reject! do |attr_name, value|
25
+ dynamic_attrs.merge!(attr_name => value) unless self.attribute_names.include?(attr_name)
26
+ end
27
+ self.class.new(attrs).tap do |object|
28
+ dynamic_attrs.each do |attr_name, value|
29
+ if object.respond_to?("#{attr_name}=")
30
+ object.send("#{attr_name}=", value)
31
+ else
32
+ object.attributes[attr_name] = value
33
+ end
34
+ end
28
35
  end
29
36
  end
30
37
  alias :dup :clone
@@ -40,7 +47,7 @@ module Mongoid
40
47
  #
41
48
  # @since 3.0.22
42
49
  def clone_document
43
- attrs = as_document.__deep_copy__
50
+ attrs = as_attributes.__deep_copy__
44
51
  process_localized_attributes(self, attrs)
45
52
  attrs
46
53
  end
@@ -27,8 +27,8 @@ module Mongoid
27
27
  def marshal_load(data)
28
28
  @scoping_options, raw_selector, raw_options = data.pop(3)
29
29
  @klass, @driver, @inclusions, @documents, @strategy, @negating = data
30
- @selector = load_hash(Origin::Selector, raw_selector)
31
- @options = load_hash(Origin::Options, raw_options)
30
+ @selector = load_hash(Queryable::Selector, raw_selector)
31
+ @options = load_hash(Queryable::Options, raw_options)
32
32
  end
33
33
 
34
34
  private
@@ -3,6 +3,10 @@ module Mongoid
3
3
  class Criteria
4
4
  module Modifiable
5
5
 
6
+ # @attribute [r] create_attrs Additional attributes to add to the Document upon creation.
7
+ # @api private
8
+ attr_reader :create_attrs
9
+
6
10
  # Build a document given the selector and return it.
7
11
  # Complex criteria, such as $in and $or operations will get ignored.
8
12
  #
@@ -57,6 +61,9 @@ module Mongoid
57
61
 
58
62
  # Define attributes with which new documents will be created.
59
63
  #
64
+ # Note that if `find_or_create_by` is called after this in a method chain, the attributes in
65
+ # the query will override those from this method.
66
+ #
60
67
  # @example Define attributes to be used when a new document is created.
61
68
  # Person.create_with(job: 'Engineer').find_or_create_by(employer: 'MongoDB')
62
69
  #
@@ -64,7 +71,9 @@ module Mongoid
64
71
  #
65
72
  # @since 5.1.0
66
73
  def create_with(attrs = {})
67
- where(selector.merge(attrs))
74
+ tap do
75
+ (@create_attrs ||= {}).merge!(attrs)
76
+ end
68
77
  end
69
78
 
70
79
  # Find the first +Document+ given the conditions, or creates a new document
@@ -172,8 +181,9 @@ module Mongoid
172
181
  #
173
182
  # @since 3.0.0
174
183
  def create_document(method, attrs = nil, &block)
175
- attributes = selector.reduce(attrs ? attrs.dup : {}) do |hash, (key, value)|
176
- unless key.to_s =~ /\$/ || value.is_a?(Hash)
184
+ attrs = (create_attrs || {}).merge(attrs || {})
185
+ attributes = selector.reduce(attrs) do |hash, (key, value)|
186
+ unless invalid_key?(hash, key) || invalid_embedded_doc?(value)
177
187
  hash[key] = value
178
188
  end
179
189
  hash
@@ -216,6 +226,22 @@ module Mongoid
216
226
  def first_or(method, attrs = {}, &block)
217
227
  first || create_document(method, attrs, &block)
218
228
  end
229
+
230
+ private
231
+
232
+ def invalid_key?(hash, key)
233
+ # @todo Change this to BSON::String::ILLEGAL_KEY when ruby driver 2.3.0 is
234
+ # released and mongoid is updated to depend on driver >= 2.3.0
235
+ key.to_s =~ Mongoid::Document::ILLEGAL_KEY || hash.key?(key.to_sym) || hash.key?(key)
236
+ end
237
+
238
+ def invalid_embedded_doc?(value)
239
+ # @todo Change this to BSON::String::ILLEGAL_KEY when ruby driver 2.3.0 is
240
+ # released and mongoid is updated to depend on driver >= 2.3.0
241
+ value.is_a?(Hash) && value.any? do |key, v|
242
+ key.to_s =~ Mongoid::Document::ILLEGAL_KEY || invalid_embedded_doc?(v)
243
+ end
244
+ end
219
245
  end
220
246
  end
221
247
  end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ class Criteria
4
+
5
+ # Module containing functionality for getting options on a Criteria object.
6
+ #
7
+ # @since 6.0.0
8
+ module Options
9
+
10
+ private
11
+
12
+ def persistence_context
13
+ klass.persistence_context
14
+ end
15
+
16
+ def set_persistence_context(options)
17
+ PersistenceContext.set(klass, options)
18
+ end
19
+
20
+ def clear_persistence_context(original_cluster)
21
+ PersistenceContext.clear(klass, original_cluster)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ class Criteria
4
+ module Queryable
5
+
6
+ # Provides a DSL around crafting aggregation framework commands.
7
+ #
8
+ # @since 2.0.0
9
+ module Aggregable
10
+ extend Macroable
11
+
12
+ # @attribute [r] pipeline The aggregation pipeline.
13
+ attr_reader :pipeline
14
+
15
+ # @attribute [rw] aggregating Flag for whether or not we are aggregating.
16
+ attr_writer :aggregating
17
+
18
+ # Has the aggregable enter an aggregation state. Ie, are only aggregation
19
+ # operations allowed at this point on.
20
+ #
21
+ # @example Is the aggregable aggregating?
22
+ # aggregable.aggregating?
23
+ #
24
+ # @return [ true, false ] If the aggregable is aggregating.
25
+ #
26
+ # @since 2.0.0
27
+ def aggregating?
28
+ !!@aggregating
29
+ end
30
+
31
+ # Add a group ($group) operation to the aggregation pipeline.
32
+ #
33
+ # @example Add a group operation being verbose.
34
+ # aggregable.group(count: { "$sum" => 1 }, max: { "$max" => "likes" })
35
+ #
36
+ # @example Add a group operation using symbol shortcuts.
37
+ # aggregable.group(:count.sum => 1, :max.max => "likes")
38
+ #
39
+ # @param [ Hash ] operation The group operation.
40
+ #
41
+ # @return [ Aggregable ] The aggregable.
42
+ #
43
+ # @since 2.0.0
44
+ def group(operation)
45
+ aggregation(operation) do |pipeline|
46
+ pipeline.group(operation)
47
+ end
48
+ end
49
+ key :avg, :override, "$avg"
50
+ key :max, :override, "$max"
51
+ key :min, :override, "$min"
52
+ key :sum, :override, "$sum"
53
+ key :last, :override, "$last"
54
+ key :push, :override, "$push"
55
+ key :first, :override, "$first"
56
+ key :add_to_set, :override, "$addToSet"
57
+
58
+ # Add a projection ($project) to the aggregation pipeline.
59
+ #
60
+ # @example Add a projection to the pipeline.
61
+ # aggregable.project(author: 1, name: 0)
62
+ #
63
+ # @param [ Hash ] operation The projection to make.
64
+ #
65
+ # @return [ Aggregable ] The aggregable.
66
+ #
67
+ # @since 2.0.0
68
+ def project(operation = nil)
69
+ aggregation(operation) do |pipeline|
70
+ pipeline.project(operation)
71
+ end
72
+ end
73
+
74
+ # Add an unwind ($unwind) to the aggregation pipeline.
75
+ #
76
+ # @example Add an unwind to the pipeline.
77
+ # aggregable.unwind(:field)
78
+ #
79
+ # @param [ String, Symbol ] field The name of the field to unwind.
80
+ #
81
+ # @return [ Aggregable ] The aggregable.
82
+ #
83
+ # @since 2.0.0
84
+ def unwind(field)
85
+ aggregation(field) do |pipeline|
86
+ pipeline.unwind(field)
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ # Add the aggregation operation.
93
+ #
94
+ # @api private
95
+ #
96
+ # @example Aggregate on the operation.
97
+ # aggregation(operation) do |pipeline|
98
+ # pipeline.push("$project" => operation)
99
+ # end
100
+ #
101
+ # @param [ Hash ] operation The operation for the pipeline.
102
+ #
103
+ # @return [ Aggregable ] The cloned aggregable.
104
+ #
105
+ # @since 2.0.0
106
+ def aggregation(operation)
107
+ return self unless operation
108
+ clone.tap do |query|
109
+ unless aggregating?
110
+ query.pipeline.concat(query.selector.to_pipeline)
111
+ query.pipeline.concat(query.options.to_pipeline)
112
+ query.aggregating = true
113
+ end
114
+ yield(query.pipeline)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end