orchestrate 0.10.0 → 0.11.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f7d5c6bc09a49e08d464387e25b98ae9777bbf93
4
- data.tar.gz: 72a68c6e456c4920c9875e7e3f7108b29bd59d12
3
+ metadata.gz: 052b46ffb077d9256c3626b2d2fe1d6618c0f3c9
4
+ data.tar.gz: 34375247e7fc3ce08d2f836df6e0df7f778273f7
5
5
  SHA512:
6
- metadata.gz: a8a2a5f7124cd557dc4a3ed3a4fc9d71064494e8241ae87409c8fcfd2fe40236eaebd0171e54c5c2ab22bcba03a1e33bb8a81171681a205edb4c153f747ef2a9
7
- data.tar.gz: 753527cab83aac5537fd178ac976cf704f6c173c63b0b64a2f9c9fdd859c30d18e6d8cd34cb92bcc4b6bb2aa3643e66f47918193d561c65e88b2f591b82f802e
6
+ metadata.gz: 99d0edff2baf770cf3c2173c9245bb42957dee1a92c1d4abca2c24bc8b510569bf6bda09159c2761bbd64d9631cdfd1d2273166597dccb11a12f85c1a110fb3d
7
+ data.tar.gz: 6afa7091564272f7f22d0b4d173b03cd76815a63992876aaccadd3b1c08ab11a38a6c20e884e2feafcf8e339651f0bf0b623339927de6a7e6b1e8c5fd27f39a3
data/README.md CHANGED
@@ -65,20 +65,20 @@ jill.add('favorite_food', 'Pizza').remove('years_to_live').update()
65
65
 
66
66
  #### Searching, Sorting for KeyValues
67
67
  ```ruby
68
- users.search("name:Jill") # returns users with name "Jill"
69
- users.search("name:Jill").order(:created_at) # returns users with name "Jill" in ascending order
68
+ users.search("name:Jill").find # returns users with name "Jill"
69
+ users.search("name:Jill").order(:created_at).find # returns users with name "Jill" in ascending order
70
70
  ```
71
71
 
72
72
  The `order` method accepts multiple arguments, allowing you to sort search results based multiple parameters. When providing multiple field names to sort by each even-numbered argument must be either `:asc` or `:desc`.
73
73
 
74
74
  ```ruby
75
- users.search("location: Portland").order(:name, :asc, :rank, :desc)
75
+ users.search("location: Portland").order(:name, :asc, :rank, :desc).find
76
76
  ```
77
77
 
78
78
  By default, odd-numbered arguments will be sorted in ascending order.
79
79
  ```ruby
80
- users.search("location: Portland").order(:name) # returns users in ascending order by name
81
- users.search("location: Portland").order(:name, :asc, :rank, :desc, :created_at) # :created_at argument defaults to :asc
80
+ users.search("location: Portland").order(:name).find # returns users in ascending order by name
81
+ users.search("location: Portland").order(:name, :asc, :rank, :desc, :created_at).find # :created_at argument defaults to :asc
82
82
  ```
83
83
 
84
84
  ### Geo Queries
@@ -87,13 +87,56 @@ users.search("location: Portland").order(:name, :asc, :rank, :desc, :created_at)
87
87
  cafes = app[:cafes]
88
88
 
89
89
  # Find cafes near a given geographic point
90
- cafes.near(:location, 12.56, 19.443, 4, 'mi') # returns cafes in a 4 mile radius of given latitude, longitude
90
+ cafes.near(:location, 12.56, 19.443, 4, 'mi').find # returns cafes in a 4 mile radius of given latitude, longitude
91
91
 
92
92
  # Sort nearby cafes by distance
93
- cafes.near(:location, 12.56, 19.443, 4, 'mi').order(:distance) # returns nearby cafes in ascending order (closest to farthest)
93
+ cafes.near(:location, 12.56, 19.443, 4, 'mi').order(:distance).find # returns nearby cafes in ascending order (closest to farthest)
94
94
 
95
95
  # Find cafes in a given area using a bounding box
96
- cafes.in(:location, {north:12.5, east:57, south:12, west:56}) # returns all cafes within specified bounding box
96
+ cafes.in(:location, {north:12.5, east:57, south:12, west:56}).find # returns all cafes within specified bounding box
97
+ ```
98
+
99
+ ### Aggregate Functions
100
+
101
+ ```ruby
102
+ # Statistical Aggregate
103
+ products.search("*").aggregate # Start the search query and aggregate param builder
104
+ .stats("price") # statistics on the price field for products matching the query
105
+ .find # return SearchResults object to execute our query
106
+ .each_aggregate # return enumerator for iterating over each aggregate result
107
+
108
+ # Range Aggregate
109
+ products.search("*").aggregate # Start the search query and aggregate param builder
110
+ .range("num_sold") # set field for range function
111
+ .below(99) # count items with num_sold value below 99
112
+ .between(1, 10) # count items with num_sold value between 1 & 10
113
+ .above(5) # count items with num_sold value above 5
114
+ .find # return SearchResults object to execute our query
115
+ .each_aggregate # return enumerator for iterating over each aggregate result
116
+
117
+ # Distance Aggregate
118
+ cafes.near(:location, 12, 19, 4, 'mi').aggregate # Start the near search query and aggregate param builder
119
+ .distance("location") # set field for distance function
120
+ .below(3) # count cafes within 3 miles of given geographic point
121
+ .between(3, 4) # count cafes between 3 and 4 miles of given geographic point
122
+ .above(1) # count cafes beyond 1 mile of given geographic point
123
+ .find # return SearchResults object to execute our query
124
+ .each_aggregate # return enumerator for iterating over each aggregate result
125
+
126
+ # Time-Series Aggregate
127
+ # Accepted intervals are: year, quarter, month, week, day, and hour
128
+ comments.search("*").aggregate # Start the near search query and aggregate param builder
129
+ .time_series("posted", "day") # get count of comments posted by day
130
+ .find # return SearchResults object to execute our query
131
+ .each_aggregate # return enumerator for iterating over each aggregate result
132
+
133
+ # Multiple Aggregate Functions
134
+ products.search("*").aggregate # Start the search query and aggregate param builder
135
+ .stats("price") # statistics on the price field for products matching the query
136
+ .range("num_sold") # set field for range function
137
+ .below(99) # count items with num_sold value below 99
138
+ .find # return SearchResults object to execute our query
139
+ .each_aggregate # return enumerator for iterating over each aggregate result
97
140
  ```
98
141
 
99
142
  ### Method Client use
@@ -173,6 +216,82 @@ query = "value:IN:{ north:12.5 east:57 south:12 west:56 }"
173
216
  client.search(:cafes, query)
174
217
  ```
175
218
 
219
+ ### Aggregate Functions
220
+
221
+ ```ruby
222
+ # Statistical Aggregate
223
+ query = "*"
224
+
225
+ options = {
226
+ aggregate: "value.price:stats" # get statistics for price across all items in the collection
227
+ }
228
+
229
+ response = client.search(:products, query, options)
230
+
231
+ response.aggregates # return aggregate results
232
+
233
+
234
+ # Range Aggregate
235
+ query = "*"
236
+
237
+ options = {
238
+ # count items with num_sold below 99, in between 1 & 10, and above 5
239
+ aggregate: "value.num_sold:range:*~99:1~10:5~*"
240
+ }
241
+
242
+ response = client.search(:products, query, options)
243
+
244
+ response.aggregates # return aggregate results
245
+
246
+
247
+ # Distance Aggregate
248
+ coords = {
249
+ lat: 12.56,
250
+ lon: 19.443,
251
+ dist: '4mi' # Define size of search radius for NEAR query
252
+ }
253
+
254
+ options = {
255
+ # count cafes near give geographic point within 3 miles, between 3 and 4 miles, and beyond 1 mile
256
+ aggregate: "value.location:distance:*~3:3~4:1~*"
257
+ }
258
+
259
+ # Distance Aggregates require a near clause in the search query
260
+ query = "value.location:NEAR:{lat:#{coords.lat} lon:#{coords.lon} dist:#{coords.dist}}"
261
+
262
+ response = client.search(:cafes, query, options)
263
+
264
+ response.aggregates # return aggregate results
265
+
266
+
267
+ # Time-Series Aggregate
268
+ # Accepted intervals are: year, quarter, month, week, day, and hour
269
+
270
+ options = {
271
+ # get count of comments posted by day
272
+ aggregate: "value.posted:time_series:day"
273
+ }
274
+
275
+ query = "*"
276
+
277
+ response = client.search(:comments, query, options)
278
+
279
+ response.aggregates # return aggregate results
280
+
281
+
282
+ # Multiple Aggregate Functions
283
+ options = {
284
+ # multiple aggregate params are separated by commas
285
+ aggregate: "value.price:stats,value.num_sold:stats,value.num_sold:range:*~99:1~10:5~*"
286
+ }
287
+
288
+ query = "*"
289
+
290
+ response = client.search(:products, query, options)
291
+
292
+ response.aggregates # return aggregate results
293
+ ```
294
+
176
295
  ### Examples and Documentation
177
296
 
178
297
  There are more examples at [Orchestrate's API Documentation][apidoc] and documentation in the [rdoc][].
@@ -277,10 +396,19 @@ end
277
396
 
278
397
  ## Release Notes
279
398
 
399
+ ### January 7, 2014: release 0.11.0
400
+ - **BACKWARDS-INCOMPATIBLE** `Orchestrate::Collection` searches require `#find` method at the end of the method call/chain. Example: `users.search('foo').find`.
401
+ - Implement `Orchestrate::Search` module, refactor functionality of prior `Orchestrate::Collection::SearchResults`.
402
+ - Implement results enumeration & request firing functionality in prior `Orchestrate::Collection::SearchResults` to `Orchestrate::Search::Results`
403
+ - Implement `Search::QueryBuilder` to construct `Collection` search queries.
404
+ - Implement `Search::AggregateBuilder` to construct aggregate params on `Collection` search queries.
405
+ - Implement `Search::StatsBuilder`, `Search::RangeBuilder`, `Search::DistanceBuilder`, & `Search::TimeSeriesBuilder` to construct aggregate function clauses for aggregate params.
406
+ - Implement `Search::AggregateResult` objects to repesent aggregate results returned from `Collection` search.
407
+
280
408
  ### December 11, 2014: release 0.10.0
281
409
  - **BACKWARDS-INCOMPATIBLE** Prior `KeyValue#update` & `KeyValue#update!` renamed to `KeyValue#set` & `KeyValue#set!`. `KeyValue#update` now used after `PATCH` operations to fire the request.
282
410
  - Implement `Collection#near` & `Collection#in`, allowing `Collection` to perform geo queries.
283
- - Implement `Client#patch`, `Client#patch_merge`, allowing `Client` to perform subdocument updates through `PATCH` requests.
411
+ - Implement `Client#patch`, `Client#patch_merge`, allowing `Client` to perform partial updates through `PATCH` requests.
284
412
  - Implement `KeyValue::OperationSet`, allowing a set of `PATCH` operations to be built by `KeyValue` through `KeyValue#add`, `KeyValue#remove`, `KeyValue#replace`, `KeyValue#move`, `KeyValue#copy`, `KeyValue#increment`, `KeyValue#decrement`, & `KeyValue#test`. The `KeyValue::OperationSet` is fired by ending the chain with `KeyValue#update`.
285
413
  - Implement `KeyValue#merge`, allowing `KeyValue` to merge partial values into existing keys through `PATCH` requests.
286
414
 
@@ -346,4 +474,4 @@ end
346
474
  ### May 21, 2014: release 0.5.0
347
475
  Initial Port from @jimcar
348
476
  - Uses Faraday HTTP Library as backend, with examples of alternate adapters
349
- - Cleanup client method signatures
477
+ - Cleanup client method signatures
@@ -109,6 +109,9 @@ module Orchestrate::API
109
109
  # @return [Array] The items in the response.
110
110
  attr_reader :results
111
111
 
112
+ # @return [Array] The aggregate items in the response.
113
+ attr_reader :aggregates
114
+
112
115
  # @return [String] The location for the next page of results
113
116
  attr_reader :next_link
114
117
 
@@ -122,6 +125,7 @@ module Orchestrate::API
122
125
  @count = body['count']
123
126
  @total_count = body['total_count']
124
127
  @results = body['results']
128
+ @aggregates = body['aggregates']
125
129
  @next_link = body['next']
126
130
  @prev_link = body['prev']
127
131
  end
@@ -345,17 +345,13 @@ module Orchestrate
345
345
  end
346
346
  end
347
347
 
348
- # @!group Searching
349
- # [Search the items in a collection](http://orchestrate.io/docs/api/#search) using a Lucene
350
- # Query Syntax.
351
348
  # @param query [#to_s] The [Lucene Query
352
349
  # String](http://lucene.apache.org/core/4_3_0/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#Overview)
353
350
  # to query the collection with.
354
- # @return [SearchResults] A loaded SearchResults object ready to enumerate over.
351
+ # @return [Orchestrate::Search::QueryBuilder] A builder object to construct the query.
355
352
  def search(query)
356
- SearchResults.new(self, query)
353
+ Search::QueryBuilder.new(self, query)
357
354
  end
358
- # @!endgroup
359
355
 
360
356
  # @!group Geo Queries
361
357
  # Performs a search for items near a specified geographic point in a collection.
@@ -366,11 +362,11 @@ module Orchestrate
366
362
  # @param distance [Integer] The number of distance units.
367
363
  # @param units [#to_s] Unit of measurement for distance, default to kilometers (km),
368
364
  # supported units: km, m, cm, mm, mi, yd, ft, in, nmi
369
- # @return [SearchResults] A loaded SearchResults object ready to enumerate over.
365
+ # @return [Orchestrate::Search::QueryBuilder] A builder object to construct the query.
370
366
  def near(field, latitude, longitude, distance, units=nil)
371
- units ||= 'km'
372
- query = "#{field}:NEAR:{lat:#{latitude} lon:#{longitude} dist:#{distance}#{units}}"
373
- SearchResults.new(self, query)
367
+ units ||= 'km'
368
+ query = "#{field}:NEAR:{lat:#{latitude} lon:#{longitude} dist:#{distance}#{units}}"
369
+ Search::QueryBuilder.new(self, query)
374
370
  end
375
371
 
376
372
  # Performs a search for items within a particular area,
@@ -380,112 +376,14 @@ module Orchestrate
380
376
  # @param box [#to_json] The values to create the bounding box,
381
377
  # @example
382
378
  # collection.in(:field, {north: 12.5, south: 15, east: 14, west: 3})
383
- # @return [SearchResults] A loaded SearchResults object ready to enumerate over.
379
+ # @return [Orchestrate::Search::QueryBuilder] A builder object to construct the query.
384
380
  def in(field, box={})
385
381
  box = box.flatten.each_slice(2).map {|dir, val| "#{dir}:#{val}" }.join(" ")
386
382
  query = "#{field}:IN:{#{box}}"
387
- SearchResults.new(self, query)
383
+ Search::QueryBuilder.new(self, query)
388
384
  end
389
385
  # @!endgroup
390
386
 
391
- # An enumerator with a query for searching an Orchestrate::Collection
392
- class SearchResults
393
- # @return [Collection] The collection this object will search.
394
- attr_reader :collection
395
-
396
- # @return [#to_s] The Lucene Query String given as the search query.
397
- attr_reader :query
398
-
399
- # @return [#to_s] The sorting parameters.
400
- attr_reader :sort
401
-
402
- # @return [Integer] The number of results to fetch per request.
403
- attr_reader :limit
404
-
405
- # Initialize a new SearchResults object
406
- # @param collection [Orchestrate::Collection] The collection to search.
407
- # @param query [#to_s] The Lucene Query to perform.
408
- def initialize(collection, query, sort=nil)
409
- @collection = collection
410
- @query = query
411
- @sort = sort
412
- @limit = 100
413
- @offset= nil
414
- end
415
-
416
- # Sets the sorting parameters for enumeration over the SearchResults items in the collection.
417
- # #order takes multiple arguments, but each even numbered argument must be either :asc or :desc
418
- # When given a single argument #order defaults to :asc
419
- # @example
420
- # @app[:items].search("location: Portland").order(:name, :asc, :rank, :desc, :created_at)
421
- # the :created_at parameter will default to :asc
422
- def order(*args)
423
- sort = args.each_slice(2).map {|field, dir| dir ||= :asc; "#{field}:#{dir}" }.join(",")
424
- self.class.new(collection, query, sort)
425
- end
426
-
427
- include Enumerable
428
-
429
- # Iterates over each result from the Search. Used as the basis for Enumerable methods. Items are
430
- # provided on the basis of score, with most relevant first.
431
- # @overload each
432
- # @return Enumerator
433
- # @overload each(&block)
434
- # @yieldparam [Array<(Float, Orchestrate::KeyValue)>] score,key_value The item's score and the item.
435
- # @example
436
- # collection.search("trendy").take(5).each do |score, item|
437
- # puts "#{item.key} has a trend score of #{score}"
438
- # end
439
- def each
440
- params = {limit:limit}
441
- params[:offset] = offset if offset
442
- params[:sort] = sort if sort
443
- @response ||= collection.perform(:search, query, params)
444
- return enum_for(:each) unless block_given?
445
- raise ResultsNotReady.new if collection.app.inside_parallel?
446
- loop do
447
- @response.results.each do |listing|
448
- yield [ listing['score'], KeyValue.from_listing(collection, listing, @response) ]
449
- end
450
- break unless @response.next_link
451
- @response = @response.next_results
452
- end
453
- @response = nil
454
- end
455
-
456
- # Creates a Lazy Enumerator for the Search Results. If called inside its
457
- # app's `in_parallel` block, will pre-fetch results.
458
- def lazy
459
- return each.lazy if collection.app.inside_parallel?
460
- super
461
- end
462
-
463
- # Returns the first n items. Equivalent to Enumerable#take. Sets the
464
- # `limit` parameter on the query to Orchestrate, so we don't ask for more than is needed.
465
- # @param count [Integer] The number of items to limit to.
466
- # @return [Array]
467
- def take(count)
468
- @limit = count > 100 ? 100 : count
469
- super(count)
470
- end
471
-
472
- # Sets the offset for the query to Orchestrate, so you can skip items. Does not fire a request.
473
- # Impelemented as separate method from drop, unlike #take, because take is a more common use case.
474
- # @overload offset
475
- # @return [Integer, nil] The number of items to skip. Nil is equivalent to zero.
476
- # @overload offset(conunt)
477
- # @param count [Integer] The number of items to skip.
478
- # @return [SearchResults] self.
479
- def offset(count=nil)
480
- if count
481
- @offset = count
482
- self
483
- else
484
- @offset
485
- end
486
- end
487
- end
488
-
489
387
  # Tells the Applicaiton to perform a request on its client, automatically
490
388
  # providing the collection name.
491
389
  # @param api_method [Symbol] The method on the client to call.
@@ -0,0 +1,290 @@
1
+ module Orchestrate::Search
2
+ # Aggregate Builder object for constructing aggregate params included in a search
3
+ class AggregateBuilder
4
+
5
+ # @return [QueryBuilder]
6
+ attr_reader :builder
7
+
8
+ # @return [Array] Aggregate param arguments
9
+ attr_reader :aggregates
10
+
11
+ extend Forwardable
12
+
13
+ # Initialize a new AggregateBuilder object
14
+ # @param builder [Orchestrate::Search::SearchBuilder] The Search Builder object
15
+ def initialize(builder)
16
+ @builder = builder
17
+ @aggregates = []
18
+ end
19
+
20
+ def_delegator :@builder, :options
21
+ def_delegator :@builder, :order
22
+ def_delegator :@builder, :limit
23
+ def_delegator :@builder, :offset
24
+ def_delegator :@builder, :aggregate
25
+ def_delegator :@builder, :find
26
+ def_delegator :@builder, :collection
27
+
28
+ # @return Pretty-Printed string representation of the AggregateBuilder object
29
+ def to_s
30
+ "#<Orchestrate::Search::AggregateBuilder collection=#{collection.name} query=#{builder.query} aggregate=#{to_param}>"
31
+ end
32
+ alias :inspect :to_s
33
+
34
+ # @return constructed aggregate string parameter for search query
35
+ def to_param
36
+ aggregates.map {|agg| agg.to_param }.join(',')
37
+ end
38
+
39
+ # @!group Aggregate Functions
40
+
41
+ # @param field_name [#to_s]
42
+ # @return [AggregateBuilder]
43
+ def stats(field_name)
44
+ stat = StatsBuilder.new(self, "#{field_name}")
45
+ aggregates << stat
46
+ stat
47
+ end
48
+
49
+ # @param field_name [#to_s]
50
+ # @return [RangeBuilder]
51
+ def range(field_name)
52
+ rng = RangeBuilder.new(self, "#{field_name}")
53
+ aggregates << rng
54
+ rng
55
+ end
56
+
57
+ # @param field_name [#to_s]
58
+ # @return [DistanceBuilder]
59
+ def distance(field_name)
60
+ dist = DistanceBuilder.new(self, "#{field_name}")
61
+ aggregates << dist
62
+ dist
63
+ end
64
+
65
+ # @param field_name [#to_s]
66
+ # @return [TimeSeriesBuilder]
67
+ def time_series(field_name)
68
+ time = TimeSeriesBuilder.new(self, "#{field_name}")
69
+ aggregates << time
70
+ time
71
+ end
72
+ # @!endgroup
73
+ end
74
+
75
+ # Stats Builder object for constructing stats functions to be included in the aggregate param
76
+ class StatsBuilder
77
+
78
+ # @return [AggregateBuilder]
79
+ attr_reader :builder
80
+
81
+ # @return [#to_s] The field to operate over
82
+ attr_reader :field_name
83
+
84
+ extend Forwardable
85
+
86
+ # Initialize a new RangeBuilder object
87
+ # @param builder [AggregateBuilder] The Aggregate Builder object
88
+ # @param field_name [#to_s]
89
+ def initialize(builder, field_name)
90
+ @builder = builder
91
+ @field_name = field_name
92
+ end
93
+
94
+ def_delegator :@builder, :options
95
+ def_delegator :@builder, :order
96
+ def_delegator :@builder, :limit
97
+ def_delegator :@builder, :offset
98
+ def_delegator :@builder, :aggregate
99
+ def_delegator :@builder, :find
100
+ def_delegator :@builder, :stats
101
+ def_delegator :@builder, :range
102
+ def_delegator :@builder, :distance
103
+ def_delegator :@builder, :time_series
104
+ def_delegator :@builder, :collection
105
+
106
+ # @return Pretty-Printed string representation of the StatsBuilder object
107
+ def to_s
108
+ "#<Orchestrate::Search::StatsBuilder collection=#{collection.name} field_name=#{field_name}>"
109
+ end
110
+ alias :inspect :to_s
111
+
112
+ # @return [#to_s] constructed aggregate string clause
113
+ def to_param
114
+ "#{field_name}:stats"
115
+ end
116
+ end
117
+
118
+ # Range Builder object for constructing range functions to be included in the aggregate param
119
+ class RangeBuilder
120
+
121
+ # @return [AggregateBuilder]
122
+ attr_reader :builder
123
+
124
+ # @return [#to_s] The field to operate over
125
+ attr_reader :field_name
126
+
127
+ # @return [#to_s] The range sets
128
+ attr_reader :ranges
129
+
130
+ extend Forwardable
131
+
132
+ # Initialize a new RangeBuilder object
133
+ # @param builder [AggregateBuilder] The Aggregate Builder object
134
+ # @param field_name [#to_s]
135
+ def initialize(builder, field_name)
136
+ @builder = builder
137
+ @field_name = field_name
138
+ @ranges = ''
139
+ end
140
+
141
+ def_delegator :@builder, :options
142
+ def_delegator :@builder, :order
143
+ def_delegator :@builder, :limit
144
+ def_delegator :@builder, :offset
145
+ def_delegator :@builder, :aggregate
146
+ def_delegator :@builder, :find
147
+ def_delegator :@builder, :stats
148
+ def_delegator :@builder, :range
149
+ def_delegator :@builder, :distance
150
+ def_delegator :@builder, :time_series
151
+ def_delegator :@builder, :collection
152
+
153
+ # @return Pretty-Printed string representation of the RangeBuilder object
154
+ def to_s
155
+ "#<Orchestrate::Search::RangeBuilder collection=#{collection.name} field_name=#{field_name} ranges=#{ranges}>"
156
+ end
157
+ alias :inspect :to_s
158
+
159
+ # @return [#to_s] constructed aggregate string clause
160
+ def to_param
161
+ "#{field_name}:range:#{ranges}"
162
+ end
163
+
164
+ # @param x [Integer]
165
+ # @return [RangeBuilder]
166
+ def below(x)
167
+ @ranges << "*~#{x}:"
168
+ self
169
+ end
170
+
171
+ # @param x [Integer]
172
+ # @return [RangeBuilder]
173
+ def above(x)
174
+ @ranges << "#{x}~*:"
175
+ self
176
+ end
177
+
178
+ # @param x [Integer]
179
+ # @param y [Integer]
180
+ # @return [RangeBuilder]
181
+ def between(x, y)
182
+ @ranges << "#{x}~#{y}:"
183
+ self
184
+ end
185
+ end
186
+
187
+ # Distance Builder object for constructing distance functions for the aggregate param
188
+ class DistanceBuilder < RangeBuilder
189
+
190
+ # @return Pretty-Printed string representation of the DistanceBuilder object
191
+ def to_s
192
+ "#<Orchestrate::Search::DistanceBuilder collection=#{collection.name} field_name=#{field_name} ranges=#{ranges}>"
193
+ end
194
+ alias :inspect :to_s
195
+
196
+ # @return [#to_s] constructed aggregate string clause
197
+ def to_param
198
+ "#{field_name}:distance:#{ranges}"
199
+ end
200
+ end
201
+
202
+ # Time Series Builder object for constructing time series functions for the aggregate param
203
+ class TimeSeriesBuilder
204
+
205
+ # @return [AggregateBuilder]
206
+ attr_reader :builder
207
+
208
+ # @return [#to_s] The field to operate over
209
+ attr_reader :field_name
210
+
211
+ # @return [#to_s] The interval of time for the TimeSeries function
212
+ attr_reader :interval
213
+
214
+ extend Forwardable
215
+
216
+ # Initialize a new TimeSeriesBuilder object
217
+ # @param builder [AggregateBuilder] The Aggregate Builder object
218
+ # @param field_name [#to_s] The field to operate over
219
+ def initialize(builder, field_name)
220
+ @builder = builder
221
+ @field_name = field_name
222
+ @interval = nil
223
+ end
224
+
225
+ def_delegator :@builder, :options
226
+ def_delegator :@builder, :order
227
+ def_delegator :@builder, :limit
228
+ def_delegator :@builder, :offset
229
+ def_delegator :@builder, :aggregate
230
+ def_delegator :@builder, :find
231
+ def_delegator :@builder, :stats
232
+ def_delegator :@builder, :range
233
+ def_delegator :@builder, :distance
234
+ def_delegator :@builder, :time_series
235
+ def_delegator :@builder, :collection
236
+
237
+ # @return Pretty-Printed string representation of the TimeSeriesBuilder object
238
+ def to_s
239
+ "#<Orchestrate::Search::TimeSeriesBuilder collection=#{collection.name} field_name=#{field_name} interval=#{interval}>"
240
+ end
241
+ alias :inspect :to_s
242
+
243
+ # @return [#to_s] constructed aggregate string clause
244
+ def to_param
245
+ "#{field_name}:time_series:#{interval}"
246
+ end
247
+
248
+ # Set time series interval to year
249
+ # @return [AggregateBuilder]
250
+ def year
251
+ @interval = 'year'
252
+ self
253
+ end
254
+
255
+ # Set time series interval to quarter
256
+ # @return [AggregateBuilder]
257
+ def quarter
258
+ @interval = 'quarter'
259
+ self
260
+ end
261
+
262
+ # Set time series interval to month
263
+ # @return [AggregateBuilder]
264
+ def month
265
+ @interval = 'month'
266
+ self
267
+ end
268
+
269
+ # Set time series interval to week
270
+ # @return [AggregateBuilder]
271
+ def week
272
+ @interval = 'week'
273
+ self
274
+ end
275
+
276
+ # Set time series interval to day
277
+ # @return [AggregateBuilder]
278
+ def day
279
+ @interval = 'day'
280
+ self
281
+ end
282
+
283
+ # Set time series interval to hour
284
+ # @return [AggregateBuilder]
285
+ def hour
286
+ @interval = 'hour'
287
+ self
288
+ end
289
+ end
290
+ end