elastomer-client 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +4 -0
  5. data/Gemfile +5 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +108 -0
  8. data/Rakefile +9 -0
  9. data/docs/notifications.md +71 -0
  10. data/elastomer-client.gemspec +30 -0
  11. data/lib/elastomer/client.rb +307 -0
  12. data/lib/elastomer/client/bulk.rb +257 -0
  13. data/lib/elastomer/client/cluster.rb +208 -0
  14. data/lib/elastomer/client/docs.rb +432 -0
  15. data/lib/elastomer/client/errors.rb +51 -0
  16. data/lib/elastomer/client/index.rb +407 -0
  17. data/lib/elastomer/client/multi_search.rb +115 -0
  18. data/lib/elastomer/client/nodes.rb +87 -0
  19. data/lib/elastomer/client/scan.rb +161 -0
  20. data/lib/elastomer/client/template.rb +85 -0
  21. data/lib/elastomer/client/warmer.rb +96 -0
  22. data/lib/elastomer/core_ext/time.rb +7 -0
  23. data/lib/elastomer/middleware/encode_json.rb +51 -0
  24. data/lib/elastomer/middleware/opaque_id.rb +69 -0
  25. data/lib/elastomer/middleware/parse_json.rb +39 -0
  26. data/lib/elastomer/notifications.rb +83 -0
  27. data/lib/elastomer/version.rb +7 -0
  28. data/script/bootstrap +16 -0
  29. data/script/cibuild +28 -0
  30. data/script/console +9 -0
  31. data/script/testsuite +10 -0
  32. data/test/assertions.rb +74 -0
  33. data/test/client/bulk_test.rb +226 -0
  34. data/test/client/cluster_test.rb +113 -0
  35. data/test/client/docs_test.rb +394 -0
  36. data/test/client/index_test.rb +244 -0
  37. data/test/client/multi_search_test.rb +129 -0
  38. data/test/client/nodes_test.rb +35 -0
  39. data/test/client/scan_test.rb +84 -0
  40. data/test/client/stubbed_client_tests.rb +40 -0
  41. data/test/client/template_test.rb +33 -0
  42. data/test/client/warmer_test.rb +56 -0
  43. data/test/client_test.rb +86 -0
  44. data/test/core_ext/time_test.rb +46 -0
  45. data/test/middleware/encode_json_test.rb +53 -0
  46. data/test/middleware/opaque_id_test.rb +39 -0
  47. data/test/middleware/parse_json_test.rb +54 -0
  48. data/test/test_helper.rb +94 -0
  49. 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