stretchy 0.4.6 → 0.4.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: be619dd65900ce5277436c2431d0c9c7740cd494
4
- data.tar.gz: c7315e692b34f98ec01e413e347746022eabe835
3
+ metadata.gz: 4bb3f7a0f6f14bc73d684b45506a015cd918317c
4
+ data.tar.gz: b19ced09b03b858c73665729c33ccfc28273ef91
5
5
  SHA512:
6
- metadata.gz: c77565e3ef0f69497770aa5a1b9311e153f4080aa414d6f1e981b0bec26c7237a97e6aebce5d392b6e8f311e857a39f6b28cca7c93759bbd0937641d94751e14
7
- data.tar.gz: 78b2637b0f5c5aa946c5c4d23475a95036a811897c16968758982d5cf3310fcb486189323c99e66684f6b932cd5af5995c419d24da6ddef04e0fb6da9b1ad6c3
6
+ metadata.gz: e398d741823c25233f394bc69aba19d1af5b550865a22d0fd58527af37c25ac4c2b60456c4d42babef7432b2c00a4895ba43d3f4755ebe449d15fcaefe33f5d7
7
+ data.tar.gz: e626fedbcd22e07642d14f28e7e97861101c615a893883fcfcd1a91b3abc1bcc742d8b976fb7f17a8470d556f012f50baf426642a6710555f8d2d69896086a3a
data/.editorconfig ADDED
@@ -0,0 +1,12 @@
1
+ # EditorConfig is awesome: http://EditorConfig.org
2
+
3
+ # top-most EditorConfig file
4
+ root = true
5
+
6
+ # Global Project Settings
7
+ [*]
8
+ end_of_line = lf
9
+ insert_final_newline = true
10
+ trim_trailing_whitespace = true
11
+ indent_style = space
12
+ indent_size = 2
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,52 @@
1
+ ### Thank You!
2
+
3
+ Thanks for considering contributing to our project! We want to make Stretchy a great tool for integrating Elasticsearch with Ruby projects, and getting help from the community is always appreciated.
4
+
5
+ ### Development
6
+
7
+ 1. Fork it ( https://github.com/[my-github-username]/stretchy/fork )
8
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
9
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
10
+ 4. Push to the branch (`git push origin my-new-feature`)
11
+ 5. Create a new Pull Request
12
+
13
+ ### Bugfixes
14
+
15
+ Please add a spec to reproduce the bug, fix it, then update any documentation necessary.
16
+
17
+ ### Documentation Changes
18
+
19
+ If you notice we've got something wrong in the docs, please feel welcome to fix it.
20
+
21
+ ### New Features
22
+
23
+ Before adding new features to the query builder, please [open a new issue](https://github.com/hired/stretchy/issues) to discuss it. This helps ensure your new feature is in line with the project's goals, and that you don't invest a bunch of time in something we might reject.
24
+
25
+ ### Documentation
26
+
27
+ Our documentation is far from perfect, but if you add new methods to the query chain (ie, anything in `lib/stretchy/clauses`) be sure to document that using the YARD syntax, and also add it to the README.
28
+
29
+ ### Testing
30
+
31
+ We use rspec for testing, with the latest version of Elasticsearch and Ruby. Until we hit 1.0, no support for older versions of either is planned.
32
+
33
+ * Use unit tests to ensure basic classes (builders, clauses, etc) behave the way you expect.
34
+ * Test the output of `.to_search` to ensure the JSON being generated for Elasticsearch is what you expect.
35
+ * Write an integration test under `spec/integration` to ensure that using your search terms actually affects the search results.
36
+
37
+ We run all specs automatically through Solano CI, and specs must pass there before any merge.
38
+
39
+ ### Versioning
40
+
41
+ The version is only bumped on master after a pull request is merged. We use [Semantic Versioning](http://semver.org/).
42
+
43
+ * Bug fixes will bump the patch version
44
+ * Small new additions will bump the minor version
45
+ * Behavior and backwards-incompatible changes will bump the major version
46
+
47
+ ### Style
48
+
49
+ 1. Use the included `.editorconfig` to manage [minor style things like indents and tabs-v-spaces](http://editorconfig.org/).
50
+ 2. Generally follow the [Github Ruby style guide](https://github.com/styleguide/ruby).
51
+ 3. [Rebase your branch](http://git-scm.com/docs/git-rebase) and squash commits into reasonable chunks with good commit messages. No `@wip` or `fix specs` commits, please. [Here are some guidelines](http://chris.beams.io/posts/git-commit/) for good commit messages.
52
+ 4. Write specs however they make sense to read. Use `describe` and `it` if your test names make a sentence, or `context` and `specify` for more specific unit tests.
data/README.md CHANGED
@@ -1,16 +1,20 @@
1
+ [![](https://ci.solanolabs.com:443/Hired/stretchy/badges/branches/master?badge_token=062c34bcb84d3502662722bf76a8b4ec9fa073d9)](https://ci.solanolabs.com:443/Hired/stretchy/suites/246591)
2
+
1
3
  # Stretchy
2
4
 
3
- Stretchy is a query builder for [Elasticsearch](https://www.elastic.co/products/elasticsearch). It helps you quickly construct the JSON to send to Elastic, which can get [rather complicated](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html).
5
+ Stretchy is a query builder for [Elasticsearch](https://www.elastic.co/products/elasticsearch). It helps you quickly construct the JSON to send to Elastic, which can get [rather complicated](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html).
4
6
 
5
- Stretchy is modeled after ActiveRecord's interface and architecture - query objects are immutable and chainable, which makes quickly building the right query and caching the results easy.
7
+ Stretchy is modeled after ActiveRecord's interface and architecture - query objects are immutable and chainable, which makes quickly building the right query and caching the results easy. The goals are:
6
8
 
7
- Stretchy is *not*:
9
+ 1. **Intuitive** - If you've used ActiveRecord, Mongoid, or other query builders, Stretchy shouldn't be a stretch
10
+ 2. **Less Typing** - Queries built here should be _way_ fewer characters than building by hand
11
+ 3. **Easy** - Implementing the right algorithms for your search needs should be simple
8
12
 
9
- 1. an integration with ActiveModel to help you index your data
10
- 2. a way to manage Elasticsearch configuration
11
- 3. a general-purpose Elasticsearch API client
13
+ Stretchy is *not*:
12
14
 
13
- The first two are very application-specific. For any non-trivial app, the level of customization necessary will have you writing almost everything yourself. The last one is better handled by the [elasticsearch gem](http://www.rubydoc.info/gems/elasticsearch-api/).
15
+ 1. an integration with ActiveModel to help you index your data - too application specific
16
+ 2. a way to manage Elasticsearch configuration - see [waistband](https://github.com/taskrabbit/waistband)
17
+ 3. a general-purpose Elasticsearch API client - see the [elasticsearch gem](http://www.rubydoc.info/gems/elasticsearch-api/)
14
18
 
15
19
  ## Installation
16
20
 
@@ -30,7 +34,9 @@ Or install it yourself as:
30
34
 
31
35
  ## Usage
32
36
 
33
- Stretchy is still in early development, so it does not yet support the full feature set of the [Elasticsearch API](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html). It does support fairly basic queries in an ActiveRecord-ish style.
37
+ Stretchy is still in early development, so it does not yet support the full feature set of the [Elasticsearch API](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html). There may be bugs, though we try for solid spec coverage. We may introduce breaking changes in minor versions, though we try to stick with [semantic versioning](http://semver.org).
38
+
39
+ It does support fairly basic queries in an ActiveRecord-ish style.
34
40
 
35
41
  ### Documentation
36
42
 
@@ -49,7 +55,7 @@ Stretchy.configure do |c|
49
55
  # Stretchy will also log, with the params
50
56
  # specified below
51
57
  c.log_level = :debug # default is :silence
52
- c.log_color = :green # default is :blue
58
+ c.log_color = :green # default is :blue
53
59
  end
54
60
  ```
55
61
 
@@ -59,9 +65,32 @@ end
59
65
  query = Stretchy.query(type: 'model_name')
60
66
  ```
61
67
 
68
+ From here, you can chain the methods to build your desired query.
69
+
70
+ ## Chainable Query Methods
71
+
62
72
  From here, you can chain the following query methods:
63
73
 
64
- ### Fulltext
74
+ * [fulltext](#fulltext) - generic fulltext search with proximity relevance
75
+ * [match](#match) - Elasticsearch match query
76
+ * [query](#query) - Add arbitrary json fragment to the query section
77
+ * [more_like](#more-like) - Get documents similar to a string or other documents
78
+ * [where](#where) - Filter based on fields in the document
79
+ * [terms](#terms) - Filter without analyzing strings or symbols
80
+ * [filter](#filter) - Add arbitrary json fragment to the filter section
81
+ * [range](#range) - Filter for a range of values
82
+ * [geo](#geo-distance) - Filter on geo_point fields within a specified distance
83
+ * [not](#not) - Get documents not matching passed conditions
84
+ * [should](#should) - Increase document score for matching documents
85
+ * [boost](#boost) - Increasing document score based on different factors
86
+ * [near](#near) - Boost score based on how close a number / date / geo point is to an origin
87
+ * [field](#field) - Boost based on the numeric value of the passed field
88
+ * [random](#random) - Add a deterministic random factor to the document score
89
+ * [explain](#explain) - Return score explanations along with documents
90
+ * [fields](#fields) - Only return the specified fields
91
+ * [page](#limit) - Limit, Offset, and Page to define which results to return
92
+
93
+ ### <a id="fulltext"></a>Fulltext
65
94
 
66
95
  ```ruby
67
96
  query = query.fulltext('Generic user-input phrase')
@@ -70,7 +99,7 @@ query = query.fulltext('Generic user-input phrase')
70
99
 
71
100
  Performs a query for the given string, either anywhere in the document or in specific fields. At least one of the terms must match, and the closer a document is to having the exact phrase, the higher its' score. See the Elasticsearch guide's [article on proximity scoring](https://www.elastic.co/guide/en/elasticsearch/guide/current/proximity-relevance.html) for more info on how this works.
72
101
 
73
- ### Match
102
+ ### <a id="match"></a>Match
74
103
 
75
104
  ```ruby
76
105
  query = query.match('welcome to my web site')
@@ -80,7 +109,31 @@ query = query.match('welcome to my web site')
80
109
 
81
110
  Performs a match query for the given string. If given a hash, it will use a match query on the specified fields, otherwise it will default to `'_all'`. By default, a match query searches for any of the analyzed terms in the document, and scores them using Lucene's [practical scoring formula](https://www.elastic.co/guide/en/elasticsearch/guide/current/practical-scoring-function.html), which combines TF/IDF, the vector space model, and a few other niceties.
82
111
 
83
- ### More Like
112
+ ### <a id="query"></a>Query
113
+
114
+ ```ruby
115
+ query = query.match.query(
116
+ multi_match: {
117
+ query: 'super smash bros',
118
+ fields: ['developer.games', 'developer.bio']
119
+ }
120
+ )
121
+
122
+ query = query.match.not.match.query(
123
+ multi_match: {
124
+ query: 'rez',
125
+ fields: ['developer.games', 'developer.bio']
126
+ }
127
+ )
128
+ ```
129
+
130
+ Adds arbitrary JSON to the query section of the final query. If you want to use a query type not currently supported by Stretchy, you can call this method and pass in the requisite json fragment. You can also prefix this with `.not` and `.should` to add your json to those sections of the query instead.
131
+
132
+ #### Caution
133
+
134
+ Stretchy tries to merge together matches on the same fields to optimize the final query to be sent to Elastic, but will not try to optimize any json added via the `.query` method.
135
+
136
+ ### <a id="more-like"></a>More Like
84
137
 
85
138
  ```ruby
86
139
  query = query.more_like(ids: [1, 2, 3])
@@ -90,7 +143,7 @@ query = query.more_like(ids: [1, 2, 3])
90
143
 
91
144
  Finds documents similar to a list of input documents. You must pass in one of the `:ids`, `:docs` or `:like_text` parameters, but everything else is optional. This method accepts any of the params available in the [Elasticsearch more_like_this query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-mlt-query.html). It can also be chained with `.not` and `.should`.
92
145
 
93
- ### Where
146
+ ### <a id="where"></a>Where
94
147
 
95
148
  ```ruby
96
149
  query = query.where(
@@ -112,7 +165,7 @@ If you pass a string or symbol for a field, it will be converted to a [Match Que
112
165
 
113
166
  To query for _exact_ matches against strings or symbols with underscores and punctuation intact, use the `.where.terms` clause.
114
167
 
115
- ### Terms
168
+ ### <a id="terms"></a>Terms
116
169
 
117
170
  ```ruby
118
171
  query = query.where.terms(
@@ -123,7 +176,29 @@ query = query.where.terms(
123
176
 
124
177
  Sometimes you store values with punctuation, underscores, or other characters Elasticsearch would normally split into separate terms. If you want to query all comments that match a specific email address, you need to make sure that Elasticsearch doesn't analyze the query terms you send it before running the query. This clause allows you to do that.
125
178
 
126
- ### Range
179
+ ### <a id="filter"></a>Filter
180
+
181
+ ```ruby
182
+ query = query.filter(
183
+ geo_polygon: {
184
+ 'person.location' => {
185
+ points: [
186
+ {lat: 40, lon: -70},
187
+ {lat: 30, lon: -80},
188
+ {lat: 20, lon: -90}
189
+ ]
190
+ }
191
+ }
192
+ )
193
+ ```
194
+
195
+ Adds arbitrary JSON to the filter section of the final query. If you want to use a filter type not currently supported by Stretchy, you can call this method and pass in the requisite json fragment. You can also prefix this with `.not` and `.should` to add your json to those sections of the filters instead.
196
+
197
+ #### Caution
198
+
199
+ Stretchy tries to merge together filters on the same fields to optimize the final query to be sent to Elastic, but will not try to optimize any json added via the `.filter` method.
200
+
201
+ ### <a id="range"></a>Range
127
202
 
128
203
  ```ruby
129
204
  query = query.range(:rating, min: 3, max: 5)
@@ -133,10 +208,10 @@ query = query.range(:rating, min: 3, max: 5)
133
208
 
134
209
  Only documents with the specified field, and within the specified range match. You can also pass in dates and times as ranges. While you could pass a normal ruby `Range` object to `.where`, this allows you to specify only a minimum or only a maximum. Range filters are inclusive by default, but you can also pass `:exclusive`, `:exclusive_min`, or `:exclusive_max`.
135
210
 
136
- ### Geo Distance
211
+ ### <a id="geo-distance"></a>Geo Distance
137
212
 
138
213
  ```ruby
139
- query = query.geo(field: 'coords', distance: '20mi', lat: 35.0117, lng: 135.7683)
214
+ query = query.geo('coords', distance: '20mi', lat: 35.0117, lng: 135.7683)
140
215
  ```
141
216
 
142
217
  Filters for documents where the specified `geo_point` field is within the given range.
@@ -145,7 +220,7 @@ Filters for documents where the specified `geo_point` field is within the given
145
220
 
146
221
  The field must be mapped as a `geo_point` field. See [Elasticsearch types](http://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-geo-point-type.html) for more info.
147
222
 
148
- ### Not
223
+ ### <a id="not"></a>Not
149
224
 
150
225
  ```ruby
151
226
  query = query.where.not(rating: 0)
@@ -155,7 +230,7 @@ query = query.where.not(rating: 0)
155
230
 
156
231
  Called after `where` or `match` will let you apply inverted filters. Any documents that match those filters will be excluded.
157
232
 
158
- ### Should
233
+ ### <a id="should"></a>Should
159
234
 
160
235
  ```ruby
161
236
  query = query.should(name: 'Sarah', awesomeness: 1000).should.not(awesomeness: 0)
@@ -163,7 +238,7 @@ query = query.should(name: 'Sarah', awesomeness: 1000).should.not(awesomeness: 0
163
238
 
164
239
  Should filters work similarly to `.where`. Documents that do not match are still returned, but they have a lower relevancy score and will appear after documents that do match in the results. See Elastic's documentation for [BoolQuery](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html) and [BoolFilter](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-filter.html) for more info.
165
240
 
166
- ### Boost
241
+ ### <a id="boost"></a>Boost
167
242
 
168
243
  ```ruby
169
244
  query = query.boost.where(category: 3, weight: 100)
@@ -174,20 +249,20 @@ query = query.boost.where(category: 3, weight: 100)
174
249
  Boosts use a [Function Score Query](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html) with filters to allow you to affect the score for the document. Each condition will be applied as a filter with an optional weight.
175
250
 
176
251
 
177
- ### Near
252
+ ### <a id="near"></a>Near
178
253
 
179
254
  ```ruby
180
255
  query = query.boost.near(field: :published_at, origin: Time.now, scale: '5d')
181
256
  .boost.near(field: :coords, lat: 35.0117, lng: 135.7683, scale: '10mi', decay: 0.33, weight: 1000)
182
257
  ```
183
258
 
184
- Boosts a document by how close a given field is to a given `:origin` . Accepts dates, times, numbers, and geographical points. Unlike `.where.range` or `.boost.geo`, `.boost.near` is not a binary operation. All documents get a score for that field, which decays the further it is away from the origin point.
259
+ Boosts a document by how close a given field is to a given `:origin` . Accepts dates, times, numbers, and geographical points. Unlike `.where.range` or `.boost.geo`, `.boost.near` is not a binary operation. All documents get a score for that field, which decays the further it is away from the origin point.
185
260
 
186
261
  The `:scale` param determines how quickly the value falls off. In the example above, if a document's `:coords` field is 10 miles away from the starting point, its score is about 1/3 that of a document at the origin point.
187
262
 
188
263
  See the [Function Score Query](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html) section on Decay Functions for more info.
189
264
 
190
- ### Field
265
+ ### <a id="field"></a>Field
191
266
 
192
267
  ```ruby
193
268
  query = query.boost.field(:popularity)
@@ -199,7 +274,7 @@ Boosts a document by a numeric value contained in the specified fields. You can
199
274
 
200
275
  See the [Boosting By Popularity Guide](https://www.elastic.co/guide/en/elasticsearch/guide/current/boosting-by-popularity.html) and the [Field Value Factor documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html#_field_value_factor) for more info.
201
276
 
202
- ### Random
277
+ ### <a id="random"></a>Random
203
278
 
204
279
  ```ruby
205
280
  query = query.boost.random(user.id, 50)
@@ -207,23 +282,42 @@ query = query.boost.random(user.id, 50)
207
282
 
208
283
  Gives each document a randomized boost with a given seed and optional weight. This allows you to show slightly different result sets to different users, but show the same result set to that user every time.
209
284
 
210
- ### Limit and Offset
285
+ ### <a id="fields"></a>Fields
286
+
287
+ ```ruby
288
+ query = query.fields(:name, :email, :id)
289
+ ```
290
+
291
+ Instead of returning the entire document, only return the specified fields.
292
+
293
+ ### <a id="limit"></a>Limit, Offset, and Page
211
294
 
212
295
  ```ruby
213
296
  query = query.limit(20).offset(1000)
297
+ # or...
298
+ query = query.page(50, per_page: 20)
214
299
  ```
215
300
 
216
- Works the same way as ActiveRecord's limit and offset methods - analogous to Elasticsearch's `from` and `size` parameters.
301
+ Works the same way as ActiveRecord's limit and offset methods - analogous to Elasticsearch's `from` and `size` parameters. The `.page` method allows you to set both at once, and is compatible with the [Kaminari gem](https://github.com/amatsuda/kaminari).
217
302
 
218
- ### Response
303
+ ### <a id="explain"></a>Explain
219
304
 
220
305
  ```ruby
221
- query.response
306
+ query = query.explain.where()
222
307
  ```
223
308
 
224
- Executes the query, returns the raw JSON response from Elasticsearch and caches it. Use this to get at search API data not in the source documents.
309
+ Tells Elasticsearch to return an explanation of the score for each document. See [the explain parameter](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-explain.html) for how this is used, and [the explain API](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-explain.html) for what the explanations will look like.
310
+
311
+ ## Result Methods
312
+
313
+ * [results](#results) - Result documents from this query
314
+ * [ids](#ids) - Ids of result documents instead of the full source
315
+ * [response](#response) - Raw response data from Elasticsearch
316
+ * [total](#total) - Total number of matching documents
317
+ * [explanations](#explanations) - Explanations for document scores
318
+ * [per_page](#per_page) - Included with `.limit_value` for Kaminari compatibility
225
319
 
226
- ### Results
320
+ ### <a id="results"></a>Results
227
321
 
228
322
  ```ruby
229
323
  query.results
@@ -231,7 +325,7 @@ query.results
231
325
 
232
326
  Executes the query and provides the parsed json for each hit returned by Elasticsearch, along with `_index`, `_type`, `_id`, and `_score` fields.
233
327
 
234
- ### Ids
328
+ ### <a id="ids"></a>Ids
235
329
 
236
330
  ```ruby
237
331
  query.ids
@@ -239,7 +333,15 @@ query.ids
239
333
 
240
334
  Provides only the ids for each hit. If your document ids are numeric (as is the case for many ActiveRecord integrations), they will be converted to integers.
241
335
 
242
- ### Total
336
+ ### <a id="response"></a>Response
337
+
338
+ ```ruby
339
+ query.response
340
+ ```
341
+
342
+ Executes the query, returns the raw JSON response from Elasticsearch and caches it. Use this to get at search API data not in the source documents.
343
+
344
+ ### <a id="total"></a>Total
243
345
 
244
346
  ```ruby
245
347
  query.total
@@ -247,16 +349,31 @@ query.total
247
349
 
248
350
  Returns the total number of matches returned by the query - not just the current page. Makes plugging into [Kaminari](https://github.com/amatsuda/kaminari) a snap.
249
351
 
250
- ## Development
352
+ ### <a id="explanations"></a>Explanations
251
353
 
252
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
354
+ ```ruby
355
+ query.explanations
356
+ ```
253
357
 
254
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
358
+ Collect the `'_explanation'` field for each result, so you can easily see how the document scores were computed.
359
+
360
+ ### <a id="per-page"></a>Per Page, Limit Value, and Total Pages
361
+
362
+ ```ruby
363
+ results = query.query_results
364
+ results.per_page
365
+ results.limit_value
366
+ results.total_pages
367
+ ```
368
+
369
+ Included in the Results object for Kaminari compatibility.
370
+
371
+ ## Development
372
+
373
+ After checking out the repo, run `bundle install` to install dependencies. Then, run `pry` for an interactive prompt that will allow you to experiment.
255
374
 
256
375
  ## Contributing
257
376
 
258
- 1. Fork it ( https://github.com/[my-github-username]/stretchy/fork )
259
- 2. Create your feature branch (`git checkout -b my-new-feature`)
260
- 3. Commit your changes (`git commit -am 'Add some feature'`)
261
- 4. Push to the branch (`git push origin my-new-feature`)
262
- 5. Create a new Pull Request
377
+ For bugs and feature requests, please [open a new issue](https://github.com/hired/stretchy/issues/new).
378
+
379
+ Please see [the CONTRIBUTING guide](https://github.com/hired/stretchy/blob/master/CONTRIBUTING.md) for guidelines on contributing to Stretchy.
@@ -2,17 +2,22 @@ module Stretchy
2
2
  module Builders
3
3
  class FilterBuilder
4
4
 
5
- attr_reader :terms, :ranges, :geos, :exists, :inverse, :should
5
+ attr_reader :terms, :ranges, :geos, :exists, :filters, :inverse, :should
6
6
 
7
7
  def initialize
8
8
  @terms = Hash.new { [] }
9
9
  @ranges = Hash.new { [] }
10
10
  @geos = Hash.new { [] }
11
+ @filters = []
11
12
  @exists = []
12
13
  end
13
14
 
14
15
  def any?
15
- @terms.any? || @ranges.any? || @geos.any? || @exists.any?
16
+ [@filters, @terms, @ranges, @geos, @exists].any?(&:any?)
17
+ end
18
+
19
+ def add_filter(filter)
20
+ @filters << filter
16
21
  end
17
22
 
18
23
  def add_terms(field, terms)
@@ -34,13 +39,13 @@ module Stretchy
34
39
  end
35
40
 
36
41
  def to_filters
37
- filters = @ranges.values + @geos.values
38
- filters += @terms.map do |field, values|
42
+ _filters = @ranges.values + @geos.values
43
+ _filters += @terms.map do |field, values|
39
44
  Filters::TermsFilter.new(field, values)
40
45
  end
41
46
 
42
- filters += @exists.map {|field| Filters::ExistsFilter.new(field) }
43
- filters
47
+ _filters += @exists.map {|field| Filters::ExistsFilter.new(field) }
48
+ filters + _filters
44
49
  end
45
50
 
46
51
  private
@@ -50,4 +55,4 @@ module Stretchy
50
55
 
51
56
  end
52
57
  end
53
- end
58
+ end
@@ -43,4 +43,4 @@ module Stretchy
43
43
 
44
44
  end
45
45
  end
46
- end
46
+ end
@@ -21,6 +21,10 @@ module Stretchy
21
21
  (must.any? && must_not.any?) || should.any? || should_not.any?
22
22
  end
23
23
 
24
+ def add_filter(filter, options = {})
25
+ builder_from_options(options).add_filter(filter)
26
+ end
27
+
24
28
  def add_param(field, param, options = {})
25
29
  case param
26
30
  when nil
@@ -106,11 +110,11 @@ module Stretchy
106
110
  elsif should_not.any?
107
111
  filters = should_not.to_filters
108
112
  if filters.count > 1
109
- filters = Stretchy::Filters::OrFilter.new(filters)
113
+ filters = Stretchy::Filters::OrFilter.new(filters)
110
114
  else
111
115
  filters = filters.first
112
116
  end
113
-
117
+
114
118
  Stretchy::Filters::NotFilter.new(filters)
115
119
  else
116
120
  filters = should.to_filters
@@ -120,4 +124,4 @@ module Stretchy
120
124
  end
121
125
  end
122
126
  end
123
- end
127
+ end