stretchy 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,241 +0,0 @@
1
- module Stretchy
2
- class Query
3
-
4
- DEFAULT_LIMIT = 40
5
-
6
- def initialize(options = {})
7
- @offset = options[:offset] || 0
8
- @limit = options[:limit] || DEFAULT_LIMIT
9
- @match = options[:match]
10
- @filters = options[:filters]
11
- @not_filters = options[:not_filters]
12
- @boosts = options[:boosts]
13
- @type = options[:type] || 'documents'
14
- @index = options[:index] || Stretchy.index_name
15
- @explain = options[:explain]
16
- end
17
-
18
- def clone_with(options)
19
- filters = [@filters, options[:filters], options[:filter]].flatten.compact
20
- not_filters = [@not_filters, options[:not_filters], options[:not_filter]].flatten.compact
21
- boosts = [@boosts, options[:boosts], options[:boost]].flatten.compact
22
-
23
- self.class.new(
24
- type: @type,
25
- index: @index,
26
- explain: options[:explain] || @explain,
27
- offset: options[:offset] || @offset,
28
- limit: options[:limit] || @limit,
29
- match: options[:match] || @match,
30
- filters: filters,
31
- not_filters: not_filters,
32
- boosts: boosts
33
- )
34
- end
35
-
36
- def offset(num)
37
- clone_with(offset: num)
38
- end
39
-
40
- def limit(num)
41
- clone_with(limit: num)
42
- end
43
-
44
- def explain
45
- clone_with(explain: true)
46
- end
47
-
48
- def where(options = {})
49
- return self if options.empty?
50
- filters, not_filters = where_clause(options)
51
- clone_with(filters: filters, not_filters: not_filters)
52
- end
53
-
54
- def not_where(options = {})
55
- return self if options.empty?
56
- # reverse the order returned here, since we're doing not_where
57
- not_filters, filters = where_clause(options)
58
- clone_with(filters: filters, not_filters: not_filters)
59
- end
60
-
61
- def boost_where(options = {})
62
- return self if options.empty?
63
- weight = options.delete(:weight) || 1.2
64
-
65
- boosts = options.map do |field, values|
66
- filter = nil
67
- if values.nil?
68
- filter = Filters::NotFilter.new(Filters::ExistsFilter.new(field))
69
- else
70
- filter = Filters::TermsFilter.new(field: field, values: Array(values))
71
- end
72
- Boosts::FilterBoost.new(filter: filter, weight: weight)
73
- end
74
- clone_with(boosts: boosts)
75
- end
76
-
77
- def boost_not_where(options = {})
78
- return self if options.empty?
79
- weight = options.delete(:weight) || 1.2
80
-
81
- boosts = []
82
- options.each do |field, values|
83
- filter = nil
84
- if values.nil?
85
- filter = Filters::ExistsFilter.new(field)
86
- elsif values.is_a?(Array)
87
- filter = NotFilter.new(Filters::TermsFilter.new(field: field, values: Array(values)))
88
- end
89
- Boosts::FilterBoost.new(filter: filter)
90
- end
91
- clone_with(boosts: boosts)
92
- end
93
-
94
- def range(field:, min: nil, max: nil)
95
- return self unless min || max
96
- clone_with(filter: Filters::RangeFilter.new(
97
- field: field,
98
- min: min,
99
- max: max
100
- ))
101
- end
102
-
103
- def not_range(field:, min: nil, max: nil)
104
- return self unless min || max
105
- clone_with(not_filter: Filters::RangeFilter.new(
106
- field: field,
107
- min: min,
108
- max: max
109
- ))
110
- end
111
-
112
- def boost_range(field:, min: nil, max: nil, weight: 1.2)
113
- return self unless min || max
114
- clone_with(boost: Boosts::Boost.new(
115
- filter: Filters::RangeFilter.new(
116
- field: field,
117
- min: min,
118
- max: max
119
- ),
120
- weight: weight
121
- ))
122
- end
123
-
124
- def geo(field: 'coords', distance: '50km', lat:, lng:)
125
- clone_with(filter: Filters::GeoFilter.new(
126
- field: field,
127
- distance: distance,
128
- lat: lat,
129
- lng: lng
130
- ))
131
- end
132
-
133
- def not_geo(field: 'coords', distance: '50km', lat:, lng:)
134
- clone_with(not_filter: Filters::GeoFilter.new(
135
- field: field,
136
- distance: distance,
137
- lat: lat,
138
- lng: lng
139
- ))
140
- end
141
-
142
- def boost_geo(options = {})
143
- return self if options.empty?
144
- clone_with(boost: Boosts::GeoBoost.new(options))
145
- end
146
-
147
- def match(string, options = {})
148
- return self if string.nil? || string.empty?
149
- field = options[:field] || '_all'
150
- operator = options[:operator] || 'and'
151
- clone_with(match: Queries::MatchQuery.new(string, field: field, operator: operator))
152
- end
153
-
154
- def not_match(string, options = {})
155
- field = options[:field] || '_all'
156
- operator = options[:operator] || 'and'
157
- clone_with(not_filter: Filters::QueryFilter.new(
158
- Queries::MatchQuery.new(string, field: field, operator: operator)
159
- ))
160
- end
161
-
162
- def boost_random(seed)
163
- clone_with(boost: Boosts::RandomBoost.new(seed))
164
- end
165
-
166
- def request
167
- @request ||= RequestBody.new(
168
- match: @match,
169
- filters: @filters,
170
- not_filters: @not_filters,
171
- boosts: @boosts,
172
- limit: @limit,
173
- offset: @offset,
174
- explain: @explain
175
- )
176
- end
177
-
178
- def response
179
- @response = Stretchy.search(type: @type, body: request.to_search)
180
- # caution: huuuuge logs, but pretty helpful
181
- # Rails.logger.debug(Colorize.purple(JSON.pretty_generate(@response)))
182
- @response
183
- end
184
-
185
- def id_response
186
- @id_response ||= Stretchy.search(type: @type, body: request.to_search, fields: [])
187
- # caution: huuuuge logs, but pretty helpful
188
- # Rails.logger.debug(Colorize.purple(JSON.pretty_generate(@id_response)))
189
- @id_response
190
- end
191
-
192
- def results
193
- @results ||= response['hits']['hits'].map do |hit|
194
- hit['_source'].merge(
195
- '_id' => hit['_id'],
196
- '_type' => hit['_type'],
197
- '_index' => hit['_index']
198
- )
199
- end
200
- end
201
-
202
- def ids
203
- @ids ||= result_metadata('hits', 'hits').map do |hit|
204
- hit['_id'].to_i == 0 ? hit['_id'] : hit['_id'].to_i
205
- end
206
- end
207
-
208
- def shards
209
- @shards ||= result_metadata('shards')
210
- end
211
-
212
- def total
213
- @total ||= result_metadata('hits', 'total')
214
- end
215
-
216
- private
217
- def result_metadata(*args)
218
- if @response
219
- args.reduce(@response){|json, field| json.nil? ? nil : json[field] }
220
- else
221
- args.reduce(id_response){|json, field| json.nil? ? nil : json[field] }
222
- end
223
- end
224
-
225
- def where_clause(options = {})
226
- return [[], []] if options.empty?
227
- filters = []
228
- not_filters = []
229
-
230
- options.each do |field, values|
231
- if values.nil?
232
- not_filters << Filters::ExistsFilter.new(field)
233
- else
234
- filters << Filters::TermsFilter.new(field: field, values: Array(values))
235
- end
236
- end
237
- [filters, not_filters]
238
- end
239
-
240
- end
241
- end
@@ -1,67 +0,0 @@
1
- module Stretchy
2
- class RequestBody
3
-
4
- def initialize(options = {})
5
- @json = {}
6
- @match = options[:match]
7
- @filters = Array(options[:filters])
8
- @not_filters = Array(options[:not_filters])
9
- @boosts = Array(options[:boosts])
10
- @offset = options[:offset] || 0
11
- @limit = options[:limit] || Query::DEFAULT_LIMIT
12
- @explain = options[:explain]
13
- end
14
-
15
- def to_search
16
- return @json unless @json.empty?
17
-
18
- query = @match || Queries::MatchAllQuery.new
19
-
20
- if @filters.any? && @not_filters.any?
21
- query = Queries::FilteredQuery.new(
22
- query: query,
23
- filter: Filters::BoolFilter.new(
24
- must: @filters,
25
- must_not: @not_filters
26
- )
27
- )
28
- elsif @filters.any?
29
- if @filters.count == 1
30
- query = Queries::FilteredQuery.new(
31
- query: query,
32
- filter: @filters.first
33
- )
34
- else
35
- query = Queries::FilteredQuery.new(
36
- query: query,
37
- filter: Filters::AndFilter.new(@filters)
38
- )
39
- end
40
- elsif @not_filters.any?
41
- query = Queries::FilteredQuery.new(
42
- query: query,
43
- filter: Filters::NotFilter.new(@not_filters)
44
- )
45
- end
46
-
47
- if @boosts.any?
48
- query = Queries::FunctionScoreQuery.new(
49
- query: query,
50
- functions: @boosts,
51
- score_mode: 'sum',
52
- boost_mode: 'max'
53
- )
54
- end
55
-
56
- @json = {}
57
- @json[:query] = query.to_search
58
- @json[:from] = @offset
59
- @json[:size] = @limit
60
- @json[:explain] = @explain if @explain
61
-
62
- # not a ton of output, usually worth having
63
- # puts "Generated elastic query: #{JSON.pretty_generate(@json)}"
64
- @json
65
- end
66
- end
67
- end