search_flip 3.4.0 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +2 -1
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +14 -0
- data/README.md +9 -1
- data/docker-compose.yml +1 -0
- data/lib/search_flip/connection.rb +50 -5
- data/lib/search_flip/criteria.rb +36 -9
- data/lib/search_flip/http_client.rb +4 -0
- data/lib/search_flip/index.rb +9 -5
- data/lib/search_flip/version.rb +1 -1
- data/lib/search_flip.rb +8 -3
- data/search_flip.gemspec +0 -1
- data/spec/search_flip/aws_sigv4_plugin_spec.rb +6 -4
- data/spec/search_flip/bulk_spec.rb +5 -8
- data/spec/search_flip/connection_spec.rb +102 -5
- data/spec/search_flip/criteria_spec.rb +44 -9
- data/spec/search_flip/index_spec.rb +12 -4
- data/spec/spec_helper.rb +5 -3
- metadata +5 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06f3e293add0a92612ea73a6b8d448cc3866f30c908d96af5700466254b779d1
|
4
|
+
data.tar.gz: 158f3cf97a4ac2443a38d71f61764e7b10e9ba9df21883722e1fa5e300f5d9ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83e349b1b4f9a67335b71e2d818649e4550fe42ae39d354aae9ca078f7c484e37169e33eea5bd7802002e09e7efbc41f09e67fbb7024d7541037af7dd7081d8f
|
7
|
+
data.tar.gz: 559742084c2374b6c66f1ec3c9edd3c4193d897b3b2df2c3935e5a2587bf558aaf062fcb9428b2601b33abe0ab79c6b46e6c100ef3b1a25e845461a053da293f
|
data/.github/workflows/test.yml
CHANGED
@@ -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
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
@@ -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
|
#
|
data/lib/search_flip/criteria.rb
CHANGED
@@ -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.
|
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
|
-
|
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
|
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
|
-
"#{
|
628
|
+
"#{url}/_search",
|
604
629
|
params: request_params.merge(scroll: scroll_args[:timeout]),
|
605
630
|
json: request
|
606
631
|
)
|
607
632
|
else
|
608
|
-
|
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
|
data/lib/search_flip/index.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
|
data/lib/search_flip/version.rb
CHANGED
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
|
37
|
-
class ConnectionError < StandardError; end
|
36
|
+
class Error < StandardError; end
|
38
37
|
|
39
|
-
class
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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, "#{
|
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
|
-
|
69
|
-
|
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(
|
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 = { "
|
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
|
-
|
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
|
-
|
1208
|
-
|
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
|
-
|
1232
|
+
query = ProductIndex.failsafe(true)
|
1211
1233
|
|
1212
|
-
|
1213
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
+
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:
|
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: []
|