noiseless 0.2.0 → 0.3.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: 7fb5d9afa49be27d359d92a2fdf24999d488f8a7d865547452ea10b7aae67850
4
- data.tar.gz: da2fb4b847d7c1b53bb17b7b0a588d2a1660006cbe9413af0b657282087ad681
3
+ metadata.gz: f66be9c1d81e3b887f0c0d4485381c4535f087f8365e2581c4c41d89cdba0934
4
+ data.tar.gz: 1565bffbcfb554d2b6afcc78ebb1ded79c5c58a8f157b1e25ccd9a449cd2abe2
5
5
  SHA512:
6
- metadata.gz: 32b345fcc0ac56e25765f628c1ab8dd7dd03740e9a1a12bbcf5e13e9ce5078ef9ad57581f2b0a0f445f4d4b543559e01ceb50dd1a0dae86e7db5d35440b8e3c8
7
- data.tar.gz: da9518b7381e61a16d7efef22e2d8d585b895b2e644820099403781c86ee9c7b541cf2ee00bad6cac7c40830764fd30aa841dd685dd9c63cb4ed227a1ed7ce8a
6
+ metadata.gz: 4a7e23f4eb136bc6c0dc457dcb6f267e000269aceddd6450264ac489429dda2e20c7b382b32c2ab2a91271faeccee419a5c72530e3781087a9f630e8bd16a15d
7
+ data.tar.gz: 605cf0980142cbe65692b9ef8d2a543969578620fa17b35e069a0344ac1a5ca5947781c6d9cf617203408a73e11cd08626c000d046e4a7a9839508919de8d463
@@ -9,6 +9,17 @@ module Noiseless
9
9
  include Instrumentation
10
10
  include Introspection
11
11
 
12
+ GEO_FILTER_KEYS = %i[geo_distance geo_bounding_box geo_polygon geo_shape].freeze
13
+ RANGE_OPERATORS = %i[gte lte gt lt].freeze
14
+
15
+ # Aggregation types that require a field (or script) to execute.
16
+ FIELD_BASED_AGG_TYPES = %i[
17
+ terms rare_terms significant_terms missing
18
+ avg sum min max cardinality value_count stats extended_stats
19
+ percentiles percentile_ranks histogram date_histogram
20
+ geohash_grid geotile_grid geo_distance
21
+ ].freeze
22
+
12
23
  def initialize(hosts: [], **connection_params)
13
24
  @hosts = hosts
14
25
  @connection_params = connection_params.dup
@@ -61,6 +72,14 @@ module Noiseless
61
72
  end
62
73
  end
63
74
 
75
+ def refresh_index(index_name)
76
+ Async do
77
+ instrument(:refresh_index, index: index_name) do
78
+ execute_refresh_index(index_name)
79
+ end
80
+ end
81
+ end
82
+
64
83
  def index_exists?(index_name)
65
84
  Async do
66
85
  execute_index_exists?(index_name)
@@ -189,11 +208,32 @@ module Noiseless
189
208
  {
190
209
  bool: {
191
210
  must: must_queries,
192
- filter: bool_node.filter.map { |f| { term: { f.field => f.value } } }
211
+ filter: bool_node.filter.map { |f| build_filter_clause(f) }
193
212
  }.reject { |_, v| v.empty? }
194
213
  }
195
214
  end
196
215
 
216
+ def build_filter_clause(node)
217
+ value = node.value
218
+ case value
219
+ when Hash
220
+ keys = value.keys.map(&:to_sym)
221
+ if keys.intersect?(GEO_FILTER_KEYS)
222
+ # Geo filters are already complete query clauses, e.g.
223
+ # { geo_distance: { distance: "100km", location: { lat:, lon: } } }
224
+ value
225
+ elsif (keys - RANGE_OPERATORS).empty?
226
+ { range: { node.field => value } }
227
+ else
228
+ { term: { node.field => value } }
229
+ end
230
+ when Array
231
+ { terms: { node.field => value } }
232
+ else
233
+ { term: { node.field => value } }
234
+ end
235
+ end
236
+
197
237
  def build_sort_hash(sort_nodes)
198
238
  return [] if sort_nodes.empty?
199
239
 
@@ -224,12 +264,12 @@ module Noiseless
224
264
  end
225
265
  error = payload.is_a?(Hash) ? payload["error"] : nil
226
266
  reason = if error.is_a?(Hash)
227
- [ error["type"], error["reason"] ].compact.join(": ")
228
- elsif error
267
+ [error["type"], error["reason"]].compact.join(": ")
268
+ elsif error
229
269
  error.to_s
230
- else
270
+ else
231
271
  "HTTP #{response.status}"
232
- end
272
+ end
233
273
  message = context ? "#{context}: #{reason}" : reason
234
274
  raise error_class.new(message, status: response.status, error_type: error.is_a?(Hash) ? error["type"] : nil)
235
275
  end
@@ -259,6 +299,10 @@ module Noiseless
259
299
  { acknowledged: true }
260
300
  end
261
301
 
302
+ def execute_refresh_index(_index_name)
303
+ { "acknowledged" => true }
304
+ end
305
+
262
306
  def execute_index_exists?(_index_name)
263
307
  true
264
308
  end
@@ -328,6 +372,10 @@ module Noiseless
328
372
  agg_body[:field] = agg.field if agg.field
329
373
  agg_body.merge!(agg.options)
330
374
 
375
+ # Convention: an unqualified field-based aggregation targets the field
376
+ # named after the aggregation itself.
377
+ agg_body[:field] = agg.name if !agg_body.key?(:field) && !agg_body.key?(:script) && FIELD_BASED_AGG_TYPES.include?(agg.type.to_sym)
378
+
331
379
  result[agg.type] = agg_body
332
380
 
333
381
  # Add sub-aggregations if any
@@ -32,6 +32,10 @@ module Noiseless
32
32
 
33
33
  def execute_delete_index(index_name, **_opts)
34
34
  response = delete_request("/#{index_name}")
35
+ # Deleting an absent index is idempotent, matching official ES/OS
36
+ # clients' ignore-404 behaviour.
37
+ return { "acknowledged" => true, "result" => "not_found" } if response.status == 404
38
+
35
39
  parse_json_response!(response, context: "delete index #{index_name}")
36
40
  ensure
37
41
  response&.close
@@ -64,6 +68,10 @@ module Noiseless
64
68
 
65
69
  def execute_delete_document(index, id, **_opts)
66
70
  response = delete_request("/#{index}/_doc/#{id}")
71
+ # 404 covers both a missing document and a missing index; either way
72
+ # the delete is idempotent.
73
+ return { "_index" => index, "_id" => id, "result" => "not_found" } if response.status == 404
74
+
67
75
  parse_json_response!(response, context: "delete document #{index}/#{id}")
68
76
  ensure
69
77
  response&.close
@@ -19,7 +19,7 @@ module Noiseless
19
19
 
20
20
  def refresh(index:)
21
21
  # Refresh the index to make documents immediately searchable
22
- @adapter.send(:execute_refresh_index, index)
22
+ @adapter.refresh_index(index).wait
23
23
  end
24
24
  end
25
25
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Noiseless
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: noiseless
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abdelkader Boudih
@@ -294,7 +294,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
294
294
  - !ruby/object:Gem::Version
295
295
  version: '0'
296
296
  requirements: []
297
- rubygems_version: 4.0.10
297
+ rubygems_version: 3.6.9
298
298
  specification_version: 4
299
299
  summary: Async-first Rails search abstraction with multi-backend support
300
300
  test_files: []