elastomer-client 2.3.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +0 -4
- data/CHANGELOG.md +8 -0
- data/docker/docker-compose.cibuild.yml +8 -0
- data/docker/docker-compose.es24.yml +34 -0
- data/docker/docker-compose.es56.yml +37 -0
- data/docker/elasticsearch.yml +15 -0
- data/docs/index.md +2 -2
- data/docs/notifications.md +1 -1
- data/elastomer-client.gemspec +2 -0
- data/lib/elastomer/client.rb +86 -33
- data/lib/elastomer/client/app_delete_by_query.rb +158 -0
- data/lib/elastomer/client/delete_by_query.rb +8 -115
- data/lib/elastomer/client/docs.rb +63 -13
- data/lib/elastomer/client/errors.rb +10 -2
- data/lib/elastomer/client/index.rb +40 -12
- data/lib/elastomer/client/multi_percolate.rb +2 -2
- data/lib/elastomer/client/native_delete_by_query.rb +60 -0
- data/lib/elastomer/client/percolator.rb +6 -3
- data/lib/elastomer/client/scroller.rb +22 -7
- data/lib/elastomer/client/tasks.rb +188 -0
- data/lib/elastomer/client/warmer.rb +6 -0
- data/lib/elastomer/notifications.rb +1 -0
- data/lib/elastomer/version.rb +1 -1
- data/lib/elastomer/version_support.rb +177 -0
- data/script/cibuild +77 -6
- data/script/cibuild-elastomer-client +1 -0
- data/script/cibuild-elastomer-client-es24 +8 -0
- data/script/cibuild-elastomer-client-es56 +8 -0
- data/script/poll-for-es +20 -0
- data/test/client/{delete_by_query_test.rb → app_delete_by_query_test.rb} +7 -7
- data/test/client/bulk_test.rb +9 -13
- data/test/client/cluster_test.rb +2 -2
- data/test/client/docs_test.rb +133 -49
- data/test/client/errors_test.rb +21 -1
- data/test/client/es_5_x_warmer_test.rb +13 -0
- data/test/client/index_test.rb +104 -39
- data/test/client/multi_percolate_test.rb +13 -6
- data/test/client/multi_search_test.rb +5 -5
- data/test/client/native_delete_by_query_test.rb +123 -0
- data/test/client/nodes_test.rb +1 -1
- data/test/client/percolator_test.rb +10 -2
- data/test/client/repository_test.rb +1 -1
- data/test/client/scroller_test.rb +16 -6
- data/test/client/snapshot_test.rb +1 -1
- data/test/client/stubbed_client_test.rb +1 -1
- data/test/client/tasks_test.rb +139 -0
- data/test/client/template_test.rb +1 -1
- data/test/client/warmer_test.rb +8 -4
- data/test/client_test.rb +99 -0
- data/test/core_ext/time_test.rb +1 -1
- data/test/notifications_test.rb +4 -0
- data/test/test_helper.rb +129 -21
- data/test/version_support_test.rb +119 -0
- metadata +59 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7db106056a2ea4d14012e82621bc4c4073c85c15
|
4
|
+
data.tar.gz: ed047e4e19b8f45e7c553998cb0935ea59c1a94e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb92e602580ce508195626093783510df3320ba20416cac7c879fcf80839ce87fbb64dd0af7c8f37b0a2775cf9f0c3d603a692251b8ab9c7c87bcfe867b955a4
|
7
|
+
data.tar.gz: 423da9494641de0a33a90dc26fdae67564469291a1120202015a5f9daecab22da06bbbf9c1e351a41a825b031563e90066ba9585b483d5c257ad3044c64ba449
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -6,10 +6,6 @@ env:
|
|
6
6
|
- ES_VERSION=2.3.5 ES_DOWNLOAD_URL=https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/${ES_VERSION}/elasticsearch-${ES_VERSION}.tar.gz
|
7
7
|
- ES_VERSION=5.6.4 ES_DOWNLOAD_URL=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz
|
8
8
|
|
9
|
-
matrix:
|
10
|
-
allow_failures:
|
11
|
-
- env: ES_VERSION=5.6.4 ES_DOWNLOAD_URL=https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-${ES_VERSION}.tar.gz
|
12
|
-
|
13
9
|
# Configure a specific version of Elasticsearch
|
14
10
|
# See: https://docs.travis-ci.com/user/database-setup/#Installing-ElasticSearch-on-trusty-container-based-infrastructure
|
15
11
|
install:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
## 3.0.0 (2017-12-15)
|
2
|
+
- Fixed swapped args in {Client,Index}#multi\_percolate count calls using block API
|
3
|
+
- Support for Elasticsearch 5.x
|
4
|
+
- Uses Elasticsearch's built-in `_delete_by_query` when supported
|
5
|
+
- GET and HEAD requests are retried when possible
|
6
|
+
- Add support for `_tasks` API
|
7
|
+
- Replace `scan` queries with `scroll` sorted by `doc_id`
|
8
|
+
|
1
9
|
## 2.3.0 (2017-11-29)
|
2
10
|
- Remove Elasticsearch 1.x and earlier code paths
|
3
11
|
- Fix CI and configure an Elasticsearch 5.6 build
|
@@ -0,0 +1,34 @@
|
|
1
|
+
version: "2.1"
|
2
|
+
|
3
|
+
services:
|
4
|
+
elasticsearch2.4:
|
5
|
+
image: elasticsearch:2.4.6
|
6
|
+
container_name: es2.4
|
7
|
+
environment:
|
8
|
+
- cluster.name=elastomer2.4
|
9
|
+
- bootstrap.memory_lock=true
|
10
|
+
- discovery.type=single-node
|
11
|
+
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
12
|
+
ulimits:
|
13
|
+
memlock:
|
14
|
+
soft: -1
|
15
|
+
hard: -1
|
16
|
+
nofile:
|
17
|
+
soft: 65536
|
18
|
+
hard: 65536
|
19
|
+
mem_limit: 1g
|
20
|
+
cap_add:
|
21
|
+
- IPC_LOCK
|
22
|
+
volumes:
|
23
|
+
- esrepos:/usr/share/elasticsearch/repos
|
24
|
+
- ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
|
25
|
+
ports:
|
26
|
+
- 127.0.0.1:19200:9200
|
27
|
+
|
28
|
+
volumes:
|
29
|
+
esrepos:
|
30
|
+
driver: local
|
31
|
+
driver_opts:
|
32
|
+
device: tmpfs
|
33
|
+
type: tmpfs
|
34
|
+
o: size=100m,uid=102,gid=102
|
@@ -0,0 +1,37 @@
|
|
1
|
+
version: "2.1"
|
2
|
+
|
3
|
+
services:
|
4
|
+
elasticsearch5.6:
|
5
|
+
image: docker.elastic.co/elasticsearch/elasticsearch:5.6.4
|
6
|
+
container_name: es5.6
|
7
|
+
environment:
|
8
|
+
- cluster.name=elastomer5.6
|
9
|
+
- bootstrap.memory_lock=true
|
10
|
+
- discovery.type=single-node
|
11
|
+
- xpack.monitoring.enabled=false
|
12
|
+
- xpack.security.enabled=false
|
13
|
+
- xpack.watcher.enabled=false
|
14
|
+
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
15
|
+
ulimits:
|
16
|
+
memlock:
|
17
|
+
soft: -1
|
18
|
+
hard: -1
|
19
|
+
nofile:
|
20
|
+
soft: 65536
|
21
|
+
hard: 65536
|
22
|
+
mem_limit: 1g
|
23
|
+
cap_add:
|
24
|
+
- IPC_LOCK
|
25
|
+
volumes:
|
26
|
+
- esrepos:/usr/share/elasticsearch/repos
|
27
|
+
- ./elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
|
28
|
+
ports:
|
29
|
+
- 127.0.0.1:19200:9200
|
30
|
+
|
31
|
+
volumes:
|
32
|
+
esrepos:
|
33
|
+
driver: local
|
34
|
+
driver_opts:
|
35
|
+
device: tmpfs
|
36
|
+
type: tmpfs
|
37
|
+
o: size=100m,uid=102,gid=102
|
@@ -0,0 +1,15 @@
|
|
1
|
+
cluster.name: "docker-cluster"
|
2
|
+
|
3
|
+
network.host: 0.0.0.0
|
4
|
+
|
5
|
+
discovery.zen.minimum_master_nodes: 1
|
6
|
+
|
7
|
+
path:
|
8
|
+
data: /usr/share/elasticsearch/data
|
9
|
+
logs: /usr/share/elasticsearch/logs
|
10
|
+
repo: /usr/share/elasticsearch/repos
|
11
|
+
|
12
|
+
transport.tcp.port: 9300
|
13
|
+
http.port: 9200
|
14
|
+
http.max_content_length: 50mb
|
15
|
+
|
data/docs/index.md
CHANGED
@@ -137,8 +137,8 @@ Let's take a look at some simple event log maintenance using elastomer-client.
|
|
137
137
|
# the previous month's event log
|
138
138
|
index = client.index "event-log-2014-09"
|
139
139
|
|
140
|
-
#
|
141
|
-
index.
|
140
|
+
# force merge the index to have only 1 segment file (expunges deleted documents)
|
141
|
+
index.force merge \
|
142
142
|
:max_num_segments => 1,
|
143
143
|
:wait_for_merge => true
|
144
144
|
|
data/docs/notifications.md
CHANGED
data/elastomer-client.gemspec
CHANGED
@@ -28,7 +28,9 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency "activesupport", ">= 3.0"
|
29
29
|
spec.add_development_dependency "minitest", "~> 5.10"
|
30
30
|
spec.add_development_dependency "minitest-fail-fast", "~> 0.1.0"
|
31
|
+
spec.add_development_dependency "minitest-focus", "~> 1.1", ">= 1.1.2"
|
31
32
|
spec.add_development_dependency "webmock", "~> 2.3"
|
32
33
|
spec.add_development_dependency "awesome_print", "~> 1.8"
|
34
|
+
spec.add_development_dependency "pry-byebug", "~> 3.4"
|
33
35
|
spec.add_development_dependency "rake"
|
34
36
|
end
|
data/lib/elastomer/client.rb
CHANGED
@@ -4,16 +4,23 @@ require "multi_json"
|
|
4
4
|
require "semantic"
|
5
5
|
|
6
6
|
require "elastomer/version"
|
7
|
+
require "elastomer/version_support"
|
7
8
|
|
8
9
|
module Elastomer
|
9
10
|
|
10
11
|
class Client
|
11
12
|
MAX_REQUEST_SIZE = 250 * 2**20 # 250 MB
|
12
13
|
|
14
|
+
RETRYABLE_METHODS = %i[get head].freeze
|
15
|
+
|
13
16
|
# Create a new client that can be used to make HTTP requests to the
|
14
|
-
# Elasticsearch server.
|
17
|
+
# Elasticsearch server. If you use `:max_retries`, then any GET or HEAD
|
18
|
+
# request will be retried up to this many times with a 75ms delay between
|
19
|
+
# the retry attempts. Only non-fatal exceptions will retried automatically.
|
20
|
+
#
|
21
|
+
# see lib/elastomer/client/errors.rb#L92-L94
|
15
22
|
#
|
16
|
-
#
|
23
|
+
# Method params:
|
17
24
|
# :host - the host as a String
|
18
25
|
# :port - the port number of the server
|
19
26
|
# :url - the URL as a String (overrides :host and :port)
|
@@ -22,25 +29,43 @@ module Elastomer
|
|
22
29
|
# :adapter - the Faraday adapter to use (defaults to :excon)
|
23
30
|
# :opaque_id - set to `true` to use the 'X-Opaque-Id' request header
|
24
31
|
# :max_request_size - the maximum allowed request size in bytes (defaults to 250 MB)
|
32
|
+
# :max_retries - the maximum number of request retires (defaults to 0)
|
33
|
+
# :retry_delay - delay in seconds between retries (defaults to 0.075)
|
25
34
|
#
|
26
|
-
def initialize(
|
27
|
-
|
28
|
-
|
29
|
-
|
35
|
+
def initialize(host: "localhost", port: 9200, url: nil,
|
36
|
+
read_timeout: 5, open_timeout: 2, max_retries: 0, retry_delay: 0.075,
|
37
|
+
opaque_id: false, adapter: Faraday.default_adapter, max_request_size: MAX_REQUEST_SIZE)
|
38
|
+
|
39
|
+
@url = url || "http://#{host}:#{port}"
|
30
40
|
|
31
41
|
uri = Addressable::URI.parse @url
|
32
42
|
@host = uri.host
|
33
43
|
@port = uri.port
|
34
44
|
|
35
|
-
@read_timeout =
|
36
|
-
@open_timeout =
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@
|
45
|
+
@read_timeout = read_timeout
|
46
|
+
@open_timeout = open_timeout
|
47
|
+
@max_retries = max_retries
|
48
|
+
@retry_delay = retry_delay
|
49
|
+
@adapter = adapter
|
50
|
+
@opaque_id = opaque_id
|
51
|
+
@max_request_size = max_request_size
|
40
52
|
end
|
41
53
|
|
42
54
|
attr_reader :host, :port, :url
|
43
|
-
attr_reader :read_timeout, :open_timeout
|
55
|
+
attr_reader :read_timeout, :open_timeout
|
56
|
+
attr_reader :max_retries, :retry_delay, :max_request_size
|
57
|
+
|
58
|
+
# Returns a duplicate of this Client connection configured in the exact same
|
59
|
+
# fashion.
|
60
|
+
def dup
|
61
|
+
self.class.new \
|
62
|
+
url: url,
|
63
|
+
read_timeout: read_timeout,
|
64
|
+
open_timeout: open_timeout,
|
65
|
+
adapter: @adapter,
|
66
|
+
opaque_id: @opaque_id,
|
67
|
+
max_request_size: max_request_size
|
68
|
+
end
|
44
69
|
|
45
70
|
# Returns true if the server is available; returns false otherwise.
|
46
71
|
def ping
|
@@ -90,6 +115,15 @@ module Elastomer
|
|
90
115
|
end
|
91
116
|
end
|
92
117
|
|
118
|
+
# Internal: Reset the client by removing the current Faraday::Connection. A
|
119
|
+
# new connection will be established on the next request.
|
120
|
+
#
|
121
|
+
# Returns this Client instance.
|
122
|
+
def reset!
|
123
|
+
@connection = nil
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
93
127
|
# Internal: Sends an HTTP HEAD request to the server.
|
94
128
|
#
|
95
129
|
# path - The path as a String
|
@@ -153,15 +187,18 @@ module Elastomer
|
|
153
187
|
# params - Parameters Hash
|
154
188
|
# :body - Will be used as the request body
|
155
189
|
# :read_timeout - Optional read timeout (in seconds) for the request
|
190
|
+
# :max_retires - Optional retry number for the request
|
156
191
|
#
|
157
192
|
# Returns a Faraday::Response
|
158
193
|
# Raises an Elastomer::Client::Error on 4XX and 5XX responses
|
159
194
|
# rubocop:disable Metrics/MethodLength
|
160
195
|
def request( method, path, params )
|
161
|
-
read_timeout = params.delete
|
162
|
-
|
163
|
-
|
196
|
+
read_timeout = params.delete(:read_timeout)
|
197
|
+
request_max_retries = params.delete(:max_retries) || max_retries
|
198
|
+
body = extract_body(params)
|
199
|
+
path = expand_path(path, params)
|
164
200
|
|
201
|
+
params[:retries] = retries = 0
|
165
202
|
instrument(path, body, params) do
|
166
203
|
begin
|
167
204
|
response =
|
@@ -195,14 +232,35 @@ module Elastomer
|
|
195
232
|
|
196
233
|
# wrap Faraday errors with appropriate Elastomer::Client error classes
|
197
234
|
rescue Faraday::Error::ClientError => boom
|
198
|
-
|
199
|
-
|
200
|
-
|
235
|
+
error = wrap_faraday_error(boom, method, path)
|
236
|
+
if error.retry? && RETRYABLE_METHODS.include?(method) && (retries += 1) <= request_max_retries
|
237
|
+
params[:retries] = retries
|
238
|
+
sleep retry_delay
|
239
|
+
retry
|
240
|
+
end
|
241
|
+
raise error
|
242
|
+
rescue OpaqueIdError => boom
|
243
|
+
reset!
|
244
|
+
raise boom
|
201
245
|
end
|
202
246
|
end
|
203
247
|
end
|
204
248
|
# rubocop:enable Metrics/MethodLength
|
205
249
|
|
250
|
+
# Internal: Returns a new Elastomer::Client error that wraps the given
|
251
|
+
# Faraday error. A generic Error is returned if we cannot wrap the given
|
252
|
+
# Faraday error.
|
253
|
+
#
|
254
|
+
# error - The Faraday error
|
255
|
+
# method - The request method
|
256
|
+
# path - The request path
|
257
|
+
#
|
258
|
+
def wrap_faraday_error(error, method, path)
|
259
|
+
error_name = error.class.name.split("::").last
|
260
|
+
error_class = Elastomer::Client.const_get(error_name) rescue Elastomer::Client::Error
|
261
|
+
error_class.new(error, method.upcase, path)
|
262
|
+
end
|
263
|
+
|
206
264
|
# Internal: Extract the :body from the params Hash and convert it to a
|
207
265
|
# JSON String format. If the params Hash does not contain a :body then no
|
208
266
|
# action is taken and `nil` is returned.
|
@@ -263,6 +321,7 @@ module Elastomer
|
|
263
321
|
query_values = params.dup
|
264
322
|
query_values.delete :action
|
265
323
|
query_values.delete :context
|
324
|
+
query_values.delete :retries
|
266
325
|
|
267
326
|
template.keys.map(&:to_sym).each do |key|
|
268
327
|
value = query_values.delete key
|
@@ -305,21 +364,11 @@ module Elastomer
|
|
305
364
|
raise ServerError, response if response.status >= 500
|
306
365
|
|
307
366
|
if response.body.is_a?(Hash) && (error = response.body["error"])
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
when "query_parsing_exception"; raise QueryParsingError, response
|
314
|
-
end
|
315
|
-
|
316
|
-
# ES 1.X style
|
317
|
-
elsif error.is_a?(String)
|
318
|
-
case error
|
319
|
-
when %r/IndexMissingException/; raise IndexNotFoundError, response
|
320
|
-
when %r/QueryParsingException/; raise QueryParsingError, response
|
321
|
-
when %r/ParseException/; raise QueryParsingError, response
|
322
|
-
end
|
367
|
+
root_cause = Array(error["root_cause"]).first || error
|
368
|
+
case root_cause["type"]
|
369
|
+
when "index_not_found_exception"; raise IndexNotFoundError, response
|
370
|
+
when "illegal_argument_exception"; raise IllegalArgument, response
|
371
|
+
when *version_support.query_parse_exception; raise QueryParsingError, response
|
323
372
|
end
|
324
373
|
|
325
374
|
raise RequestError, response
|
@@ -361,6 +410,10 @@ module Elastomer
|
|
361
410
|
end
|
362
411
|
end
|
363
412
|
|
413
|
+
def version_support
|
414
|
+
@version_support ||= VersionSupport.new(version)
|
415
|
+
end
|
416
|
+
|
364
417
|
end # Client
|
365
418
|
end # Elastomer
|
366
419
|
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Elastomer
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# DEPRECATED: Delete documents from one or more indices and one or more
|
5
|
+
# types based on a query.
|
6
|
+
#
|
7
|
+
# The return value follows the format returned by the Elasticsearch Delete
|
8
|
+
# by Query plugin: https://github.com/elastic/elasticsearch/blob/v2.4.6/docs/plugins/delete-by-query.asciidoc
|
9
|
+
#
|
10
|
+
# Internally, this method uses a combination of scroll and bulk delete
|
11
|
+
# instead of the Delete by Query API, which was removed in Elasticsearch
|
12
|
+
# 2.0.
|
13
|
+
#
|
14
|
+
# For native _delete_by_query functionality in Elasticsearch 5+, see the
|
15
|
+
# native_delete_by_query method.
|
16
|
+
#
|
17
|
+
# query - The query body as a Hash
|
18
|
+
# params - Parameters Hash
|
19
|
+
#
|
20
|
+
# Examples
|
21
|
+
#
|
22
|
+
# # request body query
|
23
|
+
# app_delete_by_query({:query => {:match_all => {}}}, :type => 'tweet')
|
24
|
+
#
|
25
|
+
# # same thing but using the URI request method
|
26
|
+
# app_delete_by_query(nil, { :q => '*:*', :type => 'tweet' })
|
27
|
+
#
|
28
|
+
# See https://www.elastic.co/guide/en/elasticsearch/plugins/current/delete-by-query-usage.html
|
29
|
+
#
|
30
|
+
# Returns a Hash of statistics about the delete operations, for example:
|
31
|
+
#
|
32
|
+
# {
|
33
|
+
# "took" : 639,
|
34
|
+
# "_indices" : {
|
35
|
+
# "_all" : {
|
36
|
+
# "found" : 5901,
|
37
|
+
# "deleted" : 5901,
|
38
|
+
# "missing" : 0,
|
39
|
+
# "failed" : 0
|
40
|
+
# },
|
41
|
+
# "twitter" : {
|
42
|
+
# "found" : 5901,
|
43
|
+
# "deleted" : 5901,
|
44
|
+
# "missing" : 0,
|
45
|
+
# "failed" : 0
|
46
|
+
# }
|
47
|
+
# },
|
48
|
+
# "failures" : [ ]
|
49
|
+
# }
|
50
|
+
def app_delete_by_query(query, params = {})
|
51
|
+
AppDeleteByQuery.new(self, query, params).execute
|
52
|
+
end
|
53
|
+
|
54
|
+
class AppDeleteByQuery
|
55
|
+
|
56
|
+
SEARCH_PARAMETERS = %i[
|
57
|
+
index
|
58
|
+
type
|
59
|
+
q
|
60
|
+
df
|
61
|
+
analyzer
|
62
|
+
analyze_wildcard
|
63
|
+
batched_reduce_size
|
64
|
+
default_operator
|
65
|
+
lenient
|
66
|
+
explain
|
67
|
+
_source
|
68
|
+
stored_fields
|
69
|
+
sort
|
70
|
+
track_scores
|
71
|
+
timeout
|
72
|
+
terminate_after
|
73
|
+
from
|
74
|
+
size
|
75
|
+
search_type
|
76
|
+
scroll
|
77
|
+
].to_set.freeze
|
78
|
+
|
79
|
+
|
80
|
+
# Create a new DeleteByQuery command for deleting documents matching a
|
81
|
+
# query
|
82
|
+
#
|
83
|
+
# client - Elastomer::Client used for HTTP requests to the server
|
84
|
+
# query - The query used to find documents to delete
|
85
|
+
# params - Other URL parameters
|
86
|
+
def initialize(client, query, params = {})
|
87
|
+
@client = client
|
88
|
+
@query = query
|
89
|
+
@params = params
|
90
|
+
@response_stats = { "took" => 0, "_indices" => { "_all" => {} }, "failures" => [] }
|
91
|
+
end
|
92
|
+
|
93
|
+
attr_reader :client, :query, :params, :response_stats
|
94
|
+
|
95
|
+
# Internal: Determine whether or not an HTTP status code is in the range
|
96
|
+
# 200 to 299
|
97
|
+
#
|
98
|
+
# status - HTTP status code
|
99
|
+
#
|
100
|
+
# Returns a boolean
|
101
|
+
def is_ok?(status)
|
102
|
+
status.between?(200, 299)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Internal: Tally the contributions of an item to the found, deleted,
|
106
|
+
# missing, and failed counts for the summary statistics
|
107
|
+
#
|
108
|
+
# item - An element of the items array from a bulk response
|
109
|
+
#
|
110
|
+
# Returns a Hash of counts for each category
|
111
|
+
def categorize(item)
|
112
|
+
{
|
113
|
+
"found" => item["found"] || item["status"] == 409 ? 1 : 0,
|
114
|
+
"deleted" => is_ok?(item["status"]) ? 1 : 0,
|
115
|
+
"missing" => !item["found"] && !item.key?("error") ? 1 : 0,
|
116
|
+
"failed" => item.key?("error") ? 1 : 0,
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
# Internal: Combine a response item with the existing statistics
|
121
|
+
#
|
122
|
+
# item - A bulk response item
|
123
|
+
def accumulate(item)
|
124
|
+
item = item["delete"]
|
125
|
+
(@response_stats["_indices"][item["_index"]] ||= {}).merge!(categorize(item)) { |_, n, m| n + m }
|
126
|
+
@response_stats["_indices"]["_all"].merge!(categorize(item)) { |_, n, m| n + m }
|
127
|
+
@response_stats["failures"] << item unless is_ok? item["status"]
|
128
|
+
end
|
129
|
+
|
130
|
+
# Perform the Delete by Query action
|
131
|
+
#
|
132
|
+
# Returns a Hash of statistics about the bulk operation
|
133
|
+
def execute
|
134
|
+
ops = Enumerator.new do |yielder|
|
135
|
+
@client.scan(@query, search_params).each_document do |hit|
|
136
|
+
yielder.yield([:delete, hit.select { |key, _| ["_index", "_type", "_id", "_routing"].include?(key) }])
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
stats = @client.bulk_stream_items(ops, bulk_params) { |item| accumulate(item) }
|
141
|
+
@response_stats["took"] = stats["took"]
|
142
|
+
@response_stats
|
143
|
+
end
|
144
|
+
|
145
|
+
# Internal: Remove parameters that are not valid for the _search endpoint
|
146
|
+
def search_params
|
147
|
+
params = @params.merge(:_source => false)
|
148
|
+
params.select {|p, _| SEARCH_PARAMETERS.include? p}
|
149
|
+
end
|
150
|
+
|
151
|
+
# Internal: Remove parameters that are not valid for the _bulk endpoint
|
152
|
+
def bulk_params
|
153
|
+
@params.clone.tap { |p| p.delete(:q) }
|
154
|
+
end
|
155
|
+
|
156
|
+
end # AppDeleteByQuery
|
157
|
+
end # Client
|
158
|
+
end # Elastomer
|