search-engine-for-typesense 30.1.6.12 → 30.1.6.13
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/lib/search_engine/base/index_maintenance/schema.rb +7 -0
- data/lib/search_engine/bulk.rb +9 -0
- data/lib/search_engine/client/services/collections.rb +15 -0
- data/lib/search_engine/client.rb +7 -0
- data/lib/search_engine/schema.rb +43 -0
- data/lib/search_engine/test/offline_client.rb +4 -0
- data/lib/search_engine/version.rb +1 -1
- data/lib/tasks/search_engine.rake +39 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 687864490764b552a2e0a30095711766c1ce073dcb0de9538ecc710ae142aa31
|
|
4
|
+
data.tar.gz: 48aa2e8cdcc750685264e432e8bf3d74b6b61fb11a0e0220d42bd393d90a4e97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 67a000ce3cfa475b57d6059c640ba42682747e32ed3f2ca680bd64f5b4f08f0def7aa205e5d1ba44df9004fc76899734602f0f395542af211962290706dfeb3e
|
|
7
|
+
data.tar.gz: 1aa9dab69f9daad382d32e9d291d0d3226b057363a0da981e77b6b595a92f516257f8776518813d01a6616c0ac823c6d5efe7f0d148473ef52d3043c37afb31d
|
|
@@ -129,6 +129,13 @@ module SearchEngine
|
|
|
129
129
|
step&.close
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
# Drop orphaned physical collections for this model's logical collection.
|
|
133
|
+
# @return [Hash] { dropped: Array<String>, kept: Array<String>, total_scanned: Integer }
|
|
134
|
+
def prune_orphans!
|
|
135
|
+
logical = respond_to?(:collection) ? collection.to_s : name.to_s
|
|
136
|
+
SearchEngine::Schema.prune_orphans!(logical: logical)
|
|
137
|
+
end
|
|
138
|
+
|
|
132
139
|
def __se_retention_cleanup!(_logical:, _client:)
|
|
133
140
|
SearchEngine::Schema.prune_history!(self)
|
|
134
141
|
end
|
data/lib/search_engine/bulk.rb
CHANGED
|
@@ -60,6 +60,15 @@ module SearchEngine
|
|
|
60
60
|
run!(mode: :reindex, targets: names, client: client)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
+
# Drop orphaned physical collections across all logical collections.
|
|
64
|
+
# Delegates to {SearchEngine::Schema.prune_orphans!}.
|
|
65
|
+
# @param client [SearchEngine::Client, nil]
|
|
66
|
+
# @param logical [String, nil] scope to a single logical collection
|
|
67
|
+
# @return [Hash] { dropped: Array<String>, kept: Array<String>, total_scanned: Integer }
|
|
68
|
+
def prune_orphans!(client: nil, logical: nil)
|
|
69
|
+
SearchEngine::Schema.prune_orphans!(client: client, logical: logical)
|
|
70
|
+
end
|
|
71
|
+
|
|
63
72
|
private
|
|
64
73
|
|
|
65
74
|
# @param mode [Symbol] :index | :reindex
|
|
@@ -57,6 +57,21 @@ module SearchEngine
|
|
|
57
57
|
instrument(:get, path, (start ? (current_monotonic_ms - start) : 0.0), {}, request_token: start)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
|
+
# @return [Array<Hash>] list of aliases, each with :name and :collection_name
|
|
61
|
+
def list_aliases
|
|
62
|
+
start = current_monotonic_ms
|
|
63
|
+
path = '/aliases'
|
|
64
|
+
|
|
65
|
+
result = with_exception_mapping(:get, path, {}, start) do
|
|
66
|
+
typesense.aliases.retrieve
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
raw = symbolize_keys_deep(result)
|
|
70
|
+
Array(raw[:aliases])
|
|
71
|
+
ensure
|
|
72
|
+
instrument(:get, path, (start ? (current_monotonic_ms - start) : 0.0), {}, request_token: start)
|
|
73
|
+
end
|
|
74
|
+
|
|
60
75
|
# @param alias_name [String]
|
|
61
76
|
# @return [Hash] Typesense delete response, or { status: 404 } when alias not found
|
|
62
77
|
def delete_alias(alias_name)
|
data/lib/search_engine/client.rb
CHANGED
|
@@ -61,6 +61,13 @@ module SearchEngine
|
|
|
61
61
|
services.fetch(:collections).retrieve_schema(collection_name, timeout_ms: timeout_ms)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
# List all aliases defined on the Typesense server.
|
|
65
|
+
# @return [Array<Hash>] list of aliases, each with :name and :collection_name
|
|
66
|
+
# @see `https://typesense.org/docs/latest/api/aliases.html#list-all-aliases`
|
|
67
|
+
def list_aliases
|
|
68
|
+
services.fetch(:collections).list_aliases
|
|
69
|
+
end
|
|
70
|
+
|
|
64
71
|
# Delete an alias by name. Returns { status: 404 } when alias not found.
|
|
65
72
|
# @param alias_name [String]
|
|
66
73
|
# @return [Hash]
|
data/lib/search_engine/schema.rb
CHANGED
|
@@ -345,6 +345,49 @@ module SearchEngine
|
|
|
345
345
|
true
|
|
346
346
|
end
|
|
347
347
|
|
|
348
|
+
# Drop orphaned physical collections that no alias points to.
|
|
349
|
+
#
|
|
350
|
+
# A physical collection is orphaned when it matches the timestamped naming
|
|
351
|
+
# pattern (`logical_YYYYMMDD_HHMMSS_###`) but is not the target of any
|
|
352
|
+
# alias. These typically accumulate from failed blue/green deployments
|
|
353
|
+
# where the alias swap never occurred.
|
|
354
|
+
#
|
|
355
|
+
# @param client [SearchEngine::Client, nil]
|
|
356
|
+
# @param logical [String, nil] scope to a single logical collection name; when nil, scans all
|
|
357
|
+
# @return [Hash] { dropped: Array<String>, kept: Array<String>, total_scanned: Integer }
|
|
358
|
+
def prune_orphans!(client: nil, logical: nil)
|
|
359
|
+
require 'set'
|
|
360
|
+
client ||= SearchEngine.client
|
|
361
|
+
|
|
362
|
+
meta_timeout = begin
|
|
363
|
+
t = SearchEngine.config.timeout_ms.to_i
|
|
364
|
+
[t, 10_000].max
|
|
365
|
+
rescue StandardError
|
|
366
|
+
10_000
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
all_collections = Array(client.list_collections(timeout_ms: meta_timeout))
|
|
370
|
+
all_names = all_collections.map { |c| (c[:name] || c['name']).to_s }
|
|
371
|
+
|
|
372
|
+
alias_targets = client.list_aliases.each_with_object(Set.new) do |a, set|
|
|
373
|
+
target = (a[:collection_name] || a['collection_name']).to_s
|
|
374
|
+
set.add(target) unless target.empty?
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
physical_re = if logical
|
|
378
|
+
/\A#{Regexp.escape(logical)}_\d{8}_\d{6}_\d{3}\z/
|
|
379
|
+
else
|
|
380
|
+
/\A.+_\d{8}_\d{6}_\d{3}\z/
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
physicals = all_names.select { |n| physical_re.match?(n) }
|
|
384
|
+
kept, orphaned = physicals.partition { |n| alias_targets.include?(n) }
|
|
385
|
+
|
|
386
|
+
orphaned.each { |name| client.delete_collection(name, timeout_ms: 60_000) }
|
|
387
|
+
|
|
388
|
+
{ dropped: orphaned, kept: kept, total_scanned: all_names.size }
|
|
389
|
+
end
|
|
390
|
+
|
|
348
391
|
private
|
|
349
392
|
|
|
350
393
|
# Generate a new physical name using UTC timestamp + 3-digit sequence.
|
|
@@ -132,6 +132,45 @@ namespace :search_engine do
|
|
|
132
132
|
warn("schema:rollback failed: #{error.message}")
|
|
133
133
|
Kernel.exit(1)
|
|
134
134
|
end
|
|
135
|
+
|
|
136
|
+
desc "Prune orphaned physicals. Usage: rails 'search_engine:schema:prune_orphans[coll]'"
|
|
137
|
+
task :prune_orphans, [:collection] => :environment do |_t, args|
|
|
138
|
+
logical = nil
|
|
139
|
+
if args[:collection] && !args[:collection].to_s.strip.empty?
|
|
140
|
+
begin
|
|
141
|
+
klass = SearchEngine::Cli.resolve_collection!(args[:collection])
|
|
142
|
+
logical = klass.respond_to?(:collection) ? klass.collection.to_s : klass.name.to_s
|
|
143
|
+
rescue ArgumentError => error
|
|
144
|
+
warn("Error: #{error.message}")
|
|
145
|
+
print_schema_usage
|
|
146
|
+
Kernel.exit(1)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
payload = { task: 'schema:prune_orphans', collection: logical || '(all)' }
|
|
151
|
+
result = nil
|
|
152
|
+
SearchEngine::Cli.with_task_instrumentation('schema:prune_orphans', payload) do
|
|
153
|
+
result = SearchEngine::Schema.prune_orphans!(logical: logical)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if SearchEngine::Cli.json_output?
|
|
157
|
+
puts(JSON.generate({ status: 'ok' }.merge(result)))
|
|
158
|
+
else
|
|
159
|
+
puts("Scanned: #{result[:total_scanned]} collections")
|
|
160
|
+
puts("Kept (alias targets): #{result[:kept].size}")
|
|
161
|
+
if result[:dropped].empty?
|
|
162
|
+
puts('No orphaned physicals found.')
|
|
163
|
+
else
|
|
164
|
+
puts("Dropped #{result[:dropped].size} orphaned physical(s):")
|
|
165
|
+
result[:dropped].each { |name| puts(" - #{name}") }
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
Kernel.exit(0)
|
|
170
|
+
rescue StandardError => error
|
|
171
|
+
warn("schema:prune_orphans failed: #{error.message}")
|
|
172
|
+
Kernel.exit(1)
|
|
173
|
+
end
|
|
135
174
|
end
|
|
136
175
|
|
|
137
176
|
# ------------------------- Index tasks -------------------------
|