stretchy 0.3.6 → 0.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/README.md +76 -46
- data/lib/stretchy/clauses/base.rb +152 -0
- data/lib/stretchy/clauses/boost_clause.rb +139 -0
- data/lib/stretchy/clauses/boost_match_clause.rb +59 -0
- data/lib/stretchy/clauses/boost_where_clause.rb +68 -0
- data/lib/stretchy/clauses/match_clause.rb +100 -0
- data/lib/stretchy/clauses/where_clause.rb +148 -0
- data/lib/stretchy/results/base.rb +20 -1
- data/lib/stretchy/utils/client_actions.rb +24 -4
- data/lib/stretchy/utils/colorize.rb +71 -0
- data/lib/stretchy/utils/configuration.rb +15 -2
- data/lib/stretchy/utils/logger.rb +59 -0
- data/lib/stretchy/utils.rb +4 -2
- data/lib/stretchy/version.rb +1 -1
- data/lib/stretchy.rb +2 -0
- data/stretchy.gemspec +1 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d19628e7bb3b4177002806dee9ae3cd41e67340
|
4
|
+
data.tar.gz: cdb6536ac2450f5f8f01a6efab5071c0bb88e9f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fb125ae7d1763bd44c2fef081d3a1ab2d3ae4e5a9289341e32b98617bd26dd667a387333b38d97ae6942da519d980ac380b28b738c12c80345e174c19b6f1d4
|
7
|
+
data.tar.gz: 1aa29437d28d4dcd4e85840d161f068f09569b2b3f4c008886cdebc1dc005eb50fc9d4aca120c21611b777721bfe971fa3daa4b7da3a116d006a6d54fbcd9960
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-m markdown lib/**/*.rb - README.md
|
data/README.md
CHANGED
@@ -33,10 +33,27 @@ Or install it yourself as:
|
|
33
33
|
|
34
34
|
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.
|
35
35
|
|
36
|
+
### Configuration
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
Stretchy.configure do |c|
|
40
|
+
c.index_name = 'my_index' # REQUIRED
|
41
|
+
c.client = $my_client # ignore below, use a custom client
|
42
|
+
c.url = 'https://user:pw@my.elastic.url' # default is ENV['ELASTICSEARCH_URL']
|
43
|
+
c.adapter = :patron # default is :excon
|
44
|
+
|
45
|
+
c.logger = Logger.new(STDOUT) # passed to elasticsearch-api gem
|
46
|
+
# Stretchy will also log, with the params
|
47
|
+
# specified below
|
48
|
+
c.log_level = :debug # default is :silence
|
49
|
+
c.log_color = :green # default is :blue
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
36
53
|
### Base
|
37
54
|
|
38
55
|
```ruby
|
39
|
-
query = Stretchy
|
56
|
+
query = Stretchy.query(type: 'model_name')
|
40
57
|
```
|
41
58
|
|
42
59
|
From here, you can chain the following query methods:
|
@@ -44,57 +61,43 @@ From here, you can chain the following query methods:
|
|
44
61
|
### Match
|
45
62
|
|
46
63
|
```ruby
|
47
|
-
query = query.match('welcome to my web site')
|
48
|
-
query = query.match('welcome to my web site', field: 'title', operator: 'or')
|
64
|
+
query = query.match('welcome to my web site').match(title: 'welcome to my web site')
|
49
65
|
```
|
50
66
|
|
51
|
-
Performs a full-text search for the given string.
|
52
|
-
|
53
|
-
#### Variants
|
54
|
-
|
55
|
-
* `not_match` - filters for documents not matching a full-text search
|
67
|
+
Performs a full-text search for the given string. If given a hash, it will use a match query on the specified fields, otherwise it will default to `'_all'`.
|
56
68
|
|
57
69
|
|
58
70
|
### Where
|
59
71
|
|
60
72
|
```ruby
|
61
73
|
query = query.where(
|
62
|
-
name: '
|
74
|
+
name: 'alice',
|
63
75
|
email: [
|
64
|
-
'
|
65
|
-
'
|
66
|
-
]
|
76
|
+
'alice@company.com',
|
77
|
+
'beatrice.christine@other_company.com'
|
78
|
+
],
|
79
|
+
commit_count: 27..33,
|
80
|
+
is_robot: nil
|
67
81
|
)
|
68
82
|
```
|
69
83
|
|
70
|
-
Allows passing a hash of matchable options
|
71
|
-
|
72
|
-
If you pass `field: nil`, Stretchy will construct the relevant `not { exists: field }` filter and apply it as expected.
|
84
|
+
Allows passing a hash of matchable options similar to ActiveRecord's `where` method. To be returned, the document must match each of the parameters. If you pass an array of parameters for a field, the document must match at least one of those parameters.
|
73
85
|
|
74
86
|
#### Gotcha
|
75
87
|
|
76
|
-
|
77
|
-
|
78
|
-
#### Variants
|
79
|
-
|
80
|
-
* `not_where` - filters for documents *not* matching the criteria
|
81
|
-
* `boost_where` - boosts the relevance score for matching documents
|
82
|
-
* `boost_not_where` - boosts the relevance score for documents not matching the criteria
|
88
|
+
If you pass a string or symbol for a field, it will be converted to a [Match Query](http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html) for the specified field. Since Elastic analyzes terms by default, string or symbol terms will be looked for by an analyzed query.
|
83
89
|
|
84
90
|
### Range
|
85
91
|
|
86
92
|
```ruby
|
87
|
-
query = query.range(
|
93
|
+
query = query.range(:rating, min: 3, max: 5)
|
94
|
+
.range(:released, min: Time.now - 60*60*24*100)
|
95
|
+
.range(:quantity, max: 100, exclusive: true)
|
88
96
|
```
|
89
97
|
|
90
|
-
Only documents with the specified field, and within the specified range
|
91
|
-
|
92
|
-
#### Variants
|
98
|
+
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`.
|
93
99
|
|
94
|
-
|
95
|
-
* `boost_range` - boosts the relevance score for matching documents
|
96
|
-
|
97
|
-
### Geo
|
100
|
+
### Geo Distance
|
98
101
|
|
99
102
|
```ruby
|
100
103
|
query = query.geo(field: 'coords', distance: '20mi', lat: 35.0117, lng: 135.7683)
|
@@ -106,34 +109,63 @@ Filters for documents where the specified `geo_point` field is within the given
|
|
106
109
|
|
107
110
|
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.
|
108
111
|
|
109
|
-
|
112
|
+
### Not
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
query = query.where.not(rating: 0)
|
116
|
+
.match.not('angry')
|
117
|
+
.where.not.geo(field: 'coords', distance: '20mi', lat: 35.0117, lng: 135.7683)
|
118
|
+
```
|
110
119
|
|
111
|
-
|
112
|
-
* `boost_geo` - boosts the relevance score for documents based on how far from the given point they are
|
120
|
+
Called after `where` or `match` will let you apply inverted filters. Any documents that match those filters will be excluded.
|
113
121
|
|
114
|
-
###
|
122
|
+
### Should
|
115
123
|
|
116
124
|
```ruby
|
117
|
-
query = query.
|
125
|
+
query = query.should(name: 'Sarah', awesomeness: 1000).should.not(awesomeness: 0)
|
118
126
|
```
|
119
127
|
|
120
|
-
|
128
|
+
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.
|
121
129
|
|
122
|
-
### Boost
|
130
|
+
### Boost
|
123
131
|
|
124
132
|
```ruby
|
125
|
-
query = query.
|
133
|
+
query = query.boost.where(category: 3, weight: 100)
|
134
|
+
.boost.range(:awesomeness, min: 10, weight: 10)
|
135
|
+
.boost.match.not('sucks')
|
126
136
|
```
|
127
137
|
|
128
|
-
|
138
|
+
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.
|
129
139
|
|
130
|
-
|
140
|
+
|
141
|
+
### Near
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
query = query.boost.near(field: :published_at, origin: Time.now, scale: '5d')
|
145
|
+
.boost.near(field: :coords, lat: 35.0117, lng: 135.7683, scale: '10mi', decay: 0.33, weight: 1000)
|
146
|
+
```
|
147
|
+
|
148
|
+
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.
|
149
|
+
|
150
|
+
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.
|
151
|
+
|
152
|
+
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.
|
153
|
+
|
154
|
+
### Random
|
131
155
|
|
132
156
|
```ruby
|
133
|
-
query = query.
|
157
|
+
query = query.boost.random(user.id, 50)
|
134
158
|
```
|
135
159
|
|
136
|
-
|
160
|
+
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.
|
161
|
+
|
162
|
+
### Limit and Offset
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
query = query.limit(20).offset(1000)
|
166
|
+
```
|
167
|
+
|
168
|
+
Works the same way as ActiveRecord's limit and offset methods - analogous to Elasticsearch's `from` and `size` parameters.
|
137
169
|
|
138
170
|
### Response
|
139
171
|
|
@@ -149,7 +181,7 @@ Executes the query, returns the raw JSON response from Elasticsearch and caches
|
|
149
181
|
query.results
|
150
182
|
```
|
151
183
|
|
152
|
-
Executes the query and provides the parsed json for each hit returned by Elasticsearch.
|
184
|
+
Executes the query and provides the parsed json for each hit returned by Elasticsearch, along with `_index`, `_type`, `_id`, and `_score` fields.
|
153
185
|
|
154
186
|
### Ids
|
155
187
|
|
@@ -157,9 +189,7 @@ Executes the query and provides the parsed json for each hit returned by Elastic
|
|
157
189
|
query.ids
|
158
190
|
```
|
159
191
|
|
160
|
-
Provides only the ids for each hit. If your document ids are numeric (as is the case for
|
161
|
-
|
162
|
-
This is somewhat intelligent - if you have already called `results` the ids will be fetched from there, otherwise it will run the search and skip the data-fetch phase in Elasticsearch.
|
192
|
+
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.
|
163
193
|
|
164
194
|
### Total
|
165
195
|
|
@@ -1,5 +1,17 @@
|
|
1
1
|
module Stretchy
|
2
2
|
module Clauses
|
3
|
+
# A Clause is the basic unit of Stretchy's chainable query syntax.
|
4
|
+
# Think of it as a state machine, with transitions between states
|
5
|
+
# being handled by methods that return another Clause. When you
|
6
|
+
# call the `where` method, it stores the params passed as an
|
7
|
+
# internal representation, to be compiled down to the Elastic query
|
8
|
+
# syntax, and returns a WhereClause. The WhereClause reflects the
|
9
|
+
# current state of the query, and gives you access to methods like
|
10
|
+
# `range` and `geo` to add more specific filters. It inherits
|
11
|
+
# other methods from the Base class, allowing other transitions.
|
12
|
+
#
|
13
|
+
# Attributes are copied when a new Clause is instanciated, so
|
14
|
+
# the underlying storage is maintained throughout the chain.
|
3
15
|
class Base
|
4
16
|
|
5
17
|
extend Forwardable
|
@@ -14,6 +26,19 @@ module Stretchy
|
|
14
26
|
:took, :shards, :total, :max_score] => :query_results
|
15
27
|
delegate [:range, :geo] => :where
|
16
28
|
|
29
|
+
#
|
30
|
+
# Generates a chainable query. The only required option for the
|
31
|
+
# first initialization is `:type` , which specifies what type
|
32
|
+
# to query on your index.
|
33
|
+
#
|
34
|
+
# @overload initialize(base_or_opts, options)
|
35
|
+
# @param base [Base] another clause to copy attributes from
|
36
|
+
# @param options [Hash] options to set on the new state
|
37
|
+
#
|
38
|
+
# @overload initialize(base_or_opts)
|
39
|
+
# @option base_or_opts [String] :index The Elastic index to query
|
40
|
+
# @option base_or_opts [String] :type The Lucene type to query on
|
41
|
+
# @option base_or_opts [true, false] :inverse Whether we are in a `not` context
|
17
42
|
def initialize(base_or_opts = nil, options = {})
|
18
43
|
if base_or_opts && !base_or_opts.is_a?(Hash)
|
19
44
|
base = base_or_opts
|
@@ -26,6 +51,7 @@ module Stretchy
|
|
26
51
|
@inverse = options[:inverse] || base.inverse
|
27
52
|
@limit = base.get_limit
|
28
53
|
@offset = base.get_offset
|
54
|
+
@explain = base.get_explain
|
29
55
|
else
|
30
56
|
options = Hash(base_or_opts).merge(options)
|
31
57
|
@index_name = options[:index] || Stretchy.index_name
|
@@ -40,38 +66,128 @@ module Stretchy
|
|
40
66
|
end
|
41
67
|
end
|
42
68
|
|
69
|
+
#
|
70
|
+
# Sets how many results to return, similar to
|
71
|
+
# ActiveRecord's limit method.
|
72
|
+
#
|
73
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html Elastic Docs - Request Body Search
|
74
|
+
#
|
75
|
+
# @param num [Integer] How many results to return
|
76
|
+
#
|
77
|
+
# @return [self]
|
43
78
|
def limit(num)
|
44
79
|
@limit = num
|
45
80
|
self
|
46
81
|
end
|
47
82
|
|
83
|
+
#
|
84
|
+
# Accessor for `@limit`
|
85
|
+
#
|
86
|
+
# @return [Integer] Value of `@limit`
|
48
87
|
def get_limit
|
49
88
|
@limit
|
50
89
|
end
|
51
90
|
|
91
|
+
#
|
92
|
+
# Sets the offset to start returning results at.
|
93
|
+
# Corresponds to Elastic's "from" parameter
|
94
|
+
#
|
95
|
+
# @param num [Integer] Offset for query
|
96
|
+
#
|
97
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-body.html Elastic Docs - Request Body Search
|
98
|
+
#
|
99
|
+
# @return [self]
|
52
100
|
def offset(num)
|
53
101
|
@offset = num
|
54
102
|
self
|
55
103
|
end
|
56
104
|
|
105
|
+
#
|
106
|
+
# Accessor for `@offset`
|
107
|
+
#
|
108
|
+
# @return [Integer] Offset for query
|
57
109
|
def get_offset
|
58
110
|
@offset
|
59
111
|
end
|
60
112
|
|
113
|
+
#
|
114
|
+
# Tells the search to explain the scoring
|
115
|
+
# mechanism for each document.
|
116
|
+
#
|
117
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/search-request-explain.html Elastic Docs - Request Body Search (explain)
|
118
|
+
#
|
119
|
+
# @return [self] Allows continuing the query chain
|
120
|
+
def explain
|
121
|
+
@explain = true
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def get_explain
|
126
|
+
!!@explain
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Used for fulltext searching. Works similarly
|
131
|
+
# to {#where} .
|
132
|
+
#
|
133
|
+
# @param options = {} [Hash] Options to be passed to
|
134
|
+
# the MatchClause
|
135
|
+
#
|
136
|
+
# @return [MatchClause] query state with fulltext matches
|
137
|
+
#
|
138
|
+
# @see MatchClause#initialize
|
139
|
+
#
|
140
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html Elastic Docs - Match Query
|
61
141
|
def match(options = {})
|
62
142
|
MatchClause.new(self, options)
|
63
143
|
end
|
64
144
|
alias :fulltext :match
|
65
145
|
|
146
|
+
#
|
147
|
+
# Used for filtering results. Works similarly to
|
148
|
+
# ActiveRecord's `where` method.
|
149
|
+
#
|
150
|
+
# @param options = {} [Hash] Options to be passed to
|
151
|
+
# the {WhereClause}
|
152
|
+
#
|
153
|
+
# @return [WhereClause] query state with filters
|
154
|
+
#
|
155
|
+
# @see WhereClause#initialize
|
66
156
|
def where(options = {})
|
67
157
|
WhereClause.new(self, options)
|
68
158
|
end
|
69
159
|
alias :filter :where
|
70
160
|
|
161
|
+
#
|
162
|
+
# Used for boosting the relevance score of
|
163
|
+
# search results. Options passed here correspond
|
164
|
+
# to `where`-style filters which boost a document
|
165
|
+
# if matched.
|
166
|
+
#
|
167
|
+
# @param options = {} [type] [description]
|
168
|
+
#
|
169
|
+
# @return [BoostClause] query in boost context
|
170
|
+
#
|
171
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html Elastic Docs - Function Score Query
|
71
172
|
def boost(options = {})
|
72
173
|
BoostClause.new(self, options)
|
73
174
|
end
|
74
175
|
|
176
|
+
#
|
177
|
+
# Inverts the current context - the next method
|
178
|
+
# called, such as {#where} or {#match} will generate
|
179
|
+
# a filter specifying the document **does not**
|
180
|
+
# match the specified filter.
|
181
|
+
#
|
182
|
+
# @overload not(string)
|
183
|
+
# @param [String] A string that must not be anywhere
|
184
|
+
# in the document
|
185
|
+
#
|
186
|
+
# @overload not(opts_or_string)
|
187
|
+
# @param [Hash] Options to be passed to an inverted {WhereClause}
|
188
|
+
#
|
189
|
+
# @return [Base] A {WhereClause}, or a {MatchClause} if only a string
|
190
|
+
# is given (ie, doing a full-text search across the whole document)
|
75
191
|
def not(opts_or_string = {}, opts = {})
|
76
192
|
if opts_or_string.is_a?(Hash)
|
77
193
|
WhereClause.new(self, opts_or_string.merge(inverse: true))
|
@@ -80,6 +196,24 @@ module Stretchy
|
|
80
196
|
end
|
81
197
|
end
|
82
198
|
|
199
|
+
#
|
200
|
+
# Adds filters in the `should` context. Operates just like
|
201
|
+
# {#where}, but these filters only serve to add to the
|
202
|
+
# relevance score of the returned documents, rather than
|
203
|
+
# being required to match.
|
204
|
+
#
|
205
|
+
# @overload should(opts_or_string)
|
206
|
+
# @param [String] A string to match via full-text search
|
207
|
+
# anywhere in the document.
|
208
|
+
#
|
209
|
+
# @overload should(opts_or_string)
|
210
|
+
# @param [Hash] Options to generate filters.
|
211
|
+
#
|
212
|
+
# @return [WhereClause] current query state with should clauses applied
|
213
|
+
#
|
214
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html Elastic Docs - Bool Query
|
215
|
+
#
|
216
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-filter.html Elastic Docs - Bool Filter
|
83
217
|
def should(opts_or_string = {}, opts = {})
|
84
218
|
if opts_or_string.is_a?(Hash)
|
85
219
|
WhereClause.new(self, opts_or_string.merge(should: true))
|
@@ -88,10 +222,22 @@ module Stretchy
|
|
88
222
|
end
|
89
223
|
end
|
90
224
|
|
225
|
+
#
|
226
|
+
# Accessor for `@inverse`
|
227
|
+
#
|
228
|
+
# @return [true, false] If current context is inverse
|
91
229
|
def inverse?
|
92
230
|
!!@inverse
|
93
231
|
end
|
94
232
|
|
233
|
+
#
|
234
|
+
# Compiles the internal representation of your filters,
|
235
|
+
# full-text queries, and boosts into the JSON to be
|
236
|
+
# passed to Elastic. If you want to know exactly what
|
237
|
+
# your query generated, you can call this method.
|
238
|
+
#
|
239
|
+
# @return [Hash] the query hash to be compiled to json
|
240
|
+
# and sent to Elastic
|
95
241
|
def to_search
|
96
242
|
return @to_search if @to_search
|
97
243
|
|
@@ -108,6 +254,12 @@ module Stretchy
|
|
108
254
|
@to_search = @to_search.to_search
|
109
255
|
end
|
110
256
|
|
257
|
+
#
|
258
|
+
# The Results object for this query, which handles
|
259
|
+
# sending the search request and providing convienent
|
260
|
+
# accessors for the response.
|
261
|
+
#
|
262
|
+
# @return [Results::Base] The results returned from Elastic
|
111
263
|
def query_results
|
112
264
|
@query_results ||= Stretchy::Results::Base.new(self)
|
113
265
|
end
|
@@ -2,27 +2,124 @@ require 'stretchy/clauses/base'
|
|
2
2
|
|
3
3
|
module Stretchy
|
4
4
|
module Clauses
|
5
|
+
#
|
6
|
+
# A Boost clause encapsulates the boost query state. It
|
7
|
+
# basically says "the next where / range / match filter
|
8
|
+
# will be used to boost a document's score instead of
|
9
|
+
# selecting documents to return."
|
10
|
+
#
|
11
|
+
# Calling `.boost` by itself doesn't do anything, but
|
12
|
+
# the next method (`.near`, `.match`, etc) will specify
|
13
|
+
# a boost using the same syntax as other clauses. These
|
14
|
+
# methods take a `:weight` parameter specifying the weight
|
15
|
+
# to assign that boost.
|
16
|
+
#
|
17
|
+
# @author [atevans]
|
18
|
+
#
|
5
19
|
class BoostClause < Base
|
6
20
|
|
7
21
|
extend Forwardable
|
8
22
|
|
9
23
|
delegate [:geo, :range] => :where
|
10
24
|
|
25
|
+
#
|
26
|
+
# Switch to the boost state, specifying that
|
27
|
+
# the next where / range / etc will be a boost
|
28
|
+
# instead of a regular filter / range / etc.
|
29
|
+
#
|
30
|
+
# @param base [Base] a clause to copy query state from
|
31
|
+
# @param options = {} [Hash] Options for the boost clause
|
32
|
+
# @option options [true, false] :inverse (nil) If this boost should also be in the inverse state
|
33
|
+
#
|
11
34
|
def initialize(base, options = {})
|
12
35
|
super(base)
|
13
36
|
@inverse = options.delete(:inverse)
|
14
37
|
end
|
15
38
|
|
39
|
+
#
|
40
|
+
# Changes query state to "match" in the context
|
41
|
+
# of boosting. Options here work the same way as
|
42
|
+
# {MatchClause#initialize}, but the combined query
|
43
|
+
# will be applied as a boost function.
|
44
|
+
#
|
45
|
+
# @param options = {} [Hash] options for full text matching
|
46
|
+
#
|
47
|
+
# @return [BoostMatchClause] query with boost match state
|
16
48
|
def match(options = {})
|
17
49
|
BoostMatchClause.new(self, options)
|
18
50
|
end
|
19
51
|
alias :fulltext :match
|
20
52
|
|
53
|
+
#
|
54
|
+
# Changes query state to "where" in the context
|
55
|
+
# of boosting. Works the same way as {WhereClause},
|
56
|
+
# but applies the generated filters as a boost
|
57
|
+
# function.
|
58
|
+
#
|
59
|
+
# @param options = {} [Hash] Filters to use in this boost.
|
60
|
+
#
|
61
|
+
# @return [BoostWhereClause] Query state with boost filters applied
|
62
|
+
#
|
21
63
|
def where(options = {})
|
22
64
|
BoostWhereClause.new(self, options)
|
23
65
|
end
|
24
66
|
alias :filter :where
|
25
67
|
|
68
|
+
#
|
69
|
+
# Adds a {Boosts::FieldDecayBoost}, which boosts
|
70
|
+
# a search result based on how close it is to a
|
71
|
+
# specified value. That value can be a date, time,
|
72
|
+
# number, or {Types::GeoPoint}
|
73
|
+
#
|
74
|
+
# Required:
|
75
|
+
#
|
76
|
+
# * `:field`
|
77
|
+
# * `:origin` or `:lat` & `:lng` combo
|
78
|
+
# * `:scale`
|
79
|
+
#
|
80
|
+
# @option options [Numeric] :field What field to check with this boost
|
81
|
+
# @option options [Date, Time, Numeric, Types::GeoPoint] :origin Boost score based on how close the field is to this value. Required unless {Types::GeoPoint} is present (:lat, :lng, etc)
|
82
|
+
# @option options [Numeric] :lat Latitude, for a geo point
|
83
|
+
# @option options [Numeric] :latitude Latitude, for a geo point
|
84
|
+
# @option options [Numeric] :lng Longitude, for a geo point
|
85
|
+
# @option options [Numeric] :lon Longitude, for a geo point
|
86
|
+
# @option options [Numeric] :longitude Longitude, for a geo point
|
87
|
+
# @option options [String] :scale When the field is this distance from origin, the boost will be multiplied by `:decay` . Default is 0.5, so when `:origin` is a geo point and `:scale` is '10mi', then this boost will be twice as much for a point at the origin as for one 10 miles away
|
88
|
+
# @option options [String] :offset Anything within this distance of the origin is boosted as if it were at the origin
|
89
|
+
# @option options [Symbol] :type (:gauss) What type of decay to use. One of `:linear`, `:exp`, or `:gauss`
|
90
|
+
# @option options [Numeric] :decay (0.5) How much the boost falls off when it is `:scale` distance from `:origin`
|
91
|
+
# @option options [Numeric] :weight (1.2) How strongly to weight this boost compared to others
|
92
|
+
#
|
93
|
+
# @example Boost near a geo point
|
94
|
+
# query.boost.near(
|
95
|
+
# field: :coords,
|
96
|
+
# distance: '27km',
|
97
|
+
# scale: '3mi',
|
98
|
+
# lat: 33.3,
|
99
|
+
# lng: 28.2
|
100
|
+
# )
|
101
|
+
#
|
102
|
+
# @example Boost near a date
|
103
|
+
# query.boost.near(
|
104
|
+
# field: :published_at,
|
105
|
+
# origin: Time.now,
|
106
|
+
# scale: '3d'
|
107
|
+
# )
|
108
|
+
#
|
109
|
+
# @example Boost near a number (with options)
|
110
|
+
# query.boost.near(
|
111
|
+
# field: :followers,
|
112
|
+
# origin: 100,
|
113
|
+
# scale: 50,
|
114
|
+
# offset: 2,
|
115
|
+
# type: :linear,
|
116
|
+
# decay: 0.75,
|
117
|
+
# weight: 10
|
118
|
+
# )
|
119
|
+
#
|
120
|
+
# @return [Base] Query with field decay filter added
|
121
|
+
#
|
122
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html Elastic Docs - Function Score Query
|
26
123
|
def near(options = {})
|
27
124
|
if options[:lat] || options[:latitude] ||
|
28
125
|
options[:lng] || options[:longitude] || options[:lon]
|
@@ -34,31 +131,73 @@ module Stretchy
|
|
34
131
|
end
|
35
132
|
alias :geo :near
|
36
133
|
|
134
|
+
#
|
135
|
+
# Adds a {Boosts::RandomBoost} to the query, for slightly
|
136
|
+
# randomizing search results.
|
137
|
+
#
|
138
|
+
# @param seed [Numeric] The seed for the random value
|
139
|
+
# @param weight [Numeric] The weight for this random value
|
140
|
+
#
|
141
|
+
# @return [Base] Query with random boost applied
|
142
|
+
#
|
143
|
+
# @see http://www.elastic.co/guide/en/elasticsearch/guide/master/random-scoring.html Elastic Docs - Random Scoring
|
37
144
|
def random(*args)
|
38
145
|
@boost_builder.functions << Stretchy::Boosts::RandomBoost.new(*args)
|
39
146
|
Base.new(self)
|
40
147
|
end
|
41
148
|
|
149
|
+
#
|
150
|
+
# Defines a global boost for all documents in the query
|
151
|
+
#
|
152
|
+
# @param num [Numeric] Boost to apply to the whole query
|
153
|
+
#
|
154
|
+
# @return [self] Boost context with overall boost applied
|
42
155
|
def all(num)
|
43
156
|
@boost_builder.overall_boost = num
|
44
157
|
self
|
45
158
|
end
|
46
159
|
|
160
|
+
#
|
161
|
+
# The maximum boost that any document can have
|
162
|
+
#
|
163
|
+
# @param num [Numeric] Maximum score a document can have
|
164
|
+
#
|
165
|
+
# @return [self] Boost context with maximum score applied
|
47
166
|
def max(num)
|
48
167
|
@boost_builder.max_boost = num
|
49
168
|
self
|
50
169
|
end
|
51
170
|
|
171
|
+
#
|
172
|
+
# Set scoring mode for when a document matches multiple
|
173
|
+
# boost functions.
|
174
|
+
#
|
175
|
+
# @param mode [Symbol] Score mode. Can be one of `multiply sum avg first max min`
|
176
|
+
#
|
177
|
+
# @return [self] Boost context with score mode applied
|
52
178
|
def score_mode(mode)
|
53
179
|
@boost_builder.score_mode = mode
|
54
180
|
self
|
55
181
|
end
|
56
182
|
|
183
|
+
#
|
184
|
+
# Set boost mode for when a document matches multiple
|
185
|
+
# boost functions.
|
186
|
+
#
|
187
|
+
# @param mode [Symbol] Boost mode. Can be one of `multiply replace sum avg max min`
|
188
|
+
#
|
189
|
+
# @return [self] Boost context with boost mode applied
|
57
190
|
def boost_mode(mode)
|
58
191
|
@boost_builder.boost_mode = mode
|
59
192
|
self
|
60
193
|
end
|
61
194
|
|
195
|
+
#
|
196
|
+
# Switches to inverse context - boosts added with {#where}
|
197
|
+
# and #{match} will be applied to documents which *do not*
|
198
|
+
# match said filters.
|
199
|
+
#
|
200
|
+
# @return [BoostClause] Boost clause in inverse context
|
62
201
|
def not(options = {})
|
63
202
|
self.class.new(self, options.merge(inverse: !inverse?))
|
64
203
|
end
|