mongoid 5.4.0 → 6.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (301) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/Rakefile +26 -0
  5. data/lib/config/locales/en.yml +40 -0
  6. data/lib/mongoid/atomic/modifiers.rb +2 -2
  7. data/lib/mongoid/atomic.rb +5 -5
  8. data/lib/mongoid/attributes/readonly.rb +22 -0
  9. data/lib/mongoid/attributes.rb +22 -21
  10. data/lib/mongoid/cacheable.rb +36 -0
  11. data/lib/mongoid/changeable.rb +36 -0
  12. data/lib/mongoid/clients/options.rb +55 -250
  13. data/lib/mongoid/clients/sessions.rb +113 -0
  14. data/lib/mongoid/clients/storage_options.rb +2 -69
  15. data/lib/mongoid/clients.rb +10 -63
  16. data/lib/mongoid/composable.rb +29 -2
  17. data/lib/mongoid/config.rb +1 -0
  18. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  19. data/lib/mongoid/contextual/atomic.rb +4 -4
  20. data/lib/mongoid/contextual/map_reduce.rb +7 -3
  21. data/lib/mongoid/contextual/memory.rb +9 -4
  22. data/lib/mongoid/contextual/mongo.rb +65 -30
  23. data/lib/mongoid/contextual/none.rb +12 -0
  24. data/lib/mongoid/copyable.rb +13 -6
  25. data/lib/mongoid/criteria/marshalable.rb +2 -2
  26. data/lib/mongoid/criteria/modifiable.rb +29 -3
  27. data/lib/mongoid/criteria/options.rb +25 -0
  28. data/lib/mongoid/criteria/queryable/aggregable.rb +120 -0
  29. data/lib/mongoid/criteria/queryable/extensions/array.rb +185 -0
  30. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +37 -0
  31. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +34 -0
  32. data/lib/mongoid/criteria/queryable/extensions/date.rb +63 -0
  33. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +53 -0
  34. data/lib/mongoid/criteria/queryable/extensions/hash.rb +200 -0
  35. data/lib/mongoid/criteria/queryable/extensions/nil_class.rb +86 -0
  36. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +90 -0
  37. data/lib/mongoid/criteria/queryable/extensions/object.rb +206 -0
  38. data/lib/mongoid/criteria/queryable/extensions/range.rb +70 -0
  39. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +79 -0
  40. data/lib/mongoid/criteria/queryable/extensions/set.rb +34 -0
  41. data/lib/mongoid/criteria/queryable/extensions/string.rb +137 -0
  42. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +79 -0
  43. data/lib/mongoid/criteria/queryable/extensions/time.rb +60 -0
  44. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +54 -0
  45. data/lib/mongoid/criteria/queryable/extensions.rb +28 -0
  46. data/lib/mongoid/criteria/queryable/forwardable.rb +65 -0
  47. data/lib/mongoid/criteria/queryable/key.rb +103 -0
  48. data/lib/mongoid/criteria/queryable/macroable.rb +27 -0
  49. data/lib/mongoid/criteria/queryable/mergeable.rb +273 -0
  50. data/lib/mongoid/criteria/queryable/optional.rb +429 -0
  51. data/lib/mongoid/criteria/queryable/options.rb +153 -0
  52. data/lib/mongoid/criteria/queryable/pipeline.rb +111 -0
  53. data/lib/mongoid/criteria/queryable/selectable.rb +689 -0
  54. data/lib/mongoid/criteria/queryable/selector.rb +212 -0
  55. data/lib/mongoid/criteria/queryable/smash.rb +104 -0
  56. data/lib/mongoid/criteria/queryable.rb +87 -0
  57. data/lib/mongoid/criteria.rb +6 -2
  58. data/lib/mongoid/document.rb +34 -41
  59. data/lib/mongoid/errors/ambiguous_relationship.rb +1 -1
  60. data/lib/mongoid/errors/in_memory_collation_not_supported.rb +1 -1
  61. data/lib/mongoid/errors/invalid_field.rb +2 -2
  62. data/lib/mongoid/errors/invalid_persistence_option.rb +29 -0
  63. data/lib/mongoid/errors/invalid_relation.rb +66 -0
  64. data/lib/mongoid/errors/invalid_session_use.rb +24 -0
  65. data/lib/mongoid/errors.rb +3 -0
  66. data/lib/mongoid/evolvable.rb +1 -1
  67. data/lib/mongoid/extensions/big_decimal.rb +17 -8
  68. data/lib/mongoid/extensions/date.rb +4 -1
  69. data/lib/mongoid/extensions/decimal128.rb +3 -3
  70. data/lib/mongoid/extensions/hash.rb +1 -0
  71. data/lib/mongoid/extensions/regexp.rb +1 -0
  72. data/lib/mongoid/extensions/string.rb +6 -3
  73. data/lib/mongoid/extensions/time.rb +4 -1
  74. data/lib/mongoid/extensions.rb +0 -4
  75. data/lib/mongoid/factory.rb +2 -1
  76. data/lib/mongoid/fields/validators/macro.rb +18 -0
  77. data/lib/mongoid/findable.rb +2 -2
  78. data/lib/mongoid/indexable.rb +16 -14
  79. data/lib/mongoid/interceptable.rb +9 -22
  80. data/lib/mongoid/matchable/all.rb +2 -2
  81. data/lib/mongoid/matchable/and.rb +3 -3
  82. data/lib/mongoid/matchable/default.rb +2 -2
  83. data/lib/mongoid/matchable/elem_match.rb +28 -0
  84. data/lib/mongoid/matchable/exists.rb +2 -2
  85. data/lib/mongoid/matchable/gt.rb +4 -2
  86. data/lib/mongoid/matchable/gte.rb +4 -2
  87. data/lib/mongoid/matchable/in.rb +2 -2
  88. data/lib/mongoid/matchable/lt.rb +4 -2
  89. data/lib/mongoid/matchable/lte.rb +4 -2
  90. data/lib/mongoid/matchable/ne.rb +2 -2
  91. data/lib/mongoid/matchable/nin.rb +2 -2
  92. data/lib/mongoid/matchable/nor.rb +37 -0
  93. data/lib/mongoid/matchable/or.rb +3 -3
  94. data/lib/mongoid/matchable/regexp.rb +3 -3
  95. data/lib/mongoid/matchable/size.rb +2 -2
  96. data/lib/mongoid/matchable.rb +16 -7
  97. data/lib/mongoid/persistable/creatable.rb +5 -3
  98. data/lib/mongoid/persistable/deletable.rb +5 -3
  99. data/lib/mongoid/persistable/destroyable.rb +1 -5
  100. data/lib/mongoid/persistable/settable.rb +5 -5
  101. data/lib/mongoid/persistable/updatable.rb +7 -14
  102. data/lib/mongoid/persistable/upsertable.rb +2 -1
  103. data/lib/mongoid/persistable.rb +4 -6
  104. data/lib/mongoid/persistence_context.rb +220 -0
  105. data/lib/mongoid/query_cache.rb +67 -23
  106. data/lib/mongoid/railtie.rb +17 -1
  107. data/lib/mongoid/railties/controller_runtime.rb +86 -0
  108. data/lib/mongoid/relations/accessors.rb +3 -0
  109. data/lib/mongoid/relations/auto_save.rb +12 -4
  110. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -4
  111. data/lib/mongoid/relations/counter_cache.rb +15 -5
  112. data/lib/mongoid/relations/eager/base.rb +3 -3
  113. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +2 -2
  114. data/lib/mongoid/relations/eager/has_many.rb +1 -1
  115. data/lib/mongoid/relations/eager.rb +6 -11
  116. data/lib/mongoid/relations/embedded/batchable.rb +20 -18
  117. data/lib/mongoid/relations/embedded/in.rb +13 -1
  118. data/lib/mongoid/relations/embedded/many.rb +51 -10
  119. data/lib/mongoid/relations/embedded/one.rb +14 -1
  120. data/lib/mongoid/relations/macros.rb +9 -1
  121. data/lib/mongoid/relations/many.rb +4 -0
  122. data/lib/mongoid/relations/metadata.rb +3 -3
  123. data/lib/mongoid/relations/options.rb +2 -2
  124. data/lib/mongoid/relations/proxy.rb +1 -31
  125. data/lib/mongoid/relations/referenced/in.rb +19 -10
  126. data/lib/mongoid/relations/referenced/many.rb +30 -26
  127. data/lib/mongoid/relations/referenced/many_to_many.rb +20 -13
  128. data/lib/mongoid/relations/referenced/one.rb +15 -1
  129. data/lib/mongoid/relations/synchronization.rb +12 -12
  130. data/lib/mongoid/relations/targets/enumerable.rb +24 -4
  131. data/lib/mongoid/relations/touchable.rb +7 -4
  132. data/lib/mongoid/reloadable.rb +2 -2
  133. data/lib/mongoid/scopable.rb +3 -3
  134. data/lib/mongoid/serializable.rb +1 -1
  135. data/lib/mongoid/stateful.rb +1 -0
  136. data/lib/mongoid/tasks/database.rb +3 -2
  137. data/lib/mongoid/threaded.rb +74 -0
  138. data/lib/mongoid/traversable.rb +1 -1
  139. data/lib/mongoid/validatable/uniqueness.rb +1 -2
  140. data/lib/mongoid/version.rb +1 -1
  141. data/lib/mongoid.rb +6 -6
  142. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +18 -3
  143. data/spec/app/models/agent.rb +2 -0
  144. data/spec/app/models/album.rb +5 -1
  145. data/spec/app/models/array_field.rb +7 -0
  146. data/spec/app/models/artist.rb +21 -0
  147. data/spec/app/models/band.rb +3 -0
  148. data/spec/app/models/book.rb +2 -1
  149. data/spec/app/models/delegating_patient.rb +16 -0
  150. data/spec/app/models/dokument.rb +1 -0
  151. data/spec/app/models/ordered_post.rb +5 -0
  152. data/spec/app/models/oscar.rb +1 -2
  153. data/spec/app/models/page.rb +1 -1
  154. data/spec/app/models/person.rb +3 -3
  155. data/spec/app/models/princess.rb +2 -0
  156. data/spec/app/models/record.rb +1 -0
  157. data/spec/app/models/subscription.rb +1 -0
  158. data/spec/app/models/thing.rb +1 -1
  159. data/spec/config/mongoid.yml +15 -0
  160. data/spec/integration/document_spec.rb +22 -0
  161. data/spec/mongoid/atomic/modifiers_spec.rb +3 -3
  162. data/spec/mongoid/atomic_spec.rb +5 -5
  163. data/spec/mongoid/attributes/nested_spec.rb +18 -14
  164. data/spec/mongoid/attributes/readonly_spec.rb +87 -44
  165. data/spec/mongoid/attributes_spec.rb +90 -5
  166. data/spec/mongoid/cacheable_spec.rb +112 -0
  167. data/spec/mongoid/changeable_spec.rb +58 -0
  168. data/spec/mongoid/clients/factory_spec.rb +80 -28
  169. data/spec/mongoid/clients/options_spec.rb +396 -95
  170. data/spec/mongoid/clients/sessions_spec.rb +334 -0
  171. data/spec/mongoid/clients_spec.rb +243 -101
  172. data/spec/mongoid/composable_spec.rb +7 -0
  173. data/spec/mongoid/config_spec.rb +67 -11
  174. data/spec/mongoid/contextual/atomic_spec.rb +3 -3
  175. data/spec/mongoid/contextual/geo_near_spec.rb +1 -0
  176. data/spec/mongoid/contextual/mongo_spec.rb +275 -22
  177. data/spec/mongoid/contextual/none_spec.rb +15 -0
  178. data/spec/mongoid/copyable_spec.rb +13 -4
  179. data/spec/mongoid/criteria/modifiable_spec.rb +297 -16
  180. data/spec/mongoid/criteria/options_spec.rb +29 -0
  181. data/spec/mongoid/criteria/queryable/aggregable_spec.rb +370 -0
  182. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +523 -0
  183. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +59 -0
  184. data/spec/mongoid/criteria/queryable/extensions/bignum_spec.rb +58 -0
  185. data/spec/mongoid/criteria/queryable/extensions/boolean_spec.rb +213 -0
  186. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +330 -0
  187. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +405 -0
  188. data/spec/mongoid/criteria/queryable/extensions/fixnum_spec.rb +58 -0
  189. data/spec/mongoid/criteria/queryable/extensions/float_spec.rb +65 -0
  190. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +327 -0
  191. data/spec/mongoid/criteria/queryable/extensions/integer_spec.rb +65 -0
  192. data/spec/mongoid/criteria/queryable/extensions/nil_class_spec.rb +77 -0
  193. data/spec/mongoid/criteria/queryable/extensions/object_spec.rb +108 -0
  194. data/spec/mongoid/criteria/queryable/extensions/range_spec.rb +309 -0
  195. data/spec/mongoid/{extensions/origin → criteria/queryable/extensions}/regexp_raw_spec.rb +2 -2
  196. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +90 -0
  197. data/spec/mongoid/criteria/queryable/extensions/set_spec.rb +39 -0
  198. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +302 -0
  199. data/spec/mongoid/criteria/queryable/extensions/symbol_spec.rb +167 -0
  200. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +376 -0
  201. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +347 -0
  202. data/spec/mongoid/criteria/queryable/forwardable_spec.rb +87 -0
  203. data/spec/mongoid/criteria/queryable/key_spec.rb +52 -0
  204. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +49 -0
  205. data/spec/mongoid/criteria/queryable/optional_spec.rb +1799 -0
  206. data/spec/mongoid/criteria/queryable/options_spec.rb +360 -0
  207. data/spec/mongoid/criteria/queryable/pipeline_spec.rb +200 -0
  208. data/spec/mongoid/criteria/queryable/queryable_spec.rb +137 -0
  209. data/spec/mongoid/criteria/queryable/selectable_spec.rb +4242 -0
  210. data/spec/mongoid/criteria/queryable/selector_spec.rb +844 -0
  211. data/spec/mongoid/criteria/queryable/smash_spec.rb +30 -0
  212. data/spec/mongoid/criteria/scopable_spec.rb +81 -0
  213. data/spec/mongoid/criteria_spec.rb +156 -22
  214. data/spec/mongoid/document_spec.rb +100 -90
  215. data/spec/mongoid/errors/invalid_relation_spec.rb +37 -0
  216. data/spec/mongoid/errors/mongoid_error_spec.rb +6 -3
  217. data/spec/mongoid/extensions/big_decimal_spec.rb +321 -19
  218. data/spec/mongoid/extensions/boolean_spec.rb +14 -0
  219. data/spec/mongoid/extensions/date_spec.rb +2 -6
  220. data/spec/mongoid/extensions/date_time_spec.rb +2 -6
  221. data/spec/mongoid/extensions/decimal128_spec.rb +1 -1
  222. data/spec/mongoid/extensions/float_spec.rb +8 -1
  223. data/spec/mongoid/extensions/hash_spec.rb +15 -0
  224. data/spec/mongoid/extensions/integer_spec.rb +8 -1
  225. data/spec/mongoid/extensions/object_spec.rb +11 -0
  226. data/spec/mongoid/extensions/regexp_spec.rb +23 -0
  227. data/spec/mongoid/extensions/string_spec.rb +53 -4
  228. data/spec/mongoid/extensions/time_spec.rb +2 -6
  229. data/spec/mongoid/extensions/time_with_zone_spec.rb +2 -6
  230. data/spec/mongoid/factory_spec.rb +11 -0
  231. data/spec/mongoid/fields_spec.rb +1 -1
  232. data/spec/mongoid/findable_spec.rb +47 -2
  233. data/spec/mongoid/indexable_spec.rb +15 -3
  234. data/spec/mongoid/interceptable_spec.rb +85 -19
  235. data/spec/mongoid/matchable/all_spec.rb +4 -4
  236. data/spec/mongoid/matchable/and_spec.rb +10 -10
  237. data/spec/mongoid/matchable/default_spec.rb +12 -12
  238. data/spec/mongoid/matchable/elem_match_spec.rb +86 -0
  239. data/spec/mongoid/matchable/exists_spec.rb +5 -5
  240. data/spec/mongoid/matchable/gt_spec.rb +18 -7
  241. data/spec/mongoid/matchable/gte_spec.rb +17 -7
  242. data/spec/mongoid/matchable/in_spec.rb +5 -5
  243. data/spec/mongoid/matchable/lt_spec.rb +18 -7
  244. data/spec/mongoid/matchable/lte_spec.rb +18 -7
  245. data/spec/mongoid/matchable/ne_spec.rb +5 -5
  246. data/spec/mongoid/matchable/nin_spec.rb +5 -5
  247. data/spec/mongoid/matchable/nor_spec.rb +209 -0
  248. data/spec/mongoid/matchable/or_spec.rb +7 -7
  249. data/spec/mongoid/matchable/regexp_spec.rb +5 -5
  250. data/spec/mongoid/matchable/size_spec.rb +3 -3
  251. data/spec/mongoid/matchable_spec.rb +199 -54
  252. data/spec/mongoid/persistable/creatable_spec.rb +7 -2
  253. data/spec/mongoid/persistable/deletable_spec.rb +35 -1
  254. data/spec/mongoid/persistable/destroyable_spec.rb +25 -2
  255. data/spec/mongoid/persistable/incrementable_spec.rb +6 -6
  256. data/spec/mongoid/persistable/savable_spec.rb +34 -29
  257. data/spec/mongoid/persistable/settable_spec.rb +77 -27
  258. data/spec/mongoid/persistable/updatable_spec.rb +182 -3
  259. data/spec/mongoid/persistable_spec.rb +16 -16
  260. data/spec/mongoid/persistence_context_spec.rb +694 -0
  261. data/spec/mongoid/positional_spec.rb +1 -1
  262. data/spec/mongoid/query_cache_spec.rb +170 -12
  263. data/spec/mongoid/relations/accessors_spec.rb +1 -1
  264. data/spec/mongoid/relations/auto_save_spec.rb +39 -6
  265. data/spec/mongoid/relations/bindings/referenced/many_to_many_spec.rb +4 -4
  266. data/spec/mongoid/relations/builders_spec.rb +37 -10
  267. data/spec/mongoid/relations/counter_cache_spec.rb +64 -3
  268. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +16 -0
  269. data/spec/mongoid/relations/eager_spec.rb +40 -0
  270. data/spec/mongoid/relations/embedded/many_spec.rb +305 -59
  271. data/spec/mongoid/relations/embedded/one_spec.rb +2 -1
  272. data/spec/mongoid/relations/macros_spec.rb +415 -7
  273. data/spec/mongoid/relations/metadata_spec.rb +15 -1
  274. data/spec/mongoid/relations/proxy_spec.rb +27 -1
  275. data/spec/mongoid/relations/referenced/in_spec.rb +41 -1
  276. data/spec/mongoid/relations/referenced/many_spec.rb +35 -25
  277. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +14 -26
  278. data/spec/mongoid/relations/synchronization_spec.rb +48 -2
  279. data/spec/mongoid/relations/targets/enumerable_spec.rb +108 -0
  280. data/spec/mongoid/relations/touchable_spec.rb +40 -0
  281. data/spec/mongoid/reloadable_spec.rb +51 -0
  282. data/spec/mongoid/scopable_spec.rb +13 -0
  283. data/spec/mongoid/serializable_spec.rb +0 -50
  284. data/spec/mongoid/threaded_spec.rb +68 -0
  285. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  286. data/spec/mongoid/validatable/uniqueness_spec.rb +18 -9
  287. data/spec/mongoid/validatable_spec.rb +16 -0
  288. data/spec/rails/controller_extension/controller_runtime_spec.rb +110 -0
  289. data/spec/spec_helper.rb +101 -8
  290. data/spec/support/cluster_config.rb +158 -0
  291. data/spec/support/constraints.rb +101 -0
  292. data/spec/support/macros.rb +20 -0
  293. data/spec/support/session_registry.rb +50 -0
  294. data/spec/support/spec_config.rb +42 -0
  295. data.tar.gz.sig +0 -0
  296. metadata +163 -61
  297. metadata.gz.sig +0 -0
  298. data/lib/mongoid/clients/thread_options.rb +0 -19
  299. data/lib/mongoid/extensions/origin/regexp_raw.rb +0 -43
  300. data/lib/mongoid/railties/document.rb +0 -12
  301. data/spec/mongoid/railties/document_spec.rb +0 -24
@@ -0,0 +1,689 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ class Criteria
4
+ module Queryable
5
+
6
+ # An queryable selectable is selectable, in that it has the ability to select
7
+ # document from the database. The selectable module brings all functionality
8
+ # to the selectable that has to do with building MongoDB selectors.
9
+ module Selectable
10
+ extend Macroable
11
+
12
+ # Constant for a LineString $geometry.
13
+ #
14
+ # @since 2.0.0
15
+ LINE_STRING = "LineString"
16
+
17
+ # Constant for a Point $geometry.
18
+ #
19
+ # @since 2.0.0
20
+ POINT = "Point"
21
+
22
+ # Constant for a Polygon $geometry.
23
+ #
24
+ # @since 2.0.0
25
+ POLYGON = "Polygon"
26
+
27
+ # @attribute [rw] negating If the next expression is negated.
28
+ # @attribute [rw] selector The query selector.
29
+ attr_accessor :negating, :selector
30
+
31
+ # Add the $all criterion.
32
+ #
33
+ # @example Add the criterion.
34
+ # selectable.all(field: [ 1, 2 ])
35
+ #
36
+ # @example Execute an $all in a where query.
37
+ # selectable.where(:field.all => [ 1, 2 ])
38
+ #
39
+ # @param [ Hash ] criterion The key value pairs for $all matching.
40
+ #
41
+ # @return [ Selectable ] The cloned selectable.
42
+ #
43
+ # @since 1.0.0
44
+ def all(criterion = nil)
45
+ send(strategy || :__union__, with_array_values(criterion), "$all")
46
+ end
47
+ alias :all_in :all
48
+ key :all, :union, "$all"
49
+
50
+ # Add the $and criterion.
51
+ #
52
+ # @example Add the criterion.
53
+ # selectable.and({ field: value }, { other: value })
54
+ #
55
+ # @param [ Array<Hash> ] criterion Multiple key/value pair matches that
56
+ # all must match to return results.
57
+ #
58
+ # @return [ Selectable ] The cloned selectable.
59
+ #
60
+ # @since 1.0.0
61
+ def and(*criterion)
62
+ __multi__(criterion, "$and")
63
+ end
64
+ alias :all_of :and
65
+
66
+ # Add the range selection.
67
+ #
68
+ # @example Match on results within a single range.
69
+ # selectable.between(field: 1..2)
70
+ #
71
+ # @example Match on results between multiple ranges.
72
+ # selectable.between(field: 1..2, other: 5..7)
73
+ #
74
+ # @param [ Hash ] criterion Multiple key/range pairs.
75
+ #
76
+ # @return [ Selectable ] The cloned selectable.
77
+ #
78
+ # @since 1.0.0
79
+ def between(criterion = nil)
80
+ selection(criterion) do |selector, field, value|
81
+ selector.store(
82
+ field,
83
+ { "$gte" => value.min, "$lte" => value.max }
84
+ )
85
+ end
86
+ end
87
+
88
+ # Select with an $elemMatch.
89
+ #
90
+ # @example Add criterion for a single match.
91
+ # selectable.elem_match(field: { name: "value" })
92
+ #
93
+ # @example Add criterion for multiple matches.
94
+ # selectable.elem_match(
95
+ # field: { name: "value" },
96
+ # other: { name: "value"}
97
+ # )
98
+ #
99
+ # @example Execute an $elemMatch in a where query.
100
+ # selectable.where(:field.elem_match => { name: "value" })
101
+ #
102
+ # @param [ Hash ] criterion The field/match pairs.
103
+ #
104
+ # @return [ Selectable ] The cloned selectable.
105
+ #
106
+ # @since 1.0.0
107
+ def elem_match(criterion = nil)
108
+ __override__(criterion, "$elemMatch")
109
+ end
110
+ key :elem_match, :override, "$elemMatch"
111
+
112
+ # Add the $exists selection.
113
+ #
114
+ # @example Add a single selection.
115
+ # selectable.exists(field: true)
116
+ #
117
+ # @example Add multiple selections.
118
+ # selectable.exists(field: true, other: false)
119
+ #
120
+ # @example Execute an $exists in a where query.
121
+ # selectable.where(:field.exists => true)
122
+ #
123
+ # @param [ Hash ] criterion The field/boolean existence checks.
124
+ #
125
+ # @return [ Selectable ] The cloned selectable.
126
+ #
127
+ # @since 1.0.0
128
+ def exists(criterion = nil)
129
+ typed_override(criterion, "$exists") do |value|
130
+ ::Boolean.evolve(value)
131
+ end
132
+ end
133
+ key :exists, :override, "$exists" do |value|
134
+ ::Boolean.evolve(value)
135
+ end
136
+
137
+ # Add a $geoIntersects or $geoWithin selection. Symbol operators must
138
+ # be used as shown in the examples to expand the criteria.
139
+ #
140
+ # @note The only valid geometry shapes for a $geoIntersects are:
141
+ # :intersects_line, :intersects_point, and :intersects_polygon.
142
+ #
143
+ # @note The only valid options for a $geoWithin query are the geometry
144
+ # shape :within_polygon and the operator :within_box.
145
+ #
146
+ # @note The :within_box operator for the $geoWithin query expects the
147
+ # lower left (south west) coordinate pair as the first argument and
148
+ # the upper right (north east) as the second argument.
149
+ # Important: When latitude and longitude are passed, longitude is
150
+ # expected as the first element of the coordinate pair.
151
+ # Source: https://docs.mongodb.com/manual/reference/operator/query/box/
152
+ #
153
+ # @example Add a geo intersect criterion for a line.
154
+ # query.geo_spacial(:location.intersects_line => [[ 1, 10 ], [ 2, 10 ]])
155
+ #
156
+ # @example Add a geo intersect criterion for a point.
157
+ # query.geo_spacial(:location.intersects_point => [[ 1, 10 ]])
158
+ #
159
+ # @example Add a geo intersect criterion for a polygon.
160
+ # query.geo_spacial(:location.intersects_polygon => [[ 1, 10 ], [ 2, 10 ], [ 1, 10 ]])
161
+ #
162
+ # @example Add a geo within criterion for a polygon.
163
+ # query.geo_spacial(:location.within_polygon => [[ 1, 10 ], [ 2, 10 ], [ 1, 10 ]])
164
+ #
165
+ # @example Add a geo within criterion for a box.
166
+ # query.geo_spacial(:location.within_box => [[ 1, 10 ], [ 2, 10 ])
167
+ #
168
+ # @param [ Hash ] criterion The criterion.
169
+ #
170
+ # @return [ Selectable ] The cloned selectable.
171
+ #
172
+ # @since 2.0.0
173
+ def geo_spacial(criterion = nil)
174
+ __merge__(criterion)
175
+ end
176
+ key :intersects_line, :override, "$geoIntersects", "$geometry" do |value|
177
+ { "type" => LINE_STRING, "coordinates" => value }
178
+ end
179
+ key :intersects_point, :override, "$geoIntersects", "$geometry" do |value|
180
+ { "type" => POINT, "coordinates" => value }
181
+ end
182
+ key :intersects_polygon, :override, "$geoIntersects", "$geometry" do |value|
183
+ { "type" => POLYGON, "coordinates" => value }
184
+ end
185
+ key :within_polygon, :override, "$geoWithin", "$geometry" do |value|
186
+ { "type" => POLYGON, "coordinates" => value }
187
+ end
188
+ key :within_box, :override, "$geoWithin", "$box"
189
+
190
+ # Add the $gt criterion to the selector.
191
+ #
192
+ # @example Add the $gt criterion.
193
+ # selectable.gt(age: 60)
194
+ #
195
+ # @example Execute an $gt in a where query.
196
+ # selectable.where(:field.gt => 10)
197
+ #
198
+ # @param [ Hash ] criterion The field/value pairs to check.
199
+ #
200
+ # @return [ Selectable ] The cloned selectable.
201
+ #
202
+ # @since 1.0.0
203
+ def gt(criterion = nil)
204
+ __override__(criterion, "$gt")
205
+ end
206
+ key :gt, :override, "$gt"
207
+
208
+ # Add the $gte criterion to the selector.
209
+ #
210
+ # @example Add the $gte criterion.
211
+ # selectable.gte(age: 60)
212
+ #
213
+ # @example Execute an $gte in a where query.
214
+ # selectable.where(:field.gte => 10)
215
+ #
216
+ # @param [ Hash ] criterion The field/value pairs to check.
217
+ #
218
+ # @return [ Selectable ] The cloned selectable.
219
+ #
220
+ # @since 1.0.0
221
+ def gte(criterion = nil)
222
+ __override__(criterion, "$gte")
223
+ end
224
+ key :gte, :override, "$gte"
225
+
226
+ # Adds the $in selection to the selectable.
227
+ #
228
+ # @example Add $in selection on an array.
229
+ # selectable.in(age: [ 1, 2, 3 ])
230
+ #
231
+ # @example Add $in selection on a range.
232
+ # selectable.in(age: 18..24)
233
+ #
234
+ # @example Execute an $in in a where query.
235
+ # selectable.where(:field.in => [ 1, 2, 3 ])
236
+ #
237
+ # @param [ Hash ] criterion The field/value criterion pairs.
238
+ #
239
+ # @return [ Selectable ] The cloned selectable.
240
+ #
241
+ # @since 1.0.0
242
+ def in(criterion = nil)
243
+ send(strategy || :__intersect__, with_array_values(criterion), "$in")
244
+ end
245
+ alias :any_in :in
246
+ key :in, :intersect, "$in"
247
+
248
+ # Add the $lt criterion to the selector.
249
+ #
250
+ # @example Add the $lt criterion.
251
+ # selectable.lt(age: 60)
252
+ #
253
+ # @example Execute an $lt in a where query.
254
+ # selectable.where(:field.lt => 10)
255
+ #
256
+ # @param [ Hash ] criterion The field/value pairs to check.
257
+ #
258
+ # @return [ Selectable ] The cloned selectable.
259
+ #
260
+ # @since 1.0.0
261
+ def lt(criterion = nil)
262
+ __override__(criterion, "$lt")
263
+ end
264
+ key :lt, :override, "$lt"
265
+
266
+ # Add the $lte criterion to the selector.
267
+ #
268
+ # @example Add the $lte criterion.
269
+ # selectable.lte(age: 60)
270
+ #
271
+ # @example Execute an $lte in a where query.
272
+ # selectable.where(:field.lte => 10)
273
+ #
274
+ # @param [ Hash ] criterion The field/value pairs to check.
275
+ #
276
+ # @return [ Selectable ] The cloned selectable.
277
+ #
278
+ # @since 1.0.0
279
+ def lte(criterion = nil)
280
+ __override__(criterion, "$lte")
281
+ end
282
+ key :lte, :override, "$lte"
283
+
284
+ # Add a $maxDistance selection to the selectable.
285
+ #
286
+ # @example Add the $maxDistance selection.
287
+ # selectable.max_distance(location: 10)
288
+ #
289
+ # @param [ Hash ] criterion The field/distance pairs.
290
+ #
291
+ # @return [ Selectable ] The cloned selectable.
292
+ #
293
+ # @since 1.0.0
294
+ def max_distance(criterion = nil)
295
+ __add__(criterion, "$maxDistance")
296
+ end
297
+
298
+ # Adds $mod selection to the selectable.
299
+ #
300
+ # @example Add the $mod selection.
301
+ # selectable.mod(field: [ 10, 1 ])
302
+ #
303
+ # @example Execute an $mod in a where query.
304
+ # selectable.where(:field.mod => [ 10, 1 ])
305
+ #
306
+ # @param [ Hash ] criterion The field/mod selections.
307
+ #
308
+ # @return [ Selectable ] The cloned selectable.
309
+ #
310
+ # @since 1.0.0
311
+ def mod(criterion = nil)
312
+ __override__(criterion, "$mod")
313
+ end
314
+ key :mod, :override, "$mod"
315
+
316
+ # Adds $ne selection to the selectable.
317
+ #
318
+ # @example Query for a value $ne to something.
319
+ # selectable.ne(field: 10)
320
+ #
321
+ # @example Execute an $ne in a where query.
322
+ # selectable.where(:field.ne => "value")
323
+ #
324
+ # @param [ Hash ] criterion The field/ne selections.
325
+ #
326
+ # @return [ Selectable ] The cloned selectable.
327
+ #
328
+ # @since 1.0.0
329
+ def ne(criterion = nil)
330
+ __override__(criterion, "$ne")
331
+ end
332
+ alias :excludes :ne
333
+ key :ne, :override, "$ne"
334
+
335
+ # Adds a $near criterion to a geo selection.
336
+ #
337
+ # @example Add the $near selection.
338
+ # selectable.near(location: [ 23.1, 12.1 ])
339
+ #
340
+ # @example Execute an $near in a where query.
341
+ # selectable.where(:field.near => [ 23.2, 12.1 ])
342
+ #
343
+ # @param [ Hash ] criterion The field/location pair.
344
+ #
345
+ # @return [ Selectable ] The cloned selectable.
346
+ #
347
+ # @since 1.0.0
348
+ def near(criterion = nil)
349
+ __override__(criterion, "$near")
350
+ end
351
+ key :near, :override, "$near"
352
+
353
+ # Adds a $nearSphere criterion to a geo selection.
354
+ #
355
+ # @example Add the $nearSphere selection.
356
+ # selectable.near_sphere(location: [ 23.1, 12.1 ])
357
+ #
358
+ # @example Execute an $nearSphere in a where query.
359
+ # selectable.where(:field.near_sphere => [ 10.11, 3.22 ])
360
+ #
361
+ # @param [ Hash ] criterion The field/location pair.
362
+ #
363
+ # @return [ Selectable ] The cloned selectable.
364
+ #
365
+ # @since 1.0.0
366
+ def near_sphere(criterion = nil)
367
+ __override__(criterion, "$nearSphere")
368
+ end
369
+ key :near_sphere, :override, "$nearSphere"
370
+
371
+ # Adds the $nin selection to the selectable.
372
+ #
373
+ # @example Add $nin selection on an array.
374
+ # selectable.nin(age: [ 1, 2, 3 ])
375
+ #
376
+ # @example Add $nin selection on a range.
377
+ # selectable.nin(age: 18..24)
378
+ #
379
+ # @example Execute an $nin in a where query.
380
+ # selectable.where(:field.nin => [ 1, 2, 3 ])
381
+ #
382
+ # @param [ Hash ] criterion The field/value criterion pairs.
383
+ #
384
+ # @return [ Selectable ] The cloned selectable.
385
+ #
386
+ # @since 1.0.0
387
+ def nin(criterion = nil)
388
+ send(strategy || :__intersect__, with_array_values(criterion), "$nin")
389
+ end
390
+ alias :not_in :nin
391
+ key :nin, :intersect, "$nin"
392
+
393
+ # Adds $nor selection to the selectable.
394
+ #
395
+ # @example Add the $nor selection.
396
+ # selectable.nor(field: 1, field: 2)
397
+ #
398
+ # @param [ Array ] criterion An array of hash criterion.
399
+ #
400
+ # @return [ Selectable ] The cloned selectable.
401
+ #
402
+ # @since 1.0.0
403
+ def nor(*criterion)
404
+ __multi__(criterion, "$nor")
405
+ end
406
+
407
+ # Is the current selectable negating the next selection?
408
+ #
409
+ # @example Is the selectable negating?
410
+ # selectable.negating?
411
+ #
412
+ # @return [ true, false ] If the selectable is negating.
413
+ #
414
+ # @since 1.0.0
415
+ def negating?
416
+ !!negating
417
+ end
418
+
419
+ # Negate the next selection.
420
+ #
421
+ # @example Negate the selection.
422
+ # selectable.not.in(field: [ 1, 2 ])
423
+ #
424
+ # @example Add the $not criterion.
425
+ # selectable.not(name: /Bob/)
426
+ #
427
+ # @example Execute a $not in a where query.
428
+ # selectable.where(:field.not => /Bob/)
429
+ #
430
+ # @param [ Hash ] criterion The field/value pairs to negate.
431
+ #
432
+ # @return [ Selectable ] The negated selectable.
433
+ #
434
+ # @since 1.0.0
435
+ def not(*criterion)
436
+ if criterion.empty?
437
+ tap { |query| query.negating = true }
438
+ else
439
+ __override__(criterion.first, "$not")
440
+ end
441
+ end
442
+ key :not, :override, "$not"
443
+
444
+ # Adds $or selection to the selectable.
445
+ #
446
+ # @example Add the $or selection.
447
+ # selectable.or(field: 1, field: 2)
448
+ #
449
+ # @param [ Array ] criterion An array of hash criterion.
450
+ #
451
+ # @return [ Selectable ] The cloned selectable.
452
+ #
453
+ # @since 1.0.0
454
+ def or(*criterion)
455
+ __multi__(criterion, "$or")
456
+ end
457
+ alias :any_of :or
458
+
459
+ # Add a $size selection for array fields.
460
+ #
461
+ # @example Add the $size selection.
462
+ # selectable.with_size(field: 5)
463
+ #
464
+ # @note This method is named #with_size not to conflict with any existing
465
+ # #size method on enumerables or symbols.
466
+ #
467
+ # @example Execute an $size in a where query.
468
+ # selectable.where(:field.with_size => 10)
469
+ #
470
+ # @param [ Hash ] criterion The field/size pairs criterion.
471
+ #
472
+ # @return [ Selectable ] The cloned selectable.
473
+ #
474
+ # @since 1.0.0
475
+ def with_size(criterion = nil)
476
+ typed_override(criterion, "$size") do |value|
477
+ ::Integer.evolve(value)
478
+ end
479
+ end
480
+ key :with_size, :override, "$size" do |value|
481
+ ::Integer.evolve(value)
482
+ end
483
+
484
+ # Adds a $type selection to the selectable.
485
+ #
486
+ # @example Add the $type selection.
487
+ # selectable.with_type(field: 15)
488
+ #
489
+ # @example Execute an $type in a where query.
490
+ # selectable.where(:field.with_type => 15)
491
+ #
492
+ # @note http://vurl.me/PGOU contains a list of all types.
493
+ #
494
+ # @param [ Hash ] criterion The field/type pairs.
495
+ #
496
+ # @return [ Selectable ] The cloned selectable.
497
+ #
498
+ # @since 1.0.0
499
+ def with_type(criterion = nil)
500
+ typed_override(criterion, "$type") do |value|
501
+ ::Integer.evolve(value)
502
+ end
503
+ end
504
+ key :with_type, :override, "$type" do |value|
505
+ ::Integer.evolve(value)
506
+ end
507
+
508
+ # Construct a text search selector.
509
+ #
510
+ # @example Construct a text search selector.
511
+ # selectable.text_search("testing")
512
+ #
513
+ # @example Construct a text search selector with options.
514
+ # selectable.text_search("testing", :$language => "fr")
515
+ #
516
+ # @note Per https://docs.mongodb.com/manual/reference/operator/query/text/
517
+ # it is not currently possible to supply multiple text search
518
+ # conditions in a query. Mongoid will build such a query but the
519
+ # server will return an error when trying to execute it.
520
+ #
521
+ # @param [ String, Symbol ] terms A string of terms that MongoDB parses
522
+ # and uses to query the text index.
523
+ # @param [ Hash ] opts Text search options. See MongoDB documentation
524
+ # for options.
525
+ #
526
+ # @return [ Selectable ] The cloned selectable.
527
+ #
528
+ # @since 2.2.0
529
+ def text_search(terms, opts = nil)
530
+ clone.tap do |query|
531
+ if terms
532
+ criterion = {'$text' => { '$search' => terms }}
533
+ criterion['$text'].merge!(opts) if opts
534
+ if query.selector['$text']
535
+ # Per https://docs.mongodb.com/manual/reference/operator/query/text/
536
+ # multiple $text expressions are not currently supported by
537
+ # MongoDB server, but build the query correctly instead of
538
+ # overwriting previous text search condition with the currently
539
+ # given one.
540
+ Mongoid.logger.warn('Multiple $text expressions per query are not currently supported by the server')
541
+ query.selector = {'$and' => [query.selector]}.merge(criterion)
542
+ else
543
+ query.selector = query.selector.merge(criterion)
544
+ end
545
+ end
546
+ end
547
+ end
548
+
549
+ # This is the general entry point for most MongoDB queries. This either
550
+ # creates a standard field: value selection, and expanded selection with
551
+ # the use of hash methods, or a $where selection if a string is provided.
552
+ #
553
+ # @example Add a standard selection.
554
+ # selectable.where(name: "syd")
555
+ #
556
+ # @example Add a javascript selection.
557
+ # selectable.where("this.name == 'syd'")
558
+ #
559
+ # @param [ String, Hash ] criterion The javascript or standard selection.
560
+ #
561
+ # @return [ Selectable ] The cloned selectable.
562
+ #
563
+ # @since 1.0.0
564
+ def where(criterion = nil)
565
+ criterion.is_a?(String) ? js_query(criterion) : expr_query(criterion)
566
+ end
567
+
568
+ private
569
+
570
+ # Create the standard expression query.
571
+ #
572
+ # @api private
573
+ #
574
+ # @example Create the selection.
575
+ # selectable.expr_query(age: 50)
576
+ #
577
+ # @param [ Hash ] criterion The field/value pairs.
578
+ #
579
+ # @return [ Selectable ] The cloned selectable.
580
+ #
581
+ # @since 1.0.0
582
+ def expr_query(criterion)
583
+ selection(criterion) do |selector, field, value|
584
+ selector.merge!(field.__expr_part__(value.__expand_complex__, negating?))
585
+ end
586
+ end
587
+
588
+ # Force the values of the criterion to be evolved.
589
+ #
590
+ # @api private
591
+ #
592
+ # @example Force values to booleans.
593
+ # selectable.force_typing(criterion) do |val|
594
+ # Boolean.evolve(val)
595
+ # end
596
+ #
597
+ # @param [ Hash ] criterion The criterion.
598
+ #
599
+ # @since 1.0.0
600
+ def typed_override(criterion, operator)
601
+ if criterion
602
+ criterion.update_values do |value|
603
+ yield(value)
604
+ end
605
+ end
606
+ __override__(criterion, operator)
607
+ end
608
+
609
+ # Create a javascript selection.
610
+ #
611
+ # @api private
612
+ #
613
+ # @example Create the javascript selection.
614
+ # selectable.js_query("this.age == 50")
615
+ #
616
+ # @param [ String ] criterion The javascript as a string.
617
+ #
618
+ # @return [ Selectable ] The cloned selectable
619
+ #
620
+ # @since 1.0.0
621
+ def js_query(criterion)
622
+ clone.tap do |query|
623
+ query.selector.merge!("$where" => criterion)
624
+ end
625
+ end
626
+
627
+ # Take the provided criterion and store it as a selection in the query
628
+ # selector.
629
+ #
630
+ # @api private
631
+ #
632
+ # @example Store the selection.
633
+ # selectable.selection({ field: "value" })
634
+ #
635
+ # @param [ Hash ] criterion The selection to store.
636
+ #
637
+ # @return [ Selectable ] The cloned selectable.
638
+ #
639
+ # @since 1.0.0
640
+ def selection(criterion = nil)
641
+ clone.tap do |query|
642
+ if criterion
643
+ criterion.each_pair do |field, value|
644
+ yield(query.selector, field.is_a?(Key) ? field : field.to_s, value)
645
+ end
646
+ end
647
+ query.reset_strategies!
648
+ end
649
+ end
650
+
651
+ # Convert the criterion values to $in friendly values. This means you,
652
+ # array.
653
+ #
654
+ # @api private
655
+ #
656
+ # @example Convert all the values to arrays.
657
+ # selectable.with_array_values({ key: 1...4 })
658
+ #
659
+ # @param [ Hash ] criterion The criterion.
660
+ #
661
+ # @return [ Hash ] The $in friendly criterion (array values).
662
+ #
663
+ # @since 1.0.0
664
+ def with_array_values(criterion)
665
+ return nil unless criterion
666
+ criterion.each_pair do |key, value|
667
+ criterion[key] = value.__array__
668
+ end
669
+ end
670
+
671
+ class << self
672
+
673
+ # Get the methods on the selectable that can be forwarded to from a model.
674
+ #
675
+ # @example Get the forwardable methods.
676
+ # Selectable.forwardables
677
+ #
678
+ # @return [ Array<Symbol> ] The names of the forwardable methods.
679
+ #
680
+ # @since 1.0.0
681
+ def forwardables
682
+ public_instance_methods(false) -
683
+ [ :negating, :negating=, :negating?, :selector, :selector= ]
684
+ end
685
+ end
686
+ end
687
+ end
688
+ end
689
+ end