mongoid 5.4.1 → 6.0.0.beta

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 (264) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/lib/config/locales/en.yml +23 -16
  5. data/lib/mongoid.rb +4 -9
  6. data/lib/mongoid/atomic.rb +1 -1
  7. data/lib/mongoid/atomic/modifiers.rb +8 -12
  8. data/lib/mongoid/attributes.rb +9 -11
  9. data/lib/mongoid/attributes/dynamic.rb +5 -6
  10. data/lib/mongoid/attributes/nested.rb +1 -1
  11. data/lib/mongoid/attributes/processing.rb +4 -0
  12. data/lib/mongoid/attributes/readonly.rb +22 -0
  13. data/lib/mongoid/cacheable.rb +36 -0
  14. data/lib/mongoid/changeable.rb +37 -1
  15. data/lib/mongoid/clients.rb +0 -63
  16. data/lib/mongoid/clients/factory.rb +0 -2
  17. data/lib/mongoid/clients/options.rb +54 -249
  18. data/lib/mongoid/clients/storage_options.rb +1 -69
  19. data/lib/mongoid/composable.rb +26 -2
  20. data/lib/mongoid/config.rb +1 -1
  21. data/lib/mongoid/config/options.rb +1 -1
  22. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -0
  23. data/lib/mongoid/contextual/atomic.rb +6 -9
  24. data/lib/mongoid/contextual/geo_near.rb +2 -3
  25. data/lib/mongoid/contextual/map_reduce.rb +97 -24
  26. data/lib/mongoid/contextual/memory.rb +7 -4
  27. data/lib/mongoid/contextual/mongo.rb +63 -54
  28. data/lib/mongoid/contextual/none.rb +2 -2
  29. data/lib/mongoid/copyable.rb +19 -19
  30. data/lib/mongoid/criteria.rb +5 -4
  31. data/lib/mongoid/criteria/findable.rb +2 -3
  32. data/lib/mongoid/criteria/includable.rb +63 -16
  33. data/lib/mongoid/criteria/marshalable.rb +2 -2
  34. data/lib/mongoid/criteria/modifiable.rb +17 -1
  35. data/lib/mongoid/criteria/options.rb +25 -0
  36. data/lib/mongoid/criteria/queryable.rb +86 -0
  37. data/lib/mongoid/criteria/queryable/aggregable.rb +120 -0
  38. data/lib/mongoid/criteria/queryable/extensions.rb +28 -0
  39. data/lib/mongoid/criteria/queryable/extensions/array.rb +185 -0
  40. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +37 -0
  41. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +34 -0
  42. data/lib/mongoid/criteria/queryable/extensions/date.rb +63 -0
  43. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +53 -0
  44. data/lib/mongoid/criteria/queryable/extensions/hash.rb +200 -0
  45. data/lib/mongoid/criteria/queryable/extensions/nil_class.rb +86 -0
  46. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +90 -0
  47. data/lib/mongoid/criteria/queryable/extensions/object.rb +206 -0
  48. data/lib/mongoid/criteria/queryable/extensions/range.rb +70 -0
  49. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +45 -0
  50. data/lib/mongoid/criteria/queryable/extensions/set.rb +34 -0
  51. data/lib/mongoid/criteria/queryable/extensions/string.rb +137 -0
  52. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +79 -0
  53. data/lib/mongoid/criteria/queryable/extensions/time.rb +60 -0
  54. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +54 -0
  55. data/lib/mongoid/criteria/queryable/forwardable.rb +65 -0
  56. data/lib/mongoid/criteria/queryable/key.rb +103 -0
  57. data/lib/mongoid/criteria/queryable/macroable.rb +27 -0
  58. data/lib/mongoid/criteria/queryable/mergeable.rb +271 -0
  59. data/lib/mongoid/criteria/queryable/optional.rb +411 -0
  60. data/lib/mongoid/criteria/queryable/options.rb +136 -0
  61. data/lib/mongoid/criteria/queryable/pipeline.rb +111 -0
  62. data/lib/mongoid/criteria/queryable/selectable.rb +662 -0
  63. data/lib/mongoid/criteria/queryable/selector.rb +196 -0
  64. data/lib/mongoid/criteria/queryable/smash.rb +103 -0
  65. data/lib/mongoid/document.rb +9 -23
  66. data/lib/mongoid/errors.rb +2 -1
  67. data/lib/mongoid/errors/ambiguous_relationship.rb +1 -1
  68. data/lib/mongoid/errors/delete_restriction.rb +2 -2
  69. data/lib/mongoid/errors/invalid_field.rb +2 -2
  70. data/lib/mongoid/errors/invalid_persistence_option.rb +29 -0
  71. data/lib/mongoid/errors/invalid_relation.rb +66 -0
  72. data/lib/mongoid/errors/inverse_not_found.rb +1 -1
  73. data/lib/mongoid/errors/mongoid_error.rb +1 -1
  74. data/lib/mongoid/evolvable.rb +1 -1
  75. data/lib/mongoid/extensions.rb +0 -5
  76. data/lib/mongoid/extensions/big_decimal.rb +17 -8
  77. data/lib/mongoid/extensions/date.rb +4 -1
  78. data/lib/mongoid/extensions/hash.rb +2 -3
  79. data/lib/mongoid/extensions/object.rb +2 -2
  80. data/lib/mongoid/extensions/string.rb +4 -3
  81. data/lib/mongoid/extensions/time.rb +5 -2
  82. data/lib/mongoid/factory.rb +1 -0
  83. data/lib/mongoid/fields/foreign_key.rb +2 -2
  84. data/lib/mongoid/fields/localized.rb +3 -8
  85. data/lib/mongoid/fields/validators/macro.rb +18 -0
  86. data/lib/mongoid/findable.rb +3 -3
  87. data/lib/mongoid/indexable.rb +17 -16
  88. data/lib/mongoid/indexable/specification.rb +1 -1
  89. data/lib/mongoid/indexable/validators/options.rb +1 -2
  90. data/lib/mongoid/interceptable.rb +6 -17
  91. data/lib/mongoid/loggable.rb +1 -1
  92. data/lib/mongoid/matchable.rb +3 -10
  93. data/lib/mongoid/matchable/gt.rb +2 -0
  94. data/lib/mongoid/matchable/gte.rb +2 -0
  95. data/lib/mongoid/matchable/lt.rb +2 -0
  96. data/lib/mongoid/matchable/lte.rb +2 -0
  97. data/lib/mongoid/persistable.rb +6 -5
  98. data/lib/mongoid/persistable/creatable.rb +2 -0
  99. data/lib/mongoid/persistable/deletable.rb +7 -3
  100. data/lib/mongoid/persistable/settable.rb +3 -16
  101. data/lib/mongoid/persistable/updatable.rb +10 -12
  102. data/lib/mongoid/persistence_context.rb +216 -0
  103. data/lib/mongoid/query_cache.rb +5 -30
  104. data/lib/mongoid/relations/accessors.rb +6 -2
  105. data/lib/mongoid/relations/auto_save.rb +12 -4
  106. data/lib/mongoid/relations/bindings/embedded/in.rb +4 -0
  107. data/lib/mongoid/relations/bindings/embedded/many.rb +8 -1
  108. data/lib/mongoid/relations/bindings/embedded/one.rb +10 -0
  109. data/lib/mongoid/relations/bindings/referenced/many.rb +4 -0
  110. data/lib/mongoid/relations/builders.rb +2 -2
  111. data/lib/mongoid/relations/builders/embedded/one.rb +1 -1
  112. data/lib/mongoid/relations/builders/nested_attributes/many.rb +1 -1
  113. data/lib/mongoid/relations/conversions.rb +1 -1
  114. data/lib/mongoid/relations/counter_cache.rb +28 -18
  115. data/lib/mongoid/relations/eager.rb +19 -7
  116. data/lib/mongoid/relations/eager/base.rb +5 -5
  117. data/lib/mongoid/relations/embedded/batchable.rb +9 -33
  118. data/lib/mongoid/relations/embedded/in.rb +16 -2
  119. data/lib/mongoid/relations/embedded/many.rb +23 -8
  120. data/lib/mongoid/relations/embedded/one.rb +17 -2
  121. data/lib/mongoid/relations/macros.rb +9 -2
  122. data/lib/mongoid/relations/metadata.rb +3 -3
  123. data/lib/mongoid/relations/nested_builder.rb +1 -1
  124. data/lib/mongoid/relations/options.rb +2 -2
  125. data/lib/mongoid/relations/proxy.rb +2 -33
  126. data/lib/mongoid/relations/referenced/in.rb +23 -11
  127. data/lib/mongoid/relations/referenced/many.rb +24 -16
  128. data/lib/mongoid/relations/referenced/many_to_many.rb +20 -13
  129. data/lib/mongoid/relations/referenced/one.rb +17 -1
  130. data/lib/mongoid/relations/reflections.rb +3 -5
  131. data/lib/mongoid/relations/touchable.rb +1 -1
  132. data/lib/mongoid/reloadable.rb +1 -1
  133. data/lib/mongoid/scopable.rb +3 -3
  134. data/lib/mongoid/serializable.rb +2 -3
  135. data/lib/mongoid/tasks/database.rb +1 -2
  136. data/lib/mongoid/threaded.rb +4 -4
  137. data/lib/mongoid/traversable.rb +1 -1
  138. data/lib/mongoid/validatable.rb +1 -1
  139. data/lib/mongoid/validatable/macros.rb +2 -4
  140. data/lib/mongoid/validatable/uniqueness.rb +1 -2
  141. data/lib/mongoid/version.rb +1 -1
  142. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +4 -7
  143. data/spec/app/models/album.rb +5 -1
  144. data/spec/app/models/artist.rb +21 -0
  145. data/spec/app/models/band.rb +0 -1
  146. data/spec/app/models/church.rb +0 -2
  147. data/spec/app/models/ordered_post.rb +5 -0
  148. data/spec/app/models/oscar.rb +1 -2
  149. data/spec/app/models/person.rb +3 -1
  150. data/spec/app/models/post.rb +0 -1
  151. data/spec/app/models/princess.rb +2 -0
  152. data/spec/app/models/record.rb +1 -0
  153. data/spec/app/models/thing.rb +1 -1
  154. data/spec/config/mongoid.yml +1 -5
  155. data/spec/mongoid/atomic/modifiers_spec.rb +17 -17
  156. data/spec/mongoid/atomic_spec.rb +17 -17
  157. data/spec/mongoid/attributes/nested_spec.rb +14 -14
  158. data/spec/mongoid/attributes/readonly_spec.rb +87 -44
  159. data/spec/mongoid/attributes_spec.rb +63 -0
  160. data/spec/mongoid/cacheable_spec.rb +112 -0
  161. data/spec/mongoid/changeable_spec.rb +58 -0
  162. data/spec/mongoid/clients/factory_spec.rb +3 -11
  163. data/spec/mongoid/clients/options_spec.rb +378 -96
  164. data/spec/mongoid/clients_spec.rb +207 -170
  165. data/spec/mongoid/composable_spec.rb +7 -0
  166. data/spec/mongoid/config_spec.rb +41 -21
  167. data/spec/mongoid/contextual/atomic_spec.rb +77 -343
  168. data/spec/mongoid/contextual/map_reduce_spec.rb +119 -111
  169. data/spec/mongoid/contextual/memory_spec.rb +56 -316
  170. data/spec/mongoid/contextual/mongo_spec.rb +104 -378
  171. data/spec/mongoid/copyable_spec.rb +8 -15
  172. data/spec/mongoid/criteria/modifiable_spec.rb +239 -7
  173. data/spec/mongoid/criteria/options_spec.rb +29 -0
  174. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +370 -0
  175. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +523 -0
  176. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +59 -0
  177. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +58 -0
  178. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +213 -0
  179. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +330 -0
  180. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +405 -0
  181. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +58 -0
  182. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +65 -0
  183. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +327 -0
  184. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +65 -0
  185. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +77 -0
  186. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +108 -0
  187. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +309 -0
  188. data/spec/mongoid/{extensions/origin/regexp_raw_spec.rb → criteria/queryable/extensions/regexp_spec.rb} +21 -20
  189. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +39 -0
  190. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +302 -0
  191. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +167 -0
  192. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +376 -0
  193. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +347 -0
  194. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +87 -0
  195. data/spec/mongoid/criteria/queryable/key_spec.rb +52 -0
  196. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +49 -0
  197. data/spec/mongoid/criteria/queryable/optional_spec.rb +1786 -0
  198. data/spec/mongoid/criteria/queryable/options_spec.rb +360 -0
  199. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +200 -0
  200. data/spec/mongoid/criteria/queryable/queryable_spec.rb +137 -0
  201. data/spec/mongoid/criteria/queryable/selectable_spec.rb +4159 -0
  202. data/spec/mongoid/criteria/queryable/selector_spec.rb +778 -0
  203. data/spec/mongoid/criteria/queryable/smash_spec.rb +30 -0
  204. data/spec/mongoid/criteria_spec.rb +45 -63
  205. data/spec/mongoid/document_spec.rb +21 -88
  206. data/spec/mongoid/errors/invalid_relation_spec.rb +37 -0
  207. data/spec/mongoid/errors/mongoid_error_spec.rb +6 -3
  208. data/spec/mongoid/extensions/big_decimal_spec.rb +320 -18
  209. data/spec/mongoid/extensions/date_spec.rb +2 -6
  210. data/spec/mongoid/extensions/date_time_spec.rb +2 -6
  211. data/spec/mongoid/extensions/float_spec.rb +8 -1
  212. data/spec/mongoid/extensions/integer_spec.rb +8 -1
  213. data/spec/mongoid/extensions/object_spec.rb +11 -0
  214. data/spec/mongoid/extensions/string_spec.rb +21 -0
  215. data/spec/mongoid/extensions/time_spec.rb +4 -8
  216. data/spec/mongoid/extensions/time_with_zone_spec.rb +2 -6
  217. data/spec/mongoid/fields/localized_spec.rb +0 -91
  218. data/spec/mongoid/findable_spec.rb +46 -1
  219. data/spec/mongoid/indexable_spec.rb +6 -46
  220. data/spec/mongoid/interceptable_spec.rb +49 -10
  221. data/spec/mongoid/matchable/gt_spec.rb +11 -0
  222. data/spec/mongoid/matchable/gte_spec.rb +10 -0
  223. data/spec/mongoid/matchable/lt_spec.rb +11 -0
  224. data/spec/mongoid/matchable/lte_spec.rb +11 -0
  225. data/spec/mongoid/matchable_spec.rb +1 -51
  226. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  227. data/spec/mongoid/persistable/deletable_spec.rb +1 -1
  228. data/spec/mongoid/persistable/destroyable_spec.rb +6 -2
  229. data/spec/mongoid/persistable/savable_spec.rb +30 -30
  230. data/spec/mongoid/persistable/settable_spec.rb +0 -185
  231. data/spec/mongoid/persistable/updatable_spec.rb +166 -5
  232. data/spec/mongoid/persistence_context_spec.rb +654 -0
  233. data/spec/mongoid/positional_spec.rb +10 -10
  234. data/spec/mongoid/query_cache_spec.rb +89 -65
  235. data/spec/mongoid/relations/accessors_spec.rb +1 -1
  236. data/spec/mongoid/relations/auto_save_spec.rb +39 -6
  237. data/spec/mongoid/relations/builders_spec.rb +37 -10
  238. data/spec/mongoid/relations/counter_cache_spec.rb +64 -15
  239. data/spec/mongoid/relations/cyclic_spec.rb +0 -22
  240. data/spec/mongoid/relations/embedded/many_spec.rb +9 -41
  241. data/spec/mongoid/relations/embedded/one_spec.rb +2 -1
  242. data/spec/mongoid/relations/macros_spec.rb +395 -7
  243. data/spec/mongoid/relations/proxy_spec.rb +3 -1
  244. data/spec/mongoid/relations/referenced/in_spec.rb +41 -1
  245. data/spec/mongoid/relations/referenced/many_spec.rb +6 -29
  246. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +6 -29
  247. data/spec/mongoid/relations/reflections_spec.rb +9 -9
  248. data/spec/mongoid/reloadable_spec.rb +51 -0
  249. data/spec/mongoid/scopable_spec.rb +0 -12
  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 +16 -9
  253. data/spec/mongoid/validatable_spec.rb +16 -0
  254. data/spec/spec_helper.rb +10 -10
  255. metadata +536 -479
  256. metadata.gz.sig +0 -0
  257. data/lib/mongoid/clients/thread_options.rb +0 -19
  258. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +0 -20
  259. data/lib/mongoid/extensions/decimal128.rb +0 -39
  260. data/lib/mongoid/extensions/origin/regexp_raw.rb +0 -43
  261. data/lib/mongoid/matchable/regexp.rb +0 -27
  262. data/spec/app/models/post_genre.rb +0 -6
  263. data/spec/mongoid/extensions/decimal128_spec.rb +0 -44
  264. data/spec/mongoid/matchable/regexp_spec.rb +0 -59
@@ -0,0 +1,103 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ class Criteria
4
+ module Queryable
5
+
6
+ # The key is a representation of a field in a queryable, that can be
7
+ # expanded to special MongoDB selectors.
8
+ class Key
9
+
10
+ # @attribute [r] name The name of the field.
11
+ # @attribute [r] block The optional block to transform values.
12
+ # @attribute [r] operator The MongoDB query operator.
13
+ # @attribute [r] expanded The MongoDB expanded query operator.
14
+ # @attribute [r] strategy The name of the merge strategy.
15
+ attr_reader :block, :name, :operator, :expanded, :strategy
16
+
17
+ # Does the key equal another object?
18
+ #
19
+ # @example Is the key equal to another?
20
+ # key == other
21
+ # key.eql? other
22
+ #
23
+ # @param [ Object ] other The object to compare to.
24
+ #
25
+ # @return [ true, false ] If the objects are equal.
26
+ #
27
+ # @since 1.0.0
28
+ def ==(other)
29
+ return false unless other.is_a?(Key)
30
+ name == other.name && operator == other.operator && expanded == other.expanded
31
+ end
32
+ alias :eql? :==
33
+
34
+ # Calculate the hash code for a key.
35
+ #
36
+ # @return [ Fixnum ] The hash code for the key.
37
+ #
38
+ # @since 1.1.0
39
+ def hash
40
+ [name, operator, expanded].hash
41
+ end
42
+
43
+ # Instantiate the new key.
44
+ #
45
+ # @example Instantiate the key.
46
+ # Key.new("age", "$gt")
47
+ #
48
+ # @param [ String, Symbol ] name The field name.
49
+ # @param [ Symbol ] strategy The name of the merge strategy.
50
+ # @param [ String ] operator The Mongo operator.
51
+ # @param [ String ] expanded The Mongo expanded operator.
52
+ #
53
+ # @since 1.0.0
54
+ def initialize(name, strategy, operator, expanded = nil, &block)
55
+ @name, @strategy, @operator, @expanded, @block =
56
+ name, strategy, operator, expanded, block
57
+ end
58
+
59
+ # Gets the raw selector that would be passed to Mongo from this key.
60
+ #
61
+ # @example Specify the raw selector.
62
+ # key.__expr_part__(50)
63
+ #
64
+ # @param [ Object ] object The value to be included.
65
+ # @param [ true, false ] negating If the selection should be negated.
66
+ #
67
+ # @return [ Hash ] The raw MongoDB selector.
68
+ #
69
+ # @since 1.0.0
70
+ def __expr_part__(object, negating = false)
71
+ value = block ? block[object] : object
72
+ expression = { operator => expanded ? { expanded => value } : value }
73
+ { name.to_s => (negating && operator != "$not") ? { "$not" => expression } : expression }
74
+ end
75
+
76
+ # Get the key as raw Mongo sorting options.
77
+ #
78
+ # @example Get the key as a sort.
79
+ # key.__sort_option__
80
+ #
81
+ # @return [ Hash ] The field/direction pair.
82
+ #
83
+ # @since 1.0.0
84
+ def __sort_option__
85
+ { name => operator }
86
+ end
87
+ alias :__sort_pair__ :__sort_option__
88
+
89
+ # Convert the key to a string.
90
+ #
91
+ # @example Convert the key to a string.
92
+ # key.to_s
93
+ #
94
+ # @return [ String ] The key as a string.
95
+ #
96
+ # @since 1.1.0
97
+ def to_s
98
+ @name.to_s
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ class Criteria
4
+ module Queryable
5
+
6
+ # Adds macro behaviour for adding symbol methods.
7
+ module Macroable
8
+
9
+ # Adds a method on Symbol for convenience in where queries for the
10
+ # provided operators.
11
+ #
12
+ # @example Add a symbol key.
13
+ # key :all, "$all
14
+ #
15
+ # @param [ Symbol ] name The name of the method.
16
+ # @param [ Symbol ] strategy The merge strategy.
17
+ # @param [ String ] operator The MongoDB operator.
18
+ # @param [ String ] additional The additional MongoDB operator.
19
+ #
20
+ # @since 1.0.0
21
+ def key(name, strategy, operator, additional = nil, &block)
22
+ ::Symbol.add_key(name, strategy, operator, additional, &block)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,271 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ class Criteria
4
+ module Queryable
5
+
6
+ # Contains behaviour for merging existing selection with new selection.
7
+ module Mergeable
8
+
9
+ # @attribute [rw] strategy The name of the current strategy.
10
+ attr_accessor :strategy
11
+
12
+ # Instruct the next mergeable call to use intersection.
13
+ #
14
+ # @example Use intersection on the next call.
15
+ # mergeable.intersect.in(field: [ 1, 2, 3 ])
16
+ #
17
+ # @return [ Mergeable ] The intersect flagged mergeable.
18
+ #
19
+ # @since 1.0.0
20
+ def intersect
21
+ use(:__intersect__)
22
+ end
23
+
24
+ # Instruct the next mergeable call to use override.
25
+ #
26
+ # @example Use override on the next call.
27
+ # mergeable.override.in(field: [ 1, 2, 3 ])
28
+ #
29
+ # @return [ Mergeable ] The override flagged mergeable.
30
+ #
31
+ # @since 1.0.0
32
+ def override
33
+ use(:__override__)
34
+ end
35
+
36
+ # Instruct the next mergeable call to use union.
37
+ #
38
+ # @example Use union on the next call.
39
+ # mergeable.union.in(field: [ 1, 2, 3 ])
40
+ #
41
+ # @return [ Mergeable ] The union flagged mergeable.
42
+ #
43
+ # @since 1.0.0
44
+ def union
45
+ use(:__union__)
46
+ end
47
+
48
+ # Reset the stratgies to nil, used after cloning.
49
+ #
50
+ # @example Reset the strategies.
51
+ # mergeable.reset_strategies!
52
+ #
53
+ # @return [ nil ] nil.
54
+ #
55
+ # @since 1.0.0
56
+ def reset_strategies!
57
+ self.strategy, self.negating = nil, nil
58
+ end
59
+
60
+ private
61
+
62
+ # Adds the criterion to the existing selection.
63
+ #
64
+ # @api private
65
+ #
66
+ # @example Add the criterion.
67
+ # mergeable.__add__({ name: 1 }, "$in")
68
+ #
69
+ # @param [ Hash ] criterion The criteria.
70
+ # @param [ String ] operator The MongoDB operator.
71
+ #
72
+ # @return [ Mergeable ] The new mergeable.
73
+ #
74
+ # @since 1.0.0
75
+ def __add__(criterion, operator)
76
+ with_strategy(:__add__, criterion, operator)
77
+ end
78
+
79
+ # Adds the criterion to the existing selection.
80
+ #
81
+ # @api private
82
+ #
83
+ # @example Add the criterion.
84
+ # mergeable.__expanded__([ 1, 10 ], "$within", "$center")
85
+ #
86
+ # @param [ Hash ] criterion The criteria.
87
+ # @param [ String ] outer The outer MongoDB operator.
88
+ # @param [ String ] inner The inner MongoDB operator.
89
+ #
90
+ # @return [ Mergeable ] The new mergeable.
91
+ #
92
+ # @since 1.0.0
93
+ def __expanded__(criterion, outer, inner)
94
+ selection(criterion) do |selector, field, value|
95
+ selector.store(field, { outer => { inner => value }})
96
+ end
97
+ end
98
+
99
+ # Perform a straight merge of the criterion into the selection and let the
100
+ # symbol overrides do all the work.
101
+ #
102
+ # @api private
103
+ #
104
+ # @example Straight merge the expanded criterion.
105
+ # mergeable.__merge__(location: [ 1, 10 ])
106
+ #
107
+ # @param [ Hash ] criterion The criteria.
108
+ #
109
+ # @return [ Mergeable ] The cloned object.
110
+ #
111
+ # @since 2.0.0
112
+ def __merge__(criterion)
113
+ selection(criterion) do |selector, field, value|
114
+ selector.merge!(field.__expr_part__(value))
115
+ end
116
+ end
117
+
118
+ # Adds the criterion to the existing selection.
119
+ #
120
+ # @api private
121
+ #
122
+ # @example Add the criterion.
123
+ # mergeable.__intersect__([ 1, 2 ], "$in")
124
+ #
125
+ # @param [ Hash ] criterion The criteria.
126
+ # @param [ String ] operator The MongoDB operator.
127
+ #
128
+ # @return [ Mergeable ] The new mergeable.
129
+ #
130
+ # @since 1.0.0
131
+ def __intersect__(criterion, operator)
132
+ with_strategy(:__intersect__, criterion, operator)
133
+ end
134
+
135
+ # Adds the criterion to the existing selection.
136
+ #
137
+ # @api private
138
+ #
139
+ # @example Add the criterion.
140
+ # mergeable.__multi__([ 1, 2 ], "$in")
141
+ #
142
+ # @param [ Hash ] criterion The criteria.
143
+ # @param [ String ] operator The MongoDB operator.
144
+ #
145
+ # @return [ Mergeable ] The new mergeable.
146
+ #
147
+ # @since 1.0.0
148
+ def __multi__(criterion, operator)
149
+ clone.tap do |query|
150
+ sel = query.selector
151
+ criterion.flatten.each do |expr|
152
+ next unless expr
153
+ criteria = sel[operator] || []
154
+ normalized = expr.inject({}) do |hash, (field, value)|
155
+ hash.merge!(field.__expr_part__(value.__expand_complex__))
156
+ hash
157
+ end
158
+ sel.store(operator, criteria.push(normalized))
159
+ end
160
+ end
161
+ end
162
+
163
+ # Adds the criterion to the existing selection.
164
+ #
165
+ # @api private
166
+ #
167
+ # @example Add the criterion.
168
+ # mergeable.__override__([ 1, 2 ], "$in")
169
+ #
170
+ # @param [ Hash ] criterion The criteria.
171
+ # @param [ String ] operator The MongoDB operator.
172
+ #
173
+ # @return [ Mergeable ] The new mergeable.
174
+ #
175
+ # @since 1.0.0
176
+ def __override__(criterion, operator)
177
+ selection(criterion) do |selector, field, value|
178
+ expression = prepare(field, operator, value)
179
+ existing = selector[field]
180
+ if existing.respond_to?(:merge!)
181
+ selector.store(field, existing.merge!(expression))
182
+ else
183
+ selector.store(field, expression)
184
+ end
185
+ end
186
+ end
187
+
188
+ # Adds the criterion to the existing selection.
189
+ #
190
+ # @api private
191
+ #
192
+ # @example Add the criterion.
193
+ # mergeable.__union__([ 1, 2 ], "$in")
194
+ #
195
+ # @param [ Hash ] criterion The criteria.
196
+ # @param [ String ] operator The MongoDB operator.
197
+ #
198
+ # @return [ Mergeable ] The new mergeable.
199
+ #
200
+ # @since 1.0.0
201
+ def __union__(criterion, operator)
202
+ with_strategy(:__union__, criterion, operator)
203
+ end
204
+
205
+ # Use the named strategy for the next operation.
206
+ #
207
+ # @api private
208
+ #
209
+ # @example Use intersection.
210
+ # mergeable.use(:__intersect__)
211
+ #
212
+ # @param [ Symbol ] strategy The strategy to use.
213
+ #
214
+ # @return [ Mergeable ] The existing mergeable.
215
+ #
216
+ # @since 1.0.0
217
+ def use(strategy)
218
+ tap do |mergeable|
219
+ mergeable.strategy = strategy
220
+ end
221
+ end
222
+
223
+ # Add criterion to the selection with the named strategy.
224
+ #
225
+ # @api private
226
+ #
227
+ # @example Add criterion with a strategy.
228
+ # mergeable.with_strategy(:__union__, [ 1, 2, 3 ], "$in")
229
+ #
230
+ # @param [ Symbol ] strategy The name of the strategy method.
231
+ # @param [ Object ] criterion The criterion to add.
232
+ # @param [ String ] operator The MongoDB operator.
233
+ #
234
+ # @return [ Mergeable ] The cloned query.
235
+ #
236
+ # @since 1.0.0
237
+ def with_strategy(strategy, criterion, operator)
238
+ selection(criterion) do |selector, field, value|
239
+ selector.store(
240
+ field,
241
+ selector[field].send(strategy, prepare(field, operator, value))
242
+ )
243
+ end
244
+ end
245
+
246
+ # Prepare the value for merging.
247
+ #
248
+ # @api private
249
+ #
250
+ # @example Prepare the value.
251
+ # mergeable.prepare("field", "$gt", 10)
252
+ #
253
+ # @param [ String ] field The name of the field.
254
+ # @param [ Object ] value The value.
255
+ #
256
+ # @return [ Object ] The serialized value.
257
+ #
258
+ # @since 1.0.0
259
+ def prepare(field, operator, value)
260
+ unless operator =~ /exists|type|size/
261
+ value = value.__expand_complex__
262
+ serializer = serializers[field]
263
+ value = serializer ? serializer.evolve(value) : value
264
+ end
265
+ selection = { operator => value }
266
+ negating? ? { "$not" => selection } : selection
267
+ end
268
+ end
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,411 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ class Criteria
4
+ module Queryable
5
+
6
+ # The optional module includes all behaviour that has to do with extra
7
+ # options surrounding queries, like skip, limit, sorting, etc.
8
+ module Optional
9
+ extend Macroable
10
+
11
+ # @attribute [rw] options The query options.
12
+ attr_accessor :options
13
+
14
+ # Add ascending sorting options for all the provided fields.
15
+ #
16
+ # @example Add ascending sorting.
17
+ # optional.ascending(:first_name, :last_name)
18
+ #
19
+ # @param [ Array<Symbol> ] fields The fields to sort.
20
+ #
21
+ # @return [ Optional ] The cloned optional.
22
+ #
23
+ # @since 1.0.0
24
+ def ascending(*fields)
25
+ sort_with_list(*fields, 1)
26
+ end
27
+ alias :asc :ascending
28
+ key :asc, :override, 1
29
+ key :ascending, :override, 1
30
+
31
+ # Adds the option for telling MongoDB how many documents to retrieve in
32
+ # it's batching.
33
+ #
34
+ # @example Apply the batch size options.
35
+ # optional.batch_size(500)
36
+ #
37
+ # @param [ Integer ] value The batch size.
38
+ #
39
+ # @return [ Optional ] The cloned optional.
40
+ #
41
+ # @since 1.0.0
42
+ def batch_size(value = nil)
43
+ option(value) { |options| options.store(:batch_size, value) }
44
+ end
45
+
46
+ # Add descending sorting options for all the provided fields.
47
+ #
48
+ # @example Add descending sorting.
49
+ # optional.descending(:first_name, :last_name)
50
+ #
51
+ # @param [ Array<Symbol> ] fields The fields to sort.
52
+ #
53
+ # @return [ Optional ] The cloned optional.
54
+ #
55
+ # @since 1.0.0
56
+ def descending(*fields)
57
+ sort_with_list(*fields, -1)
58
+ end
59
+ alias :desc :descending
60
+ key :desc, :override, -1
61
+ key :descending, :override, -1
62
+
63
+ # Add an index hint to the query options.
64
+ #
65
+ # @example Add an index hint.
66
+ # optional.hint("$natural" => 1)
67
+ #
68
+ # @param [ Hash ] value The index hint.
69
+ #
70
+ # @return [ Optional ] The cloned optional.
71
+ #
72
+ # @since 1.0.0
73
+ def hint(value = nil)
74
+ option(value) { |options| options.store(:hint, value) }
75
+ end
76
+
77
+ # Add the number of documents to limit in the returned results.
78
+ #
79
+ # @example Limit the number of returned documents.
80
+ # optional.limit(20)
81
+ #
82
+ # @param [ Integer ] value The number of documents to return.
83
+ #
84
+ # @return [ Optional ] The cloned optional.
85
+ #
86
+ # @since 1.0.0
87
+ def limit(value = nil)
88
+ option(value) do |options, query|
89
+ val = value.to_i
90
+ options.store(:limit, val)
91
+ query.pipeline.push("$limit" => val) if aggregating?
92
+ end
93
+ end
94
+
95
+ # Adds the option to limit the number of documents scanned in the
96
+ # collection.
97
+ #
98
+ # @example Add the max scan limit.
99
+ # optional.max_scan(1000)
100
+ #
101
+ # @param [ Integer ] value The max number of documents to scan.
102
+ #
103
+ # @return [ Optional ] The cloned optional.
104
+ #
105
+ # @since 1.0.0
106
+ def max_scan(value = nil)
107
+ option(value) { |options| options.store(:max_scan, value) }
108
+ end
109
+
110
+ # Adds a cumulative time limit in milliseconds for processing operations on a cursor.
111
+ #
112
+ # @example Add the max time ms option.
113
+ # optional.max_time_ms(200)
114
+ #
115
+ # @param [ Integer ] value The max time in milliseconds for processing operations on a cursor.
116
+ #
117
+ # @return [ Optional ] The cloned optional.
118
+ #
119
+ # @since 6.0.0
120
+ def max_time_ms(value = nil)
121
+ option(value) { |options| options.store(:max_time_ms, value) }
122
+ end
123
+
124
+ # Tell the query not to timeout.
125
+ #
126
+ # @example Tell the query not to timeout.
127
+ # optional.no_timeout
128
+ #
129
+ # @return [ Optional ] The cloned optional.
130
+ #
131
+ # @since 1.0.0
132
+ def no_timeout
133
+ clone.tap { |query| query.options.store(:timeout, false) }
134
+ end
135
+
136
+ # Limits the results to only contain the fields provided.
137
+ #
138
+ # @example Limit the results to the provided fields.
139
+ # optional.only(:name, :dob)
140
+ #
141
+ # @param [ Array<Symbol> ] args The fields to return.
142
+ #
143
+ # @return [ Optional ] The cloned optional.
144
+ #
145
+ # @since 1.0.0
146
+ def only(*args)
147
+ args = args.flatten
148
+ option(*args) do |options|
149
+ options.store(
150
+ :fields, args.inject(options[:fields] || {}){ |sub, field| sub.tap { sub[field] = 1 }}
151
+ )
152
+ end
153
+ end
154
+
155
+ # Adds sorting criterion to the options.
156
+ #
157
+ # @example Add sorting options via a hash with integer directions.
158
+ # optional.order_by(name: 1, dob: -1)
159
+ #
160
+ # @example Add sorting options via a hash with symbol directions.
161
+ # optional.order_by(name: :asc, dob: :desc)
162
+ #
163
+ # @example Add sorting options via a hash with string directions.
164
+ # optional.order_by(name: "asc", dob: "desc")
165
+ #
166
+ # @example Add sorting options via an array with integer directions.
167
+ # optional.order_by([[ name, 1 ], [ dob, -1 ]])
168
+ #
169
+ # @example Add sorting options via an array with symbol directions.
170
+ # optional.order_by([[ name, :asc ], [ dob, :desc ]])
171
+ #
172
+ # @example Add sorting options via an array with string directions.
173
+ # optional.order_by([[ name, "asc" ], [ dob, "desc" ]])
174
+ #
175
+ # @example Add sorting options with keys.
176
+ # optional.order_by(:name.asc, :dob.desc)
177
+ #
178
+ # @example Add sorting options via a string.
179
+ # optional.order_by("name ASC, dob DESC")
180
+ #
181
+ # @param [ Array, Hash, String ] spec The sorting specification.
182
+ #
183
+ # @return [ Optional ] The cloned optional.
184
+ #
185
+ # @since 1.0.0
186
+ def order_by(*spec)
187
+ option(spec) do |options, query|
188
+ spec.compact.each do |criterion|
189
+ criterion.__sort_option__.each_pair do |field, direction|
190
+ add_sort_option(options, field, direction)
191
+ end
192
+ query.pipeline.push("$sort" => options[:sort]) if aggregating?
193
+ end
194
+ end
195
+ end
196
+ alias :order :order_by
197
+
198
+ # Instead of merging the order criteria, use this method to completely
199
+ # replace the existing ordering with the provided.
200
+ #
201
+ # @example Replace the ordering.
202
+ # optional.reorder(name: :asc)
203
+ #
204
+ # @param [ Array, Hash, String ] spec The sorting specification.
205
+ #
206
+ # @return [ Optional ] The cloned optional.
207
+ #
208
+ # @since 2.1.0
209
+ def reorder(*spec)
210
+ clone.tap do |query|
211
+ query.options.delete(:sort)
212
+ end.order_by(*spec)
213
+ end
214
+
215
+ # Add the number of documents to skip.
216
+ #
217
+ # @example Add the number to skip.
218
+ # optional.skip(100)
219
+ #
220
+ # @param [ Integer ] value The number to skip.
221
+ #
222
+ # @return [ Optional ] The cloned optional.
223
+ #
224
+ # @since 1.0.0
225
+ def skip(value = nil)
226
+ option(value) do |options, query|
227
+ val = value.to_i
228
+ options.store(:skip, val)
229
+ query.pipeline.push("$skip" => val) if aggregating?
230
+ end
231
+ end
232
+ alias :offset :skip
233
+
234
+ # Limit the returned results via slicing embedded arrays.
235
+ #
236
+ # @example Slice the returned results.
237
+ # optional.slice(aliases: [ 0, 5 ])
238
+ #
239
+ # @param [ Hash ] criterion The slice options.
240
+ #
241
+ # @return [ Optional ] The cloned optional.
242
+ #
243
+ # @since 1.0.0
244
+ def slice(criterion = nil)
245
+ option(criterion) do |options|
246
+ options.__union__(
247
+ fields: criterion.inject({}) do |option, (field, val)|
248
+ option.tap { |opt| opt.store(field, { "$slice" => val }) }
249
+ end
250
+ )
251
+ end
252
+ end
253
+
254
+ # Tell the query to operate in snapshot mode.
255
+ #
256
+ # @example Add the snapshot option.
257
+ # optional.snapshot
258
+ #
259
+ # @return [ Optional ] The cloned optional.
260
+ #
261
+ # @since 1.0.0
262
+ def snapshot
263
+ clone.tap do |query|
264
+ query.options.store(:snapshot, true)
265
+ end
266
+ end
267
+
268
+ # Limits the results to only contain the fields not provided.
269
+ #
270
+ # @example Limit the results to the fields not provided.
271
+ # optional.without(:name, :dob)
272
+ #
273
+ # @param [ Array<Symbol> ] args The fields to ignore.
274
+ #
275
+ # @return [ Optional ] The cloned optional.
276
+ #
277
+ # @since 1.0.0
278
+ def without(*args)
279
+ args = args.flatten
280
+ option(*args) do |options|
281
+ options.store(
282
+ :fields, args.inject(options[:fields] || {}){ |sub, field| sub.tap { sub[field] = 0 }}
283
+ )
284
+ end
285
+ end
286
+
287
+ # Associate a comment with the query.
288
+ #
289
+ # @example Add a comment.
290
+ # optional.comment('slow query')
291
+ #
292
+ # @note Set profilingLevel to 2 and the comment will be logged in the profile
293
+ # collection along with the query.
294
+ #
295
+ # @param [ String ] comment The comment to be associated with the query.
296
+ #
297
+ # @return [ Optional ] The cloned optional.
298
+ #
299
+ # @since 2.2.0
300
+ def comment(comment = nil)
301
+ clone.tap do |query|
302
+ query.options.store(:comment, comment)
303
+ end
304
+ end
305
+
306
+ # Set the cursor type.
307
+ #
308
+ # @example Set the cursor type.
309
+ # optional.cursor_type(:tailable)
310
+ # optional.cursor_type(:tailable_await)
311
+ #
312
+ # @note The cursor can be type :tailable or :tailable_await.
313
+ #
314
+ # @param [ Symbol ] type The type of cursor to create.
315
+ #
316
+ # @return [ Optional ] The cloned optional.
317
+ #
318
+ # @since 2.2.0
319
+ def cursor_type(type)
320
+ clone.tap { |query| query.options.store(:cursor_type, type) }
321
+ end
322
+
323
+ private
324
+
325
+ # Add a single sort option.
326
+ #
327
+ # @api private
328
+ #
329
+ # @example Add a single sort option.
330
+ # optional.add_sort_option({}, :name, 1)
331
+ #
332
+ # @param [ Hash ] options The options.
333
+ # @param [ String ] field The field name.
334
+ # @param [ Integer ] direction The sort direction.
335
+ #
336
+ # @return [ Optional ] The cloned optional.
337
+ #
338
+ # @since 1.0.0
339
+ def add_sort_option(options, field, direction)
340
+ if driver == :mongo1x
341
+ sorting = (options[:sort] || []).dup
342
+ sorting.push([ field, direction ])
343
+ options.store(:sort, sorting)
344
+ else
345
+ sorting = (options[:sort] || {}).dup
346
+ sorting[field] = direction
347
+ options.store(:sort, sorting)
348
+ end
349
+ end
350
+
351
+ # Take the provided criterion and store it as an option in the query
352
+ # options.
353
+ #
354
+ # @api private
355
+ #
356
+ # @example Store the option.
357
+ # optional.option({ skip: 10 })
358
+ #
359
+ # @param [ Array ] args The options.
360
+ #
361
+ # @return [ Queryable ] The cloned queryable.
362
+ #
363
+ # @since 1.0.0
364
+ def option(*args)
365
+ clone.tap do |query|
366
+ unless args.compact.empty?
367
+ yield(query.options, query)
368
+ end
369
+ end
370
+ end
371
+
372
+ # Add multiple sort options at once.
373
+ #
374
+ # @api private
375
+ #
376
+ # @example Add multiple sort options.
377
+ # optional.sort_with_list(:name, :dob, 1)
378
+ #
379
+ # @param [ Array<String> ] fields The field names.
380
+ # @param [ Integer ] direction The sort direction.
381
+ #
382
+ # @return [ Optional ] The cloned optional.
383
+ #
384
+ # @since 1.0.0
385
+ def sort_with_list(*fields, direction)
386
+ option(fields) do |options, query|
387
+ fields.flatten.compact.each do |field|
388
+ add_sort_option(options, field, direction)
389
+ end
390
+ query.pipeline.push("$sort" => options[:sort]) if aggregating?
391
+ end
392
+ end
393
+
394
+ class << self
395
+
396
+ # Get the methods on the optional that can be forwarded to from a model.
397
+ #
398
+ # @example Get the forwardable methods.
399
+ # Optional.forwardables
400
+ #
401
+ # @return [ Array<Symbol> ] The names of the forwardable methods.
402
+ #
403
+ # @since 1.0.0
404
+ def forwardables
405
+ public_instance_methods(false) - [ :options, :options= ]
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end
411
+ end