search_flip 4.0.0.beta8 → 4.0.0.beta11

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d63abc415f8eaad5ade7de6fc4ca424a8695b72016266a49a1d221f49530ca49
4
- data.tar.gz: a6170648ed2663b769b123ae443c5d0863a4456ec39be35c2762186b93383cff
3
+ metadata.gz: 9e032e109a7c31df14182c17d874f793327081c3c4e5b866dbfb261e8aa4e503
4
+ data.tar.gz: 73a2ce2eb918e9f3da28f3bf129eb614c1da5492d138985732af4b41a9f15a41
5
5
  SHA512:
6
- metadata.gz: 7c7879bcf95f3e53c5186ec50592cf7e46b703b819c34e7d7e91229972652daf132b7a73b3f85051c6d86117f66278f83b5ac2f2b12c23272bb36130b0e7aa72
7
- data.tar.gz: a04b20cbcf16eceb905798d1e5e2ea8dd60bb27b45bee9a0ab6f52aaed7d58e648a2f3139981117332a9824570db429f069ed749873dffd27ceb7cc274b3a7b4
6
+ metadata.gz: 152130c3a91ae69dcf53ff43534b0b853786900f1c03847ffead4d52254f95aec5dbfb48362d6386a61fdb92f128b68fc22b2b66f4df27f2a099a9d6db356eea
7
+ data.tar.gz: '09f13f81114378ebd9dea98ae713f5be8a27dea15966bf2fc05195900be8ed74c73d6aa46d6a458a4d4aaeb8467e32cc4296fce4e22dbfee672a1dc30d4af14f'
@@ -12,6 +12,7 @@ jobs:
12
12
  - docker.elastic.co/elasticsearch/elasticsearch:6.7.0
13
13
  - docker.elastic.co/elasticsearch/elasticsearch:7.0.0
14
14
  - docker.elastic.co/elasticsearch/elasticsearch:7.11.2
15
+ - docker.elastic.co/elasticsearch/elasticsearch:8.1.1
15
16
  ruby:
16
17
  - 2.6
17
18
  - 2.7
@@ -21,6 +22,7 @@ jobs:
21
22
  image: ${{ matrix.elasticsearch }}
22
23
  env:
23
24
  discovery.type: single-node
25
+ xpack.security.enabled: false
24
26
  ports:
25
27
  - 9200:9200
26
28
  steps:
@@ -30,6 +32,5 @@ jobs:
30
32
  ruby-version: ${{ matrix.ruby }}
31
33
  - run: gem install bundler
32
34
  - run: bundle
33
- - run: sleep 10
34
35
  - run: bundle exec rspec
35
36
  - run: bundle exec rubocop
data/.rubocop.yml CHANGED
@@ -3,6 +3,9 @@ AllCops:
3
3
  TargetRubyVersion: 2.5
4
4
  SuggestExtensions: false
5
5
 
6
+ Gemspec/RequireMFA:
7
+ Enabled: false
8
+
6
9
  Style/CaseLikeIf:
7
10
  Enabled: false
8
11
 
data/CHANGELOG.md CHANGED
@@ -11,6 +11,20 @@
11
11
  * Added `SearchFlip::Connection#get_cluster_settings` and
12
12
  `#update_cluster_settings`
13
13
 
14
+ ## v3.7.0
15
+
16
+ * Add `SearchFlip::Connection#bulk` to allow more clean bulk indexing to
17
+ multiple indices at once
18
+
19
+ ## v3.6.0
20
+
21
+ * Support Elasticsearch v8
22
+
23
+ ## v3.5.0
24
+
25
+ * Add `SearchFlip::Criteria#http_timeout` to allow specifying timeouts on
26
+ a query level
27
+
14
28
  ## v3.4.0
15
29
 
16
30
  * Expose `Http#timeout` via `SearchFlip::HTTPClient`
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  Using SearchFlip it is dead-simple to create index classes that correspond to
10
10
  [Elasticsearch](https://www.elastic.co/) indices and to manipulate, query and
11
11
  aggregate these indices using a chainable, concise, yet powerful DSL. Finally,
12
- SearchFlip supports Elasticsearch 2.x, 5.x, 6.x, 7.x. Check section
12
+ SearchFlip supports Elasticsearch 2.x, 5.x, 6.x, 7.x and 8.x. Check section
13
13
  [Feature Support](#feature-support) for version dependent features.
14
14
 
15
15
  ```ruby
@@ -698,6 +698,14 @@ Specify a timeout to limit query processing time:
698
698
  CommentIndex.timeout("3s").execute
699
699
  ```
700
700
 
701
+ * `http_timeout`
702
+
703
+ Specify a http timeout for the request which will be send to Elasticsearch:
704
+
705
+ ```ruby
706
+ CommentIndex.http_timeout(3).execute
707
+ ```
708
+
701
709
  * `terminate_after`
702
710
 
703
711
  Activate early query termination to stop query processing after the specified
data/docker-compose.yml CHANGED
@@ -4,5 +4,6 @@ services:
4
4
  image: $ES_IMAGE
5
5
  environment:
6
6
  - discovery.type=single-node
7
+ - xpack.security.enabled=false
7
8
  ports:
8
9
  - 9200:9200
@@ -93,7 +93,7 @@ module SearchFlip
93
93
  def msearch(criterias)
94
94
  payload = criterias.flat_map do |criteria|
95
95
  [
96
- SearchFlip::JSON.generate(index: criteria.target.index_name_with_prefix, type: criteria.target.type_name),
96
+ SearchFlip::JSON.generate(index: criteria.target.index_name_with_prefix, **(version.to_i < 8 ? { type: criteria.target.type_name } : {})),
97
97
  SearchFlip::JSON.generate(criteria.request)
98
98
  ]
99
99
  end
@@ -329,8 +329,8 @@ module SearchFlip
329
329
  # @return [Boolean] Returns true or raises SearchFlip::ResponseError
330
330
 
331
331
  def update_mapping(index_name, mapping, type_name: nil)
332
- url = type_name ? type_url(index_name, type_name) : index_url(index_name)
333
- params = type_name && version.to_f >= 6.7 ? { include_type_name: true } : {}
332
+ url = type_name && version.to_i < 8 ? type_url(index_name, type_name) : index_url(index_name)
333
+ params = type_name && version.to_f >= 6.7 && version.to_i < 8 ? { include_type_name: true } : {}
334
334
 
335
335
  http_client.put("#{url}/_mapping", params: params, json: mapping)
336
336
 
@@ -347,8 +347,8 @@ module SearchFlip
347
347
  # @return [Hash] The current type mapping
348
348
 
349
349
  def get_mapping(index_name, type_name: nil)
350
- url = type_name ? type_url(index_name, type_name) : index_url(index_name)
351
- params = type_name && version.to_f >= 6.7 ? { include_type_name: true } : {}
350
+ url = type_name && version.to_i < 8 ? type_url(index_name, type_name) : index_url(index_name)
351
+ params = type_name && version.to_f >= 6.7 && version.to_i < 8 ? { include_type_name: true } : {}
352
352
 
353
353
  response = http_client.headers(accept: "application/json").get("#{url}/_mapping", params: params)
354
354
 
@@ -384,6 +384,51 @@ module SearchFlip
384
384
  raise e
385
385
  end
386
386
 
387
+ # Initiates and yields a bulk object, such that index, import, create,
388
+ # update and delete requests can be appended to the bulk request. Please
389
+ # note that you need to manually pass the desired index name as well as
390
+ # type name (depending on the Elasticsearch version) when using #bulk on a
391
+ # connection object or Elasticsearch will return an error. After the bulk
392
+ # requests are successfully processed all existing indices will
393
+ # subsequently be refreshed when auto_refresh is enabled.
394
+ #
395
+ # @see SearchFlip::Config See SearchFlip::Config for auto_refresh
396
+ #
397
+ # @example
398
+ # connection = SearchFlip::Connection.new
399
+ #
400
+ # connection.bulk ignore_errors: [409] do |bulk|
401
+ # bulk.create comment.id, CommentIndex.serialize(comment),
402
+ # _index: CommentIndex.index_name, version: comment.version, version_type: "external_gte"
403
+ #
404
+ # bulk.delete product.id, _index: ProductIndex.index_name, routing: product.user_id
405
+ #
406
+ # # ...
407
+ # end
408
+ #
409
+ # @param options [Hash] Specifies options regarding the bulk indexing
410
+ # @option options ignore_errors [Array] Specifies an array of http status
411
+ # codes that shouldn't raise any exceptions, like eg 409 for conflicts,
412
+ # ie when optimistic concurrency control is used.
413
+ # @option options raise [Boolean] Prevents any exceptions from being
414
+ # raised. Please note that this only applies to the bulk response, not to
415
+ # the request in general, such that connection errors, etc will still
416
+ # raise.
417
+
418
+ def bulk(options = {})
419
+ default_options = {
420
+ http_client: http_client,
421
+ bulk_limit: bulk_limit,
422
+ bulk_max_mb: bulk_max_mb
423
+ }
424
+
425
+ SearchFlip::Bulk.new("#{base_url}/_bulk", default_options.merge(options)) do |indexer|
426
+ yield indexer
427
+ end
428
+
429
+ refresh if SearchFlip::Config[:auto_refresh]
430
+ end
431
+
387
432
  # Returns the full Elasticsearch type URL, ie base URL, index name with
388
433
  # prefix and type name.
389
434
  #
@@ -26,7 +26,8 @@ module SearchFlip
26
26
 
27
27
  attr_accessor :target, :profile_value, :source_value, :suggest_values, :includes_values,
28
28
  :eager_load_values, :preload_values, :failsafe_value, :scroll_args, :terminate_after_value,
29
- :timeout_value, :preference_value, :search_type_value, :routing_value, :track_total_hits_value
29
+ :timeout_value, :preference_value, :search_type_value, :routing_value, :track_total_hits_value,
30
+ :http_timeout_value
30
31
 
31
32
  # Creates a new criteria while merging the attributes (constraints,
32
33
  # settings, etc) of the current criteria with the attributes of another one
@@ -47,7 +48,7 @@ module SearchFlip
47
48
  [
48
49
  :profile_value, :failsafe_value, :terminate_after_value, :timeout_value, :offset_value,
49
50
  :limit_value, :scroll_args, :source_value, :preference_value, :search_type_value,
50
- :routing_value, :track_total_hits_value, :explain_value
51
+ :routing_value, :track_total_hits_value, :explain_value, :http_timeout_value
51
52
  ].each do |name|
52
53
  criteria.send(:"#{name}=", other.send(name)) unless other.send(name).nil?
53
54
  end
@@ -148,6 +149,22 @@ module SearchFlip
148
149
  end
149
150
  end
150
151
 
152
+ # Specifies a http timeout, such that a SearchFlip::TimeoutError will be
153
+ # thrown when the request times out.
154
+ #
155
+ # @example
156
+ # ProductIndex.http_timeout(3).search("hello world")
157
+ #
158
+ # @param value [Fixnum] The timeout value
159
+ #
160
+ # @return [SearchFlip::Criteria] A newly created extended criteria
161
+
162
+ def http_timeout(value)
163
+ fresh.tap do |criteria|
164
+ criteria.http_timeout_value = value
165
+ end
166
+ end
167
+
151
168
  # Specifies early query termination, such that the processing will be
152
169
  # stopped after the specified number of results has been accumulated.
153
170
  #
@@ -330,10 +347,15 @@ module SearchFlip
330
347
  dupped_request.delete(:from)
331
348
  dupped_request.delete(:size)
332
349
 
350
+ http_request = connection.http_client
351
+ http_request = http_request.timeout(http_timeout_value) if http_timeout_value
352
+
333
353
  if connection.version.to_i >= 5
334
- connection.http_client.post("#{target.type_url}/_delete_by_query", params: request_params.merge(params), json: dupped_request)
354
+ url = connection.version.to_i < 8 ? target.type_url : target.index_url
355
+
356
+ http_request.post("#{url}/_delete_by_query", params: request_params.merge(params), json: dupped_request)
335
357
  else
336
- connection.http_client.delete("#{target.type_url}/_query", params: request_params.merge(params), json: dupped_request)
358
+ http_request.delete("#{target.type_url}/_query", params: request_params.merge(params), json: dupped_request)
337
359
  end
338
360
 
339
361
  target.refresh if SearchFlip::Config[:auto_refresh]
@@ -501,8 +523,8 @@ module SearchFlip
501
523
  end
502
524
 
503
525
  # Executes the search request for the current criteria, ie sends the
504
- # request to Elasticsearch and returns the response. Connection and
505
- # response errors will be rescued if you specify the criteria to be
526
+ # request to Elasticsearch and returns the response. Connection, timeout
527
+ # and response errors will be rescued if you specify the criteria to be
506
528
  # #failsafe, such that an empty response is returned instead.
507
529
  #
508
530
  # @example
@@ -590,6 +612,7 @@ module SearchFlip
590
612
 
591
613
  def execute!
592
614
  http_request = connection.http_client.headers(accept: "application/json")
615
+ http_request = http_request.timeout(http_timeout_value) if http_timeout_value
593
616
 
594
617
  http_response =
595
618
  if scroll_args && scroll_args[:id]
@@ -599,17 +622,21 @@ module SearchFlip
599
622
  json: { scroll: scroll_args[:timeout], scroll_id: scroll_args[:id] }
600
623
  )
601
624
  elsif scroll_args
625
+ url = connection.version.to_i < 8 ? target.type_url : target.index_url
626
+
602
627
  http_request.post(
603
- "#{target.type_url}/_search",
628
+ "#{url}/_search",
604
629
  params: request_params.merge(scroll: scroll_args[:timeout]),
605
630
  json: request
606
631
  )
607
632
  else
608
- http_request.post("#{target.type_url}/_search", params: request_params, json: request)
633
+ url = connection.version.to_i < 8 ? target.type_url : target.index_url
634
+
635
+ http_request.post("#{url}/_search", params: request_params, json: request)
609
636
  end
610
637
 
611
638
  SearchFlip::Response.new(self, SearchFlip::JSON.parse(http_response.to_s))
612
- rescue SearchFlip::ConnectionError, SearchFlip::ResponseError => e
639
+ rescue SearchFlip::ConnectionError, SearchFlip::TimeoutError, SearchFlip::ResponseError => e
613
640
  raise e unless failsafe_value
614
641
 
615
642
  SearchFlip::Response.new(self, "took" => 0, "hits" => { "total" => 0, "hits" => [] })
@@ -66,6 +66,10 @@ module SearchFlip
66
66
  response
67
67
  rescue HTTP::ConnectionError => e
68
68
  raise SearchFlip::ConnectionError, e.message
69
+ rescue HTTP::TimeoutError => e
70
+ raise SearchFlip::TimeoutError, e.message
71
+ rescue HTTP::Error => e
72
+ raise SearchFlip::HttpError, e.message
69
73
  end
70
74
  end
71
75
  end
@@ -254,7 +254,7 @@ module SearchFlip
254
254
  :page, :per, :search, :highlight, :suggest, :custom, :find_in_batches, :find_results_in_batches,
255
255
  :find_each, :find_each_result, :failsafe, :total_entries, :total_count, :timeout, :terminate_after,
256
256
  :records, :results, :must, :must_not, :should, :preference, :search_type, :routing,
257
- :track_total_hits, :explain
257
+ :track_total_hits, :explain, :http_timeout
258
258
 
259
259
  # Override to specify the type name used within Elasticsearch. Recap,
260
260
  # this gem uses an individual index for each index class, because
@@ -455,7 +455,8 @@ module SearchFlip
455
455
  # @return [Hash] The specified document
456
456
 
457
457
  def get(id, params = {})
458
- response = connection.http_client.headers(accept: "application/json").get("#{type_url}/#{id}", params: params)
458
+ url = connection.version.to_i < 8 ? type_url : "#{index_url}/_doc"
459
+ response = connection.http_client.headers(accept: "application/json").get("#{url}/#{id}", params: params)
459
460
 
460
461
  SearchFlip::JSON.parse(response.to_s)
461
462
  end
@@ -473,7 +474,8 @@ module SearchFlip
473
474
  # @return [Hash] The raw response
474
475
 
475
476
  def mget(request, params = {})
476
- response = connection.http_client.headers(accept: "application/json").post("#{type_url}/_mget", json: request, params: params)
477
+ url = connection.version.to_i < 8 ? type_url : index_url
478
+ response = connection.http_client.headers(accept: "application/json").post("#{url}/_mget", json: request, params: params)
477
479
 
478
480
  SearchFlip::JSON.parse(response.to_s)
479
481
  end
@@ -599,7 +601,7 @@ module SearchFlip
599
601
  scope
600
602
  end
601
603
 
602
- # Initiates and yields the bulk object, such that index, import, create,
604
+ # Initiates and yields a bulk object, such that index, import, create,
603
605
  # update and delete requests can be appended to the bulk request. Sends a
604
606
  # refresh request afterwards if auto_refresh is enabled.
605
607
  #
@@ -631,7 +633,9 @@ module SearchFlip
631
633
  bulk_max_mb: connection.bulk_max_mb
632
634
  }
633
635
 
634
- SearchFlip::Bulk.new("#{type_url}/_bulk", default_options.merge(options)) do |indexer|
636
+ url = connection.version.to_i < 8 ? type_url : index_url
637
+
638
+ SearchFlip::Bulk.new("#{url}/_bulk", default_options.merge(options)) do |indexer|
635
639
  yield indexer
636
640
  end
637
641
 
@@ -1,3 +1,3 @@
1
1
  module SearchFlip
2
- VERSION = "4.0.0.beta8"
2
+ VERSION = "4.0.0.beta11"
3
3
  end
data/lib/search_flip.rb CHANGED
@@ -32,10 +32,15 @@ require "search_flip/index"
32
32
  require "search_flip/model"
33
33
 
34
34
  module SearchFlip
35
- class NotSupportedError < StandardError; end
36
- class ConnectionError < StandardError; end
35
+ class Error < StandardError; end
37
36
 
38
- class ResponseError < StandardError
37
+ class NotSupportedError < Error; end
38
+
39
+ class HttpError < Error; end
40
+ class ConnectionError < HttpError; end
41
+ class TimeoutError < HttpError; end
42
+
43
+ class ResponseError < Error
39
44
  attr_reader :code, :body
40
45
 
41
46
  def initialize(code:, body:)
data/search_flip.gemspec CHANGED
@@ -15,7 +15,6 @@ Gem::Specification.new do |spec|
15
15
 
16
16
  spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
18
  spec.require_paths = ["lib"]
20
19
 
21
20
  spec.post_install_message = <<~MESSAGE
@@ -16,10 +16,12 @@ RSpec.describe SearchFlip::AwsSigv4Plugin do
16
16
  it "adds the signed headers to the request" do
17
17
  Timecop.freeze Time.parse("2020-01-01 12:00:00 UTC") do
18
18
  expect(client).to receive(:headers).with(
19
- "host" => "localhost",
20
- "authorization" => /.*/,
21
- "x-amz-content-sha256" => /.*/,
22
- "x-amz-date" => /20200101T120000Z/
19
+ an_object_matching(
20
+ "host" => "localhost",
21
+ "authorization" => /.*/,
22
+ "x-amz-content-sha256" => /.*/,
23
+ "x-amz-date" => /20200101T120000Z/
24
+ )
23
25
  )
24
26
 
25
27
  plugin.call(client, :get, "http://localhost/index")
@@ -60,18 +60,15 @@ RSpec.describe SearchFlip::Bulk do
60
60
 
61
61
  it "uses the specified http_client" do
62
62
  product = create(:product)
63
+ url = ProductIndex.connection.version.to_i < 8 ? ProductIndex.type_url : ProductIndex.index_url
63
64
 
64
- stub_request(:put, "#{ProductIndex.type_url}/_bulk")
65
- .with(headers: { "X-Header" => "Value" })
66
- .to_return(status: 500)
65
+ stub_request(:put, "#{url}/_bulk").with(headers: { "X-Header" => "Value" }).to_return(status: 200, body: "{}")
67
66
 
68
- block = lambda do
69
- ProductIndex.bulk http_client: ProductIndex.connection.http_client.headers("X-Header" => "Value") do |bulk|
70
- bulk.index product.id, ProductIndex.serialize(product)
71
- end
67
+ ProductIndex.bulk http_client: ProductIndex.connection.http_client.headers("X-Header" => "Value") do |bulk|
68
+ bulk.index product.id, ProductIndex.serialize(product)
72
69
  end
73
70
 
74
- expect(&block).to raise_error(SearchFlip::ResponseError)
71
+ expect(WebMock).to have_requested(:put, "#{url}/_bulk").with(headers: { "X-Header" => "Value" })
75
72
  end
76
73
 
77
74
  it "transmits up to bulk_max_mb only" do
@@ -199,7 +199,7 @@ RSpec.describe SearchFlip::Connection do
199
199
  it "freezes the specified index" do
200
200
  connection = SearchFlip::Connection.new
201
201
 
202
- if connection.version.to_f >= 6.6
202
+ if connection.version.to_f >= 6.6 && connection.version.to_i < 8
203
203
  begin
204
204
  connection.create_index("index_name")
205
205
  connection.freeze_index("index_name")
@@ -216,7 +216,7 @@ RSpec.describe SearchFlip::Connection do
216
216
  it "unfreezes the specified index" do
217
217
  connection = SearchFlip::Connection.new
218
218
 
219
- if connection.version.to_f >= 6.6
219
+ if connection.version.to_f >= 6.6 && connection.version.to_i < 8
220
220
  begin
221
221
  connection.create_index("index_name")
222
222
  connection.freeze_index("index_name")
@@ -291,12 +291,19 @@ RSpec.describe SearchFlip::Connection do
291
291
  begin
292
292
  connection = SearchFlip::Connection.new
293
293
 
294
- mapping = { "type_name" => { "properties" => { "id" => { "type" => "long" } } } }
294
+ mapping = { "properties" => { "id" => { "type" => "long" } } }
295
295
 
296
296
  connection.create_index("index_name")
297
- connection.update_mapping("index_name", mapping, type_name: "type_name")
298
297
 
299
- expect(connection.get_mapping("index_name", type_name: "type_name")).to eq("index_name" => { "mappings" => mapping })
298
+ if connection.version.to_i < 8
299
+ connection.update_mapping("index_name", { "type_name" => mapping }, type_name: "type_name")
300
+
301
+ expect(connection.get_mapping("index_name", type_name: "type_name")).to eq("index_name" => { "mappings" => { "type_name" => mapping } })
302
+ else
303
+ connection.update_mapping("index_name", mapping)
304
+
305
+ expect(connection.get_mapping("index_name")).to eq("index_name" => { "mappings" => mapping })
306
+ end
300
307
  ensure
301
308
  connection.delete_index("index_name") if connection.index_exists?("index_name")
302
309
  end
@@ -337,6 +344,96 @@ RSpec.describe SearchFlip::Connection do
337
344
  end
338
345
  end
339
346
 
347
+ describe "#bulk" do
348
+ it "imports objects to the specified indices" do
349
+ connection = SearchFlip::Connection.new
350
+
351
+ bulk = proc do
352
+ connection.bulk do |indexer|
353
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
354
+ indexer.index 2, { id: 2 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
355
+ indexer.index 1, { id: 1 }, _index: CommentIndex.index_name, ** connection.version.to_i < 8 ? { _type: CommentIndex.type_name } : {}
356
+ end
357
+ end
358
+
359
+ expect(&bulk).to(change { CommentIndex.total_count }.by(1).and(change { CommentIndex.total_count }.by(1)))
360
+ end
361
+
362
+ it "raises when no index is given" do
363
+ connection = SearchFlip::Connection.new
364
+
365
+ bulk = proc do
366
+ connection.bulk do |indexer|
367
+ indexer.index 1, id: 1
368
+ end
369
+ end
370
+
371
+ expect(&bulk).to raise_error(SearchFlip::ResponseError)
372
+ end
373
+
374
+ it "respects options" do
375
+ connection = SearchFlip::Connection.new
376
+
377
+ connection.bulk do |indexer|
378
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
379
+ indexer.index 2, { id: 2 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
380
+ end
381
+
382
+ bulk = proc do
383
+ connection.bulk do |indexer|
384
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, version: 1, version_type: "external", ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
385
+ indexer.index 2, { id: 2 }, _index: ProductIndex.index_name, version: 1, version_type: "external", ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
386
+ end
387
+ end
388
+
389
+ expect(&bulk).to raise_error(SearchFlip::Bulk::Error)
390
+
391
+ bulk = proc do
392
+ connection.bulk ignore_errors: [409] do |indexer|
393
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, version: 1, version_type: "external", ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
394
+ indexer.index 2, { id: 2 }, _index: ProductIndex.index_name, version: 1, version_type: "external", ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
395
+ end
396
+ end
397
+
398
+ expect(&bulk).not_to(change { ProductIndex.total_count })
399
+ end
400
+
401
+ it "passes default options" do
402
+ allow(SearchFlip::Bulk).to receive(:new)
403
+
404
+ connection = SearchFlip::Connection.new
405
+
406
+ connection.bulk do |indexer|
407
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
408
+ end
409
+
410
+ expect(SearchFlip::Bulk).to have_received(:new).with(
411
+ anything,
412
+ http_client: connection.http_client,
413
+ bulk_limit: connection.bulk_limit,
414
+ bulk_max_mb: connection.bulk_max_mb
415
+ )
416
+ end
417
+
418
+ it "passes custom options" do
419
+ allow(SearchFlip::Bulk).to receive(:new)
420
+
421
+ connection = SearchFlip::Connection.new
422
+
423
+ options = {
424
+ bulk_limit: "bulk limit",
425
+ bulk_max_mb: "bulk max mb",
426
+ http_client: "http client"
427
+ }
428
+
429
+ connection.bulk(options) do |indexer|
430
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
431
+ end
432
+
433
+ expect(SearchFlip::Bulk).to have_received(:new).with(anything, options)
434
+ end
435
+ end
436
+
340
437
  describe "#index_url" do
341
438
  it "returns the index url for the specified index" do
342
439
  connection = SearchFlip::Connection.new(base_url: "base_url")
@@ -97,7 +97,8 @@ RSpec.describe SearchFlip::Criteria do
97
97
  methods = [
98
98
  :profile_value, :failsafe_value, :terminate_after_value, :timeout_value,
99
99
  :offset_value, :limit_value, :scroll_args, :source_value, :preference_value,
100
- :search_type_value, :routing_value, :track_total_hits_value, :explain_value
100
+ :search_type_value, :routing_value, :track_total_hits_value, :explain_value,
101
+ :http_timeout_value
101
102
  ]
102
103
 
103
104
  methods.each do |method|
@@ -191,6 +192,22 @@ RSpec.describe SearchFlip::Criteria do
191
192
  end
192
193
  end
193
194
 
195
+ describe "#http_timeout" do
196
+ it "sets the query timeout" do
197
+ http_client = double("client").as_null_object
198
+ allow(http_client).to receive(:timeout).and_return(http_client)
199
+ allow(http_client).to receive(:post).and_raise(SearchFlip::TimeoutError)
200
+ allow(ProductIndex.connection).to receive(:http_client).and_return(http_client)
201
+
202
+ expect { ProductIndex.http_timeout(1).execute }.to raise_error(SearchFlip::TimeoutError)
203
+ expect(http_client).to have_received(:timeout).with(1)
204
+ end
205
+
206
+ it "executes without errors" do
207
+ expect { ProductIndex.http_timeout(1).execute }.not_to raise_error
208
+ end
209
+ end
210
+
194
211
  describe "#terminate_after" do
195
212
  it "sets the terminate after value" do
196
213
  query = ProductIndex.terminate_after(1)
@@ -1204,13 +1221,19 @@ RSpec.describe SearchFlip::Criteria do
1204
1221
  end
1205
1222
 
1206
1223
  describe "#failsafe" do
1207
- it "prevents query syntax exceptions" do
1208
- expect { ProductIndex.search("syntax/error").records }.to raise_error(SearchFlip::ResponseError)
1224
+ [SearchFlip::ConnectionError, SearchFlip::TimeoutError, SearchFlip::ResponseError.new(code: "code", body: "body")].each do |error|
1225
+ it "prevents #{error}" do
1226
+ http_client = double("client").as_null_object
1227
+ allow(http_client).to receive(:post).and_raise(error)
1228
+ allow(ProductIndex.connection).to receive(:http_client).and_return(http_client)
1229
+
1230
+ expect { ProductIndex.all.execute }.to raise_error(error)
1209
1231
 
1210
- query = ProductIndex.failsafe(true).search("syntax/error")
1232
+ query = ProductIndex.failsafe(true)
1211
1233
 
1212
- expect(query.records).to eq([])
1213
- expect(query.total_entries).to eq(0)
1234
+ expect(query.records).to eq([])
1235
+ expect(query.total_entries).to eq(0)
1236
+ end
1214
1237
  end
1215
1238
  end
1216
1239
 
@@ -1327,28 +1350,40 @@ RSpec.describe SearchFlip::Criteria do
1327
1350
 
1328
1351
  describe "#preference" do
1329
1352
  it "sets the preference" do
1330
- stub_request(:post, "http://127.0.0.1:9200/products/products/_search?preference=value")
1353
+ url = ProductIndex.connection.version.to_i < 8 ? "products/products" : "products"
1354
+
1355
+ stub_request(:post, "http://127.0.0.1:9200/#{url}/_search?preference=value")
1331
1356
  .to_return(status: 200, headers: { content_type: "application/json" }, body: "{}")
1332
1357
 
1333
1358
  ProductIndex.preference("value").execute
1359
+
1360
+ expect(WebMock).to have_requested(:post, "http://127.0.0.1:9200/#{url}/_search?preference=value")
1334
1361
  end
1335
1362
  end
1336
1363
 
1337
1364
  describe "#search_type" do
1338
1365
  it "sets the search_type" do
1339
- stub_request(:post, "http://127.0.0.1:9200/products/products/_search?search_type=value")
1366
+ url = ProductIndex.connection.version.to_i < 8 ? "products/products" : "products"
1367
+
1368
+ stub_request(:post, "http://127.0.0.1:9200/#{url}/_search?search_type=value")
1340
1369
  .to_return(status: 200, headers: { content_type: "application/json" }, body: "{}")
1341
1370
 
1342
1371
  ProductIndex.search_type("value").execute
1372
+
1373
+ expect(WebMock).to have_requested(:post, "http://127.0.0.1:9200/#{url}/_search?search_type=value")
1343
1374
  end
1344
1375
  end
1345
1376
 
1346
1377
  describe "#routing" do
1347
1378
  it "sets the search_type" do
1348
- stub_request(:post, "http://127.0.0.1:9200/products/products/_search?routing=value")
1379
+ url = ProductIndex.connection.version.to_i < 8 ? "products/products" : "products"
1380
+
1381
+ stub_request(:post, "http://127.0.0.1:9200/#{url}/_search?routing=value")
1349
1382
  .to_return(status: 200, headers: { content_type: "application/json" }, body: "{}")
1350
1383
 
1351
1384
  ProductIndex.routing("value").execute
1385
+
1386
+ expect(WebMock).to have_requested(:post, "http://127.0.0.1:9200/#{url}/_search?routing=value")
1352
1387
  end
1353
1388
  end
1354
1389
  end
@@ -14,7 +14,7 @@ RSpec.describe SearchFlip::Index do
14
14
  :total_entries, :total_count, :terminate_after, :timeout, :records, :results,
15
15
  :must, :must_not, :should, :find_each_result,
16
16
  :find_results_in_batches, :preference, :search_type, :routing,
17
- :track_total_hits, :explain
17
+ :track_total_hits, :explain, :http_timeout
18
18
  ]
19
19
 
20
20
  methods.each do |method|
@@ -215,7 +215,11 @@ RSpec.describe SearchFlip::Index do
215
215
 
216
216
  TestIndex.update_mapping
217
217
 
218
- expect(TestIndex.connection).to have_received(:update_mapping).with("test", { "test" => mapping }, type_name: "test")
218
+ if TestIndex.connection.version.to_i < 8
219
+ expect(TestIndex.connection).to have_received(:update_mapping).with("test", { "test" => mapping }, type_name: "test")
220
+ else
221
+ expect(TestIndex.connection).to have_received(:update_mapping).with("test", mapping)
222
+ end
219
223
  end
220
224
 
221
225
  it "updates the mapping" do
@@ -268,7 +272,11 @@ RSpec.describe SearchFlip::Index do
268
272
 
269
273
  TestIndex.get_mapping
270
274
 
271
- expect(TestIndex.connection).to have_received(:get_mapping).with("test", type_name: "test")
275
+ if TestIndex.connection.version.to_i < 8
276
+ expect(TestIndex.connection).to have_received(:get_mapping).with("test", type_name: "test")
277
+ else
278
+ expect(TestIndex.connection).to have_received(:get_mapping).with("test")
279
+ end
272
280
  end
273
281
 
274
282
  it "returns the mapping" do
@@ -332,7 +340,7 @@ RSpec.describe SearchFlip::Index do
332
340
 
333
341
  TestIndex.type_url
334
342
 
335
- expect(TestIndex.connection).to have_received(:type_url).with("test", "test")
343
+ expect(TestIndex.connection).to have_received(:type_url).with("test", TestIndex.connection.version.to_i < 8 ? "test" : "_doc")
336
344
  end
337
345
  end
338
346
 
data/spec/spec_helper.rb CHANGED
@@ -16,6 +16,8 @@ RSpec.configure do |config|
16
16
  TestIndex.delete_index if TestIndex.index_exists?
17
17
  ProductIndex.match_all.delete
18
18
  Product.delete_all
19
+ CommentIndex.match_all.delete
20
+ Comment.delete_all
19
21
  end
20
22
  end
21
23
 
@@ -84,7 +86,7 @@ class CommentIndex
84
86
  include SearchFlip::Index
85
87
 
86
88
  def self.type_name
87
- "comments"
89
+ connection.version.to_i < 8 ? "comments" : "_doc"
88
90
  end
89
91
 
90
92
  def self.index_name
@@ -134,7 +136,7 @@ class ProductIndex
134
136
  end
135
137
 
136
138
  def self.type_name
137
- "products"
139
+ connection.version.to_i < 8 ? "products" : "_doc"
138
140
  end
139
141
 
140
142
  def self.index_name
@@ -175,7 +177,7 @@ class TestIndex
175
177
  end
176
178
 
177
179
  def self.type_name
178
- "test"
180
+ connection.version.to_i < 8 ? "test" : "_doc"
179
181
  end
180
182
 
181
183
  def self.index_name
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: search_flip
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0.beta8
4
+ version: 4.0.0.beta11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Vetter
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-15 00:00:00.000000000 Z
11
+ date: 2022-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -288,21 +288,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
288
288
  version: 1.3.1
289
289
  requirements: []
290
290
  rubygems_version: 3.2.3
291
- signing_key:
291
+ signing_key:
292
292
  specification_version: 4
293
293
  summary: Full-Featured Elasticsearch Ruby Client with a Chainable DSL
294
- test_files:
295
- - spec/delegate_matcher.rb
296
- - spec/search_flip/aggregation_spec.rb
297
- - spec/search_flip/aws_sigv4_plugin_spec.rb
298
- - spec/search_flip/bulk_spec.rb
299
- - spec/search_flip/connection_spec.rb
300
- - spec/search_flip/criteria_spec.rb
301
- - spec/search_flip/http_client_spec.rb
302
- - spec/search_flip/index_spec.rb
303
- - spec/search_flip/json_spec.rb
304
- - spec/search_flip/model_spec.rb
305
- - spec/search_flip/null_instrumenter_spec.rb
306
- - spec/search_flip/response_spec.rb
307
- - spec/search_flip/result_spec.rb
308
- - spec/spec_helper.rb
294
+ test_files: []