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 +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: []
|