origin 0.0.0.alpha → 1.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/Rakefile +3 -0
  2. data/lib/origin.rb +3 -4
  3. data/lib/origin/extensions.rb +25 -0
  4. data/lib/origin/extensions/array.rb +153 -0
  5. data/lib/origin/extensions/big_decimal.rb +33 -0
  6. data/lib/origin/extensions/boolean.rb +30 -0
  7. data/lib/origin/extensions/date.rb +59 -0
  8. data/lib/origin/extensions/date_time.rb +44 -0
  9. data/lib/origin/extensions/hash.rb +180 -0
  10. data/lib/origin/extensions/nil_class.rb +82 -0
  11. data/lib/origin/extensions/numeric.rb +86 -0
  12. data/lib/origin/extensions/object.rb +182 -0
  13. data/lib/origin/extensions/range.rb +66 -0
  14. data/lib/origin/extensions/regexp.rb +41 -0
  15. data/lib/origin/extensions/set.rb +28 -0
  16. data/lib/origin/extensions/string.rb +100 -0
  17. data/lib/origin/extensions/symbol.rb +74 -0
  18. data/lib/origin/extensions/time.rb +44 -0
  19. data/lib/origin/extensions/time_with_zone.rb +50 -0
  20. data/lib/origin/forwardable.rb +57 -0
  21. data/lib/origin/key.rb +74 -0
  22. data/lib/origin/macroable.rb +23 -0
  23. data/lib/origin/mergeable.rb +226 -0
  24. data/lib/origin/optional.rb +314 -29
  25. data/lib/origin/options.rb +64 -1
  26. data/lib/origin/queryable.rb +55 -12
  27. data/lib/origin/selectable.rb +613 -0
  28. data/lib/origin/selector.rb +140 -1
  29. data/lib/origin/smash.rb +85 -0
  30. data/lib/origin/version.rb +1 -1
  31. metadata +94 -62
  32. data/lib/origin/ext.rb +0 -5
  33. data/lib/origin/ext/array.rb +0 -21
  34. data/lib/origin/ext/hash.rb +0 -38
  35. data/lib/origin/ext/nil.rb +0 -9
  36. data/lib/origin/ext/object.rb +0 -25
  37. data/lib/origin/optional/batch_size.rb +0 -11
  38. data/lib/origin/optional/hint.rb +0 -15
  39. data/lib/origin/optional/limit.rb +0 -11
  40. data/lib/origin/optional/max_scan.rb +0 -11
  41. data/lib/origin/optional/no_timeout.rb +0 -11
  42. data/lib/origin/optional/only.rb +0 -15
  43. data/lib/origin/optional/read.rb +0 -11
  44. data/lib/origin/optional/return_key.rb +0 -11
  45. data/lib/origin/optional/show_disk_loc.rb +0 -11
  46. data/lib/origin/optional/skip.rb +0 -11
  47. data/lib/origin/optional/slice.rb +0 -17
  48. data/lib/origin/optional/snapshot.rb +0 -13
  49. data/lib/origin/optional/transformer.rb +0 -11
  50. data/lib/origin/optional/without.rb +0 -15
  51. data/lib/origin/selection.rb +0 -59
  52. data/lib/origin/selection/all.rb +0 -18
  53. data/lib/origin/selection/and.rb +0 -11
  54. data/lib/origin/selection/between.rb +0 -16
  55. data/lib/origin/selection/elem_match.rb +0 -18
  56. data/lib/origin/selection/exists.rb +0 -18
  57. data/lib/origin/selection/gt.rb +0 -18
  58. data/lib/origin/selection/gte.rb +0 -18
  59. data/lib/origin/selection/in.rb +0 -18
  60. data/lib/origin/selection/key.rb +0 -20
  61. data/lib/origin/selection/lt.rb +0 -18
  62. data/lib/origin/selection/lte.rb +0 -18
  63. data/lib/origin/selection/max_distance.rb +0 -11
  64. data/lib/origin/selection/mod.rb +0 -18
  65. data/lib/origin/selection/ne.rb +0 -18
  66. data/lib/origin/selection/near.rb +0 -18
  67. data/lib/origin/selection/near_sphere.rb +0 -18
  68. data/lib/origin/selection/nin.rb +0 -18
  69. data/lib/origin/selection/nor.rb +0 -11
  70. data/lib/origin/selection/or.rb +0 -11
  71. data/lib/origin/selection/size.rb +0 -18
  72. data/lib/origin/selection/strategies.rb +0 -40
  73. data/lib/origin/selection/strategies/add.rb +0 -18
  74. data/lib/origin/selection/strategies/expanded.rb +0 -15
  75. data/lib/origin/selection/strategies/intersect.rb +0 -22
  76. data/lib/origin/selection/strategies/multi.rb +0 -21
  77. data/lib/origin/selection/strategies/override.rb +0 -19
  78. data/lib/origin/selection/strategies/union.rb +0 -22
  79. data/lib/origin/selection/type.rb +0 -18
  80. data/lib/origin/selection/where.rb +0 -29
  81. data/lib/origin/selection/within_box.rb +0 -18
  82. data/lib/origin/selection/within_circle.rb +0 -18
  83. data/lib/origin/selection/within_spherical_circle.rb +0 -18
@@ -1,5 +1,68 @@
1
1
  # encoding: utf-8
2
2
  module Origin
3
- class Options < Hash
3
+
4
+ # The options is a hash representation of options passed to MongoDB queries,
5
+ # such as skip, limit, and sorting criteria.
6
+ class Options < Smash
7
+
8
+ # Store the value in the options for the provided key. The options will
9
+ # handle all necessary serialization and localization in this step.
10
+ #
11
+ # @example Store a value in the options.
12
+ # options.store(:key, "testing")
13
+ #
14
+ # @param [ String, Symbol ] key The name of the attribute.
15
+ # @param [ Object ] value The value to add.
16
+ #
17
+ # @return [ Object ] The stored object.
18
+ #
19
+ # @since 1.0.0
20
+ def store(key, value)
21
+ super(key, evolve(value))
22
+ end
23
+ alias :[]= :store
24
+
25
+ private
26
+
27
+ # Evolve a single key selection with various types of values.
28
+ #
29
+ # @api private
30
+ #
31
+ # @example Evolve a simple selection.
32
+ # options.evolve(field, 5)
33
+ #
34
+ # @param [ Object ] value The value to serialize.
35
+ #
36
+ # @return [ Object ] The serialized object.
37
+ #
38
+ # @since 1.0.0
39
+ def evolve(value)
40
+ case value
41
+ when Hash
42
+ evolve_hash(value)
43
+ else
44
+ value
45
+ end
46
+ end
47
+
48
+ # Evolve a single key selection with hash values.
49
+ #
50
+ # @api private
51
+ #
52
+ # @example Evolve a simple selection.
53
+ # options.evolve(field, { "$gt" => 5 })
54
+ #
55
+ # @param [ Hash ] value The hash to serialize.
56
+ #
57
+ # @return [ Object ] The serialized hash.
58
+ #
59
+ # @since 1.0.0
60
+ def evolve_hash(value)
61
+ value.inject({}) do |hash, (field, _value)|
62
+ name, serializer = storage_pair(field)
63
+ hash[normalized_key(name, serializer)] = _value
64
+ hash
65
+ end
66
+ end
4
67
  end
5
68
  end
@@ -1,31 +1,74 @@
1
1
  # encoding: utf-8
2
- require "origin/ext"
2
+ require "origin/extensions"
3
+ require "origin/key"
4
+ require "origin/macroable"
5
+ require "origin/mergeable"
6
+ require "origin/smash"
7
+ require "origin/optional"
8
+ require "origin/options"
9
+ require "origin/selectable"
10
+ require "origin/selector"
3
11
 
4
12
  module Origin
5
13
 
6
- autoload :Optional, "origin/optional"
7
- autoload :Options, "origin/options"
8
- autoload :Selection, "origin/selection"
9
- autoload :Selector, "origin/selector"
10
-
14
+ # A queryable is any object that needs origin's dsl injected into it to build
15
+ # MongoDB queries. For example, a Mongoid::Criteria is an Origin::Queryable.
16
+ #
17
+ # @example Include queryable functionality.
18
+ # class Criteria
19
+ # include Origin::Queryable
20
+ # end
11
21
  module Queryable
22
+ include Selectable
12
23
  include Optional
13
- include Selection
14
24
 
15
- attr_reader :options, :selector
25
+ # @attribute [r] aliases The aliases.
26
+ # @attribute [r] serializers The serializers.
27
+ attr_reader :aliases, :serializers
16
28
 
29
+ # Is this queryable equal to another object? Is true if the selector and
30
+ # options are equal.
31
+ #
32
+ # @example Are the objects equal?
33
+ # queryable == criteria
34
+ #
35
+ # @param [ Object ] other The object to compare against.
36
+ #
37
+ # @return [ true, false ] If the objects are equal.
38
+ #
39
+ # @since 1.0.0
17
40
  def ==(other)
18
- return false unless other.is_a?(Query)
41
+ return false unless other.is_a?(Queryable)
19
42
  selector == other.selector && options == other.options
20
43
  end
21
44
 
22
- def initialize
23
- @options, @selector = Options.new, Selector.new
45
+ # Initialize the new queryable. Will yield itself to the block if a block
46
+ # is provided for objects that need additional behaviour.
47
+ #
48
+ # @example Initialize the queryable.
49
+ # Origin::Queryable.new
50
+ #
51
+ # @param [ Hash ] serializers The optional field serializers.
52
+ #
53
+ # @since 1.0.0
54
+ def initialize(aliases = {}, serializers = {})
55
+ @aliases, @serializers = aliases, serializers
56
+ @options, @selector =
57
+ Options.new(aliases, serializers), Selector.new(aliases, serializers)
24
58
  yield(self) if block_given?
25
59
  end
26
60
 
61
+ # Handle the creation of a copy via #clone or #dup.
62
+ #
63
+ # @example Handle copy initialization.
64
+ # queryable.initialize_copy(criteria)
65
+ #
66
+ # @param [ Queryable ] other The original copy.
67
+ #
68
+ # @since 1.0.0
27
69
  def initialize_copy(other)
28
- @options, @selector = other.options.deep_copy, other.selector.deep_copy
70
+ @options = other.options.__deep_copy__
71
+ @selector = other.selector.__deep_copy__
29
72
  end
30
73
  end
31
74
  end
@@ -0,0 +1,613 @@
1
+ # encoding: utf-8
2
+ module Origin
3
+
4
+ # An origin queryable is selectable, in that it has the ability to select
5
+ # document from the database. The selectable module brings all functionality
6
+ # to the queryable that has to do with building MongoDB selectors.
7
+ module Selectable
8
+ include Mergeable
9
+ extend Macroable
10
+
11
+ # @attribute [rw] selector The query selector.
12
+ attr_accessor :selector
13
+
14
+ # Add the $all criterion.
15
+ #
16
+ # @example Add the criterion.
17
+ # queryable.all(field: [ 1, 2 ])
18
+ #
19
+ # @example Execute an $all in a where query.
20
+ # queryable.where(:field.all => [ 1, 2 ])
21
+ #
22
+ # @param [ Hash ] criterion The key value pairs for $all matching.
23
+ #
24
+ # @return [ Selectable ] The cloned queryable.
25
+ #
26
+ # @since 1.0.0
27
+ def all(criterion = nil)
28
+ send(strategy || :__union__, with_array_values(criterion), "$all")
29
+ end
30
+ alias :all_in :all
31
+ key :all, :union, "$all"
32
+
33
+ # Add the $and criterion.
34
+ #
35
+ # @example Add the criterion.
36
+ # queryable.and({ field: value }, { other: value })
37
+ #
38
+ # @param [ Array<Hash> ] criterion Multiple key/value pair matches that
39
+ # all must match to return results.
40
+ #
41
+ # @return [ Selectable ] The cloned queryable.
42
+ #
43
+ # @since 1.0.0
44
+ def and(*criterion)
45
+ __multi__(criterion, "$and")
46
+ end
47
+ alias :all_of :and
48
+
49
+ # Add the range selection.
50
+ #
51
+ # @example Match on results within a single range.
52
+ # queryable.between(field: 1..2)
53
+ #
54
+ # @example Match on results between multiple ranges.
55
+ # queryable.between(field: 1..2, other: 5..7)
56
+ #
57
+ # @param [ Hash ] criterion Multiple key/range pairs.
58
+ #
59
+ # @return [ Selectable ] The cloned queryable.
60
+ #
61
+ # @since 1.0.0
62
+ def between(criterion = nil)
63
+ selection(criterion) do |selector, field, value|
64
+ selector.store(
65
+ field,
66
+ { "$gte" => value.min, "$lte" => value.max }
67
+ )
68
+ end
69
+ end
70
+
71
+ # Select with an $elemMatch.
72
+ #
73
+ # @example Add criterion for a single match.
74
+ # queryable.elem_match(field: { name: "value" })
75
+ #
76
+ # @example Add criterion for multiple matches.
77
+ # queryable.elem_match(
78
+ # field: { name: "value" },
79
+ # other: { name: "value"}
80
+ # )
81
+ #
82
+ # @example Execute an $elemMatch in a where query.
83
+ # queryable.where(:field.elem_match => { name: "value" })
84
+ #
85
+ # @param [ Hash ] criterion The field/match pairs.
86
+ #
87
+ # @return [ Selectable ] The cloned queryable.
88
+ #
89
+ # @since 1.0.0
90
+ def elem_match(criterion = nil)
91
+ __override__(criterion, "$elemMatch")
92
+ end
93
+ key :elem_match, :override, "$elemMatch"
94
+
95
+ # Add the $exists selection.
96
+ #
97
+ # @example Add a single selection.
98
+ # queryable.exists(field: true)
99
+ #
100
+ # @example Add multiple selections.
101
+ # queryable.exists(field: true, other: false)
102
+ #
103
+ # @example Execute an $exists in a where query.
104
+ # queryable.where(:field.exists => true)
105
+ #
106
+ # @param [ Hash ] criterion The field/boolean existence checks.
107
+ #
108
+ # @return [ Selectable ] The cloned queryable.
109
+ #
110
+ # @since 1.0.0
111
+ def exists(criterion = nil)
112
+ typed_override(criterion, "$exists") do |value|
113
+ ::Boolean.evolve(value)
114
+ end
115
+ end
116
+ key :exists, :override, "$exists" do |value|
117
+ ::Boolean.evolve(value)
118
+ end
119
+
120
+ # Add the $gt criterion to the selector.
121
+ #
122
+ # @example Add the $gt criterion.
123
+ # queryable.gt(age: 60)
124
+ #
125
+ # @example Execute an $gt in a where query.
126
+ # queryable.where(:field.gt => 10)
127
+ #
128
+ # @param [ Hash ] criterion The field/value pairs to check.
129
+ #
130
+ # @return [ Selectable ] The cloned queryable.
131
+ #
132
+ # @since 1.0.0
133
+ def gt(criterion = nil)
134
+ __override__(criterion, "$gt")
135
+ end
136
+ key :gt, :override, "$gt"
137
+
138
+ # Add the $gte criterion to the selector.
139
+ #
140
+ # @example Add the $gte criterion.
141
+ # queryable.gte(age: 60)
142
+ #
143
+ # @example Execute an $gte in a where query.
144
+ # queryable.where(:field.gte => 10)
145
+ #
146
+ # @param [ Hash ] criterion The field/value pairs to check.
147
+ #
148
+ # @return [ Selectable ] The cloned queryable.
149
+ #
150
+ # @since 1.0.0
151
+ def gte(criterion = nil)
152
+ __override__(criterion, "$gte")
153
+ end
154
+ key :gte, :override, "$gte"
155
+
156
+ # Adds the $in selection to the queryable.
157
+ #
158
+ # @example Add $in selection on an array.
159
+ # queryable.in(age: [ 1, 2, 3 ])
160
+ #
161
+ # @example Add $in selection on a range.
162
+ # queryable.in(age: 18..24)
163
+ #
164
+ # @example Execute an $in in a where query.
165
+ # queryable.where(:field.in => [ 1, 2, 3 ])
166
+ #
167
+ # @param [ Hash ] criterion The field/value criterion pairs.
168
+ #
169
+ # @return [ Selectable ] The cloned queryable.
170
+ #
171
+ # @since 1.0.0
172
+ def in(criterion = nil)
173
+ send(strategy || :__intersect__, with_array_values(criterion), "$in")
174
+ end
175
+ alias :any_in :in
176
+ key :in, :intersect, "$in"
177
+
178
+ # Add the $lt criterion to the selector.
179
+ #
180
+ # @example Add the $lt criterion.
181
+ # queryable.lt(age: 60)
182
+ #
183
+ # @example Execute an $lt in a where query.
184
+ # queryable.where(:field.lt => 10)
185
+ #
186
+ # @param [ Hash ] criterion The field/value pairs to check.
187
+ #
188
+ # @return [ Selectable ] The cloned queryable.
189
+ #
190
+ # @since 1.0.0
191
+ def lt(criterion = nil)
192
+ __override__(criterion, "$lt")
193
+ end
194
+ key :lt, :override, "$lt"
195
+
196
+ # Add the $lte criterion to the selector.
197
+ #
198
+ # @example Add the $lte criterion.
199
+ # queryable.lte(age: 60)
200
+ #
201
+ # @example Execute an $lte in a where query.
202
+ # queryable.where(:field.lte => 10)
203
+ #
204
+ # @param [ Hash ] criterion The field/value pairs to check.
205
+ #
206
+ # @return [ Selectable ] The cloned queryable.
207
+ #
208
+ # @since 1.0.0
209
+ def lte(criterion = nil)
210
+ __override__(criterion, "$lte")
211
+ end
212
+ key :lte, :override, "$lte"
213
+
214
+ # Add a $maxDistance selection to the queryable.
215
+ #
216
+ # @example Add the $maxDistance selection.
217
+ # queryable.max_distance(location: 10)
218
+ #
219
+ # @param [ Hash ] criterion The field/distance pairs.
220
+ #
221
+ # @return [ Selectable ] The cloned queryable.
222
+ #
223
+ # @since 1.0.0
224
+ def max_distance(criterion = nil)
225
+ __add__(criterion, "$maxDistance")
226
+ end
227
+
228
+ # Adds $mod selection to the queryable.
229
+ #
230
+ # @example Add the $mod selection.
231
+ # queryable.mod(field: [ 10, 1 ])
232
+ #
233
+ # @example Execute an $mod in a where query.
234
+ # queryable.where(:field.mod => [ 10, 1 ])
235
+ #
236
+ # @param [ Hash ] criterion The field/mod selections.
237
+ #
238
+ # @return [ Selectable ] The cloned queryable.
239
+ #
240
+ # @since 1.0.0
241
+ def mod(criterion = nil)
242
+ __override__(criterion, "$mod")
243
+ end
244
+ key :mod, :override, "$mod"
245
+
246
+ # Adds $ne selection to the queryable.
247
+ #
248
+ # @example Query for a value $ne to something.
249
+ # queryable.ne(field: 10)
250
+ #
251
+ # @example Execute an $ne in a where query.
252
+ # queryable.where(:field.ne => "value")
253
+ #
254
+ # @param [ Hash ] criterion The field/ne selections.
255
+ #
256
+ # @return [ Selectable ] The cloned queryable.
257
+ #
258
+ # @since 1.0.0
259
+ def ne(criterion = nil)
260
+ __override__(criterion, "$ne")
261
+ end
262
+ alias :excludes :ne
263
+ key :ne, :override, "$ne"
264
+
265
+ # Adds a $near criterion to a geo selection.
266
+ #
267
+ # @example Add the $near selection.
268
+ # queryable.near(location: [ 23.1, 12.1 ])
269
+ #
270
+ # @example Execute an $near in a where query.
271
+ # queryable.where(:field.near => [ 23.2, 12.1 ])
272
+ #
273
+ # @param [ Hash ] criterion The field/location pair.
274
+ #
275
+ # @return [ Selectable ] The cloned queryable.
276
+ #
277
+ # @since 1.0.0
278
+ def near(criterion = nil)
279
+ __override__(criterion, "$near")
280
+ end
281
+ key :near, :override, "$near"
282
+
283
+ # Adds a $nearSphere criterion to a geo selection.
284
+ #
285
+ # @example Add the $nearSphere selection.
286
+ # queryable.near_sphere(location: [ 23.1, 12.1 ])
287
+ #
288
+ # @example Execute an $nearSphere in a where query.
289
+ # queryable.where(:field.near_sphere => [ 10.11, 3.22 ])
290
+ #
291
+ # @param [ Hash ] criterion The field/location pair.
292
+ #
293
+ # @return [ Selectable ] The cloned queryable.
294
+ #
295
+ # @since 1.0.0
296
+ def near_sphere(criterion = nil)
297
+ __override__(criterion, "$nearSphere")
298
+ end
299
+ key :near_sphere, :override, "$nearSphere"
300
+
301
+ # Adds the $nin selection to the queryable.
302
+ #
303
+ # @example Add $nin selection on an array.
304
+ # queryable.nin(age: [ 1, 2, 3 ])
305
+ #
306
+ # @example Add $nin selection on a range.
307
+ # queryable.nin(age: 18..24)
308
+ #
309
+ # @example Execute an $nin in a where query.
310
+ # queryable.where(:field.nin => [ 1, 2, 3 ])
311
+ #
312
+ # @param [ Hash ] criterion The field/value criterion pairs.
313
+ #
314
+ # @return [ Selectable ] The cloned queryable.
315
+ #
316
+ # @since 1.0.0
317
+ def nin(criterion = nil)
318
+ send(strategy || :__intersect__, with_array_values(criterion), "$nin")
319
+ end
320
+ alias :not_in :nin
321
+ key :nin, :intersect, "$nin"
322
+
323
+ # Adds $nor selection to the queryable.
324
+ #
325
+ # @example Add the $nor selection.
326
+ # queryable.nor(field: 1, field: 2)
327
+ #
328
+ # @param [ Array ] criterion An array of hash criterion.
329
+ #
330
+ # @return [ Selectable ] The cloned queryable.
331
+ #
332
+ # @since 1.0.0
333
+ def nor(*criterion)
334
+ __multi__(criterion, "$nor")
335
+ end
336
+
337
+ # Adds $or selection to the queryable.
338
+ #
339
+ # @example Add the $or selection.
340
+ # queryable.or(field: 1, field: 2)
341
+ #
342
+ # @param [ Array ] criterion An array of hash criterion.
343
+ #
344
+ # @return [ Selectable ] The cloned queryable.
345
+ #
346
+ # @since 1.0.0
347
+ def or(*criterion)
348
+ __multi__(criterion, "$or")
349
+ end
350
+ alias :any_of :or
351
+
352
+ # Add a $size selection for array fields.
353
+ #
354
+ # @example Add the $size selection.
355
+ # queryable.with_size(field: 5)
356
+ #
357
+ # @note This method is named #with_size not to conflict with any existing
358
+ # #size method on enumerables or symbols.
359
+ #
360
+ # @example Execute an $size in a where query.
361
+ # queryable.where(:field.with_size => 10)
362
+ #
363
+ # @param [ Hash ] criterion The field/size pairs criterion.
364
+ #
365
+ # @return [ Selectable ] The cloned queryable.
366
+ #
367
+ # @since 1.0.0
368
+ def with_size(criterion = nil)
369
+ typed_override(criterion, "$size") do |value|
370
+ ::Integer.evolve(value)
371
+ end
372
+ end
373
+ key :with_size, :override, "$size" do |value|
374
+ ::Integer.evolve(value)
375
+ end
376
+
377
+ # Adds a $type selection to the queryable.
378
+ #
379
+ # @example Add the $type selection.
380
+ # queryable.with_type(field: 15)
381
+ #
382
+ # @example Execute an $type in a where query.
383
+ # queryable.where(:field.with_type => 15)
384
+ #
385
+ # @note http://vurl.me/PGOU contains a list of all types.
386
+ #
387
+ # @param [ Hash ] criterion The field/type pairs.
388
+ #
389
+ # @return [ Selectable ] The cloned queryable.
390
+ #
391
+ # @since 1.0.0
392
+ def with_type(criterion = nil)
393
+ typed_override(criterion, "$type") do |value|
394
+ ::Integer.evolve(value)
395
+ end
396
+ end
397
+ key :with_type, :override, "$type" do |value|
398
+ ::Integer.evolve(value)
399
+ end
400
+
401
+ # This is the general entry point for most MongoDB queries. This either
402
+ # creates a standard field: value selection, and expanded selection with
403
+ # the use of hash methods, or a $where selection if a string is provided.
404
+ #
405
+ # @example Add a standard selection.
406
+ # queryable.where(name: "syd")
407
+ #
408
+ # @example Add a javascript selection.
409
+ # queryable.where("this.name == 'syd'")
410
+ #
411
+ # @param [ String, Hash ] criterion The javascript or standard selection.
412
+ #
413
+ # @return [ Selectable ] The cloned queryable.
414
+ #
415
+ # @since 1.0.0
416
+ def where(criterion = nil)
417
+ criterion.is_a?(String) ? js_query(criterion) : expr_query(criterion)
418
+ end
419
+
420
+ # Adds the $within/$box selection to the queryable.
421
+ #
422
+ # @example Add the selection.
423
+ # queryable.within_box(location: [[ 1, 10 ], [ 10, 1 ]])
424
+ #
425
+ # @example Execute an $within/$box in a where query.
426
+ # queryable.where(:field.within_box => [[ 1, 10 ], [ 10, 1 ]])
427
+ #
428
+ # @param [ Hash ] criterion The field/box corner criterion.
429
+ #
430
+ # @return [ Selectable ] The cloned queryable.
431
+ #
432
+ # @since 1.0.0
433
+ def within_box(criterion = nil)
434
+ __expanded__(criterion, "$within", "$box")
435
+ end
436
+ key :within_box, :expanded, "$within", "$box"
437
+
438
+ # Adds the $within/$center selection to the queryable.
439
+ #
440
+ # @example Add the selection.
441
+ # queryable.within_circle(location: [[ 1, 10 ], 25 ])
442
+ #
443
+ # @example Execute an $within/$center in a where query.
444
+ # queryable.where(:field.within_circle => [[ 1, 10 ], 25 ])
445
+ #
446
+ # @param [ Hash ] criterion The field/radius criterion.
447
+ #
448
+ # @return [ Selectable ] The cloned queryable.
449
+ #
450
+ # @since 1.0.0
451
+ def within_circle(criterion = nil)
452
+ __expanded__(criterion, "$within", "$center")
453
+ end
454
+ key :within_circle, :expanded, "$within", "$center"
455
+
456
+ # Adds the $within/$polygon selection to the queryable.
457
+ #
458
+ # @example Add the selection.
459
+ # queryable.within_polygon(
460
+ # location: [[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]
461
+ # )
462
+ #
463
+ # @example Execute an $within/$polygon in a where query.
464
+ # queryable.where(
465
+ # :field.within_polygon => [[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]
466
+ # )
467
+ #
468
+ # @param [ Hash ] criterion The field/polygon points criterion.
469
+ #
470
+ # @return [ Selectable ] The cloned queryable.
471
+ #
472
+ # @since 1.0.0
473
+ def within_polygon(criterion = nil)
474
+ __expanded__(criterion, "$within", "$polygon")
475
+ end
476
+ key :within_polygon, :expanded, "$within", "$polygon"
477
+
478
+ # Adds the $within/$centerSphere selection to the queryable.
479
+ #
480
+ # @example Add the selection.
481
+ # queryable.within_spherical_circle(location: [[ 1, 10 ], 25 ])
482
+ #
483
+ # @example Execute an $within/$centerSphere in a where query.
484
+ # queryable.where(:field.within_spherical_circle => [[ 1, 10 ], 25 ])
485
+ #
486
+ # @param [ Hash ] criterion The field/distance criterion.
487
+ #
488
+ # @return [ Selectable ] The cloned queryable.
489
+ #
490
+ # @since 1.0.0
491
+ def within_spherical_circle(criterion = nil)
492
+ __expanded__(criterion, "$within", "$centerSphere")
493
+ end
494
+ key :within_spherical_circle, :expanded, "$within", "$centerSphere"
495
+
496
+ private
497
+
498
+ # Create the standard expression query.
499
+ #
500
+ # @api private
501
+ #
502
+ # @example Create the selection.
503
+ # queryable.expr_query(age: 50)
504
+ #
505
+ # @param [ Hash ] criterion The field/value pairs.
506
+ #
507
+ # @return [ Selectable ] The cloned queryable.
508
+ #
509
+ # @since 1.0.0
510
+ def expr_query(criterion)
511
+ selection(criterion) do |selector, field, value|
512
+ selector.merge!(field.specify(value))
513
+ end
514
+ end
515
+
516
+ # Force the values of the criterion to be evolved.
517
+ #
518
+ # @api private
519
+ #
520
+ # @example Force values to booleans.
521
+ # queryable.force_typing(criterion) do |val|
522
+ # Boolean.evolve(val)
523
+ # end
524
+ #
525
+ # @param [ Hash ] criterion The criterion.
526
+ #
527
+ # @since 1.0.0
528
+ def typed_override(criterion, operator)
529
+ if criterion
530
+ criterion.update_values do |value|
531
+ yield(value)
532
+ end
533
+ end
534
+ __override__(criterion, operator)
535
+ end
536
+
537
+ # Create a javascript selection.
538
+ #
539
+ # @api private
540
+ #
541
+ # @example Create the javascript selection.
542
+ # queryable.js_query("this.age == 50")
543
+ #
544
+ # @param [ String ] criterion The javascript as a string.
545
+ #
546
+ # @return [ Selectable ] The cloned queryable
547
+ #
548
+ # @since 1.0.0
549
+ def js_query(criterion)
550
+ clone.tap do |query|
551
+ query.selector.merge!("$where" => criterion)
552
+ end
553
+ end
554
+
555
+ # Take the provided criterion and store it as a selection in the query
556
+ # selector.
557
+ #
558
+ # @api private
559
+ #
560
+ # @example Store the selection.
561
+ # selectable.selection({ field: "value" })
562
+ #
563
+ # @param [ Hash ] criterion The selection to store.
564
+ #
565
+ # @return [ Selectable ] The cloned queryable.
566
+ #
567
+ # @since 1.0.0
568
+ def selection(criterion = nil)
569
+ clone.tap do |query|
570
+ if criterion
571
+ criterion.each_pair do |field, value|
572
+ yield(query.selector, field.is_a?(Key) ? field : field.to_s, value)
573
+ end
574
+ end
575
+ end
576
+ end
577
+
578
+ # Convert the criterion values to $in friendly values. This means you,
579
+ # array.
580
+ #
581
+ # @api private
582
+ #
583
+ # @example Convert all the values to arrays.
584
+ # queryable.with_array_values({ key: 1...4 })
585
+ #
586
+ # @param [ Hash ] criterion The criterion.
587
+ #
588
+ # @return [ Hash ] The $in friendly criterion (array values).
589
+ #
590
+ # @since 1.0.0
591
+ def with_array_values(criterion)
592
+ return nil unless criterion
593
+ criterion.each_pair do |key, value|
594
+ criterion[key] = value.__array__
595
+ end
596
+ end
597
+
598
+ class << self
599
+
600
+ # Get the methods on the selectable that can be forwarded to from a model.
601
+ #
602
+ # @example Get the forwardable methods.
603
+ # Selectable.forwardables
604
+ #
605
+ # @return [ Array<Symbol> ] The names of the forwardable methods.
606
+ #
607
+ # @since 1.0.0
608
+ def forwardables
609
+ public_instance_methods(false) - [ :selector, :selector= ]
610
+ end
611
+ end
612
+ end
613
+ end