noiseless 0.7.2 → 0.8.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/README.md +2 -2
- data/lib/noiseless/adapter.rb +28 -35
- data/lib/noiseless/adapters/elasticsearch.rb +0 -1
- data/lib/noiseless/adapters/execution_modules/elasticsearch_execution.rb +8 -6
- data/lib/noiseless/adapters/execution_modules/es_compatible_execution.rb +65 -17
- data/lib/noiseless/adapters/execution_modules/http_transport.rb +29 -18
- data/lib/noiseless/adapters/execution_modules/opensearch_execution.rb +102 -114
- data/lib/noiseless/adapters/execution_modules/pgvector_support.rb +42 -47
- data/lib/noiseless/adapters/execution_modules/postgresql_execution.rb +63 -324
- data/lib/noiseless/adapters/execution_modules/postgresql_query.rb +269 -0
- data/lib/noiseless/adapters/execution_modules/typesense_execution.rb +172 -296
- data/lib/noiseless/adapters/execution_modules/typesense_query.rb +143 -0
- data/lib/noiseless/adapters/indices_api.rb +3 -1
- data/lib/noiseless/adapters/open_search.rb +30 -88
- data/lib/noiseless/adapters/postgresql.rb +5 -5
- data/lib/noiseless/adapters/typesense.rb +1 -2
- data/lib/noiseless/ast/aggregation.rb +0 -23
- data/lib/noiseless/ast/aggregation_builder.rb +28 -0
- data/lib/noiseless/ast/hybrid.rb +4 -2
- data/lib/noiseless/ast/join.rb +4 -5
- data/lib/noiseless/ast/vector.rb +25 -4
- data/lib/noiseless/bulk_importer.rb +16 -27
- data/lib/noiseless/callbacks.rb +4 -30
- data/lib/noiseless/connection_manager.rb +9 -3
- data/lib/noiseless/controller_runtime.rb +37 -0
- data/lib/noiseless/document_manager.rb +21 -36
- data/lib/noiseless/embedding.rb +33 -0
- data/lib/noiseless/instrumentation.rb +2 -136
- data/lib/noiseless/introspection/mermaid_diagrams.rb +143 -0
- data/lib/noiseless/introspection/query_visualizer.rb +3 -228
- data/lib/noiseless/introspection.rb +1 -1
- data/lib/noiseless/log_subscriber.rb +103 -0
- data/lib/noiseless/model.rb +2 -2
- data/lib/noiseless/multi_search.rb +2 -4
- data/lib/noiseless/pagination.rb +3 -3
- data/lib/noiseless/query_builder.rb +5 -4
- data/lib/noiseless/railtie.rb +0 -2
- data/lib/noiseless/response/records.rb +2 -1
- data/lib/noiseless/runtime_reset_middleware.rb +1 -1
- data/lib/noiseless/search_index_update_job.rb +13 -14
- data/lib/noiseless/test_case.rb +0 -1
- data/lib/noiseless/version.rb +1 -1
- data/lib/noiseless.rb +1 -10
- metadata +31 -13
- data/lib/tasks/benchmark.rake +0 -35
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c023b22c96a7186d99210b2a4420fa6661e3be195522cf86dbe9191d5301acfa
|
|
4
|
+
data.tar.gz: ef2f26a7f11bad65b6a604f08641d398e9da90e637af8bbd3f9a1750e8119589
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 272b9ffa8aa20f2719018ca2abbac572640f997742fdbc79c413432b1d35ee97501dd6ea1475ffa02e67d04987cd31595f27c12df824c1c21832acc4f1de875a
|
|
7
|
+
data.tar.gz: 00be400acc534f50c40138e57e4c93118ba6829e14e72ebb7a18df546310168b4aaab9e2e421cfb950c6ad6d9b8dc5bcc68b70d38b071fedb9d35fa04c17854c
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Async-first search abstraction for Rails with multi-backend support (OpenSearch,
|
|
|
6
6
|
|
|
7
7
|
- **Chainable DSL** — fluent query builder with runtime validation
|
|
8
8
|
- **Multi-backend** — OpenSearch, Elasticsearch, Typesense, PostgreSQL adapters
|
|
9
|
-
- **Async-first** — built on Ruby
|
|
9
|
+
- **Async-first** — built on Ruby 4.0+ fiber scheduler with non-blocking I/O
|
|
10
10
|
- **HTTP/2 connection pooling** — persistent connections via `Async::Pool`
|
|
11
11
|
- **Rails integration** — Railtie with log subscriber and controller runtime tracking
|
|
12
12
|
- **Lazy loading** — adapters loaded on-demand, test files excluded from production
|
|
@@ -17,7 +17,7 @@ Async-first search abstraction for Rails with multi-backend support (OpenSearch,
|
|
|
17
17
|
gem "noiseless"
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
`noiseless` is a Rails gem. It requires Ruby >=
|
|
20
|
+
`noiseless` is a Rails gem. It requires Ruby >= 4.0 and Rails >= 8.1.
|
|
21
21
|
|
|
22
22
|
## Configuration
|
|
23
23
|
|
data/lib/noiseless/adapter.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "async"
|
|
4
4
|
require "json"
|
|
5
|
-
require_relative "introspection"
|
|
6
5
|
|
|
7
6
|
module Noiseless
|
|
8
7
|
class Adapter
|
|
@@ -127,6 +126,8 @@ module Noiseless
|
|
|
127
126
|
|
|
128
127
|
private
|
|
129
128
|
|
|
129
|
+
def instance_variables_to_inspect = instance_variables - %i[@connection_params]
|
|
130
|
+
|
|
130
131
|
# Convert AST to Hash - override in subclasses for adapter-specific format
|
|
131
132
|
def ast_to_hash(ast_node)
|
|
132
133
|
result = {}
|
|
@@ -153,14 +154,8 @@ module Noiseless
|
|
|
153
154
|
# Aggregations
|
|
154
155
|
result[:aggs] = build_aggregations_hash(ast_node.aggregations) if ast_node.aggregations.any?
|
|
155
156
|
|
|
156
|
-
|
|
157
|
-
result[:
|
|
158
|
-
|
|
159
|
-
# Hybrid search (combines text + vector with RRF or weighted scoring)
|
|
160
|
-
if ast_node.hybrid_search?
|
|
161
|
-
hybrid_config = build_hybrid_query(ast_node.hybrid)
|
|
162
|
-
result.merge!(hybrid_config)
|
|
163
|
-
end
|
|
157
|
+
result[:knn] = build_knn_query(ast_node.vector, filters: result.dig(:query, :bool, :filter)) if ast_node.vector_search?
|
|
158
|
+
result[:query] = build_hybrid_query(ast_node.hybrid, result[:query]) if ast_node.hybrid_search?
|
|
164
159
|
|
|
165
160
|
# Search pipeline (OpenSearch only)
|
|
166
161
|
result[:search_pipeline] = ast_node.pipeline if ast_node.has_pipeline?
|
|
@@ -168,36 +163,27 @@ module Noiseless
|
|
|
168
163
|
result
|
|
169
164
|
end
|
|
170
165
|
|
|
171
|
-
def build_knn_query(vector_node)
|
|
166
|
+
def build_knn_query(vector_node, filters: nil)
|
|
172
167
|
{
|
|
173
168
|
field: vector_node.field.to_s,
|
|
174
|
-
query_vector: vector_node.embedding,
|
|
169
|
+
query_vector: encode_vector(vector_node.embedding),
|
|
175
170
|
k: vector_node.k,
|
|
176
|
-
|
|
177
|
-
}
|
|
171
|
+
filter: filters.presence
|
|
172
|
+
}.compact
|
|
178
173
|
end
|
|
179
174
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
},
|
|
194
|
-
knn: build_knn_query(hybrid_node.vector),
|
|
195
|
-
rank: {
|
|
196
|
-
rrf: {
|
|
197
|
-
window_size: hybrid_node.vector.k * 2
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
175
|
+
def build_hybrid_query(hybrid_node, query)
|
|
176
|
+
bool = query&.fetch(:bool, nil)&.dup || {}
|
|
177
|
+
text = { multi_match: { query: hybrid_node.text_query, fields: hybrid_node.fields.presence, boost: hybrid_node.text_weight }.compact }
|
|
178
|
+
vector = { knn: build_knn_query(hybrid_node.vector, filters: bool[:filter]).merge(boost: hybrid_node.vector_weight) }
|
|
179
|
+
|
|
180
|
+
{ bool: bool.merge(should: [text, vector], minimum_should_match: 1) }
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def encode_vector(values) = [values.pack("g*")].pack("m0")
|
|
184
|
+
|
|
185
|
+
def encode_document(document)
|
|
186
|
+
document.to_h.transform_values { it.is_a?(Noiseless::Embedding) ? encode_vector(it.values) : it }
|
|
201
187
|
end
|
|
202
188
|
|
|
203
189
|
def build_query_hash(bool_node)
|
|
@@ -262,7 +248,7 @@ module Noiseless
|
|
|
262
248
|
rescue JSON::ParserError, TypeError
|
|
263
249
|
nil
|
|
264
250
|
end
|
|
265
|
-
error = payload.is_a?(Hash) ? payload["error"] : nil
|
|
251
|
+
error = payload.is_a?(Hash) ? payload["error"] || payload["message"] : nil
|
|
266
252
|
reason = if error.is_a?(Hash)
|
|
267
253
|
[error["type"], error["reason"]].compact.join(": ")
|
|
268
254
|
elsif error
|
|
@@ -274,6 +260,13 @@ module Noiseless
|
|
|
274
260
|
raise error_class.new(message, status: response.status, error_type: error.is_a?(Hash) ? error["type"] : nil)
|
|
275
261
|
end
|
|
276
262
|
|
|
263
|
+
def exists_response?(response, context:)
|
|
264
|
+
return true if response.success?
|
|
265
|
+
return false if response.status == 404
|
|
266
|
+
|
|
267
|
+
raise Noiseless::RequestError.new("#{context}: HTTP #{response.status}", status: response.status)
|
|
268
|
+
end
|
|
269
|
+
|
|
277
270
|
# Override in subclasses
|
|
278
271
|
def execute_search(_query_hash, **_opts)
|
|
279
272
|
{
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
-
require_relative "es_compatible_execution"
|
|
5
4
|
|
|
6
5
|
module Noiseless
|
|
7
6
|
module Adapters
|
|
@@ -13,7 +12,7 @@ module Noiseless
|
|
|
13
12
|
|
|
14
13
|
def execute_search(query_hash, indexes: [], **_opts)
|
|
15
14
|
path = indexes.any? ? "/#{indexes.join(',')}/_search" : "/_search"
|
|
16
|
-
body =
|
|
15
|
+
body = dump_json(query_hash)
|
|
17
16
|
|
|
18
17
|
response = post_request(path, body)
|
|
19
18
|
parse_json_response!(response, error_class: Noiseless::SearchError, context: "search")
|
|
@@ -26,15 +25,15 @@ module Noiseless
|
|
|
26
25
|
body[:mappings] = mappings if mappings
|
|
27
26
|
body[:settings] = settings if settings
|
|
28
27
|
|
|
29
|
-
response = put_request("/#{index_name}", body.any? ?
|
|
28
|
+
response = put_request("/#{index_name}", body.any? ? dump_json(body) : nil)
|
|
30
29
|
parse_json_response!(response, context: "create index #{index_name}")
|
|
31
30
|
ensure
|
|
32
31
|
response&.close
|
|
33
32
|
end
|
|
34
33
|
|
|
35
|
-
def execute_index_document(index, id, document, **_opts)
|
|
36
|
-
path = id ? "/#{index}/_doc/#{id}" : "/#{index}/_doc"
|
|
37
|
-
body =
|
|
34
|
+
def execute_index_document(index, id, document, refresh: nil, **_opts)
|
|
35
|
+
path = (id ? "/#{index}/_doc/#{id}" : "/#{index}/_doc") + query_string(refresh:)
|
|
36
|
+
body = dump_json(encode_document(document))
|
|
38
37
|
|
|
39
38
|
response = id ? put_request(path, body) : post_request(path, body)
|
|
40
39
|
parse_json_response!(response, context: "index document #{index}/#{id}")
|
|
@@ -42,6 +41,9 @@ module Noiseless
|
|
|
42
41
|
response&.close
|
|
43
42
|
end
|
|
44
43
|
|
|
44
|
+
def pit_path = "_pit"
|
|
45
|
+
def pit_close_body(pit_id) = { id: pit_id }
|
|
46
|
+
|
|
45
47
|
def execute_cluster_health(**_opts)
|
|
46
48
|
response = get_request("/_cluster/health")
|
|
47
49
|
JSON.parse(response.read)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
-
require_relative "http_transport"
|
|
5
4
|
|
|
6
5
|
module Noiseless
|
|
7
6
|
module Adapters
|
|
@@ -11,20 +10,67 @@ module Noiseless
|
|
|
11
10
|
module EsCompatibleExecution
|
|
12
11
|
include HttpTransport
|
|
13
12
|
|
|
13
|
+
# Pages through every hit of a query with a point in time and
|
|
14
|
+
# search_after, which unlike from/size has no depth limit and sees a
|
|
15
|
+
# consistent snapshot. The point in time is always closed.
|
|
16
|
+
def each_page(ast_node, keep_alive: "1m", model_class: nil, response_type: nil)
|
|
17
|
+
return enum_for(__method__, ast_node, keep_alive:, model_class:, response_type:) unless block_given?
|
|
18
|
+
|
|
19
|
+
Sync do
|
|
20
|
+
query = ast_to_hash(ast_node).except(:from)
|
|
21
|
+
query[:sort] = [*query[:sort], { _shard_doc: "asc" }]
|
|
22
|
+
pit_id = execute_open_pit(ast_node.indexes, keep_alive:)
|
|
23
|
+
|
|
24
|
+
loop do
|
|
25
|
+
raw = execute_pit_search(query.merge(pit: { id: pit_id, keep_alive: }))
|
|
26
|
+
hits = raw.dig("hits", "hits")
|
|
27
|
+
break if hits.empty?
|
|
28
|
+
|
|
29
|
+
yield ResponseFactory.create(raw, model_class:, response_type:)
|
|
30
|
+
pit_id = raw["pit_id"] || pit_id
|
|
31
|
+
query[:search_after] = hits.last["sort"]
|
|
32
|
+
end
|
|
33
|
+
ensure
|
|
34
|
+
execute_close_pit(pit_id) if pit_id
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
14
38
|
private
|
|
15
39
|
|
|
16
|
-
def
|
|
40
|
+
def execute_open_pit(indexes, keep_alive:)
|
|
41
|
+
response = post_request("/#{indexes.join(',')}/#{pit_path}?keep_alive=#{keep_alive}", nil)
|
|
42
|
+
parse_json_response!(response, context: "open point in time").then { it["pit_id"] || it["id"] }
|
|
43
|
+
ensure
|
|
44
|
+
response&.close
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def execute_close_pit(pit_id)
|
|
48
|
+
response = delete_request("/#{pit_path}", dump_json(pit_close_body(pit_id)))
|
|
49
|
+
parse_json_response!(response, context: "close point in time")
|
|
50
|
+
ensure
|
|
51
|
+
response&.close
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def execute_pit_search(query_hash)
|
|
55
|
+
response = post_request("/_search", dump_json(query_hash))
|
|
56
|
+
parse_json_response!(response, error_class: Noiseless::SearchError, context: "point-in-time search")
|
|
57
|
+
ensure
|
|
58
|
+
response&.close
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def execute_bulk(actions, refresh: nil, **_opts)
|
|
17
62
|
body = actions.map do |action|
|
|
18
63
|
if action[:index]
|
|
19
64
|
action_line = { index: { _index: action[:index][:_index], _id: action[:index][:_id] } }
|
|
20
|
-
data_line = action[:index][:data]
|
|
21
|
-
"#{
|
|
65
|
+
data_line = encode_document(action[:index][:data])
|
|
66
|
+
"#{dump_json(action_line)}\n#{dump_json(data_line)}\n"
|
|
22
67
|
else
|
|
23
|
-
"#{
|
|
68
|
+
"#{dump_json(action)}\n"
|
|
24
69
|
end
|
|
25
70
|
end.join
|
|
26
71
|
|
|
27
|
-
|
|
72
|
+
path = "/_bulk#{query_string(refresh:, filter_path: 'errors,items.*.status,items.*.error')}"
|
|
73
|
+
response = post_request(path, body, content_type: "application/x-ndjson")
|
|
28
74
|
parse_json_response!(response, context: "bulk")
|
|
29
75
|
ensure
|
|
30
76
|
response&.close
|
|
@@ -41,6 +87,12 @@ module Noiseless
|
|
|
41
87
|
response&.close
|
|
42
88
|
end
|
|
43
89
|
|
|
90
|
+
def query_string(refresh: nil, **params)
|
|
91
|
+
params = params.compact
|
|
92
|
+
params[:refresh] = refresh if refresh
|
|
93
|
+
params.empty? ? "" : "?#{params.map { |key, value| "#{key}=#{value}" }.join('&')}"
|
|
94
|
+
end
|
|
95
|
+
|
|
44
96
|
def execute_refresh_index(index_name)
|
|
45
97
|
response = post_request("/#{index_name}/_refresh", nil)
|
|
46
98
|
parse_json_response!(response, context: "refresh index #{index_name}")
|
|
@@ -50,24 +102,22 @@ module Noiseless
|
|
|
50
102
|
|
|
51
103
|
def execute_index_exists?(index_name)
|
|
52
104
|
response = head_request("/#{index_name}")
|
|
53
|
-
response
|
|
54
|
-
rescue StandardError
|
|
55
|
-
false
|
|
105
|
+
exists_response?(response, context: "index exists #{index_name}")
|
|
56
106
|
ensure
|
|
57
107
|
response&.close
|
|
58
108
|
end
|
|
59
109
|
|
|
60
|
-
def execute_update_document(index, id, changes, **_opts)
|
|
61
|
-
body =
|
|
110
|
+
def execute_update_document(index, id, changes, refresh: nil, **_opts)
|
|
111
|
+
body = dump_json(doc: encode_document(changes))
|
|
62
112
|
|
|
63
|
-
response = post_request("/#{index}/_update/#{id}", body)
|
|
113
|
+
response = post_request("/#{index}/_update/#{id}#{query_string(refresh:)}", body)
|
|
64
114
|
parse_json_response!(response, context: "update document #{index}/#{id}")
|
|
65
115
|
ensure
|
|
66
116
|
response&.close
|
|
67
117
|
end
|
|
68
118
|
|
|
69
|
-
def execute_delete_document(index, id, **_opts)
|
|
70
|
-
response = delete_request("/#{index}/_doc/#{id}")
|
|
119
|
+
def execute_delete_document(index, id, refresh: nil, **_opts)
|
|
120
|
+
response = delete_request("/#{index}/_doc/#{id}#{query_string(refresh:)}")
|
|
71
121
|
# 404 covers both a missing document and a missing index; either way
|
|
72
122
|
# the delete is idempotent.
|
|
73
123
|
return { "_index" => index, "_id" => id, "result" => "not_found" } if response.status == 404
|
|
@@ -79,9 +129,7 @@ module Noiseless
|
|
|
79
129
|
|
|
80
130
|
def execute_document_exists?(index, id)
|
|
81
131
|
response = head_request("/#{index}/_doc/#{id}")
|
|
82
|
-
response
|
|
83
|
-
rescue StandardError
|
|
84
|
-
false
|
|
132
|
+
exists_response?(response, context: "document exists #{index}/#{id}")
|
|
85
133
|
ensure
|
|
86
134
|
response&.close
|
|
87
135
|
end
|
|
@@ -19,7 +19,8 @@ module Noiseless
|
|
|
19
19
|
SocketError,
|
|
20
20
|
IOError,
|
|
21
21
|
IO::TimeoutError,
|
|
22
|
-
Timeout::Error
|
|
22
|
+
Timeout::Error,
|
|
23
|
+
Protocol::HTTP::Error
|
|
23
24
|
].freeze
|
|
24
25
|
|
|
25
26
|
# Default per-operation IO timeout (seconds) for the search backend.
|
|
@@ -39,13 +40,29 @@ module Noiseless
|
|
|
39
40
|
# deadline (long bulk imports may need a higher value or nil).
|
|
40
41
|
DEFAULT_REQUEST_TIMEOUT = 30
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
# Default cap on concurrent connections per host. Without it the pool
|
|
44
|
+
# opens one socket per concurrent request. Waiting for a free
|
|
45
|
+
# connection counts against request_timeout. Override per-connection
|
|
46
|
+
# with +pool_limit:+.
|
|
47
|
+
DEFAULT_POOL_LIMIT = 16
|
|
48
|
+
|
|
49
|
+
# json 3 serializes objects it does not know with to_s, so a Time
|
|
50
|
+
# became "2025-12-11 14:18:40 UTC", which search engines reject.
|
|
51
|
+
# Falling back to as_json keeps ActiveSupport's ISO 8601 times.
|
|
52
|
+
JSON_CODER = JSON::Coder.new { |object| object.respond_to?(:as_json) ? object.as_json : object.to_s }
|
|
53
|
+
|
|
54
|
+
BufferedResponse = Data.define(:status, :body) do
|
|
43
55
|
def read = body
|
|
44
56
|
def success? = (200..299).cover?(status)
|
|
45
57
|
def close = nil
|
|
46
58
|
end
|
|
47
59
|
|
|
48
|
-
def initialize(hosts: [], timeout: DEFAULT_TIMEOUT, request_timeout: DEFAULT_REQUEST_TIMEOUT,
|
|
60
|
+
def initialize(hosts: [], timeout: DEFAULT_TIMEOUT, request_timeout: DEFAULT_REQUEST_TIMEOUT,
|
|
61
|
+
pool_limit: DEFAULT_POOL_LIMIT, **connection_params)
|
|
62
|
+
unless request_timeout.nil? || (request_timeout.is_a?(Numeric) && request_timeout.positive? && request_timeout.finite?)
|
|
63
|
+
raise ArgumentError, "request_timeout must be a positive finite number or nil, got #{request_timeout.inspect}"
|
|
64
|
+
end
|
|
65
|
+
|
|
49
66
|
# Ensure we always have at least one host
|
|
50
67
|
hosts_array = Array(hosts)
|
|
51
68
|
@hosts = hosts_array.empty? ? ["http://localhost:#{default_port}"] : hosts_array
|
|
@@ -59,7 +76,7 @@ module Noiseless
|
|
|
59
76
|
@clients = {}
|
|
60
77
|
@hosts.each do |host|
|
|
61
78
|
endpoint = Async::HTTP::Endpoint.parse(host, timeout: @timeout)
|
|
62
|
-
@clients[host] = Async::HTTP::Client.new(endpoint)
|
|
79
|
+
@clients[host] = Async::HTTP::Client.new(endpoint, limit: pool_limit)
|
|
63
80
|
end
|
|
64
81
|
|
|
65
82
|
super(hosts: @hosts, **connection_params)
|
|
@@ -78,27 +95,19 @@ module Noiseless
|
|
|
78
95
|
end
|
|
79
96
|
end
|
|
80
97
|
|
|
81
|
-
def post_request(path, body, content_type: "application/json")
|
|
82
|
-
|
|
98
|
+
def post_request(path, body, content_type: "application/json") = body_request(:post, path, body, content_type)
|
|
99
|
+
def put_request(path, body, content_type: "application/json") = body_request(:put, path, body, content_type)
|
|
100
|
+
def patch_request(path, body, content_type: "application/json") = body_request(:patch, path, body, content_type)
|
|
83
101
|
|
|
84
|
-
|
|
85
|
-
client.post(path, headers, body)
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def put_request(path, body, content_type: "application/json")
|
|
102
|
+
def body_request(verb, path, body, content_type)
|
|
90
103
|
headers = body ? default_headers + [["content-type", content_type]] : default_headers
|
|
91
104
|
|
|
92
105
|
with_client do |client|
|
|
93
|
-
client.
|
|
106
|
+
client.public_send(verb, path, headers, body)
|
|
94
107
|
end
|
|
95
108
|
end
|
|
96
109
|
|
|
97
|
-
def delete_request(path)
|
|
98
|
-
with_client do |client|
|
|
99
|
-
client.delete(path, default_headers)
|
|
100
|
-
end
|
|
101
|
-
end
|
|
110
|
+
def delete_request(path, body = nil) = body_request(:delete, path, body, "application/json")
|
|
102
111
|
|
|
103
112
|
def head_request(path)
|
|
104
113
|
with_client do |client|
|
|
@@ -146,6 +155,8 @@ module Noiseless
|
|
|
146
155
|
"search backend unreachable#{location} (#{e.class}: #{e.message})"
|
|
147
156
|
end
|
|
148
157
|
|
|
158
|
+
def dump_json(object) = JSON_CODER.dump(object)
|
|
159
|
+
|
|
149
160
|
def default_headers
|
|
150
161
|
[
|
|
151
162
|
["accept", "application/json"],
|