elastomer-client 0.3.1
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 +7 -0
- data/.gitignore +8 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +108 -0
- data/Rakefile +9 -0
- data/docs/notifications.md +71 -0
- data/elastomer-client.gemspec +30 -0
- data/lib/elastomer/client.rb +307 -0
- data/lib/elastomer/client/bulk.rb +257 -0
- data/lib/elastomer/client/cluster.rb +208 -0
- data/lib/elastomer/client/docs.rb +432 -0
- data/lib/elastomer/client/errors.rb +51 -0
- data/lib/elastomer/client/index.rb +407 -0
- data/lib/elastomer/client/multi_search.rb +115 -0
- data/lib/elastomer/client/nodes.rb +87 -0
- data/lib/elastomer/client/scan.rb +161 -0
- data/lib/elastomer/client/template.rb +85 -0
- data/lib/elastomer/client/warmer.rb +96 -0
- data/lib/elastomer/core_ext/time.rb +7 -0
- data/lib/elastomer/middleware/encode_json.rb +51 -0
- data/lib/elastomer/middleware/opaque_id.rb +69 -0
- data/lib/elastomer/middleware/parse_json.rb +39 -0
- data/lib/elastomer/notifications.rb +83 -0
- data/lib/elastomer/version.rb +7 -0
- data/script/bootstrap +16 -0
- data/script/cibuild +28 -0
- data/script/console +9 -0
- data/script/testsuite +10 -0
- data/test/assertions.rb +74 -0
- data/test/client/bulk_test.rb +226 -0
- data/test/client/cluster_test.rb +113 -0
- data/test/client/docs_test.rb +394 -0
- data/test/client/index_test.rb +244 -0
- data/test/client/multi_search_test.rb +129 -0
- data/test/client/nodes_test.rb +35 -0
- data/test/client/scan_test.rb +84 -0
- data/test/client/stubbed_client_tests.rb +40 -0
- data/test/client/template_test.rb +33 -0
- data/test/client/warmer_test.rb +56 -0
- data/test/client_test.rb +86 -0
- data/test/core_ext/time_test.rb +46 -0
- data/test/middleware/encode_json_test.rb +53 -0
- data/test/middleware/opaque_id_test.rb +39 -0
- data/test/middleware/parse_json_test.rb +54 -0
- data/test/test_helper.rb +94 -0
- metadata +210 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Elastomer
|
4
|
+
module Middleware
|
5
|
+
|
6
|
+
# This Faraday middleware implements the "X-Opaque-Id" request / response
|
7
|
+
# headers for ElasticSearch. The X-Opaque-Id header, when provided on the
|
8
|
+
# request header, will be returned as a header in the response. This is
|
9
|
+
# useful in environments which reuse connections to ensure that cross-talk
|
10
|
+
# does not occur between two requests.
|
11
|
+
#
|
12
|
+
# The SecureRandom lib is used to generate a UUID string for each request.
|
13
|
+
# This value is used as the content for the "X-Opaque-Id" header. If the
|
14
|
+
# value is different between the request and the response, then an
|
15
|
+
# `Elastomer::Client::OpaqueIdError` is raised. In this case no response
|
16
|
+
# will be returned.
|
17
|
+
#
|
18
|
+
# See [ElasticSearch "X-Opaque-Id"
|
19
|
+
# header](https://github.com/elasticsearch/elasticsearch/issues/1202)
|
20
|
+
# for more details.
|
21
|
+
class OpaqueId < ::Faraday::Middleware
|
22
|
+
X_OPAQUE_ID = 'X-Opaque-Id'.freeze
|
23
|
+
COUNTER_MAX = 2**32 - 1
|
24
|
+
|
25
|
+
# Faraday middleware implementation.
|
26
|
+
#
|
27
|
+
# env - Faraday environment Hash
|
28
|
+
#
|
29
|
+
# Returns the environment Hash
|
30
|
+
def call( env )
|
31
|
+
uuid = generate_uuid.freeze
|
32
|
+
env[:request_headers][X_OPAQUE_ID] = uuid
|
33
|
+
|
34
|
+
@app.call(env).on_complete do |renv|
|
35
|
+
if uuid != renv[:response_headers][X_OPAQUE_ID]
|
36
|
+
raise ::Elastomer::Client::OpaqueIdError, "conflicting 'X-Opaque-Id' headers"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Generate a UUID using the built-in SecureRandom class. This can be a
|
42
|
+
# little slow at times, so we will reuse the same UUID and append an
|
43
|
+
# incrementing counter.
|
44
|
+
#
|
45
|
+
# Returns the UUID string.
|
46
|
+
def generate_uuid
|
47
|
+
t = Thread.current
|
48
|
+
|
49
|
+
unless t.key? :opaque_id_base
|
50
|
+
t[:opaque_id_base] = (SecureRandom.urlsafe_base64(12) + '%08x').freeze
|
51
|
+
t[:opaque_id_counter] = -1
|
52
|
+
end
|
53
|
+
|
54
|
+
t[:opaque_id_counter] += 1
|
55
|
+
t[:opaque_id_counter] = 0 if t[:opaque_id_counter] > COUNTER_MAX
|
56
|
+
t[:opaque_id_base] % t[:opaque_id_counter]
|
57
|
+
end
|
58
|
+
|
59
|
+
end # OpaqueId
|
60
|
+
end # Middleware
|
61
|
+
|
62
|
+
# Error raised when a conflict is detected between the UUID sent in the
|
63
|
+
# 'X-Opaque-Id' request header and the one received in the response header.
|
64
|
+
Client::OpaqueIdError = Class.new Client::Error
|
65
|
+
|
66
|
+
end # Elastomer
|
67
|
+
|
68
|
+
Faraday::Request.register_middleware \
|
69
|
+
:opaque_id => ::Elastomer::Middleware::OpaqueId
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Elastomer
|
2
|
+
module Middleware
|
3
|
+
|
4
|
+
# Parse response bodies as JSON.
|
5
|
+
class ParseJson < Faraday::Middleware
|
6
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
7
|
+
MIME_TYPE = 'application/json'.freeze
|
8
|
+
|
9
|
+
def call(environment)
|
10
|
+
@app.call(environment).on_complete do |env|
|
11
|
+
if process_response?(env)
|
12
|
+
env[:body] = parse env[:body]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Parse the response body.
|
18
|
+
def parse(body)
|
19
|
+
MultiJson.load(body) if body.respond_to?(:to_str) and !body.strip.empty?
|
20
|
+
rescue StandardError, SyntaxError => e
|
21
|
+
raise Faraday::Error::ParsingError, e
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_response?(env)
|
25
|
+
type = response_type(env)
|
26
|
+
type.empty? or type == MIME_TYPE
|
27
|
+
end
|
28
|
+
|
29
|
+
def response_type(env)
|
30
|
+
type = env[:response_headers][CONTENT_TYPE].to_s
|
31
|
+
type = type.split(';', 2).first if type.index(';')
|
32
|
+
type
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Faraday::Response.register_middleware \
|
39
|
+
:parse_json => ::Elastomer::Middleware::ParseJson
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'active_support/notifications'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'elastomer/client'
|
4
|
+
|
5
|
+
module Elastomer
|
6
|
+
|
7
|
+
# So you want to get notifications from your ElasticSearch client? Well,
|
8
|
+
# you've come to the right place!
|
9
|
+
#
|
10
|
+
# require 'elastomer/notifications'
|
11
|
+
#
|
12
|
+
# Requiring this module will add ActiveSupport notifications to all
|
13
|
+
# ElasticSearch requests. To subscribe to those requests ...
|
14
|
+
#
|
15
|
+
# ActiveSupport::Notifications.subscribe('request.client.elastomer') do |name, start_time, end_time, _, payload|
|
16
|
+
# duration = end_time - start_time
|
17
|
+
# $stderr.puts '[%s] %s %s (%.3f)' % [payload[:status], payload[:index], payload[:action], duration]
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# The payload contains the following bits of information:
|
21
|
+
#
|
22
|
+
# * :index - index name (if any)
|
23
|
+
# * :type - documeht type (if any)
|
24
|
+
# * :action - the action being performed
|
25
|
+
# * :url - request URL
|
26
|
+
# * :method - request method (:head, :get, :put, :post, :delete)
|
27
|
+
# * :status - response status code
|
28
|
+
#
|
29
|
+
# If you want to use your own notifications service then you will need to
|
30
|
+
# let Elastomer know by setting the `service` here in the Notifications
|
31
|
+
# module. The service should adhere to the ActiveSupport::Notifications
|
32
|
+
# specification.
|
33
|
+
#
|
34
|
+
# Elastomer::Notifications.service = your_own_service
|
35
|
+
#
|
36
|
+
module Notifications
|
37
|
+
|
38
|
+
class << self
|
39
|
+
attr_accessor :service
|
40
|
+
end
|
41
|
+
|
42
|
+
# The name to subscribe to for notifications
|
43
|
+
NAME = 'request.client.elastomer'.freeze
|
44
|
+
|
45
|
+
# Internal: Execute the given block and provide instrumentaiton info to
|
46
|
+
# subscribers. The name we use for subscriptions is
|
47
|
+
# `request.client.elastomer` and a supplemental payload is provided with
|
48
|
+
# more information about the specific ElasticSearch request.
|
49
|
+
#
|
50
|
+
# path - The full request path as a String
|
51
|
+
# body - The request body as a String or `nil`
|
52
|
+
# params - The request params Hash
|
53
|
+
# block - The block that will be instrumented
|
54
|
+
#
|
55
|
+
# Returns the response from the block
|
56
|
+
def instrument( path, body, params )
|
57
|
+
payload = {
|
58
|
+
:index => params[:index],
|
59
|
+
:type => params[:type],
|
60
|
+
:action => params[:action],
|
61
|
+
:context => params[:context],
|
62
|
+
:body => body
|
63
|
+
}
|
64
|
+
|
65
|
+
::Elastomer::Notifications.service.instrument(NAME, payload) do
|
66
|
+
response = yield
|
67
|
+
payload[:url] = response.env[:url]
|
68
|
+
payload[:method] = response.env[:method]
|
69
|
+
payload[:status] = response.status
|
70
|
+
response
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# use ActiveSupport::Notifications as the default instrumentaiton service
|
76
|
+
Notifications.service = ActiveSupport::Notifications
|
77
|
+
|
78
|
+
# inject our instrument method into the Client class
|
79
|
+
class Client
|
80
|
+
remove_method :instrument
|
81
|
+
include ::Elastomer::Notifications
|
82
|
+
end
|
83
|
+
end
|
data/script/bootstrap
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
set -e
|
3
|
+
|
4
|
+
# force use of gcc under mac due to hiredis issues under clang
|
5
|
+
if [ $(uname) = Darwin ]; then
|
6
|
+
export CC=/usr/bin/gcc
|
7
|
+
export CXX=/usr/bin/g++
|
8
|
+
export LD=/usr/bin/gcc
|
9
|
+
fi
|
10
|
+
|
11
|
+
cd "$(dirname "$0")/.."
|
12
|
+
if bundle check 1>/dev/null 2>&1; then
|
13
|
+
echo "Gem environment up-to-date"
|
14
|
+
else
|
15
|
+
exec bundle install --binstubs --path vendor/gems "$@"
|
16
|
+
fi
|
data/script/cibuild
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# Usage: script/cibuild
|
3
|
+
# CI build script.
|
4
|
+
# This is tailored for the janky build machines.
|
5
|
+
set -e
|
6
|
+
|
7
|
+
# change into root dir and setup path
|
8
|
+
cd $(dirname "$0")/..
|
9
|
+
PATH="$(pwd)/bin:$(pwd)/script:/usr/share/rbenv/shims:$PATH"
|
10
|
+
|
11
|
+
# Write commit we're building at
|
12
|
+
git log -n 1 || true
|
13
|
+
echo
|
14
|
+
|
15
|
+
result=0
|
16
|
+
|
17
|
+
export RBENV_VERSION="$(cat .ruby-version)"
|
18
|
+
echo "Building under $RBENV_VERSION"
|
19
|
+
script/bootstrap
|
20
|
+
script/testsuite || result=$?
|
21
|
+
|
22
|
+
if [ $result -ne 0 ]; then
|
23
|
+
exit $result
|
24
|
+
fi
|
25
|
+
|
26
|
+
# echo
|
27
|
+
# echo "Running benchmarks"
|
28
|
+
# script/benchmark
|
data/script/console
ADDED
data/script/testsuite
ADDED
data/test/assertions.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
module Minitest::Assertions
|
2
|
+
#COMPATIBILITY
|
3
|
+
# ES 1.0 replaced the 'ok' attribute with a 'created' attribute
|
4
|
+
# in index responses. Check for either one so we are compatible
|
5
|
+
# with 0.90 and 1.0.
|
6
|
+
def assert_created(response)
|
7
|
+
assert response['created'] || response['ok'], 'document was not created'
|
8
|
+
end
|
9
|
+
|
10
|
+
#COMPATIBILITY
|
11
|
+
# ES 1.0 replaced the 'ok' attribute with an 'acknowledged' attribute
|
12
|
+
# in many responses. Check for either one so we are compatible
|
13
|
+
# with 0.90 and 1.0.
|
14
|
+
def assert_acknowledged(response)
|
15
|
+
assert response['acknowledged'] || response['ok'], 'document was not acknowledged'
|
16
|
+
end
|
17
|
+
|
18
|
+
#COMPATIBILITY
|
19
|
+
# ES 1.0 replaced the 'exists' attribute with a 'found' attribute in the
|
20
|
+
# get document response. Check for either one so we are compatible
|
21
|
+
# with 0.90 and 1.0.
|
22
|
+
def assert_found(response)
|
23
|
+
assert response['found'] || response['exists'], 'document was not found'
|
24
|
+
end
|
25
|
+
|
26
|
+
def refute_found(response)
|
27
|
+
refute response['found'] || response['exists'], 'document was unexpectedly found'
|
28
|
+
end
|
29
|
+
|
30
|
+
#COMPATIBILITY
|
31
|
+
# ES 1.0 replaced the 'ok' attribute in the bulk response item with a
|
32
|
+
# 'status' attribute. Here we check for either one for compatibility
|
33
|
+
# with 0.90 and 1.0.
|
34
|
+
def assert_bulk_index(item, message='bulk index did not succeed')
|
35
|
+
ok = item['index']['ok']
|
36
|
+
status = item['index']['status']
|
37
|
+
assert ok == true || status == 201, message
|
38
|
+
end
|
39
|
+
|
40
|
+
def assert_bulk_create(item, message='bulk create did not succeed')
|
41
|
+
ok = item['create']['ok']
|
42
|
+
status = item['create']['status']
|
43
|
+
assert ok == true || status == 201, message
|
44
|
+
end
|
45
|
+
|
46
|
+
def assert_bulk_delete(item, message='bulk delete did not succeed')
|
47
|
+
ok = item['delete']['ok']
|
48
|
+
status = item['delete']['status']
|
49
|
+
assert ok == true || status == 200, message
|
50
|
+
end
|
51
|
+
|
52
|
+
#COMPATIBILITY
|
53
|
+
# ES 1.0 nests mappings in a "mappings" element under the index name, e.g.
|
54
|
+
# mapping["test-index"]["mappings"]["doco"]
|
55
|
+
# ES 0.90 doesn't have the "mappings" element:
|
56
|
+
# mapping["test-index"]["doco"]
|
57
|
+
def assert_mapping_exists(response, type, message="mapping expected to exist, but doesn't")
|
58
|
+
mapping = if response.has_key?('mappings')
|
59
|
+
response['mappings'][type]
|
60
|
+
else
|
61
|
+
response[type]
|
62
|
+
end
|
63
|
+
refute_nil mapping, message
|
64
|
+
end
|
65
|
+
|
66
|
+
def assert_property_exists(response, type, property, message="property expected to exist, but doesn't")
|
67
|
+
mapping = if response.has_key?('mappings')
|
68
|
+
response['mappings'][type]
|
69
|
+
else
|
70
|
+
response[type]
|
71
|
+
end
|
72
|
+
assert mapping['properties'].has_key?(property), message
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,226 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastomer::Client::Bulk do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@name = 'elastomer-bulk-test'
|
7
|
+
@index = $client.index(@name)
|
8
|
+
|
9
|
+
unless @index.exists?
|
10
|
+
@index.create \
|
11
|
+
:settings => { 'index.number_of_shards' => 1, 'index.number_of_replicas' => 0 },
|
12
|
+
:mappings => {
|
13
|
+
:tweet => {
|
14
|
+
:_source => { :enabled => true }, :_all => { :enabled => false },
|
15
|
+
:properties => {
|
16
|
+
:message => { :type => 'string', :analyzer => 'standard' },
|
17
|
+
:author => { :type => 'string', :index => 'not_analyzed' }
|
18
|
+
}
|
19
|
+
},
|
20
|
+
:book => {
|
21
|
+
:_source => { :enabled => true }, :_all => { :enabled => false },
|
22
|
+
:properties => {
|
23
|
+
:title => { :type => 'string', :analyzer => 'standard' },
|
24
|
+
:author => { :type => 'string', :index => 'not_analyzed' }
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
wait_for_index(@name)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
after do
|
34
|
+
@index.delete if @index.exists?
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'performs bulk index actions' do
|
38
|
+
body = [
|
39
|
+
'{"index" : {"_id":"1", "_type":"tweet", "_index":"elastomer-bulk-test"}}',
|
40
|
+
'{"author":"pea53", "message":"just a test tweet"}',
|
41
|
+
'{"index" : {"_id":"1", "_type":"book", "_index":"elastomer-bulk-test"}}',
|
42
|
+
'{"author":"John Scalzi", "title":"Old Mans War"}',
|
43
|
+
nil
|
44
|
+
]
|
45
|
+
body = body.join "\n"
|
46
|
+
h = $client.bulk body
|
47
|
+
|
48
|
+
assert_bulk_index(h['items'][0])
|
49
|
+
assert_bulk_index(h['items'][1])
|
50
|
+
|
51
|
+
@index.refresh
|
52
|
+
|
53
|
+
h = @index.docs('tweet').get :id => 1
|
54
|
+
assert_equal 'pea53', h['_source']['author']
|
55
|
+
|
56
|
+
h = @index.docs('book').get :id => 1
|
57
|
+
assert_equal 'John Scalzi', h['_source']['author']
|
58
|
+
|
59
|
+
|
60
|
+
body = [
|
61
|
+
'{"index" : {"_id":"2", "_type":"book"}}',
|
62
|
+
'{"author":"Tolkien", "title":"The Silmarillion"}',
|
63
|
+
'{"delete" : {"_id":"1", "_type":"book"}}',
|
64
|
+
nil
|
65
|
+
]
|
66
|
+
body = body.join "\n"
|
67
|
+
h = $client.bulk body, :index => @name
|
68
|
+
|
69
|
+
assert_bulk_index h['items'].first, 'expected to index a book'
|
70
|
+
assert_bulk_delete h['items'].last, 'expected to delete a book'
|
71
|
+
|
72
|
+
@index.refresh
|
73
|
+
|
74
|
+
h = @index.docs('book').get :id => 1
|
75
|
+
assert !h['exists'], 'was not successfully deleted'
|
76
|
+
|
77
|
+
h = @index.docs('book').get :id => 2
|
78
|
+
assert_equal 'Tolkien', h['_source']['author']
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'supports a nice block syntax' do
|
82
|
+
h = @index.bulk do |b|
|
83
|
+
b.index :_id => 1, :_type => 'tweet', :author => 'pea53', :message => 'just a test tweet'
|
84
|
+
b.index :_id => nil, :_type => 'book', :author => 'John Scalzi', :title => 'Old Mans War'
|
85
|
+
end
|
86
|
+
items = h['items']
|
87
|
+
|
88
|
+
assert_instance_of Fixnum, h['took']
|
89
|
+
|
90
|
+
assert_bulk_index h['items'].first
|
91
|
+
assert_bulk_create h['items'].last
|
92
|
+
|
93
|
+
book_id = items.last['create']['_id']
|
94
|
+
assert_match %r/^\S{22}$/, book_id
|
95
|
+
|
96
|
+
@index.refresh
|
97
|
+
|
98
|
+
h = @index.docs('tweet').get :id => 1
|
99
|
+
assert_equal 'pea53', h['_source']['author']
|
100
|
+
|
101
|
+
h = @index.docs('book').get :id => book_id
|
102
|
+
assert_equal 'John Scalzi', h['_source']['author']
|
103
|
+
|
104
|
+
|
105
|
+
h = @index.bulk do |b|
|
106
|
+
b.index :_id => '', :_type => 'book', :author => 'Tolkien', :title => 'The Silmarillion'
|
107
|
+
b.delete :_id => book_id, :_type => 'book'
|
108
|
+
end
|
109
|
+
items = h['items']
|
110
|
+
|
111
|
+
assert_bulk_create h['items'].first, 'expected to create a book'
|
112
|
+
assert_bulk_delete h['items'].last, 'expected to delete a book'
|
113
|
+
|
114
|
+
book_id2 = items.first['create']['_id']
|
115
|
+
assert_match %r/^\S{22}$/, book_id2
|
116
|
+
|
117
|
+
@index.refresh
|
118
|
+
|
119
|
+
h = @index.docs('book').get :id => book_id
|
120
|
+
assert !h['exists'], 'was not successfully deleted'
|
121
|
+
|
122
|
+
h = @index.docs('book').get :id => book_id2
|
123
|
+
assert_equal 'Tolkien', h['_source']['author']
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'allows documents to be JSON strings' do
|
127
|
+
h = @index.bulk do |b|
|
128
|
+
b.index '{"author":"pea53", "message":"just a test tweet"}', :_id => 1, :_type => 'tweet'
|
129
|
+
b.create '{"author":"John Scalzi", "title":"Old Mans War"}', :_id => 1, :_type => 'book'
|
130
|
+
end
|
131
|
+
items = h['items']
|
132
|
+
|
133
|
+
assert_instance_of Fixnum, h['took']
|
134
|
+
|
135
|
+
assert_bulk_index h['items'].first
|
136
|
+
assert_bulk_create h['items'].last
|
137
|
+
|
138
|
+
@index.refresh
|
139
|
+
|
140
|
+
h = @index.docs('tweet').get :id => 1
|
141
|
+
assert_equal 'pea53', h['_source']['author']
|
142
|
+
|
143
|
+
h = @index.docs('book').get :id => 1
|
144
|
+
assert_equal 'John Scalzi', h['_source']['author']
|
145
|
+
|
146
|
+
|
147
|
+
h = @index.bulk do |b|
|
148
|
+
b.index '{"author":"Tolkien", "title":"The Silmarillion"}', :_id => 2, :_type => 'book'
|
149
|
+
b.delete :_id => 1, :_type => 'book'
|
150
|
+
end
|
151
|
+
items = h['items']
|
152
|
+
|
153
|
+
assert_bulk_index h['items'].first, 'expected to index a book'
|
154
|
+
assert_bulk_delete h['items'].last, 'expected to delete a book'
|
155
|
+
|
156
|
+
@index.refresh
|
157
|
+
|
158
|
+
h = @index.docs('book').get :id => 1
|
159
|
+
assert !h['exists'], 'was not successfully deleted'
|
160
|
+
|
161
|
+
h = @index.docs('book').get :id => 2
|
162
|
+
assert_equal 'Tolkien', h['_source']['author']
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'executes a bulk API call when a request size is reached' do
|
166
|
+
ary = []
|
167
|
+
ary << @index.bulk(:request_size => 300) do |b|
|
168
|
+
2.times { |num|
|
169
|
+
document = {:_id => num, :_type => 'tweet', :author => 'pea53', :message => "tweet #{num} is a 100 character request"}
|
170
|
+
ary << b.index(document)
|
171
|
+
}
|
172
|
+
ary.compact!
|
173
|
+
assert_equal 0, ary.length
|
174
|
+
|
175
|
+
7.times { |num|
|
176
|
+
document = {:_id => num+2, :_type => 'tweet', :author => 'pea53', :message => "tweet #{num+2} is a 100 character request"}
|
177
|
+
ary << b.index(document)
|
178
|
+
}
|
179
|
+
ary.compact!
|
180
|
+
assert_equal 3, ary.length
|
181
|
+
|
182
|
+
document = {:_id => 10, :_type => 'tweet', :author => 'pea53', :message => "tweet 10 is a 102 character request"}
|
183
|
+
ary << b.index(document)
|
184
|
+
end
|
185
|
+
ary.compact!
|
186
|
+
|
187
|
+
assert_equal 4, ary.length
|
188
|
+
ary.each { |a| a['items'].each { |b| assert_bulk_index(b) } }
|
189
|
+
|
190
|
+
@index.refresh
|
191
|
+
h = @index.docs.search :q => '*:*', :search_type => 'count'
|
192
|
+
|
193
|
+
assert_equal 10, h['hits']['total']
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'executes a bulk API call when an action count is reached' do
|
197
|
+
ary = []
|
198
|
+
ary << @index.bulk(:action_count => 3) do |b|
|
199
|
+
2.times { |num|
|
200
|
+
document = {:_id => num, :_type => 'tweet', :author => 'pea53', :message => "this is tweet number #{num}"}
|
201
|
+
ary << b.index(document)
|
202
|
+
}
|
203
|
+
ary.compact!
|
204
|
+
assert_equal 0, ary.length
|
205
|
+
|
206
|
+
7.times { |num|
|
207
|
+
document = {:_id => num+2, :_type => 'tweet', :author => 'pea53', :message => "this is tweet number #{num+2}"}
|
208
|
+
ary << b.index(document)
|
209
|
+
}
|
210
|
+
ary.compact!
|
211
|
+
assert_equal 3, ary.length
|
212
|
+
|
213
|
+
document = {:_id => 10, :_type => 'tweet', :author => 'pea53', :message => "this is tweet number 10"}
|
214
|
+
ary << b.index(document)
|
215
|
+
end
|
216
|
+
ary.compact!
|
217
|
+
|
218
|
+
assert_equal 4, ary.length
|
219
|
+
ary.each { |a| a['items'].each { |b| assert_bulk_index(b) } }
|
220
|
+
|
221
|
+
@index.refresh
|
222
|
+
h = @index.docs.search :q => '*:*', :search_type => 'count'
|
223
|
+
|
224
|
+
assert_equal 10, h['hits']['total']
|
225
|
+
end
|
226
|
+
end
|