search_flip 3.4.0 → 3.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6722b210d8b3d0baa98e3370f26e2f293a21ea7db15dd08ba47deb2a1dba4e1c
4
- data.tar.gz: a66d9f5482e06ae11cffeece148a8eb290523ee6ec4c91f510c1258b1c780225
3
+ metadata.gz: 06f3e293add0a92612ea73a6b8d448cc3866f30c908d96af5700466254b779d1
4
+ data.tar.gz: 158f3cf97a4ac2443a38d71f61764e7b10e9ba9df21883722e1fa5e300f5d9ca
5
5
  SHA512:
6
- metadata.gz: 4e60dbae8d1bf0c4be3964bd0daf0740c4dd89827d61481d21960af17f34b237b512dde09f08111949d9b728bc3290c59a535930e168aaa562c30c9f74faa841
7
- data.tar.gz: 3b7d941dee59924dc47b486e5cd3d88af8a3a9a42e8f6952e14934087e2a0acbe0014ee09caabec373640c35c5ae1d72a433e2100fcd2257e8d920c09f39e3b6
6
+ metadata.gz: 83e349b1b4f9a67335b71e2d818649e4550fe42ae39d354aae9ca078f7c484e37169e33eea5bd7802002e09e7efbc41f09e67fbb7024d7541037af7dd7081d8f
7
+ data.tar.gz: 559742084c2374b6c66f1ec3c9edd3c4193d897b3b2df2c3935e5a2587bf558aaf062fcb9428b2601b33abe0ab79c6b46e6c100ef3b1a25e845461a053da293f
@@ -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
  Layout/EmptyLineBetweenDefs:
7
10
  EmptyLineBetweenClassDefs: false
8
11
 
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
 
2
2
  # CHANGELOG
3
3
 
4
+ ## v3.7.0
5
+
6
+ * Add `SearchFlip::Connection#bulk` to allow more clean bulk indexing to
7
+ multiple indices at once
8
+
9
+ ## v3.6.0
10
+
11
+ * Support Elasticsearch v8
12
+
13
+ ## v3.5.0
14
+
15
+ * Add `SearchFlip::Criteria#http_timeout` to allow specifying timeouts on
16
+ a query level
17
+
4
18
  ## v3.4.0
5
19
 
6
20
  * 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
@@ -64,7 +64,7 @@ module SearchFlip
64
64
  def msearch(criterias)
65
65
  payload = criterias.flat_map do |criteria|
66
66
  [
67
- SearchFlip::JSON.generate(index: criteria.target.index_name_with_prefix, type: criteria.target.type_name),
67
+ SearchFlip::JSON.generate(index: criteria.target.index_name_with_prefix, **(version.to_i < 8 ? { type: criteria.target.type_name } : {})),
68
68
  SearchFlip::JSON.generate(criteria.request)
69
69
  ]
70
70
  end
@@ -300,8 +300,8 @@ module SearchFlip
300
300
  # @return [Boolean] Returns true or raises SearchFlip::ResponseError
301
301
 
302
302
  def update_mapping(index_name, mapping, type_name: nil)
303
- url = type_name ? type_url(index_name, type_name) : index_url(index_name)
304
- params = type_name && version.to_f >= 6.7 ? { include_type_name: true } : {}
303
+ url = type_name && version.to_i < 8 ? type_url(index_name, type_name) : index_url(index_name)
304
+ params = type_name && version.to_f >= 6.7 && version.to_i < 8 ? { include_type_name: true } : {}
305
305
 
306
306
  http_client.put("#{url}/_mapping", params: params, json: mapping)
307
307
 
@@ -318,8 +318,8 @@ module SearchFlip
318
318
  # @return [Hash] The current type mapping
319
319
 
320
320
  def get_mapping(index_name, type_name: nil)
321
- url = type_name ? type_url(index_name, type_name) : index_url(index_name)
322
- params = type_name && version.to_f >= 6.7 ? { include_type_name: true } : {}
321
+ url = type_name && version.to_i < 8 ? type_url(index_name, type_name) : index_url(index_name)
322
+ params = type_name && version.to_f >= 6.7 && version.to_i < 8 ? { include_type_name: true } : {}
323
323
 
324
324
  response = http_client.headers(accept: "application/json").get("#{url}/_mapping", params: params)
325
325
 
@@ -355,6 +355,51 @@ module SearchFlip
355
355
  raise e
356
356
  end
357
357
 
358
+ # Initiates and yields a bulk object, such that index, import, create,
359
+ # update and delete requests can be appended to the bulk request. Please
360
+ # note that you need to manually pass the desired index name as well as
361
+ # type name (depending on the Elasticsearch version) when using #bulk on a
362
+ # connection object or Elasticsearch will return an error. After the bulk
363
+ # requests are successfully processed all existing indices will
364
+ # subsequently be refreshed when auto_refresh is enabled.
365
+ #
366
+ # @see SearchFlip::Config See SearchFlip::Config for auto_refresh
367
+ #
368
+ # @example
369
+ # connection = SearchFlip::Connection.new
370
+ #
371
+ # connection.bulk ignore_errors: [409] do |bulk|
372
+ # bulk.create comment.id, CommentIndex.serialize(comment),
373
+ # _index: CommentIndex.index_name, version: comment.version, version_type: "external_gte"
374
+ #
375
+ # bulk.delete product.id, _index: ProductIndex.index_name, routing: product.user_id
376
+ #
377
+ # # ...
378
+ # end
379
+ #
380
+ # @param options [Hash] Specifies options regarding the bulk indexing
381
+ # @option options ignore_errors [Array] Specifies an array of http status
382
+ # codes that shouldn't raise any exceptions, like eg 409 for conflicts,
383
+ # ie when optimistic concurrency control is used.
384
+ # @option options raise [Boolean] Prevents any exceptions from being
385
+ # raised. Please note that this only applies to the bulk response, not to
386
+ # the request in general, such that connection errors, etc will still
387
+ # raise.
388
+
389
+ def bulk(options = {})
390
+ default_options = {
391
+ http_client: http_client,
392
+ bulk_limit: bulk_limit,
393
+ bulk_max_mb: bulk_max_mb
394
+ }
395
+
396
+ SearchFlip::Bulk.new("#{base_url}/_bulk", default_options.merge(options)) do |indexer|
397
+ yield indexer
398
+ end
399
+
400
+ refresh if SearchFlip::Config[:auto_refresh]
401
+ end
402
+
358
403
  # Returns the full Elasticsearch type URL, ie base URL, index name with
359
404
  # prefix and type name.
360
405
  #
@@ -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 = "3.4.0"
2
+ VERSION = "3.7.0"
3
3
  end
data/lib/search_flip.rb CHANGED
@@ -33,10 +33,15 @@ require "search_flip/index"
33
33
  require "search_flip/model"
34
34
 
35
35
  module SearchFlip
36
- class NotSupportedError < StandardError; end
37
- class ConnectionError < StandardError; end
36
+ class Error < StandardError; end
38
37
 
39
- class ResponseError < StandardError
38
+ class NotSupportedError < Error; end
39
+
40
+ class HttpError < Error; end
41
+ class ConnectionError < HttpError; end
42
+ class TimeoutError < HttpError; end
43
+
44
+ class ResponseError < Error
40
45
  attr_reader :code, :body
41
46
 
42
47
  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
@@ -170,7 +170,7 @@ RSpec.describe SearchFlip::Connection do
170
170
  it "freezes the specified index" do
171
171
  connection = SearchFlip::Connection.new
172
172
 
173
- if connection.version.to_f >= 6.6
173
+ if connection.version.to_f >= 6.6 && connection.version.to_i < 8
174
174
  begin
175
175
  connection.create_index("index_name")
176
176
  connection.freeze_index("index_name")
@@ -187,7 +187,7 @@ RSpec.describe SearchFlip::Connection do
187
187
  it "unfreezes the specified index" do
188
188
  connection = SearchFlip::Connection.new
189
189
 
190
- if connection.version.to_f >= 6.6
190
+ if connection.version.to_f >= 6.6 && connection.version.to_i < 8
191
191
  begin
192
192
  connection.create_index("index_name")
193
193
  connection.freeze_index("index_name")
@@ -262,12 +262,19 @@ RSpec.describe SearchFlip::Connection do
262
262
  begin
263
263
  connection = SearchFlip::Connection.new
264
264
 
265
- mapping = { "type_name" => { "properties" => { "id" => { "type" => "long" } } } }
265
+ mapping = { "properties" => { "id" => { "type" => "long" } } }
266
266
 
267
267
  connection.create_index("index_name")
268
- connection.update_mapping("index_name", mapping, type_name: "type_name")
269
268
 
270
- expect(connection.get_mapping("index_name", type_name: "type_name")).to eq("index_name" => { "mappings" => mapping })
269
+ if connection.version.to_i < 8
270
+ connection.update_mapping("index_name", { "type_name" => mapping }, type_name: "type_name")
271
+
272
+ expect(connection.get_mapping("index_name", type_name: "type_name")).to eq("index_name" => { "mappings" => { "type_name" => mapping } })
273
+ else
274
+ connection.update_mapping("index_name", mapping)
275
+
276
+ expect(connection.get_mapping("index_name")).to eq("index_name" => { "mappings" => mapping })
277
+ end
271
278
  ensure
272
279
  connection.delete_index("index_name") if connection.index_exists?("index_name")
273
280
  end
@@ -308,6 +315,96 @@ RSpec.describe SearchFlip::Connection do
308
315
  end
309
316
  end
310
317
 
318
+ describe "#bulk" do
319
+ it "imports objects to the specified indices" do
320
+ connection = SearchFlip::Connection.new
321
+
322
+ bulk = proc do
323
+ connection.bulk do |indexer|
324
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
325
+ indexer.index 2, { id: 2 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
326
+ indexer.index 1, { id: 1 }, _index: CommentIndex.index_name, ** connection.version.to_i < 8 ? { _type: CommentIndex.type_name } : {}
327
+ end
328
+ end
329
+
330
+ expect(&bulk).to(change { CommentIndex.total_count }.by(1).and(change { CommentIndex.total_count }.by(1)))
331
+ end
332
+
333
+ it "raises when no index is given" do
334
+ connection = SearchFlip::Connection.new
335
+
336
+ bulk = proc do
337
+ connection.bulk do |indexer|
338
+ indexer.index 1, id: 1
339
+ end
340
+ end
341
+
342
+ expect(&bulk).to raise_error(SearchFlip::ResponseError)
343
+ end
344
+
345
+ it "respects options" do
346
+ connection = SearchFlip::Connection.new
347
+
348
+ connection.bulk do |indexer|
349
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
350
+ indexer.index 2, { id: 2 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
351
+ end
352
+
353
+ bulk = proc do
354
+ connection.bulk do |indexer|
355
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, version: 1, version_type: "external", ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
356
+ indexer.index 2, { id: 2 }, _index: ProductIndex.index_name, version: 1, version_type: "external", ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
357
+ end
358
+ end
359
+
360
+ expect(&bulk).to raise_error(SearchFlip::Bulk::Error)
361
+
362
+ bulk = proc do
363
+ connection.bulk ignore_errors: [409] do |indexer|
364
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, version: 1, version_type: "external", ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
365
+ indexer.index 2, { id: 2 }, _index: ProductIndex.index_name, version: 1, version_type: "external", ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
366
+ end
367
+ end
368
+
369
+ expect(&bulk).not_to(change { ProductIndex.total_count })
370
+ end
371
+
372
+ it "passes default options" do
373
+ allow(SearchFlip::Bulk).to receive(:new)
374
+
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
+ end
380
+
381
+ expect(SearchFlip::Bulk).to have_received(:new).with(
382
+ anything,
383
+ http_client: connection.http_client,
384
+ bulk_limit: connection.bulk_limit,
385
+ bulk_max_mb: connection.bulk_max_mb
386
+ )
387
+ end
388
+
389
+ it "passes custom options" do
390
+ allow(SearchFlip::Bulk).to receive(:new)
391
+
392
+ connection = SearchFlip::Connection.new
393
+
394
+ options = {
395
+ bulk_limit: "bulk limit",
396
+ bulk_max_mb: "bulk max mb",
397
+ http_client: "http client"
398
+ }
399
+
400
+ connection.bulk(options) do |indexer|
401
+ indexer.index 1, { id: 1 }, _index: ProductIndex.index_name, ** connection.version.to_i < 8 ? { _type: ProductIndex.type_name } : {}
402
+ end
403
+
404
+ expect(SearchFlip::Bulk).to have_received(:new).with(anything, options)
405
+ end
406
+ end
407
+
311
408
  describe "#index_url" do
312
409
  it "returns the index url for the specified index" do
313
410
  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: 3.4.0
4
+ version: 3.7.0
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
@@ -302,21 +302,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
302
302
  version: '0'
303
303
  requirements: []
304
304
  rubygems_version: 3.2.3
305
- signing_key:
305
+ signing_key:
306
306
  specification_version: 4
307
307
  summary: Full-Featured Elasticsearch Ruby Client with a Chainable DSL
308
- test_files:
309
- - spec/delegate_matcher.rb
310
- - spec/search_flip/aggregation_spec.rb
311
- - spec/search_flip/aws_sigv4_plugin_spec.rb
312
- - spec/search_flip/bulk_spec.rb
313
- - spec/search_flip/connection_spec.rb
314
- - spec/search_flip/criteria_spec.rb
315
- - spec/search_flip/http_client_spec.rb
316
- - spec/search_flip/index_spec.rb
317
- - spec/search_flip/json_spec.rb
318
- - spec/search_flip/model_spec.rb
319
- - spec/search_flip/null_instrumenter_spec.rb
320
- - spec/search_flip/response_spec.rb
321
- - spec/search_flip/result_spec.rb
322
- - spec/spec_helper.rb
308
+ test_files: []