mongoid 5.4.1 → 6.1.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 (260) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +3 -3
  3. data/lib/config/locales/en.yml +19 -0
  4. data/lib/mongoid.rb +4 -4
  5. data/lib/mongoid/atomic.rb +2 -2
  6. data/lib/mongoid/atomic/modifiers.rb +8 -12
  7. data/lib/mongoid/attributes.rb +22 -21
  8. data/lib/mongoid/attributes/readonly.rb +22 -0
  9. data/lib/mongoid/cacheable.rb +36 -0
  10. data/lib/mongoid/changeable.rb +36 -0
  11. data/lib/mongoid/clients.rb +8 -63
  12. data/lib/mongoid/clients/options.rb +55 -250
  13. data/lib/mongoid/clients/storage_options.rb +1 -69
  14. data/lib/mongoid/composable.rb +29 -3
  15. data/lib/mongoid/config.rb +1 -0
  16. data/lib/mongoid/contextual/atomic.rb +5 -8
  17. data/lib/mongoid/contextual/map_reduce.rb +0 -4
  18. data/lib/mongoid/contextual/memory.rb +2 -2
  19. data/lib/mongoid/contextual/mongo.rb +40 -22
  20. data/lib/mongoid/contextual/none.rb +12 -0
  21. data/lib/mongoid/copyable.rb +13 -6
  22. data/lib/mongoid/criteria.rb +5 -2
  23. data/lib/mongoid/criteria/marshalable.rb +2 -2
  24. data/lib/mongoid/criteria/modifiable.rb +17 -1
  25. data/lib/mongoid/criteria/options.rb +25 -0
  26. data/lib/mongoid/criteria/queryable.rb +87 -0
  27. data/lib/mongoid/criteria/queryable/aggregable.rb +120 -0
  28. data/lib/mongoid/criteria/queryable/extensions.rb +28 -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/forwardable.rb +65 -0
  46. data/lib/mongoid/criteria/queryable/key.rb +103 -0
  47. data/lib/mongoid/criteria/queryable/macroable.rb +27 -0
  48. data/lib/mongoid/criteria/queryable/mergeable.rb +271 -0
  49. data/lib/mongoid/criteria/queryable/optional.rb +429 -0
  50. data/lib/mongoid/criteria/queryable/options.rb +153 -0
  51. data/lib/mongoid/criteria/queryable/pipeline.rb +111 -0
  52. data/lib/mongoid/criteria/queryable/selectable.rb +662 -0
  53. data/lib/mongoid/criteria/queryable/selector.rb +212 -0
  54. data/lib/mongoid/criteria/queryable/smash.rb +104 -0
  55. data/lib/mongoid/document.rb +30 -37
  56. data/lib/mongoid/errors.rb +2 -0
  57. data/lib/mongoid/errors/ambiguous_relationship.rb +1 -1
  58. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +1 -1
  59. data/lib/mongoid/errors/invalid_field.rb +2 -2
  60. data/lib/mongoid/errors/invalid_persistence_option.rb +29 -0
  61. data/lib/mongoid/errors/invalid_relation.rb +66 -0
  62. data/lib/mongoid/evolvable.rb +1 -1
  63. data/lib/mongoid/extensions.rb +0 -4
  64. data/lib/mongoid/extensions/big_decimal.rb +17 -8
  65. data/lib/mongoid/extensions/date.rb +4 -1
  66. data/lib/mongoid/extensions/decimal128.rb +3 -3
  67. data/lib/mongoid/extensions/hash.rb +1 -0
  68. data/lib/mongoid/extensions/string.rb +4 -3
  69. data/lib/mongoid/extensions/time.rb +4 -1
  70. data/lib/mongoid/fields/validators/macro.rb +18 -0
  71. data/lib/mongoid/findable.rb +2 -2
  72. data/lib/mongoid/indexable.rb +15 -13
  73. data/lib/mongoid/interceptable.rb +5 -22
  74. data/lib/mongoid/matchable.rb +13 -7
  75. data/lib/mongoid/matchable/all.rb +2 -2
  76. data/lib/mongoid/matchable/and.rb +3 -3
  77. data/lib/mongoid/matchable/default.rb +2 -2
  78. data/lib/mongoid/matchable/elem_match.rb +28 -0
  79. data/lib/mongoid/matchable/exists.rb +2 -2
  80. data/lib/mongoid/matchable/gt.rb +4 -2
  81. data/lib/mongoid/matchable/gte.rb +4 -2
  82. data/lib/mongoid/matchable/in.rb +2 -2
  83. data/lib/mongoid/matchable/lt.rb +4 -2
  84. data/lib/mongoid/matchable/lte.rb +4 -2
  85. data/lib/mongoid/matchable/ne.rb +2 -2
  86. data/lib/mongoid/matchable/nin.rb +2 -2
  87. data/lib/mongoid/matchable/or.rb +3 -3
  88. data/lib/mongoid/matchable/regexp.rb +3 -3
  89. data/lib/mongoid/matchable/size.rb +2 -2
  90. data/lib/mongoid/persistable.rb +3 -5
  91. data/lib/mongoid/persistable/creatable.rb +2 -2
  92. data/lib/mongoid/persistable/deletable.rb +1 -1
  93. data/lib/mongoid/persistable/settable.rb +1 -1
  94. data/lib/mongoid/persistable/updatable.rb +5 -12
  95. data/lib/mongoid/persistable/upsertable.rb +1 -1
  96. data/lib/mongoid/persistence_context.rb +215 -0
  97. data/lib/mongoid/query_cache.rb +3 -6
  98. data/lib/mongoid/relations/accessors.rb +3 -0
  99. data/lib/mongoid/relations/auto_save.rb +12 -4
  100. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -4
  101. data/lib/mongoid/relations/counter_cache.rb +15 -5
  102. data/lib/mongoid/relations/eager.rb +6 -11
  103. data/lib/mongoid/relations/eager/base.rb +3 -3
  104. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +2 -2
  105. data/lib/mongoid/relations/eager/has_many.rb +1 -1
  106. data/lib/mongoid/relations/embedded/batchable.rb +12 -36
  107. data/lib/mongoid/relations/embedded/in.rb +13 -1
  108. data/lib/mongoid/relations/embedded/many.rb +28 -10
  109. data/lib/mongoid/relations/embedded/one.rb +14 -1
  110. data/lib/mongoid/relations/macros.rb +9 -1
  111. data/lib/mongoid/relations/metadata.rb +3 -3
  112. data/lib/mongoid/relations/options.rb +2 -2
  113. data/lib/mongoid/relations/proxy.rb +1 -31
  114. data/lib/mongoid/relations/referenced/in.rb +19 -10
  115. data/lib/mongoid/relations/referenced/many.rb +23 -17
  116. data/lib/mongoid/relations/referenced/many_to_many.rb +20 -13
  117. data/lib/mongoid/relations/referenced/one.rb +15 -1
  118. data/lib/mongoid/relations/synchronization.rb +11 -11
  119. data/lib/mongoid/relations/touchable.rb +6 -3
  120. data/lib/mongoid/reloadable.rb +1 -1
  121. data/lib/mongoid/serializable.rb +1 -1
  122. data/lib/mongoid/traversable.rb +1 -1
  123. data/lib/mongoid/validatable/uniqueness.rb +1 -2
  124. data/lib/mongoid/version.rb +1 -1
  125. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +14 -3
  126. data/spec/app/models/album.rb +5 -1
  127. data/spec/app/models/artist.rb +21 -0
  128. data/spec/app/models/book.rb +2 -1
  129. data/spec/app/models/dokument.rb +1 -0
  130. data/spec/app/models/ordered_post.rb +5 -0
  131. data/spec/app/models/oscar.rb +1 -2
  132. data/spec/app/models/page.rb +1 -1
  133. data/spec/app/models/person.rb +3 -3
  134. data/spec/app/models/princess.rb +2 -0
  135. data/spec/app/models/record.rb +1 -0
  136. data/spec/app/models/subscription.rb +1 -0
  137. data/spec/app/models/thing.rb +1 -1
  138. data/spec/config/mongoid.yml +15 -0
  139. data/spec/mongoid/atomic/modifiers_spec.rb +17 -17
  140. data/spec/mongoid/atomic_spec.rb +17 -17
  141. data/spec/mongoid/attributes/nested_spec.rb +14 -14
  142. data/spec/mongoid/attributes/readonly_spec.rb +87 -44
  143. data/spec/mongoid/attributes_spec.rb +90 -5
  144. data/spec/mongoid/cacheable_spec.rb +112 -0
  145. data/spec/mongoid/changeable_spec.rb +58 -0
  146. data/spec/mongoid/clients/factory_spec.rb +31 -3
  147. data/spec/mongoid/clients/options_spec.rb +382 -96
  148. data/spec/mongoid/clients_spec.rb +243 -101
  149. data/spec/mongoid/composable_spec.rb +7 -0
  150. data/spec/mongoid/config_spec.rb +67 -11
  151. data/spec/mongoid/contextual/atomic_spec.rb +3 -3
  152. data/spec/mongoid/contextual/mongo_spec.rb +165 -20
  153. data/spec/mongoid/contextual/none_spec.rb +15 -0
  154. data/spec/mongoid/copyable_spec.rb +13 -4
  155. data/spec/mongoid/criteria/modifiable_spec.rb +239 -7
  156. data/spec/mongoid/criteria/options_spec.rb +29 -0
  157. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +370 -0
  158. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +523 -0
  159. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +59 -0
  160. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +58 -0
  161. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +213 -0
  162. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +330 -0
  163. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +405 -0
  164. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +58 -0
  165. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +65 -0
  166. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +327 -0
  167. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +65 -0
  168. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +77 -0
  169. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +108 -0
  170. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +309 -0
  171. data/spec/mongoid/{extensions/origin → criteria/queryable/extensions}/regexp_raw_spec.rb +2 -2
  172. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +90 -0
  173. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +39 -0
  174. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +302 -0
  175. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +167 -0
  176. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +376 -0
  177. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +347 -0
  178. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +87 -0
  179. data/spec/mongoid/criteria/queryable/key_spec.rb +52 -0
  180. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +49 -0
  181. data/spec/mongoid/criteria/queryable/optional_spec.rb +1799 -0
  182. data/spec/mongoid/criteria/queryable/options_spec.rb +360 -0
  183. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +200 -0
  184. data/spec/mongoid/criteria/queryable/queryable_spec.rb +137 -0
  185. data/spec/mongoid/criteria/queryable/selectable_spec.rb +4174 -0
  186. data/spec/mongoid/criteria/queryable/selector_spec.rb +844 -0
  187. data/spec/mongoid/criteria/queryable/smash_spec.rb +30 -0
  188. data/spec/mongoid/criteria_spec.rb +152 -21
  189. data/spec/mongoid/document_spec.rb +37 -88
  190. data/spec/mongoid/errors/invalid_relation_spec.rb +37 -0
  191. data/spec/mongoid/errors/mongoid_error_spec.rb +6 -3
  192. data/spec/mongoid/extensions/big_decimal_spec.rb +320 -18
  193. data/spec/mongoid/extensions/boolean_spec.rb +14 -0
  194. data/spec/mongoid/extensions/date_spec.rb +2 -6
  195. data/spec/mongoid/extensions/date_time_spec.rb +2 -6
  196. data/spec/mongoid/extensions/decimal128_spec.rb +1 -1
  197. data/spec/mongoid/extensions/float_spec.rb +8 -1
  198. data/spec/mongoid/extensions/hash_spec.rb +15 -0
  199. data/spec/mongoid/extensions/integer_spec.rb +8 -1
  200. data/spec/mongoid/extensions/object_spec.rb +11 -0
  201. data/spec/mongoid/extensions/string_spec.rb +21 -0
  202. data/spec/mongoid/extensions/time_spec.rb +2 -6
  203. data/spec/mongoid/extensions/time_with_zone_spec.rb +2 -6
  204. data/spec/mongoid/findable_spec.rb +46 -1
  205. data/spec/mongoid/indexable_spec.rb +15 -3
  206. data/spec/mongoid/interceptable_spec.rb +68 -10
  207. data/spec/mongoid/matchable/all_spec.rb +4 -4
  208. data/spec/mongoid/matchable/and_spec.rb +10 -10
  209. data/spec/mongoid/matchable/default_spec.rb +12 -12
  210. data/spec/mongoid/matchable/elem_match_spec.rb +86 -0
  211. data/spec/mongoid/matchable/exists_spec.rb +5 -5
  212. data/spec/mongoid/matchable/gt_spec.rb +18 -7
  213. data/spec/mongoid/matchable/gte_spec.rb +17 -7
  214. data/spec/mongoid/matchable/in_spec.rb +5 -5
  215. data/spec/mongoid/matchable/lt_spec.rb +18 -7
  216. data/spec/mongoid/matchable/lte_spec.rb +18 -7
  217. data/spec/mongoid/matchable/ne_spec.rb +5 -5
  218. data/spec/mongoid/matchable/nin_spec.rb +5 -5
  219. data/spec/mongoid/matchable/or_spec.rb +7 -7
  220. data/spec/mongoid/matchable/regexp_spec.rb +5 -5
  221. data/spec/mongoid/matchable/size_spec.rb +3 -3
  222. data/spec/mongoid/matchable_spec.rb +173 -53
  223. data/spec/mongoid/persistable/creatable_spec.rb +7 -2
  224. data/spec/mongoid/persistable/deletable_spec.rb +16 -1
  225. data/spec/mongoid/persistable/destroyable_spec.rb +6 -2
  226. data/spec/mongoid/persistable/savable_spec.rb +35 -30
  227. data/spec/mongoid/persistable/settable_spec.rb +45 -29
  228. data/spec/mongoid/persistable/updatable_spec.rb +184 -5
  229. data/spec/mongoid/persistence_context_spec.rb +680 -0
  230. data/spec/mongoid/positional_spec.rb +10 -10
  231. data/spec/mongoid/query_cache_spec.rb +89 -0
  232. data/spec/mongoid/relations/accessors_spec.rb +1 -1
  233. data/spec/mongoid/relations/auto_save_spec.rb +39 -6
  234. data/spec/mongoid/relations/bindings/referenced/many_to_many_spec.rb +4 -4
  235. data/spec/mongoid/relations/builders_spec.rb +37 -10
  236. data/spec/mongoid/relations/counter_cache_spec.rb +64 -3
  237. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +16 -0
  238. data/spec/mongoid/relations/eager_spec.rb +40 -0
  239. data/spec/mongoid/relations/embedded/many_spec.rb +63 -47
  240. data/spec/mongoid/relations/embedded/one_spec.rb +2 -1
  241. data/spec/mongoid/relations/macros_spec.rb +395 -7
  242. data/spec/mongoid/relations/metadata_spec.rb +15 -1
  243. data/spec/mongoid/relations/proxy_spec.rb +27 -1
  244. data/spec/mongoid/relations/referenced/in_spec.rb +41 -1
  245. data/spec/mongoid/relations/referenced/many_spec.rb +13 -25
  246. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +14 -26
  247. data/spec/mongoid/relations/synchronization_spec.rb +48 -2
  248. data/spec/mongoid/relations/touchable_spec.rb +40 -0
  249. data/spec/mongoid/reloadable_spec.rb +51 -0
  250. data/spec/mongoid/serializable_spec.rb +0 -50
  251. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  252. data/spec/mongoid/validatable/uniqueness_spec.rb +18 -9
  253. data/spec/mongoid/validatable_spec.rb +16 -0
  254. data/spec/spec_helper.rb +20 -11
  255. metadata +524 -469
  256. checksums.yaml.gz.sig +0 -0
  257. data.tar.gz.sig +0 -0
  258. data/lib/mongoid/clients/thread_options.rb +0 -19
  259. data/lib/mongoid/extensions/origin/regexp_raw.rb +0 -43
  260. metadata.gz.sig +0 -0
@@ -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
@@ -106,21 +106,18 @@ module Mongoid
106
106
  view.update_many("$push" => collect_operations(pushes))
107
107
  end
108
108
 
109
- # Perform an atomic $push/$each operation on the matching documents.
109
+ # Perform an atomic $pushAll 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(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)
120
- push_each_updates = collect_operations(pushes).each.inject({}) do |ops, (field, elements)|
121
- ops.merge!(field => { '$each' => elements })
122
- end
123
- view.update_many("$push" => push_each_updates)
118
+ # @since 3.0.0
119
+ def push_all(pushes)
120
+ view.update_many("$pushAll" => collect_operations(pushes))
124
121
  end
125
122
 
126
123
  # Perform an atomic $rename of fields on the matching documents.
@@ -111,7 +111,6 @@ module Mongoid
111
111
  end
112
112
 
113
113
  # Specifies where the map/reduce output is to be stored.
114
- # Please see MongoDB documentation for supported map reduce options.
115
114
  #
116
115
  # @example Store output in memory.
117
116
  # map_reduce.out(inline: 1)
@@ -125,9 +124,6 @@ module Mongoid
125
124
  # @example Store output in a collection, reducing existing documents.
126
125
  # map_reduce.out(reduce: "collection_name")
127
126
  #
128
- # @example Return results from map reduce.
129
- # map_reduce.out(inline: 1)
130
- #
131
127
  # @param [ Hash ] location The place to store the results.
132
128
  #
133
129
  # @return [ MapReduce ] The map/reduce object.
@@ -44,7 +44,7 @@ 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(
@@ -148,7 +148,7 @@ module Mongoid
148
148
  @documents = criteria.documents.select do |doc|
149
149
  @root ||= doc._root
150
150
  @collection ||= root.collection
151
- doc.matches?(criteria.selector)
151
+ doc._matches?(criteria.selector)
152
152
  end
153
153
  apply_sorting
154
154
  apply_options
@@ -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,
@@ -110,7 +111,9 @@ module Mongoid
110
111
  #
111
112
  # @since 3.0.0
112
113
  def distinct(field)
113
- view.distinct(klass.database_field_name(field))
114
+ view.distinct(klass.database_field_name(field)).map do |value|
115
+ value.class.demongoize(value)
116
+ end
114
117
  end
115
118
 
116
119
  # Iterate over the context. If provided a block, yield to a Mongoid
@@ -233,21 +236,33 @@ module Mongoid
233
236
  # @example Get the first document.
234
237
  # context.first
235
238
  #
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.
239
+ # @note Automatically adding a sort on _id when no other sort is
240
+ # defined on the criteria has the potential to cause bad performance issues.
241
+ # If you experience unexpected poor performance when using #first or #last
242
+ # and have no sort defined on the criteria, use the option { id_sort: :none }.
243
+ # Be aware that #first/#last won't guarantee order in this case.
244
+ #
245
+ # @param [ Hash ] opts The options for the query returning the first document.
246
+ #
247
+ # @option opts [ :none ] :id_sort Don't apply a sort on _id if no other sort
248
+ # is defined on the criteria.
241
249
  #
242
250
  # @return [ Document ] The first document.
243
251
  #
244
252
  # @since 3.0.0
245
- def first
253
+ def first(opts = {})
246
254
  return documents.first if cached? && cache_loaded?
247
255
  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
256
+ if sort = view.sort || ({ _id: 1 } unless opts[:id_sort] == :none)
257
+ if raw_doc = view.sort(sort).limit(-1).first
258
+ doc = Factory.from_db(klass, raw_doc, criteria.options[:fields])
259
+ eager_load([doc]).first
260
+ end
261
+ else
262
+ if raw_doc = view.limit(-1).first
263
+ doc = Factory.from_db(klass, raw_doc, criteria.options[:fields])
264
+ eager_load([doc]).first
265
+ end
251
266
  end
252
267
  end
253
268
  end
@@ -323,7 +338,7 @@ module Mongoid
323
338
  # @since 3.0.0
324
339
  def initialize(criteria)
325
340
  @criteria, @klass, @cache = criteria, criteria.klass, criteria.options[:cache]
326
- @collection = @klass.with(criteria.persistence_options || {}).collection
341
+ @collection = @klass.collection
327
342
  criteria.send(:merge_type_selection)
328
343
  @view = collection.find(criteria.selector)
329
344
  apply_options
@@ -336,18 +351,21 @@ module Mongoid
336
351
  # @example Get the last document.
337
352
  # context.last
338
353
  #
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.
354
+ # @note Automatically adding a sort on _id when no other sort is
355
+ # defined on the criteria has the potential to cause bad performance issues.
356
+ # If you experience unexpected poor performance when using #first or #last
357
+ # and have no sort defined on the criteria, use the option { id_sort: :none }.
358
+ # Be aware that #first/#last won't guarantee order in this case.
359
+ #
360
+ # @param [ Hash ] opts The options for the query returning the first document.
344
361
  #
345
- # @return [ Document ] The last document.
362
+ # @option opts [ :none ] :id_sort Don't apply a sort on _id if no other sort
363
+ # is defined on the criteria.
346
364
  #
347
365
  # @since 3.0.0
348
- def last
366
+ def last(opts = {})
349
367
  try_cache(:last) do
350
- with_inverse_sorting do
368
+ with_inverse_sorting(opts) do
351
369
  if raw_doc = view.limit(-1).first
352
370
  doc = Factory.from_db(klass, raw_doc, criteria.options[:fields])
353
371
  eager_load([doc]).first
@@ -583,10 +601,10 @@ module Mongoid
583
601
  # context.with_inverse_sorting
584
602
  #
585
603
  # @since 3.0.0
586
- def with_inverse_sorting
604
+ def with_inverse_sorting(opts = {})
587
605
  begin
588
- if spec = criteria.options[:sort]
589
- @view = view.sort(Hash[spec.map{|k, v| [k, -1*v]}])
606
+ if sort = criteria.options[:sort] || ( { _id: 1 } unless opts[:id_sort] == :none )
607
+ @view = view.sort(Hash[sort.map{|k, v| [k, -1*v]}])
590
608
  end
591
609
  yield
592
610
  ensure
@@ -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
@@ -4,7 +4,9 @@ require "mongoid/criteria/includable"
4
4
  require "mongoid/criteria/inspectable"
5
5
  require "mongoid/criteria/marshalable"
6
6
  require "mongoid/criteria/modifiable"
7
+ require "mongoid/criteria/queryable"
7
8
  require "mongoid/criteria/scopable"
9
+ require "mongoid/criteria/options"
8
10
 
9
11
  module Mongoid
10
12
 
@@ -17,7 +19,7 @@ module Mongoid
17
19
  class Criteria
18
20
  include Enumerable
19
21
  include Contextual
20
- include Origin::Queryable
22
+ include Queryable
21
23
  include Findable
22
24
  include Inspectable
23
25
  include Includable
@@ -25,6 +27,7 @@ module Mongoid
25
27
  include Modifiable
26
28
  include Scopable
27
29
  include Clients::Options
30
+ include Options
28
31
 
29
32
  # Static array used to check with method missing - we only need to ever
30
33
  # instantiate once.
@@ -208,7 +211,7 @@ module Mongoid
208
211
  #
209
212
  # @example Merge the criteria with a hash. The hash must contain a klass
210
213
  # key and the key/value pairs correspond to method names/args.
211
-
214
+ #
212
215
  # criteria.merge({
213
216
  # klass: Band,
214
217
  # where: { name: "Depeche Mode" },
@@ -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
@@ -173,7 +173,7 @@ module Mongoid
173
173
  # @since 3.0.0
174
174
  def create_document(method, attrs = nil, &block)
175
175
  attributes = selector.reduce(attrs ? attrs.dup : {}) do |hash, (key, value)|
176
- unless key.to_s =~ /\$/ || value.is_a?(Hash)
176
+ unless invalid_key?(hash, key) || invalid_embedded_doc?(value)
177
177
  hash[key] = value
178
178
  end
179
179
  hash
@@ -216,6 +216,22 @@ module Mongoid
216
216
  def first_or(method, attrs = {}, &block)
217
217
  first || create_document(method, attrs, &block)
218
218
  end
219
+
220
+ private
221
+
222
+ def invalid_key?(hash, key)
223
+ # @todo Change this to BSON::String::ILLEGAL_KEY when ruby driver 2.3.0 is
224
+ # released and mongoid is updated to depend on driver >= 2.3.0
225
+ key.to_s =~ Mongoid::Document::ILLEGAL_KEY || hash.key?(key.to_sym) || hash.key?(key)
226
+ end
227
+
228
+ def invalid_embedded_doc?(value)
229
+ # @todo Change this to BSON::String::ILLEGAL_KEY when ruby driver 2.3.0 is
230
+ # released and mongoid is updated to depend on driver >= 2.3.0
231
+ value.is_a?(Hash) && value.any? do |key, v|
232
+ key.to_s =~ Mongoid::Document::ILLEGAL_KEY || invalid_embedded_doc?(v)
233
+ end
234
+ end
219
235
  end
220
236
  end
221
237
  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,87 @@
1
+ # encoding: utf-8
2
+ require "mongoid/criteria/queryable/extensions"
3
+ require "mongoid/criteria/queryable/forwardable"
4
+ require "mongoid/criteria/queryable/key"
5
+ require "mongoid/criteria/queryable/macroable"
6
+ require "mongoid/criteria/queryable/mergeable"
7
+ require "mongoid/criteria/queryable/smash"
8
+ require "mongoid/criteria/queryable/aggregable"
9
+ require "mongoid/criteria/queryable/pipeline"
10
+ require "mongoid/criteria/queryable/optional"
11
+ require "mongoid/criteria/queryable/options"
12
+ require "mongoid/criteria/queryable/selectable"
13
+ require "mongoid/criteria/queryable/selector"
14
+
15
+ module Mongoid
16
+ class Criteria
17
+
18
+ # A queryable is any object that needs queryable's dsl injected into it to build
19
+ # MongoDB queries. For example, a Mongoid::Criteria is an Queryable.
20
+ #
21
+ # @example Include queryable functionality.
22
+ # class Criteria
23
+ # include Queryable
24
+ # end
25
+ module Queryable
26
+ include Mergeable
27
+ include Aggregable
28
+ include Selectable
29
+ include Optional
30
+
31
+ # @attribute [r] aliases The aliases.
32
+ # @attribute [r] driver The Mongo driver being used.
33
+ # @attribute [r] serializers The serializers.
34
+ attr_reader :aliases, :driver, :serializers
35
+
36
+ # Is this queryable equal to another object? Is true if the selector and
37
+ # options are equal.
38
+ #
39
+ # @example Are the objects equal?
40
+ # queryable == criteria
41
+ #
42
+ # @param [ Object ] other The object to compare against.
43
+ #
44
+ # @return [ true, false ] If the objects are equal.
45
+ #
46
+ # @since 1.0.0
47
+ def ==(other)
48
+ return false unless other.is_a?(Queryable)
49
+ selector == other.selector && options == other.options
50
+ end
51
+
52
+ # Initialize the new queryable. Will yield itself to the block if a block
53
+ # is provided for objects that need additional behaviour.
54
+ #
55
+ # @example Initialize the queryable.
56
+ # Queryable.new
57
+ #
58
+ # @param [ Hash ] aliases The optional field aliases.
59
+ # @param [ Hash ] serializers The optional field serializers.
60
+ # @param [ Symbol ] driver The driver being used.
61
+ #
62
+ # @since 1.0.0
63
+ def initialize(aliases = {}, serializers = {}, driver = :mongo)
64
+ @aliases, @driver, @serializers = aliases, driver.to_sym, serializers
65
+ @options = Options.new(aliases, serializers)
66
+ @selector = Selector.new(aliases, serializers)
67
+ @pipeline = Pipeline.new(aliases)
68
+ @aggregating = nil
69
+ yield(self) if block_given?
70
+ end
71
+
72
+ # Handle the creation of a copy via #clone or #dup.
73
+ #
74
+ # @example Handle copy initialization.
75
+ # queryable.initialize_copy(criteria)
76
+ #
77
+ # @param [ Queryable ] other The original copy.
78
+ #
79
+ # @since 1.0.0
80
+ def initialize_copy(other)
81
+ @options = other.options.__deep_copy__
82
+ @selector = other.selector.__deep_copy__
83
+ @pipeline = other.pipeline.__deep_copy__
84
+ end
85
+ end
86
+ end
87
+ 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