search_flip 1.0.0

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.
@@ -0,0 +1,252 @@
1
+
2
+ module SearchFlip
3
+ # The SearchFlip::PostFilterable mixin provides chainable methods like
4
+ # #post_where, #post_exists, #post_range, etc to add and apply search
5
+ # filters after aggregations have already been calculated.
6
+ #
7
+ # @example
8
+ # query = ProductIndex.search("harry potter")
9
+ #
10
+ # query = query.aggregate(price_ranges: {
11
+ # range: {
12
+ # field: "price",
13
+ # ranges: [
14
+ # { key: "range1", from: 0, to: 20 },
15
+ # { key: "range2", from: 20, to: 50 },
16
+ # { key: "range3", from: 50, to: 100 }
17
+ # ]
18
+ # }
19
+ # })
20
+ #
21
+ # query = query.post_where(price: 20 ... 50)
22
+
23
+ module PostFilterable
24
+ def self.included(base)
25
+ base.class_eval do
26
+ attr_accessor :post_search_values, :post_must_values, :post_must_not_values, :post_should_values, :post_filter_values
27
+ end
28
+ end
29
+
30
+ # Adds a post query string query to the criteria while using AND as the
31
+ # default operator unless otherwise specified. Check out the
32
+ # ElasticSearch docs for further details.
33
+ #
34
+ # @example
35
+ # CommentIndex.aggregate(:user_id).post_search("message:hello OR message:worl*")
36
+ #
37
+ # @param q [String] The query string query
38
+ #
39
+ # @param options [Hash] Additional options for the query string query, like
40
+ # eg default_operator, default_field, etc.
41
+ #
42
+ # @return [SearchFlip::Criteria] A newly created extended criteria
43
+
44
+ def post_search(q, options = {})
45
+ raise(SearchFlip::NotSupportedError) if SearchFlip.version.to_i < 2
46
+
47
+ fresh.tap do |criteria|
48
+ criteria.post_search_values = (post_search_values || []) + [query_string: { query: q, :default_operator => :AND }.merge(options)] if q.to_s.strip.length > 0
49
+ end
50
+ end
51
+
52
+ # Adds post filters to your criteria for the supplied hash composed of
53
+ # field-to-filter mappings which specify terms, term or range filters,
54
+ # depending on the type of the respective hash value, namely array, range
55
+ # or scalar type like Fixnum, String, etc.
56
+ #
57
+ # @example Array values
58
+ # query = CommentIndex.aggregate("...")
59
+ # query = query.post_where(id: [1, 2, 3], state: ["approved", "declined"])
60
+ #
61
+ # @example Range values
62
+ # query = CommentIndex.aggregate("...")
63
+ # query = query.post_where(created_at: Time.parse("2016-01-01") .. Time.parse("2017-01-01"))
64
+ #
65
+ # @example Scalar types
66
+ # query = CommentIndex.aggregate("...")
67
+ # query = query.post_where(id: 1, message: "hello world")
68
+ #
69
+ # @param hash [Hash] A field-to-filter mapping specifying filter values for
70
+ # the respective fields
71
+ #
72
+ # @return [SearchFlip::Criteria] A newly created extended criteria
73
+
74
+ def post_where(hash)
75
+ hash.inject(fresh) do |memo, (key, value)|
76
+ if value.is_a?(Array)
77
+ memo.post_filter terms: { key => value }
78
+ elsif value.is_a?(Range)
79
+ memo.post_filter range: { key => { gte: value.min, lte: value.max } }
80
+ else
81
+ memo.post_filter term: { key => value }
82
+ end
83
+ end
84
+ end
85
+
86
+ # Adds post filters to exclude documents in accordance to the supplied hash
87
+ # composed of field-to-filter mappings. Check out #post_where for further
88
+ # details.
89
+ #
90
+ # @example Array values
91
+ # query = CommentIndex.aggregate("...")
92
+ # query = query.post_where_not(id: [1, 2, 3])
93
+ #
94
+ # @example Range values
95
+ # query = CommentIndex.aggregate("...")
96
+ # query = query.post_where_not(created_at: Time.parse("2016-01-01") .. Time.parse("2017-01-01"))
97
+ #
98
+ # @example
99
+ # query = CommentIndex.aggregate("...")
100
+ # query = query.post_where_not(state: "approved")
101
+ #
102
+ # @param hash [Hash] A field-to-filter mapping specifying filter values for the
103
+ # respective fields
104
+ #
105
+ # @return [SearchFlip::Criteria] A newly created extended criteria
106
+
107
+ def post_where_not(hash)
108
+ hash.inject(fresh) do |memo, (key,value)|
109
+ if value.is_a?(Array)
110
+ memo.post_must_not terms: { key => value }
111
+ elsif value.is_a?(Range)
112
+ memo.post_must_not range: { key => { gte: value.min, lte: value.max } }
113
+ else
114
+ memo.post_must_not term: { key => value }
115
+ end
116
+ end
117
+ end
118
+
119
+ # Adds raw post filter queries to the criteria.
120
+ #
121
+ # @example Raw post term filter query
122
+ # query = CommentIndex.aggregate("...")
123
+ # query = query.post_filter(term: { state: "new" })
124
+ #
125
+ # @example Raw post range filter query
126
+ # query = CommentIndex.aggregate("...")
127
+ # query = query.post_filter(range: { created_at: { gte: Time.parse("2016-01-01") }})
128
+ #
129
+ # @param args [Array, Hash] The raw filter query arguments
130
+ #
131
+ # @return [SearchFlip::Criteria] A newly created extended criteria
132
+
133
+ def post_filter(*args)
134
+ fresh.tap do |criteria|
135
+ criteria.post_filter_values = (post_filter_values || []) + args
136
+ end
137
+ end
138
+
139
+ # Adds raw post must queries to the criteria.
140
+ #
141
+ # @example Raw post term must query
142
+ # query = CommentIndex.aggregate("...")
143
+ # query = query.post_must(term: { state: "new" })
144
+ #
145
+ # @example Raw post range must query
146
+ # query = CommentIndex.aggregate("...")
147
+ # query = query.post_must(range: { created_at: { gte: Time.parse("2016-01-01") }})
148
+ #
149
+ # @param args [Array, Hash] The raw must query arguments
150
+ #
151
+ # @return [SearchFlip::Criteria] A newly created extended criteria
152
+
153
+ def post_must(*args)
154
+ fresh.tap do |criteria|
155
+ criteria.post_must_values = (post_must_values || []) + args
156
+ end
157
+ end
158
+
159
+ # Adds raw post must_not queries to the criteria.
160
+ #
161
+ # @example Raw post term must_not query
162
+ # query = CommentIndex.aggregate("...")
163
+ # query = query.post_must_not(term: { state: "new" })
164
+ #
165
+ # @example Raw post range must_not query
166
+ # query = CommentIndex.aggregate("...")
167
+ # query = query.post_must_not(range: { created_at: { gte: Time.parse("2016-01-01") }})
168
+ #
169
+ # @param args [Array, Hash] The raw must_not query arguments
170
+ #
171
+ # @return [SearchFlip::Criteria] A newly created extended criteria
172
+
173
+ def post_must_not(*args)
174
+ fresh.tap do |criteria|
175
+ criteria.post_must_not_values = (post_must_not_values || []) + args
176
+ end
177
+ end
178
+
179
+ # Adds raw post should queries to the criteria.
180
+ #
181
+ # @example Raw post term should query
182
+ # query = CommentIndex.aggregate("...")
183
+ # query = query.post_should(term: { state: "new" })
184
+ #
185
+ # @example Raw post range should query
186
+ # query = CommentIndex.aggregate("...")
187
+ # query = query.post_should(range: { created_at: { gte: Time.parse("2016-01-01") }})
188
+ #
189
+ # @param args [Array, Hash] The raw should query arguments
190
+ #
191
+ # @return [SearchFlip::Criteria] A newly created extended criteria
192
+
193
+ def post_should(*args)
194
+ fresh.tap do |criteria|
195
+ criteria.post_should_values = (post_should_values || []) + args
196
+ end
197
+ end
198
+
199
+ # Adds a post range filter to the criteria without being forced to specify
200
+ # the left and right end of the range, such that you can eg simply specify
201
+ # lt, lte, gt and gte. For fully specified ranges, you can easily use
202
+ # #post_where, etc. Check out the ElasticSearch docs for further details
203
+ # regarding the range filter.
204
+ #
205
+ # @example
206
+ # query = CommentIndex.aggregate("...")
207
+ # query = query.post_range(:created_at, gte: Time.parse("2016-01-01"))
208
+ #
209
+ # query = CommentIndex.aggregate("...")
210
+ # query = query.post_range(:likes_count, gt: 10, lt: 100)
211
+ #
212
+ # @param field [Symbol, String] The field name to specify the range for
213
+ # @param options [Hash] The range filter specification, like lt, lte, etc
214
+ #
215
+ # @return [SearchFlip::Criteria] A newly created extended criteria
216
+
217
+ def post_range(field, options = {})
218
+ post_filter range: { field => options }
219
+ end
220
+
221
+ # Adds a post exists filter to the criteria, which selects all documents
222
+ # for which the specified field has a non-null value.
223
+ #
224
+ # @example
225
+ # query = CommentIndex.aggregate("...")
226
+ # query = query.post_exists(:notified_at)
227
+ #
228
+ # @param field [Symbol, String] The field that should have a non-null value
229
+ #
230
+ # @return [SearchFlip::Criteria] A newly created extended criteria
231
+
232
+ def post_exists(field)
233
+ post_filter exists: { field: field }
234
+ end
235
+
236
+ # Adds a post exists not filter to the criteria, which selects all documents
237
+ # for which the specified field's value is null.
238
+ #
239
+ # @example
240
+ # query = CommentIndex.aggregate("...")
241
+ # query = query.post_exists_not(:notified_at)
242
+ #
243
+ # @param field [Symbol, String] The field that should have a null value
244
+ #
245
+ # @return [SearchFlip::Criteria] A newly created extended criteria
246
+
247
+ def post_exists_not(field)
248
+ post_must_not exists: { field: field }
249
+ end
250
+ end
251
+ end
252
+
@@ -0,0 +1,319 @@
1
+
2
+ module SearchFlip
3
+ # The SearchFlip::Response class wraps a raw SearchFlip response and
4
+ # decorates it with methods for aggregations, hits, records, pagination, etc.
5
+
6
+ class Response
7
+ extend Forwardable
8
+
9
+ attr_accessor :criteria, :response
10
+
11
+ # @api private
12
+ #
13
+ # Initializes a new response object for the provided criteria and raw
14
+ # ElasticSearch response.
15
+
16
+ def initialize(criteria, response)
17
+ self.criteria = criteria
18
+ self.response = response
19
+ end
20
+
21
+ # Returns the raw response, ie a hash derived from the ElasticSearch JSON
22
+ # response.
23
+ #
24
+ # @example
25
+ # CommentIndex.search("hello world").raw_response
26
+ # # => {"took"=>3, "timed_out"=>false, "_shards"=>"..."}
27
+ #
28
+ # @return [Hash] The raw response hash
29
+
30
+ def raw_response
31
+ response
32
+ end
33
+
34
+ # Returns the total number of results.
35
+ #
36
+ # @example
37
+ # CommentIndex.search("hello world").total_entries
38
+ # # => 13
39
+ #
40
+ # @return [Fixnum] The total number of results
41
+
42
+ def total_entries
43
+ hits["total"]
44
+ end
45
+
46
+ alias_method :total_count, :total_entries
47
+
48
+ # Returns whether or not the current page is the first page.
49
+ #
50
+ # @example
51
+ # CommentIndex.paginate(page: 1).first_page?
52
+ # # => true
53
+ #
54
+ # CommentIndex.paginate(page: 2).first_page?
55
+ # # => false
56
+ #
57
+ # @return [Boolean] Returns true if the current page is the
58
+ # first page or false otherwise
59
+
60
+ def first_page?
61
+ current_page == 1
62
+ end
63
+
64
+ # Returns whether or not the current page is the last page.
65
+ #
66
+ # @example
67
+ # CommentIndex.paginate(page: 100).last_page?
68
+ # # => true
69
+ #
70
+ # CommentIndex.paginate(page: 1).last_page?
71
+ # # => false
72
+ #
73
+ # @return [Boolean] Returns true if the current page is the
74
+ # last page or false otherwise
75
+
76
+ def last_page?
77
+ current_page == total_pages
78
+ end
79
+
80
+ # Returns whether or not the current page is out of range,
81
+ # ie. smaller than 1 or larger than #total_pages
82
+ #
83
+ # @example
84
+ # CommentIndex.paginate(page: 1_000_000).out_of_range?
85
+ # # => true
86
+ #
87
+ # CommentIndex.paginate(page: 1).out_of_range?
88
+ # # => false
89
+ #
90
+ # @return [Boolean] Returns true if the current page is out
91
+ # of range
92
+
93
+ def out_of_range?
94
+ current_page < 1 || current_page > total_pages
95
+ end
96
+
97
+ # Returns the current page number, useful for pagination.
98
+ #
99
+ # @example
100
+ # CommentIndex.search("hello world").paginate(page: 10).current_page
101
+ # # => 10
102
+ #
103
+ # @return [Fixnum] The current page number
104
+
105
+ def current_page
106
+ 1 + (criteria.offset_value_with_default / criteria.limit_value_with_default.to_f).ceil
107
+ end
108
+
109
+ # Returns the number of total pages for the current pagination settings, ie
110
+ # per page/limit settings.
111
+ #
112
+ # @example
113
+ # CommentIndex.search("hello world").paginate(per_page: 60).total_pages
114
+ # # => 5
115
+ #
116
+ # @return [Fixnum] The total number of pages
117
+
118
+ def total_pages
119
+ [(total_entries / criteria.limit_value_with_default.to_f).ceil, 1].max
120
+ end
121
+
122
+ # Returns the previous page number or nil if no previous page exists, ie if
123
+ # the current page is the first page.
124
+ #
125
+ # @example
126
+ # CommentIndex.search("hello world").paginate(page: 2).previous_page
127
+ # # => 1
128
+ #
129
+ # CommentIndex.search("hello world").paginate(page: 1).previous_page
130
+ # # => nil
131
+ #
132
+ # @return [Fixnum, nil] The previous page number
133
+
134
+ def previous_page
135
+ return nil if current_page <= 1
136
+ return total_pages if current_page > total_pages
137
+
138
+ current_page - 1
139
+ end
140
+
141
+ alias_method :prev_page, :previous_page
142
+
143
+ # Returns the next page number or nil if there is no next page, ie the
144
+ # current page is the last page.
145
+ #
146
+ # @example
147
+ # CommentIndex.search("hello world").paginate(page: 2).next_page
148
+ # # => 3
149
+ #
150
+ # @return [Fixnum, nil] The next page number
151
+
152
+ def next_page
153
+ return nil if current_page >= total_pages
154
+ return 1 if current_page < 1
155
+
156
+ return current_page + 1
157
+ end
158
+
159
+ # Returns the results, ie hits, wrapped in a SearchFlip::Result object
160
+ # which basically is a Hashie::Mash. Check out the Hashie docs for further
161
+ # details.
162
+ #
163
+ # @example
164
+ # CommentIndex.search("hello world").results
165
+ # # => [#<SearchFlip::Result ...>, ...]
166
+ #
167
+ # @return [Array] An array of results
168
+
169
+ def results
170
+ @results ||= hits["hits"].map { |hit| Result.new hit["_source"].merge(hit["highlight"] ? { highlight: hit["highlight"] } : {}) }
171
+ end
172
+
173
+ # Returns the named sugggetion, if a name is specified or alle suggestions.
174
+ #
175
+ # @example
176
+ # query = CommentIndex.suggest(:suggestion, text: "helo", term: { field: "message" })
177
+ # query.suggestions # => {"suggestion"=>[{"text"=>...}, ...]}
178
+ #
179
+ # @example Named suggestions
180
+ # query = CommentIndex.suggest(:suggestion, text: "helo", term: { field: "message" })
181
+ # query.suggestions(:sugestion).first["text"] # => "hello"
182
+ #
183
+ # @return [Hash, Array] The named suggestion or all suggestions
184
+
185
+ def suggestions(name = nil)
186
+ if name
187
+ response["suggest"][name.to_s].first["options"]
188
+ else
189
+ response["suggest"]
190
+ end
191
+ end
192
+
193
+ # Returns the hits returned by ElasticSearch.
194
+ #
195
+ # @example
196
+ # CommentIndex.search("hello world").hits
197
+ # # => {"total"=>3, "max_score"=>2.34, "hits"=>[{...}, ...]}
198
+ #
199
+ # @return [Hash] The hits returned by ElasticSearch
200
+
201
+ def hits
202
+ response["hits"]
203
+ end
204
+
205
+ # Returns the scroll id returned by ElasticSearch, that can be used in the
206
+ # following request to fetch the next batch of records.
207
+ #
208
+ # @example
209
+ # CommentIndex.scroll(timeout: "1m").scroll_id #=> "cXVlcnlUaGVuRmV0Y2..."
210
+ #
211
+ # @return [String] The scroll id returned by ElasticSearch
212
+
213
+ def scroll_id
214
+ response["_scroll_id"]
215
+ end
216
+
217
+ # Returns the database records, usually ActiveRecord objects, depending on
218
+ # the ORM you're using. The records are sorted using the order returned by
219
+ # ElasticSearch.
220
+ #
221
+ # @example
222
+ # CommentIndex.search("hello world").records # => [#<Comment ...>, ...]
223
+ #
224
+ # @return [Array] An array of database records
225
+
226
+ def records(options = {})
227
+ @records ||= begin
228
+ sort_map = ids.each_with_index.each_with_object({}) { |(id, index), hash| hash[id.to_s] = index }
229
+
230
+ scope.to_a.sort_by { |record| sort_map[criteria.target.record_id(record).to_s] }
231
+ end
232
+ end
233
+
234
+ # Builds and returns a scope for the array of ids in the current result set
235
+ # returned by ElasticSearch, including the eager load, preload and includes
236
+ # associations, if specified. A scope is eg an ActiveRecord::Relation,
237
+ # depending on the ORM you're using.
238
+ #
239
+ # @example
240
+ # CommentIndex.preload(:user).scope # => #<Comment::ActiveRecord_Criteria:0x0...>
241
+ #
242
+ # @return The scope for the array of ids in the current result set
243
+
244
+ def scope
245
+ res = criteria.target.fetch_records(ids)
246
+
247
+ res = res.includes(*criteria.includes_values) if criteria.includes_values
248
+ res = res.eager_load(*criteria.eager_load_values) if criteria.eager_load_values
249
+ res = res.preload(*criteria.preload_values) if criteria.preload_values
250
+
251
+ res
252
+ end
253
+
254
+ # Returns the array of ids returned by ElasticSearch for the current result
255
+ # set, ie the ids listed in the hits section of the response.
256
+ #
257
+ # @example
258
+ # CommentIndex.match_all.ids # => [20341, 12942, ...]
259
+ #
260
+ # @return The array of ids in the current result set
261
+
262
+ def ids
263
+ @ids ||= hits["hits"].map { |hit| hit["_id"] }
264
+ end
265
+
266
+ def_delegators :ids, :size, :count, :length
267
+
268
+ # Returns the response time in milliseconds of ElasticSearch specified in
269
+ # the took info of the response.
270
+ #
271
+ # @example
272
+ # CommentIndex.match_all.took # => 6
273
+ #
274
+ # @return [Fixnum] The ElasticSearch response time in milliseconds
275
+
276
+ def took
277
+ response["took"]
278
+ end
279
+
280
+ # Returns a single or all aggregations returned by ElasticSearch, depending
281
+ # on whether or not a name is specified. If no name is specified, the raw
282
+ # aggregation hash is simply returned. Contrary, if a name is specified,
283
+ # only this aggregation is returned. Moreover, if a name is specified and
284
+ # the aggregation includes a buckets section, a post-processed aggregation
285
+ # hash is returned.
286
+ #
287
+ # @example All aggregations
288
+ # CommentIndex.aggregate(:user_id).aggregations
289
+ # # => {"user_id"=>{..., "buckets"=>[{"key"=>4922, "doc_count"=>1129}, ...]}
290
+ #
291
+ # @example Specific and post-processed aggregations
292
+ # CommentIndex.aggregate(:user_id).aggregations(:user_id)
293
+ # # => {4922=>1129, ...}
294
+ #
295
+ # @return [Hash] Specific or all aggregations returned by ElasticSearch
296
+
297
+ def aggregations(name = nil)
298
+ return response["aggregations"] || {} unless name
299
+
300
+ @aggregations ||= {}
301
+
302
+ key = name.to_s
303
+
304
+ return @aggregations[key] if @aggregations.key?(key)
305
+
306
+ @aggregations[key] =
307
+ if response["aggregations"].nil? || response["aggregations"][key].nil?
308
+ Result.new
309
+ elsif response["aggregations"][key]["buckets"].is_a?(Array)
310
+ response["aggregations"][key]["buckets"].each_with_object({}) { |bucket, hash| hash[bucket["key"]] = Result.new(bucket) }
311
+ elsif response["aggregations"][key]["buckets"].is_a?(Hash)
312
+ Result.new response["aggregations"][key]["buckets"]
313
+ else
314
+ Result.new response["aggregations"][key]
315
+ end
316
+ end
317
+ end
318
+ end
319
+
@@ -0,0 +1,12 @@
1
+
2
+ module SearchFlip
3
+ # The SearchFlip::Result class basically is a hash wrapper that uses
4
+ # Hashie::Mash to provide convenient method access to the hash attributes.
5
+
6
+ class Result < Hashie::Mash
7
+ def self.disable_warnings?
8
+ true
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,31 @@
1
+
2
+ require "time"
3
+ require "date"
4
+ require "json"
5
+
6
+ class Time
7
+ def to_json
8
+ iso8601(6).to_json
9
+ end
10
+ end
11
+
12
+ class Date
13
+ def to_json
14
+ iso8601.to_json
15
+ end
16
+ end
17
+
18
+ class DateTime
19
+ def to_json
20
+ iso8601(6).to_json
21
+ end
22
+ end
23
+
24
+ if defined?(ActiveSupport)
25
+ class ActiveSupport::TimeWithZone
26
+ def to_json
27
+ iso8601(6).to_json
28
+ end
29
+ end
30
+ end
31
+
@@ -0,0 +1,5 @@
1
+
2
+ module SearchFlip
3
+ VERSION = "1.0.0"
4
+ end
5
+
@@ -0,0 +1,82 @@
1
+
2
+ require "forwardable"
3
+ require "http"
4
+ require "hashie"
5
+ require "oj"
6
+ require "set"
7
+
8
+ require "search_flip/version"
9
+ require "search_flip/json"
10
+ require "search_flip/http_client"
11
+ require "search_flip/config"
12
+ require "search_flip/bulk"
13
+ require "search_flip/filterable"
14
+ require "search_flip/post_filterable"
15
+ require "search_flip/aggregatable"
16
+ require "search_flip/aggregation"
17
+ require "search_flip/criteria"
18
+ require "search_flip/response"
19
+ require "search_flip/result"
20
+ require "search_flip/index"
21
+ require "search_flip/model"
22
+
23
+ module SearchFlip
24
+ class NotSupportedError < StandardError; end
25
+ class ConnectionError < StandardError; end
26
+
27
+ class ResponseError < StandardError
28
+ attr_reader :code, :body
29
+
30
+ def initialize(code:, body:)
31
+ @code = code
32
+ @body = body
33
+ end
34
+
35
+ def to_s
36
+ "#{self.class.name} (#{code}): #{body}"
37
+ end
38
+ end
39
+
40
+ # Uses the ElasticSearch Multi Search API to execute multiple search requests
41
+ # within a single request. Raises SearchFlip::ResponseError in case any
42
+ # errors occur.
43
+ #
44
+ # @example
45
+ # SearchFlip.msearch [ProductIndex.match_all, CommentIndex.match_all]
46
+ #
47
+ # @param criterias [Array<SearchFlip::Criteria>] An array of search
48
+ # queries to execute in parallel
49
+ #
50
+ # @return [Array<SearchFlip::Response>] An array of responses
51
+
52
+ def self.msearch(criterias)
53
+ payload = criterias.flat_map do |criteria|
54
+ [SearchFlip::JSON.generate(index: criteria.target.index_name_with_prefix, type: criteria.target.type_name), SearchFlip::JSON.generate(criteria.request)]
55
+ end
56
+
57
+ payload = payload.join("\n")
58
+ payload << "\n"
59
+
60
+ SearchFlip::HTTPClient.headers(accept: "application/json", content_type: "application/x-ndjson").post("#{SearchFlip::Config[:base_url]}/_msearch", body: payload).parse["responses"].map.with_index do |response, index|
61
+ SearchFlip::Response.new(criterias[index], response)
62
+ end
63
+ end
64
+
65
+ # Used to manipulate, ie add and remove index aliases. Raises an
66
+ # SearchFlip::ResponseError in case any errors occur.
67
+ #
68
+ # @example
69
+ # ElasticSearch.post_aliases(actions: [
70
+ # { remove: { index: "test1", alias: "alias1" }},
71
+ # { add: { index: "test2", alias: "alias1" }}
72
+ # ])
73
+ #
74
+ # @param payload [Hash] The raw request payload
75
+ #
76
+ # @return [SearchFlip::Response] The raw response
77
+
78
+ def self.aliases(payload)
79
+ SearchFlip::HTTPClient.headers(accept: "application/json").post("#{SearchFlip::Config[:base_url]}/_aliases", body: SearchFlip::JSON.generate(payload))
80
+ end
81
+ end
82
+