stretchy 0.4.6 → 0.4.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/.editorconfig +12 -0
- data/CONTRIBUTING.md +52 -0
- data/README.md +157 -40
- data/lib/stretchy/builders/filter_builder.rb +12 -7
- data/lib/stretchy/builders/query_builder.rb +1 -1
- data/lib/stretchy/builders/where_builder.rb +7 -3
- data/lib/stretchy/clauses/base.rb +63 -57
- data/lib/stretchy/clauses/boost_clause.rb +47 -49
- data/lib/stretchy/clauses/boost_match_clause.rb +52 -19
- data/lib/stretchy/clauses/boost_where_clause.rb +46 -28
- data/lib/stretchy/clauses/match_clause.rb +52 -42
- data/lib/stretchy/clauses/where_clause.rb +86 -70
- data/lib/stretchy/filters/params_filter.rb +27 -0
- data/lib/stretchy/filters.rb +1 -0
- data/lib/stretchy/queries/params_query.rb +21 -0
- data/lib/stretchy/queries.rb +1 -0
- data/lib/stretchy/version.rb +1 -1
- metadata +6 -2
@@ -2,13 +2,13 @@ require 'stretchy/clauses/boost_clause'
|
|
2
2
|
|
3
3
|
module Stretchy
|
4
4
|
module Clauses
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# Boosts documents that match certain filters. Most filters will
|
7
|
-
# be passed into {#initialize}, but you can also use `.range` and
|
7
|
+
# be passed into {#initialize}, but you can also use `.range` and
|
8
8
|
# `.geo` .
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# @author [atevans]
|
11
|
-
#
|
11
|
+
#
|
12
12
|
class BoostWhereClause < BoostClause
|
13
13
|
|
14
14
|
def boost_where(params = {}, options = {})
|
@@ -20,18 +20,18 @@ module Stretchy
|
|
20
20
|
self
|
21
21
|
end
|
22
22
|
|
23
|
-
#
|
23
|
+
#
|
24
24
|
# Returns to the base context; filters passed here
|
25
25
|
# will be used to filter documents.
|
26
|
-
#
|
26
|
+
#
|
27
27
|
# @example Returning to base context
|
28
28
|
# query.boost.where(number_field: 33).where(other_field: 64)
|
29
|
-
#
|
29
|
+
#
|
30
30
|
# @example Staying in boost context
|
31
31
|
# query.boost.where(number_field: 33).boost.where(other_field: 99)
|
32
|
-
#
|
32
|
+
#
|
33
33
|
# @see {WhereClause#initialize}
|
34
|
-
#
|
34
|
+
#
|
35
35
|
# @return [WhereClause] Query with where clause applied
|
36
36
|
def where(*args)
|
37
37
|
WhereClause.new(base).where(*args)
|
@@ -42,64 +42,82 @@ module Stretchy
|
|
42
42
|
boost_where(params, options)
|
43
43
|
end
|
44
44
|
|
45
|
-
#
|
45
|
+
#
|
46
|
+
# Boosts documents that match a filter composed of arbitrary json
|
47
|
+
# passed into the method.
|
48
|
+
#
|
49
|
+
# @param [Hash] Parameters for the filter. See {WhereClause#filter}
|
50
|
+
# @param [Hash] Options for Stretchy to determine behavior of this boost
|
51
|
+
#
|
52
|
+
# @see {WhereClause#query}
|
53
|
+
#
|
54
|
+
# @return [Base] Query with filter boost applied
|
55
|
+
def filter(params = {}, options = {})
|
56
|
+
weight = params.delete(:weight) || options[:weight]
|
57
|
+
clause = WhereClause.new.filter(params)
|
58
|
+
boost = clause.to_boost
|
59
|
+
base.boost_builder.add_boost(boost) if boost
|
60
|
+
Base.new(base)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
46
64
|
# Returns to the base context. Queries passed here
|
47
65
|
# will be used to filter documents.
|
48
|
-
#
|
66
|
+
#
|
49
67
|
# @example Returning to base context
|
50
68
|
# query.boost.where(number_field: 89).match('username')
|
51
|
-
#
|
69
|
+
#
|
52
70
|
# @example Staying in boost context
|
53
71
|
# query.boost.where(number_field: 89).boost.match('love')
|
54
|
-
#
|
72
|
+
#
|
55
73
|
# @see {MatchClause#initialize}
|
56
|
-
#
|
74
|
+
#
|
57
75
|
# @return [MatchClause] Base context with match queries applied
|
58
76
|
def match(*args)
|
59
77
|
MatchClause.new(base).match(*args)
|
60
78
|
end
|
61
79
|
|
62
|
-
#
|
80
|
+
#
|
63
81
|
# Applies a range filter with a min or max
|
64
82
|
# as a boost.
|
65
|
-
#
|
83
|
+
#
|
66
84
|
# @see {WhereClause#range}
|
67
|
-
#
|
85
|
+
#
|
68
86
|
# @see {Filters::RangeFilter}
|
69
|
-
#
|
87
|
+
#
|
70
88
|
# @see http://www.elastic.co/guide/en/elasticsearch/guide/master/_ranges.html Elastic Guides - Ranges
|
71
|
-
#
|
89
|
+
#
|
72
90
|
# @return [Base] Query in base context with range boost applied
|
73
91
|
def range(field, options = {})
|
74
92
|
weight = options[:weight]
|
75
93
|
options[:inverse] = true if inverse?
|
76
|
-
|
94
|
+
|
77
95
|
clause = WhereClause.new.range(field, options)
|
78
96
|
boost = clause.to_boost(weight)
|
79
97
|
base.boost_builder.add_boost(boost) if boost
|
80
|
-
|
98
|
+
|
81
99
|
Base.new(base)
|
82
100
|
end
|
83
101
|
|
84
|
-
#
|
102
|
+
#
|
85
103
|
# Boosts a document if it matches a geo filter.
|
86
104
|
# This is different than {BoostClause#near} -
|
87
105
|
# while `.near` applies a decay function that boosts
|
88
106
|
# based on how close a field is to a geo point,
|
89
107
|
# `.geo` applies a filter that either boosts or doesn't
|
90
108
|
# boost the document.
|
91
|
-
#
|
109
|
+
#
|
92
110
|
# @see {WhereFunction#geo}
|
93
|
-
#
|
111
|
+
#
|
94
112
|
# @see {Filters::GeoFilter}
|
95
|
-
#
|
113
|
+
#
|
96
114
|
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-distance-filter.html Elastic Docs - Geo Distance Filter
|
97
|
-
#
|
115
|
+
#
|
98
116
|
# @return [Base] Query in base context with geo filter boost applied
|
99
117
|
def geo(field, options = {})
|
100
118
|
weight = options[:weight]
|
101
119
|
options[:inverse] = true if inverse?
|
102
|
-
|
120
|
+
|
103
121
|
clause = WhereClause.new.geo(field, options)
|
104
122
|
boost = clause.to_boost(weight)
|
105
123
|
base.boost_builder.add_boost(boost) if boost
|
@@ -107,4 +125,4 @@ module Stretchy
|
|
107
125
|
end
|
108
126
|
end
|
109
127
|
end
|
110
|
-
end
|
128
|
+
end
|
@@ -2,13 +2,13 @@ require 'stretchy/clauses/base'
|
|
2
2
|
|
3
3
|
module Stretchy
|
4
4
|
module Clauses
|
5
|
-
#
|
5
|
+
#
|
6
6
|
# A Match clause inherits the same state as any clause.
|
7
7
|
# There aren't any more specific methods to chain, as
|
8
8
|
# this clause only handles basic full-text searches.
|
9
|
-
#
|
9
|
+
#
|
10
10
|
# @author [atevans]
|
11
|
-
#
|
11
|
+
#
|
12
12
|
class MatchClause < Base
|
13
13
|
|
14
14
|
FULLTEXT_SLOP = 50
|
@@ -20,14 +20,27 @@ module Stretchy
|
|
20
20
|
self
|
21
21
|
end
|
22
22
|
|
23
|
-
#
|
23
|
+
#
|
24
|
+
# Add arbitrary json as a query in the appropriate context.
|
25
|
+
# This can be used to add query types that are not currently
|
26
|
+
# supported by Stretchy to be used in the final query.
|
27
|
+
#
|
28
|
+
# @param params = {} [Hash] Query to be applied to the new state
|
29
|
+
# @option options [true, false] :inverse (nil) Ignore query state and add to the `not` query
|
30
|
+
# @option options [true, false] :should (nil) Ignore query state and add to the `should` query
|
31
|
+
def query(params = {}, options = {})
|
32
|
+
base.add_query(Queries::ParamsQuery.new(params), merge_state(options))
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
24
37
|
# Specifies that values for this field should be
|
25
38
|
# matched with a proximity boost, rather than as
|
26
39
|
# a generic match query.
|
27
|
-
#
|
40
|
+
#
|
28
41
|
# This means searching for "quick brown fox" will
|
29
42
|
# return matches in the following order:
|
30
|
-
#
|
43
|
+
#
|
31
44
|
# * "the quick brown fox jumped over"
|
32
45
|
# * "the brown quick fox jumped over"
|
33
46
|
# * "the fox, brown & quick jumped over"
|
@@ -35,23 +48,23 @@ module Stretchy
|
|
35
48
|
# * "the quick green and purple sparkly fox jumped over"
|
36
49
|
# * "the quick dog jumped over"
|
37
50
|
# * "the adoreable puppy jumped over" **not returned**
|
38
|
-
#
|
51
|
+
#
|
39
52
|
# @overload phrase(params)
|
40
|
-
# @param params = {} [String] A phrase that will
|
53
|
+
# @param params = {} [String] A phrase that will
|
41
54
|
# be matched anywhere in the document
|
42
|
-
#
|
55
|
+
#
|
43
56
|
# @overload phrase(params)
|
44
57
|
# @param params = {} [Hash] A hash of fields and phrases
|
45
58
|
# that should be matched in those fields
|
46
|
-
#
|
59
|
+
#
|
47
60
|
# @example Matching multiple words together
|
48
61
|
# query.match.phrase('hugs and love')
|
49
|
-
#
|
62
|
+
#
|
50
63
|
# @example Not matching a phrase
|
51
64
|
# query.match.not.phrase(comment: 'offensive words to hide')
|
52
|
-
#
|
65
|
+
#
|
53
66
|
# @return [self] Allows continuing the query chain
|
54
|
-
#
|
67
|
+
#
|
55
68
|
# @see https://www.elastic.co/guide/en/elasticsearch/guide/current/proximity-relevance.html Elasticsearch guide: proximity for relevance
|
56
69
|
def fulltext(params = {})
|
57
70
|
add_params(params)
|
@@ -59,8 +72,8 @@ module Stretchy
|
|
59
72
|
self
|
60
73
|
end
|
61
74
|
|
62
|
-
#
|
63
|
-
# Adds a MoreLikeThis query to the chain. Pass in document ids,
|
75
|
+
#
|
76
|
+
# Adds a MoreLikeThis query to the chain. Pass in document ids,
|
64
77
|
# an array of index/name/ids, or a string to get documents that
|
65
78
|
# have similar terms.
|
66
79
|
#
|
@@ -81,9 +94,9 @@ module Stretchy
|
|
81
94
|
# Defaults to '_all'
|
82
95
|
# @option params [Array] :include Whether the source documents should be
|
83
96
|
# included in the result set. Defaults to `false`
|
84
|
-
#
|
97
|
+
#
|
85
98
|
# @return [MatchClause] allows continuing the query chain
|
86
|
-
#
|
99
|
+
#
|
87
100
|
# @example Getting more like a document by id
|
88
101
|
# query.more_like(ids: other_result_id)
|
89
102
|
#
|
@@ -92,32 +105,29 @@ module Stretchy
|
|
92
105
|
#
|
93
106
|
# @example Getting more like a document from a string
|
94
107
|
# query.more_like(like_text: 'puppies and kittens are great')
|
95
|
-
#
|
108
|
+
#
|
96
109
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-mlt-query.html Elasticsearch more-like-this query documentation
|
97
110
|
#
|
98
111
|
def more_like(params = {}, options = {})
|
99
112
|
query = Queries::MoreLikeThisQuery.new(params)
|
100
|
-
options
|
101
|
-
options[:should] = true if should?
|
102
|
-
|
103
|
-
base.add_query(query, options)
|
113
|
+
base.add_query(query, merge_state(options))
|
104
114
|
self
|
105
115
|
end
|
106
116
|
|
107
|
-
#
|
117
|
+
#
|
108
118
|
# Switches to inverted context. Matches applied here work the same way as
|
109
119
|
# {#initialize}, but returned documents must **not** match these filters.
|
110
|
-
#
|
120
|
+
#
|
111
121
|
# @overload not(params)
|
112
122
|
# @param [String] A string that must not be matched anywhere in the document
|
113
123
|
# @overload not(params)
|
114
124
|
# @param [Hash] A hash of fields and strings that must not be matched in those fields
|
115
|
-
#
|
125
|
+
#
|
116
126
|
# @return [MatchClause] inverted query state with match filters applied
|
117
|
-
#
|
127
|
+
#
|
118
128
|
# @example Inverted full-text
|
119
129
|
# query.match.not("hello")
|
120
|
-
#
|
130
|
+
#
|
121
131
|
# @example Inverted full-text matching for specific fields
|
122
132
|
# query.match.not(
|
123
133
|
# my_field: "not_match_1",
|
@@ -129,35 +139,35 @@ module Stretchy
|
|
129
139
|
self
|
130
140
|
end
|
131
141
|
|
132
|
-
#
|
142
|
+
#
|
133
143
|
# Switches to `should` context. Applies full-text matches
|
134
144
|
# that are not required, but boost the relevance score for
|
135
145
|
# matching documents.
|
136
|
-
#
|
146
|
+
#
|
137
147
|
# Can be chained with {#not}
|
138
|
-
#
|
148
|
+
#
|
139
149
|
# @overload should(params)
|
140
150
|
# @param [String] A string that should be matched anywhere in the document
|
141
151
|
# @overload should(params)
|
142
152
|
# @param [Hash] A hash of fields and strings that should be matched in those fields
|
143
|
-
#
|
153
|
+
#
|
144
154
|
# @return [MatchClause] query state with should filters added
|
145
|
-
#
|
155
|
+
#
|
146
156
|
# @example Should match with full-text
|
147
157
|
# query.match.should("anywhere")
|
148
|
-
#
|
158
|
+
#
|
149
159
|
# @example Should match specific fields
|
150
160
|
# query.match.should(
|
151
161
|
# field_one: "one",
|
152
162
|
# field_two: "two"
|
153
163
|
# )
|
154
|
-
#
|
164
|
+
#
|
155
165
|
# @example Should not match
|
156
166
|
# query.match.should.not(
|
157
167
|
# field_one: "one",
|
158
168
|
# field_two: "two"
|
159
169
|
# )
|
160
|
-
#
|
170
|
+
#
|
161
171
|
# @see http://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html Elastic Docs - Bool Query
|
162
172
|
def should(params = {}, options = {})
|
163
173
|
@should = true
|
@@ -166,16 +176,16 @@ module Stretchy
|
|
166
176
|
self
|
167
177
|
end
|
168
178
|
|
169
|
-
#
|
179
|
+
#
|
170
180
|
# Converts this match context to a set of boosts
|
171
181
|
# to use in a {Stretchy::Queries::FunctionScoreQuery}
|
172
|
-
#
|
182
|
+
#
|
173
183
|
# @param weight = nil [Numeric] Weight of generated boost
|
174
|
-
#
|
184
|
+
#
|
175
185
|
# @return [Stretchy::Boosts::FilterBoost] boost containing these match parameters
|
176
186
|
def to_boost(weight = nil)
|
177
187
|
weight ||= Stretchy::Boosts::FilterBoost::DEFAULT_WEIGHT
|
178
|
-
|
188
|
+
|
179
189
|
Stretchy::Boosts::FilterBoost.new(
|
180
190
|
filter: Stretchy::Filters::QueryFilter.new(
|
181
191
|
base.match_builder.to_query
|
@@ -184,9 +194,9 @@ module Stretchy
|
|
184
194
|
)
|
185
195
|
end
|
186
196
|
|
187
|
-
#
|
197
|
+
#
|
188
198
|
# Accessor for `@should`
|
189
|
-
#
|
199
|
+
#
|
190
200
|
# @return [true, false] `@should`
|
191
201
|
def should?
|
192
202
|
!!@should
|
@@ -213,4 +223,4 @@ module Stretchy
|
|
213
223
|
|
214
224
|
end
|
215
225
|
end
|
216
|
-
end
|
226
|
+
end
|