mongoid 5.4.1 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.
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