elastomer-client 2.3.0 → 3.0.0

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +0 -4
  4. data/CHANGELOG.md +8 -0
  5. data/docker/docker-compose.cibuild.yml +8 -0
  6. data/docker/docker-compose.es24.yml +34 -0
  7. data/docker/docker-compose.es56.yml +37 -0
  8. data/docker/elasticsearch.yml +15 -0
  9. data/docs/index.md +2 -2
  10. data/docs/notifications.md +1 -1
  11. data/elastomer-client.gemspec +2 -0
  12. data/lib/elastomer/client.rb +86 -33
  13. data/lib/elastomer/client/app_delete_by_query.rb +158 -0
  14. data/lib/elastomer/client/delete_by_query.rb +8 -115
  15. data/lib/elastomer/client/docs.rb +63 -13
  16. data/lib/elastomer/client/errors.rb +10 -2
  17. data/lib/elastomer/client/index.rb +40 -12
  18. data/lib/elastomer/client/multi_percolate.rb +2 -2
  19. data/lib/elastomer/client/native_delete_by_query.rb +60 -0
  20. data/lib/elastomer/client/percolator.rb +6 -3
  21. data/lib/elastomer/client/scroller.rb +22 -7
  22. data/lib/elastomer/client/tasks.rb +188 -0
  23. data/lib/elastomer/client/warmer.rb +6 -0
  24. data/lib/elastomer/notifications.rb +1 -0
  25. data/lib/elastomer/version.rb +1 -1
  26. data/lib/elastomer/version_support.rb +177 -0
  27. data/script/cibuild +77 -6
  28. data/script/cibuild-elastomer-client +1 -0
  29. data/script/cibuild-elastomer-client-es24 +8 -0
  30. data/script/cibuild-elastomer-client-es56 +8 -0
  31. data/script/poll-for-es +20 -0
  32. data/test/client/{delete_by_query_test.rb → app_delete_by_query_test.rb} +7 -7
  33. data/test/client/bulk_test.rb +9 -13
  34. data/test/client/cluster_test.rb +2 -2
  35. data/test/client/docs_test.rb +133 -49
  36. data/test/client/errors_test.rb +21 -1
  37. data/test/client/es_5_x_warmer_test.rb +13 -0
  38. data/test/client/index_test.rb +104 -39
  39. data/test/client/multi_percolate_test.rb +13 -6
  40. data/test/client/multi_search_test.rb +5 -5
  41. data/test/client/native_delete_by_query_test.rb +123 -0
  42. data/test/client/nodes_test.rb +1 -1
  43. data/test/client/percolator_test.rb +10 -2
  44. data/test/client/repository_test.rb +1 -1
  45. data/test/client/scroller_test.rb +16 -6
  46. data/test/client/snapshot_test.rb +1 -1
  47. data/test/client/stubbed_client_test.rb +1 -1
  48. data/test/client/tasks_test.rb +139 -0
  49. data/test/client/template_test.rb +1 -1
  50. data/test/client/warmer_test.rb +8 -4
  51. data/test/client_test.rb +99 -0
  52. data/test/core_ext/time_test.rb +1 -1
  53. data/test/notifications_test.rb +4 -0
  54. data/test/test_helper.rb +129 -21
  55. data/test/version_support_test.rb +119 -0
  56. metadata +59 -5
@@ -1,4 +1,4 @@
1
- require File.expand_path("../../test_helper", __FILE__)
1
+ require_relative "../test_helper"
2
2
 
3
3
  describe Elastomer::Client::Error do
4
4
 
@@ -72,4 +72,24 @@ describe Elastomer::Client::Error do
72
72
  assert !Elastomer::Client::ConnectionFailed.fatal, "Connection failures are not fatal"
73
73
  assert !Elastomer::Client::ServerError.fatal, "Server errors are not fatal"
74
74
  end
75
+
76
+ if parameter_validation?
77
+ it "wraps illegal argument exceptions" do
78
+ begin
79
+ $client.get("/_cluster/health?consistency=all")
80
+ assert false, "IllegalArgument exception was not raised"
81
+ rescue Elastomer::Client::IllegalArgument => err
82
+ assert_match(/request \[\/_cluster\/health\] contains unrecognized parameter: \[consistency\]/, err.message)
83
+ end
84
+ end
85
+ else
86
+ it "does not raise illegal argument exceptions" do
87
+ begin
88
+ $client.get("/_cluster/health?consistency=all")
89
+ assert true, "Exception was not raised"
90
+ rescue Elastomer::Client::Error => err
91
+ assert false, "Exception #{err} was raised"
92
+ end
93
+ end
94
+ end
75
95
  end
@@ -0,0 +1,13 @@
1
+ require_relative "../test_helper"
2
+
3
+ describe "Elastomer::Client::Warmer under ES 5.x" do
4
+ it "cannot be instantiated" do
5
+ if $client.version_support.supports_warmers?
6
+ skip "warmers are still supported in ES #{$client.version}."
7
+ end
8
+
9
+ assert_raises(Elastomer::Client::IncompatibleVersionException) do
10
+ Elastomer::Client::Warmer.new($client, "index", "warmer")
11
+ end
12
+ end
13
+ end
@@ -1,4 +1,4 @@
1
- require File.expand_path("../../test_helper", __FILE__)
1
+ require_relative "../test_helper"
2
2
 
3
3
  describe Elastomer::Client::Index do
4
4
 
@@ -55,8 +55,8 @@ describe Elastomer::Client::Index do
55
55
  :_source => { :enabled => false },
56
56
  :_all => { :enabled => false },
57
57
  :properties => {
58
- :title => { :type => "string", :analyzer => "standard" },
59
- :author => { :type => "string", :index => "not_analyzed" }
58
+ :title => $client.version_support.text(analyzer: "standard"),
59
+ :author => $client.version_support.keyword
60
60
  }
61
61
  }
62
62
  }
@@ -74,8 +74,8 @@ describe Elastomer::Client::Index do
74
74
  :_source => { :enabled => false },
75
75
  :_all => { :enabled => false },
76
76
  :properties => {
77
- :title => { :type => "string", :analyzer => "standard" },
78
- :author => { :type => "string", :index => "not_analyzed" }
77
+ :title => $client.version_support.text(analyzer: "standard"),
78
+ :author => $client.version_support.keyword
79
79
  }
80
80
  }
81
81
  }
@@ -110,7 +110,7 @@ describe Elastomer::Client::Index do
110
110
  :doco => {
111
111
  :_source => { :enabled => false },
112
112
  :_all => { :enabled => false },
113
- :properties => {:title => { :type => "string", :analyzer => "standard" }}
113
+ :properties => {:title => $client.version_support.text(analyzer: "standard")}
114
114
  }
115
115
  }
116
116
  )
@@ -118,14 +118,14 @@ describe Elastomer::Client::Index do
118
118
  assert_property_exists @index.mapping[@name], "doco", "title"
119
119
 
120
120
  @index.update_mapping "doco", { :doco => { :properties => {
121
- :author => { :type => "string", :index => "not_analyzed" }
121
+ :author => $client.version_support.keyword
122
122
  }}}
123
123
 
124
124
  assert_property_exists @index.mapping[@name], "doco", "author"
125
125
  assert_property_exists @index.mapping[@name], "doco", "title"
126
126
 
127
127
  @index.update_mapping "mux_mool", { :mux_mool => { :properties => {
128
- :song => { :type => "string", :index => "not_analyzed" }
128
+ :song => $client.version_support.keyword
129
129
  }}}
130
130
 
131
131
  assert_property_exists @index.mapping[@name], "mux_mool", "song"
@@ -137,7 +137,7 @@ describe Elastomer::Client::Index do
137
137
  :doco => {
138
138
  :_source => { :enabled => false },
139
139
  :_all => { :enabled => false },
140
- :properties => {:title => { :type => "string", :analyzer => "standard" }}
140
+ :properties => {:title => $client.version_support.text(analyzer: "standard")}
141
141
  }
142
142
  }
143
143
  )
@@ -145,14 +145,14 @@ describe Elastomer::Client::Index do
145
145
  assert_property_exists @index.mapping[@name], "doco", "title"
146
146
 
147
147
  @index.put_mapping "doco", { :doco => { :properties => {
148
- :author => { :type => "string", :index => "not_analyzed" }
148
+ :author => $client.version_support.keyword
149
149
  }}}
150
150
 
151
151
  assert_property_exists @index.mapping[@name], "doco", "author"
152
152
  assert_property_exists @index.mapping[@name], "doco", "title"
153
153
 
154
154
  @index.put_mapping "mux_mool", { :mux_mool => { :properties => {
155
- :song => { :type => "string", :index => "not_analyzed" }
155
+ :song => $client.version_support.keyword
156
156
  }}}
157
157
 
158
158
  assert_property_exists @index.mapping[@name], "mux_mool", "song"
@@ -163,10 +163,29 @@ describe Elastomer::Client::Index do
163
163
  assert_equal({@name => {"aliases" => {}}}, @index.get_aliases)
164
164
 
165
165
  $client.cluster.update_aliases :add => {:index => @name, :alias => "foofaloo"}
166
- assert_equal({@name => {"aliases" => {"foofaloo" => {}}}}, @index.get_aliases)
166
+ $client.cluster.update_aliases :add => {:index => @name, :alias => "bar"}
167
+
168
+ assert_equal({@name => {"aliases" => {"foofaloo" => {}, "bar" => {}}}}, @index.get_aliases)
167
169
 
168
170
  assert_equal({@name => {"aliases" => {"foofaloo" => {}}}}, @index.get_alias("f*"))
169
- assert_equal({}, @index.get_alias("r*"))
171
+ assert_equal({@name => {"aliases" => {"foofaloo" => {}, "bar" => {}}}}, @index.get_alias("*"))
172
+
173
+ if fetching_non_existent_alias_returns_error?
174
+ exception = assert_raises(Elastomer::Client::RequestError) do
175
+ @index.get_alias("not-there")
176
+ end
177
+ assert_equal("alias [not-there] missing", exception.message)
178
+ assert_equal(404, exception.status)
179
+
180
+ exception = assert_raises(Elastomer::Client::RequestError) do
181
+ @index.get_alias("not*")
182
+ end
183
+ assert_equal("alias [not*] missing", exception.message)
184
+ assert_equal(404, exception.status)
185
+ else
186
+ assert_equal({}, @index.get_alias("not-there"))
187
+ assert_equal({}, @index.get_alias("not*"))
188
+ end
170
189
  end
171
190
 
172
191
  it "adds and deletes aliases to the index" do
@@ -186,7 +205,7 @@ describe Elastomer::Client::Index do
186
205
  end
187
206
 
188
207
  it "analyzes text and returns tokens" do
189
- tokens = @index.analyze "Just a few words to analyze.", :analyzer => "standard", :index => nil
208
+ tokens = @index.analyze({text: "Just a few words to analyze.", analyzer: "standard"}, index: nil)
190
209
  tokens = tokens["tokens"].map { |h| h["token"] }
191
210
  assert_equal %w[just a few words to analyze], tokens
192
211
 
@@ -206,7 +225,7 @@ describe Elastomer::Client::Index do
206
225
  )
207
226
  wait_for_index(@name)
208
227
 
209
- tokens = @index.analyze "Just a few words to analyze.", :analyzer => "english_standard"
228
+ tokens = @index.analyze({text: "Just a few words to analyze.", analyzer: "english_standard"})
210
229
  tokens = tokens["tokens"].map { |h| h["token"] }
211
230
  assert_equal %w[just few words analyze], tokens
212
231
  end
@@ -224,9 +243,12 @@ describe Elastomer::Client::Index do
224
243
  :type => "completion",
225
244
  :analyzer => "simple",
226
245
  :search_analyzer => "simple",
227
- :payloads => false
228
246
  }
229
247
 
248
+ # COMPATIBILITY
249
+ # ES 5.x drops support for index-time payloads
250
+ suggest[:payloads] = false if index_time_payloads?
251
+
230
252
  @index.create(
231
253
  :settings => { :number_of_shards => 1, :number_of_replicas => 0 },
232
254
  :mappings => {
@@ -234,8 +256,8 @@ describe Elastomer::Client::Index do
234
256
  :_source => { :enabled => false },
235
257
  :_all => { :enabled => false },
236
258
  :properties => {
237
- :title => { :type => "string", :analyzer => "standard" },
238
- :author => { :type => "string", :index => "not_analyzed" },
259
+ :title => $client.version_support.text(analyzer: "standard"),
260
+ :author => $client.version_support.keyword,
239
261
  :suggest => suggest
240
262
  }
241
263
  }
@@ -270,11 +292,15 @@ describe Elastomer::Client::Index do
270
292
  assert_equal 0, response["_shards"]["failed"]
271
293
  end
272
294
 
273
- it "optimizes" do
274
- response = @index.optimize
295
+ it "force merges" do
296
+ response = @index.forcemerge
275
297
  assert_equal 0, response["_shards"]["failed"]
276
298
  end
277
299
 
300
+ it "optimizes through force merge" do
301
+ assert_equal @index.method(:forcemerge), @index.method(:optimize)
302
+ end
303
+
278
304
  it "recovery" do
279
305
  response = @index.recovery
280
306
  assert_includes response, "elastomer-index-test"
@@ -304,20 +330,25 @@ describe Elastomer::Client::Index do
304
330
  @index.docs("foo").index("foo" => "bar")
305
331
  @index.refresh
306
332
  r = @index.delete_by_query(:q => "*")
307
- assert_equal({
308
- "_all" => {
309
- "found" => 1,
310
- "deleted" => 1,
311
- "missing" => 0,
312
- "failed" => 0,
313
- },
314
- @name => {
315
- "found" => 1,
316
- "deleted" => 1,
317
- "missing" => 0,
318
- "failed" => 0,
319
- }
320
- }, r["_indices"])
333
+
334
+ if supports_native_delete_by_query?
335
+ assert_equal(1, r['deleted'])
336
+ else
337
+ assert_equal({
338
+ "_all" => {
339
+ "found" => 1,
340
+ "deleted" => 1,
341
+ "missing" => 0,
342
+ "failed" => 0,
343
+ },
344
+ @name => {
345
+ "found" => 1,
346
+ "deleted" => 1,
347
+ "missing" => 0,
348
+ "failed" => 0,
349
+ }
350
+ }, r["_indices"])
351
+ end
321
352
  end
322
353
 
323
354
  it "creates a Percolator" do
@@ -327,6 +358,11 @@ describe Elastomer::Client::Index do
327
358
  end
328
359
 
329
360
  it "performs multi percolate queries" do
361
+ # COMPATIBILITY
362
+ if requires_percolator_mapping?
363
+ @index.update_mapping("percolator", { :properties => { :query => { :type => "percolator" } } })
364
+ end
365
+
330
366
  @index.docs.index \
331
367
  :_id => 1,
332
368
  :_type => "doco",
@@ -341,11 +377,12 @@ describe Elastomer::Client::Index do
341
377
 
342
378
  @index.percolator("1").create :query => { :match_all => { } }
343
379
  @index.percolator("2").create :query => { :match => { :author => "pea53" } }
380
+ @index.refresh
344
381
 
345
382
  h = @index.multi_percolate(:type => "doco") do |m|
346
383
  m.percolate :author => "pea53"
347
384
  m.percolate :author => "grantr"
348
- m.count({}, { :author => "grantr" })
385
+ m.count({ :author => "grantr" }, {})
349
386
  end
350
387
 
351
388
  response1, response2, response3 = h["responses"]
@@ -360,14 +397,14 @@ describe Elastomer::Client::Index do
360
397
  :_type => "doco",
361
398
  :title => "the magnificent",
362
399
  :author => "greg",
363
- :suggest => {:input => %w[Greg greg], :output => "Greg", :weight => 2}
400
+ :suggest => {:input => "greg", :weight => 2}
364
401
 
365
402
  @index.docs.index \
366
403
  :_id => 2,
367
404
  :_type => "doco",
368
405
  :title => "the author of rubber-band",
369
406
  :author => "grant",
370
- :suggest => {:input => %w[Grant grant], :output => "Grant", :weight => 1}
407
+ :suggest => {:input => "grant", :weight => 1}
371
408
 
372
409
  @index.refresh
373
410
  response = @index.suggest({:name => {:text => "gr", :completion => {:field => :suggest}}})
@@ -378,8 +415,36 @@ describe Elastomer::Client::Index do
378
415
 
379
416
  options = hash["options"]
380
417
  assert_equal 2, options.length
381
- assert_equal "Greg", options.first["text"]
382
- assert_equal "Grant", options.last["text"]
418
+ assert_equal "greg", options.first["text"]
419
+ assert_equal "grant", options.last["text"]
420
+ end
421
+
422
+ it "handles output parameter of field" do
423
+ document = {
424
+ _id: 1,
425
+ _type: "doco",
426
+ title: "the magnificent",
427
+ author: "greg",
428
+ suggest: {input: %w[Greg greg], output: "Greg", weight: 2}
429
+ }
430
+
431
+ if supports_suggest_output?
432
+ # It is not an error to index `output`...
433
+ @index.docs.index(document)
434
+
435
+ # ...and `output` is used in the search response
436
+ @index.refresh
437
+ response = @index.suggest({:name => {:text => "gr", :completion => {:field => :suggest}}})
438
+ assert_equal "Greg", response.fetch("name").first.fetch("options").first.fetch("text")
439
+ else
440
+ # Indexing the document fails when `output` is provided
441
+ exception = assert_raises(Elastomer::Client::RequestError) do
442
+ @index.docs.index(document)
443
+ end
444
+
445
+ assert_equal(400, exception.status)
446
+ assert_match(/\[output\]/, exception.message)
447
+ end
383
448
  end
384
449
  end
385
450
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path("../../test_helper", __FILE__)
1
+ require_relative "../test_helper"
2
2
 
3
3
  describe Elastomer::Client::MultiPercolate do
4
4
 
@@ -7,25 +7,32 @@ describe Elastomer::Client::MultiPercolate do
7
7
  @index = $client.index(@name)
8
8
 
9
9
  unless @index.exists?
10
- @index.create \
10
+ base_mappings_settings = {
11
11
  :settings => { "index.number_of_shards" => 1, "index.number_of_replicas" => 0 },
12
12
  :mappings => {
13
13
  :doc1 => {
14
14
  :_source => { :enabled => true }, :_all => { :enabled => false },
15
15
  :properties => {
16
- :title => { :type => "string", :analyzer => "standard" },
17
- :author => { :type => "string", :index => "not_analyzed" }
16
+ :title => $client.version_support.text(analyzer: "standard"),
17
+ :author => $client.version_support.keyword
18
18
  }
19
19
  },
20
20
  :doc2 => {
21
21
  :_source => { :enabled => true }, :_all => { :enabled => false },
22
22
  :properties => {
23
- :title => { :type => "string", :analyzer => "standard" },
24
- :author => { :type => "string", :index => "not_analyzed" }
23
+ :title => $client.version_support.text(analyzer: "standard"),
24
+ :author => $client.version_support.keyword
25
25
  }
26
26
  }
27
27
  }
28
+ }
28
29
 
30
+ # COMPATIBILITY
31
+ if requires_percolator_mapping?
32
+ base_mappings_settings[:mappings][:percolator] = { :properties => { :query => { :type => "percolator" } } }
33
+ end
34
+
35
+ @index.create base_mappings_settings
29
36
  wait_for_index(@name)
30
37
  end
31
38
 
@@ -1,4 +1,4 @@
1
- require File.expand_path("../../test_helper", __FILE__)
1
+ require_relative "../test_helper"
2
2
 
3
3
  describe Elastomer::Client::MultiSearch do
4
4
 
@@ -13,15 +13,15 @@ describe Elastomer::Client::MultiSearch do
13
13
  :doc1 => {
14
14
  :_source => { :enabled => true }, :_all => { :enabled => false },
15
15
  :properties => {
16
- :title => { :type => "string", :analyzer => "standard" },
17
- :author => { :type => "string", :index => "not_analyzed" }
16
+ :title => $client.version_support.text(analyzer: "standard"),
17
+ :author => $client.version_support.keyword
18
18
  }
19
19
  },
20
20
  :doc2 => {
21
21
  :_source => { :enabled => true }, :_all => { :enabled => false },
22
22
  :properties => {
23
- :title => { :type => "string", :analyzer => "standard" },
24
- :author => { :type => "string", :index => "not_analyzed" }
23
+ :title => $client.version_support.text(analyzer: "standard"),
24
+ :author => $client.version_support.keyword
25
25
  }
26
26
  }
27
27
  }
@@ -0,0 +1,123 @@
1
+ require_relative "../test_helper"
2
+
3
+ describe Elastomer::Client::NativeDeleteByQuery do
4
+ before do
5
+ @index = $client.index "elastomer-delete-by-query-test"
6
+ @index.delete if @index.exists?
7
+ @docs = @index.docs("docs")
8
+ end
9
+
10
+ after do
11
+ @index.delete if @index.exists?
12
+ end
13
+
14
+ if supports_native_delete_by_query?
15
+ describe "when an index with documents exists" do
16
+ before do
17
+ @index.create(nil)
18
+ wait_for_index(@index.name)
19
+ end
20
+
21
+ it "deletes by query" do
22
+ @docs.index({ :_id => 0, :name => "mittens" })
23
+ @docs.index({ :_id => 1, :name => "luna" })
24
+
25
+ @index.refresh
26
+
27
+ query = {
28
+ query: {
29
+ match: {
30
+ name: "mittens"
31
+ }
32
+ }
33
+ }
34
+
35
+ response = @index.native_delete_by_query(query)
36
+ refute_nil response["took"]
37
+ assert_equal(false, response["timed_out"])
38
+ assert_equal(1, response["batches"])
39
+ assert_equal(1, response["total"])
40
+ assert_equal(1, response["deleted"])
41
+ assert_empty(response["failures"])
42
+
43
+ @index.refresh
44
+ response = @docs.multi_get(:ids => [0, 1])
45
+ refute_found response.fetch("docs")[0]
46
+ assert_found response.fetch("docs")[1]
47
+ end
48
+
49
+ it "fails when internal version is 0" do
50
+ @docs.index({_id: 0, name: "mittens"})
51
+ # Creating a document with external version 0 also sets the internal version to 0
52
+ # Otherwise you can't index a document with version 0.
53
+ @docs.index({_id: 1, _version: 0, _version_type: "external", name: "mittens"})
54
+ @index.refresh
55
+
56
+ query = {
57
+ query: {
58
+ match: {
59
+ name: "mittens"
60
+ }
61
+ }
62
+ }
63
+
64
+ assert_raises(Elastomer::Client::RequestError) do
65
+ @index.native_delete_by_query(query)
66
+ end
67
+ end
68
+
69
+ it "fails when an unknown parameter is provided" do
70
+ assert_raises(Elastomer::Client::IllegalArgument) do
71
+ @index.native_delete_by_query({}, foo: "bar")
72
+ end
73
+ end
74
+
75
+ it "deletes by query when routing is specified" do
76
+ index = $client.index "elastomer-delete-by-query-routing-test"
77
+ index.delete if index.exists?
78
+ type = "docs"
79
+ index.create({mappings: { type => { _routing: { required: true } } } })
80
+ wait_for_index(@index.name)
81
+ docs = index.docs(type)
82
+
83
+ docs.index({ :_id => 0, :_routing => "cat", :name => "mittens" })
84
+ docs.index({ :_id => 1, :_routing => "cat", :name => "luna" })
85
+ docs.index({ :_id => 2, :_routing => "dog", :name => "mittens" })
86
+
87
+ query = {
88
+ query: {
89
+ match: {
90
+ name: "mittens"
91
+ }
92
+ }
93
+ }
94
+
95
+ index.refresh
96
+ response = index.native_delete_by_query(query, routing: "cat")
97
+
98
+ assert_equal(1, response["deleted"])
99
+
100
+ response = docs.multi_get({
101
+ :docs => [
102
+ { :_id => 0, :_routing => "cat" },
103
+ { :_id => 1, :_routing => "cat" },
104
+ { :_id => 2, :_routing => "dog" },
105
+ ]
106
+ })
107
+ refute_found response["docs"][0]
108
+ assert_found response["docs"][1]
109
+ assert_found response["docs"][2]
110
+
111
+ index.delete if index.exists?
112
+ end
113
+ end
114
+ else
115
+ describe "when native _delete_by_query is not supported" do
116
+ it "raises an error" do
117
+ assert_raises(Elastomer::Client::IncompatibleVersionException) do
118
+ @index.native_delete_by_query({query: {match_all: {}}})
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end