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.
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