stretchy 0.3.6 → 0.3.7
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 +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
|