elastomer-client 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,244 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastomer::Client::Index do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@name = 'elastomer-index-test'
|
7
|
+
@index = $client.index @name
|
8
|
+
@index.delete if @index.exists?
|
9
|
+
end
|
10
|
+
|
11
|
+
after do
|
12
|
+
@index.delete if @index.exists?
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'requires an index name' do
|
16
|
+
assert_raises(ArgumentError) { $client.index }
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'determines if an index exists' do
|
20
|
+
assert !@index.exists?, 'the index should not yet exist'
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'when creating an index' do
|
24
|
+
it 'creates an index' do
|
25
|
+
@index.create :settings => { :number_of_shards => 3, :number_of_replicas => 0 }
|
26
|
+
assert @index.exists?, 'the index should now exist'
|
27
|
+
|
28
|
+
settings = @index.settings[@name]['settings']
|
29
|
+
|
30
|
+
# COMPATIBILITY
|
31
|
+
# ES 1.0 changed the default return format of index settings to always
|
32
|
+
# expand nested properties, e.g.
|
33
|
+
# {"index.number_of_replicas": "1"} changed to
|
34
|
+
# {"index": {"number_of_replicas":"1"}}
|
35
|
+
|
36
|
+
# To support both versions, we check for either return format.
|
37
|
+
value = settings['index.number_of_shards'] ||
|
38
|
+
settings['index']['number_of_shards']
|
39
|
+
assert_equal '3', value
|
40
|
+
value = settings['index.number_of_replicas'] ||
|
41
|
+
settings['index']['number_of_replicas']
|
42
|
+
assert_equal '0', value
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'adds mappings for document types' do
|
46
|
+
@index.create(
|
47
|
+
:settings => { :number_of_shards => 1, :number_of_replicas => 0 },
|
48
|
+
:mappings => {
|
49
|
+
:doco => {
|
50
|
+
:_source => { :enabled => false },
|
51
|
+
:_all => { :enabled => false },
|
52
|
+
:properties => {
|
53
|
+
:title => { :type => 'string', :analyzer => 'standard' },
|
54
|
+
:author => { :type => 'string', :index => 'not_analyzed' }
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
)
|
59
|
+
|
60
|
+
assert @index.exists?, 'the index should now exist'
|
61
|
+
assert_mapping_exists @index.mapping[@name], 'doco'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'updates index settings' do
|
66
|
+
@index.create :settings => { :number_of_shards => 1, :number_of_replicas => 0 }
|
67
|
+
|
68
|
+
@index.update_settings 'index.number_of_replicas' => 1
|
69
|
+
settings = @index.settings[@name]['settings']
|
70
|
+
|
71
|
+
# COMPATIBILITY
|
72
|
+
# ES 1.0 changed the default return format of index settings to always
|
73
|
+
# expand nested properties, e.g.
|
74
|
+
# {"index.number_of_replicas": "1"} changed to
|
75
|
+
# {"index": {"number_of_replicas":"1"}}
|
76
|
+
|
77
|
+
# To support both versions, we check for either return format.
|
78
|
+
value = settings['index.number_of_replicas'] ||
|
79
|
+
settings['index']['number_of_replicas']
|
80
|
+
assert_equal '1', value
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'updates document mappings' do
|
84
|
+
@index.create(
|
85
|
+
:mappings => {
|
86
|
+
:doco => {
|
87
|
+
:_source => { :enabled => false },
|
88
|
+
:_all => { :enabled => false },
|
89
|
+
:properties => {:title => { :type => 'string', :analyzer => 'standard' }}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
)
|
93
|
+
|
94
|
+
assert_property_exists @index.mapping[@name], 'doco', 'title'
|
95
|
+
|
96
|
+
@index.update_mapping 'doco', { :doco => { :properties => {
|
97
|
+
:author => { :type => 'string', :index => 'not_analyzed' }
|
98
|
+
}}}
|
99
|
+
|
100
|
+
assert_property_exists @index.mapping[@name], 'doco', 'author'
|
101
|
+
assert_property_exists @index.mapping[@name], 'doco', 'title'
|
102
|
+
|
103
|
+
@index.update_mapping 'mux_mool', { :mux_mool => { :properties => {
|
104
|
+
:song => { :type => 'string', :index => 'not_analyzed' }
|
105
|
+
}}}
|
106
|
+
|
107
|
+
assert_property_exists @index.mapping[@name], 'mux_mool', 'song'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'deletes document mappings' do
|
111
|
+
@index.create(
|
112
|
+
:mappings => {
|
113
|
+
:doco => {
|
114
|
+
:_source => { :enabled => false },
|
115
|
+
:_all => { :enabled => false },
|
116
|
+
:properties => {:title => { :type => 'string', :analyzer => 'standard' }}
|
117
|
+
}
|
118
|
+
}
|
119
|
+
)
|
120
|
+
assert_mapping_exists @index.mapping[@name], 'doco'
|
121
|
+
|
122
|
+
response = @index.delete_mapping 'doco'
|
123
|
+
assert_acknowledged response
|
124
|
+
assert @index.mapping == {} || @index.mapping[@name] == {}
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'lists all aliases to the index' do
|
128
|
+
@index.create(nil)
|
129
|
+
assert_equal({@name => {'aliases' => {}}}, @index.get_aliases)
|
130
|
+
|
131
|
+
$client.cluster.update_aliases :add => {:index => @name, :alias => 'foofaloo'}
|
132
|
+
assert_equal({@name => {'aliases' => {'foofaloo' => {}}}}, @index.get_aliases)
|
133
|
+
end
|
134
|
+
|
135
|
+
# COMPATIBILITY ES 1.x removed English stopwords from the default analyzers,
|
136
|
+
# so create a custom one with the English stopwords added.
|
137
|
+
if es_version_1_x?
|
138
|
+
it 'analyzes text and returns tokens' do
|
139
|
+
tokens = @index.analyze 'Just a few words to analyze.', :analyzer => 'standard', :index => nil
|
140
|
+
tokens = tokens['tokens'].map { |h| h['token'] }
|
141
|
+
assert_equal %w[just a few words to analyze], tokens
|
142
|
+
|
143
|
+
@index.create(
|
144
|
+
:settings => {
|
145
|
+
:number_of_shards => 1,
|
146
|
+
:number_of_replicas => 0,
|
147
|
+
:analysis => {
|
148
|
+
:analyzer => {
|
149
|
+
:english_standard => {
|
150
|
+
:type => :standard,
|
151
|
+
:stopwords => "_english_"
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
)
|
157
|
+
|
158
|
+
tokens = @index.analyze 'Just a few words to analyze.', :analyzer => 'english_standard'
|
159
|
+
tokens = tokens['tokens'].map { |h| h['token'] }
|
160
|
+
assert_equal %w[just few words analyze], tokens
|
161
|
+
end
|
162
|
+
else
|
163
|
+
it 'analyzes text and returns tokens' do
|
164
|
+
tokens = @index.analyze 'Just a few words to analyze.', :index => nil
|
165
|
+
tokens = tokens['tokens'].map { |h| h['token'] }
|
166
|
+
assert_equal %w[just few words analyze], tokens
|
167
|
+
|
168
|
+
tokens = @index.analyze 'Just a few words to analyze.', :analyzer => 'simple', :index => nil
|
169
|
+
tokens = tokens['tokens'].map { |h| h['token'] }
|
170
|
+
assert_equal %w[just a few words to analyze], tokens
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "when an index exists" do
|
175
|
+
before do
|
176
|
+
@index.create(nil)
|
177
|
+
wait_for_index(@name)
|
178
|
+
end
|
179
|
+
|
180
|
+
#TODO assert this only hits the desired index
|
181
|
+
it 'deletes' do
|
182
|
+
response = @index.delete
|
183
|
+
assert_acknowledged response
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'opens' do
|
187
|
+
response = @index.open
|
188
|
+
assert_acknowledged response
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'closes' do
|
192
|
+
response = @index.close
|
193
|
+
assert_acknowledged response
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'refreshes' do
|
197
|
+
response = @index.refresh
|
198
|
+
assert_equal 0, response["_shards"]["failed"]
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'flushes' do
|
202
|
+
response = @index.flush
|
203
|
+
assert_equal 0, response["_shards"]["failed"]
|
204
|
+
end
|
205
|
+
|
206
|
+
it 'optimizes' do
|
207
|
+
response = @index.optimize
|
208
|
+
assert_equal 0, response["_shards"]["failed"]
|
209
|
+
end
|
210
|
+
|
211
|
+
# COMPATIBILITY ES 1.2 removed support for the gateway snapshot API.
|
212
|
+
if es_version_supports_gateway_snapshots?
|
213
|
+
it 'snapshots' do
|
214
|
+
response = @index.snapshot
|
215
|
+
assert_equal 0, response["_shards"]["failed"]
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'clears caches' do
|
220
|
+
response = @index.clear_cache
|
221
|
+
assert_equal 0, response["_shards"]["failed"]
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'gets stats' do
|
225
|
+
response = @index.stats
|
226
|
+
if response.key? 'indices'
|
227
|
+
assert_includes response["indices"], "elastomer-index-test"
|
228
|
+
else
|
229
|
+
assert_includes response["_all"]["indices"], "elastomer-index-test"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'gets status' do
|
234
|
+
response = @index.status
|
235
|
+
assert_includes response["indices"], "elastomer-index-test"
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'gets segments' do
|
239
|
+
@index.docs('foo').index("foo" => "bar")
|
240
|
+
response = @index.segments
|
241
|
+
assert_includes response["indices"], "elastomer-index-test"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastomer::Client::MultiSearch do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@name = 'elastomer-msearch-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
|
+
:doc1 => {
|
14
|
+
:_source => { :enabled => true }, :_all => { :enabled => false },
|
15
|
+
:properties => {
|
16
|
+
:title => { :type => 'string', :analyzer => 'standard' },
|
17
|
+
:author => { :type => 'string', :index => 'not_analyzed' }
|
18
|
+
}
|
19
|
+
},
|
20
|
+
:doc2 => {
|
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
|
+
|
32
|
+
@docs = @index.docs
|
33
|
+
end
|
34
|
+
|
35
|
+
after do
|
36
|
+
@index.delete if @index.exists?
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'performs multisearches' do
|
40
|
+
populate!
|
41
|
+
|
42
|
+
body = [
|
43
|
+
'{"index" : "elastomer-msearch-test", "search_type" : "count"}',
|
44
|
+
'{"query" : {"match_all" : {}}}',
|
45
|
+
'{"index" : "elastomer-msearch-test", "type": "doc2"}',
|
46
|
+
'{"query" : {"match": {"author" : "grantr"}}}',
|
47
|
+
nil
|
48
|
+
]
|
49
|
+
body = body.join "\n"
|
50
|
+
h = $client.multi_search body
|
51
|
+
response1, response2 = h["responses"]
|
52
|
+
assert_equal 4, response1["hits"]["total"]
|
53
|
+
assert_equal 1, response2["hits"]["total"]
|
54
|
+
assert_equal "2", response2["hits"]["hits"][0]["_id"]
|
55
|
+
|
56
|
+
body = [
|
57
|
+
'{}',
|
58
|
+
'{"query" : {"match": {"author" : "grantr"}}}',
|
59
|
+
nil
|
60
|
+
]
|
61
|
+
body = body.join "\n"
|
62
|
+
h = $client.multi_search body, :index => @name
|
63
|
+
response1 = h["responses"].first
|
64
|
+
assert_equal 1, response1["hits"]["total"]
|
65
|
+
assert_equal "2", response1["hits"]["hits"][0]["_id"]
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'supports a nice block syntax' do
|
69
|
+
populate!
|
70
|
+
|
71
|
+
h = $client.multi_search do |m|
|
72
|
+
m.search({:query => { :match_all => {}}}, :index => @name, :search_type => :count)
|
73
|
+
m.search({:query => { :match => { "title" => "author" }}}, :index => @name, :type => 'doc2')
|
74
|
+
end
|
75
|
+
|
76
|
+
response1, response2 = h["responses"]
|
77
|
+
|
78
|
+
assert_equal 4, response1["hits"]["total"]
|
79
|
+
assert_equal 2, response2["hits"]["total"]
|
80
|
+
|
81
|
+
h = @index.multi_search do |m|
|
82
|
+
m.search({:query => { :match_all => {}}}, :search_type => :count)
|
83
|
+
m.search({:query => { :match => { "title" => "author" }}}, :type => 'doc2')
|
84
|
+
end
|
85
|
+
|
86
|
+
response1, response2 = h["responses"]
|
87
|
+
|
88
|
+
assert_equal 4, response1["hits"]["total"]
|
89
|
+
assert_equal 2, response2["hits"]["total"]
|
90
|
+
|
91
|
+
h = @index.docs('doc1').multi_search do |m|
|
92
|
+
m.search({:query => { :match_all => {}}}, :search_type => :count)
|
93
|
+
m.search({:query => { :match => { "title" => "logging" }}}, :type => 'doc2')
|
94
|
+
end
|
95
|
+
|
96
|
+
response1, response2 = h["responses"]
|
97
|
+
|
98
|
+
assert_equal 2, response1["hits"]["total"]
|
99
|
+
assert_equal 1, response2["hits"]["total"]
|
100
|
+
end
|
101
|
+
|
102
|
+
def populate!
|
103
|
+
@docs.add \
|
104
|
+
:_id => 1,
|
105
|
+
:_type => 'doc1',
|
106
|
+
:title => 'the author of gravatar',
|
107
|
+
:author => 'mojombo'
|
108
|
+
|
109
|
+
@docs.add \
|
110
|
+
:_id => 2,
|
111
|
+
:_type => 'doc1',
|
112
|
+
:title => 'the author of resque',
|
113
|
+
:author => 'defunkt'
|
114
|
+
|
115
|
+
@docs.add \
|
116
|
+
:_id => 1,
|
117
|
+
:_type => 'doc2',
|
118
|
+
:title => 'the author of logging',
|
119
|
+
:author => 'pea53'
|
120
|
+
|
121
|
+
@docs.add \
|
122
|
+
:_id => 2,
|
123
|
+
:_type => 'doc2',
|
124
|
+
:title => 'the author of rubber-band',
|
125
|
+
:author => 'grantr'
|
126
|
+
|
127
|
+
@index.refresh
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastomer::Client::Nodes do
|
4
|
+
|
5
|
+
it 'gets info for the ndoe(s)' do
|
6
|
+
h = $client.nodes.info
|
7
|
+
assert h.key?('cluster_name'), 'the cluster name is returned'
|
8
|
+
assert_instance_of Hash, h['nodes'], 'the node list is returned'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'gets stats for the node(s)' do
|
12
|
+
h = $client.nodes.stats
|
13
|
+
assert_instance_of Hash, h['nodes'], 'the node list is returned'
|
14
|
+
|
15
|
+
node = h['nodes'].values.first
|
16
|
+
assert node.key?('indices'), 'indices stats are returned'
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'gets the hot threads for the node(s)' do
|
20
|
+
str = $client.nodes.hot_threads :read_timeout => 2
|
21
|
+
assert_instance_of String, str
|
22
|
+
assert_match %r/cpu usage by thread/, str
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can be scoped to a single node' do
|
26
|
+
h = $client.nodes('node-with-no-name').info
|
27
|
+
assert_empty h['nodes']
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can be scoped to a multiple nodes' do
|
31
|
+
h = $client.nodes(%w[node1 node2 node3]).info
|
32
|
+
assert_empty h['nodes']
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastomer::Client::Scan do
|
4
|
+
|
5
|
+
before do
|
6
|
+
@name = 'elastomer-scan-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
|
+
populate!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'scans over all documents in an index' do
|
35
|
+
scan = @index.scan '{"query":{"match_all":{}}}', :size => 10
|
36
|
+
|
37
|
+
counts = {'tweet' => 0, 'book' => 0}
|
38
|
+
scan.each_document { |h| counts[h['_type']] += 1 }
|
39
|
+
|
40
|
+
assert_equal 50, counts['tweet']
|
41
|
+
assert_equal 25, counts['book']
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'restricts the scan to a single document type' do
|
45
|
+
scan = @index.scan '{"query":{"match_all":{}}}', :type => 'book'
|
46
|
+
|
47
|
+
counts = {'tweet' => 0, 'book' => 0}
|
48
|
+
scan.each_document { |h| counts[h['_type']] += 1 }
|
49
|
+
|
50
|
+
assert_equal 0, counts['tweet']
|
51
|
+
assert_equal 25, counts['book']
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'limits results by query' do
|
55
|
+
scan = @index.scan :query => { :bool => { :should => [
|
56
|
+
{:match => {:author => 'pea53'}},
|
57
|
+
{:match => {:title => '17'}}
|
58
|
+
]}}
|
59
|
+
|
60
|
+
counts = {'tweet' => 0, 'book' => 0}
|
61
|
+
scan.each_document { |h| counts[h['_type']] += 1 }
|
62
|
+
|
63
|
+
assert_equal 50, counts['tweet']
|
64
|
+
assert_equal 1, counts['book']
|
65
|
+
end
|
66
|
+
|
67
|
+
def populate!
|
68
|
+
h = @index.bulk do |b|
|
69
|
+
50.times { |num|
|
70
|
+
b.index %Q({"author":"pea53","message":"this is tweet number #{num}"}), :_id => num, :_type => 'tweet'
|
71
|
+
}
|
72
|
+
end
|
73
|
+
h['items'].each {|item| assert_bulk_index(item) }
|
74
|
+
|
75
|
+
h = @index.bulk do |b|
|
76
|
+
25.times { |num|
|
77
|
+
b.index %Q({"author":"Pratchett","title":"DiscWorld Book #{num}"}), :_id => num, :_type => 'book'
|
78
|
+
}
|
79
|
+
end
|
80
|
+
h['items'].each {|item| assert_bulk_index(item) }
|
81
|
+
|
82
|
+
@index.refresh
|
83
|
+
end
|
84
|
+
end
|