search-engine-for-typesense 30.1.4 → 30.1.5
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae511da617970ab104c22bde9c151ed099d9eb9c55909d9bc34f328ea56ac5f4
|
|
4
|
+
data.tar.gz: 49d4de291b6239a7f70e6bd93de83ef175916d7c403029d7a7382189fe90b9d7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba3c5e26e351975c5c967954b3f20e0fe14f438aab7126672c1b9ad42ec1bbc15c224f5412a5d0cd778e237451af23a8a4e237731ab2c9bc2e2dabb3650256e9
|
|
7
|
+
data.tar.gz: 82766d2a92e2f2a3a81f430d066254ff83b14966a3cce4b7171102979c07e0fe53925e2aa118075b6c679f8dd0881364d5c5d681c4c56e9c819b6ecb5aceaea1
|
|
@@ -13,19 +13,20 @@ module SearchEngine
|
|
|
13
13
|
# @param client [SearchEngine::Client, nil]
|
|
14
14
|
# @param pre [Symbol, nil] :ensure (ensure presence) or :index (ensure + fix drift)
|
|
15
15
|
# @param force_rebuild [Boolean] when true, force schema rebuild (blue/green)
|
|
16
|
-
# @return [
|
|
16
|
+
# @return [Hash, nil] result hash with :status, :docs_total, :success_total, :failed_total, :sample_error
|
|
17
17
|
def index_collection(partition: nil, client: nil, pre: nil, force_rebuild: false)
|
|
18
18
|
logical = respond_to?(:collection) ? collection.to_s : name.to_s
|
|
19
19
|
puts
|
|
20
20
|
puts(%(>>>>>> Indexing Collection "#{logical}"))
|
|
21
21
|
client_obj = client || SearchEngine.client
|
|
22
22
|
|
|
23
|
-
if partition.nil?
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
result = if partition.nil?
|
|
24
|
+
__se_index_full(client: client_obj, pre: pre, force_rebuild: force_rebuild)
|
|
25
|
+
else
|
|
26
|
+
__se_index_partial(partition: partition, client: client_obj, pre: pre)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
result.is_a?(Hash) ? result.merge(collection: logical) : { collection: logical, status: :ok }
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def reindex_collection!(pre: nil)
|
|
@@ -66,8 +67,9 @@ module SearchEngine
|
|
|
66
67
|
indexed_inside_apply,
|
|
67
68
|
force_rebuild
|
|
68
69
|
)
|
|
69
|
-
__se_full_indexation(applied, indexed_inside_apply)
|
|
70
|
+
result = __se_full_indexation(applied, indexed_inside_apply)
|
|
70
71
|
__se_full_retention(applied, logical, client)
|
|
72
|
+
result
|
|
71
73
|
end
|
|
72
74
|
|
|
73
75
|
def __se_full_apply_if_missing(client, missing)
|
|
@@ -116,21 +118,19 @@ module SearchEngine
|
|
|
116
118
|
end
|
|
117
119
|
|
|
118
120
|
def __se_full_indexation(applied, indexed_inside_apply)
|
|
119
|
-
|
|
121
|
+
result = nil
|
|
120
122
|
if applied && indexed_inside_apply
|
|
121
123
|
puts('Step 5: Indexing — skip (performed during schema apply)')
|
|
122
|
-
|
|
123
|
-
cascade_ok = indexed_inside_apply.to_sym == :ok
|
|
124
|
-
rescue StandardError
|
|
125
|
-
cascade_ok = false
|
|
126
|
-
end
|
|
124
|
+
result = indexed_inside_apply if indexed_inside_apply.is_a?(Hash)
|
|
127
125
|
else
|
|
128
126
|
puts('Step 5: Indexing — processing')
|
|
129
|
-
|
|
127
|
+
result = __se_index_partitions!(into: nil)
|
|
130
128
|
puts('Step 5: Indexing — done')
|
|
131
|
-
cascade_ok = (idx_status == :ok)
|
|
132
129
|
end
|
|
130
|
+
|
|
131
|
+
cascade_ok = result.is_a?(Hash) ? result[:status] == :ok : false
|
|
133
132
|
__se_cascade_after_indexation!(context: :full) if cascade_ok
|
|
133
|
+
result
|
|
134
134
|
end
|
|
135
135
|
|
|
136
136
|
def __se_full_retention(applied, logical, client)
|
|
@@ -152,32 +152,33 @@ module SearchEngine
|
|
|
152
152
|
puts("Step 1: Presence — processing → #{missing ? 'missing' : 'present'}")
|
|
153
153
|
if missing
|
|
154
154
|
puts('Partial: collection is not present. Quit early.')
|
|
155
|
-
return
|
|
155
|
+
return { status: :failed, docs_total: 0, success_total: 0, failed_total: 0,
|
|
156
|
+
sample_error: 'Collection not present' }
|
|
156
157
|
end
|
|
157
158
|
|
|
158
159
|
puts('Step 2: Check Schema Status — processing')
|
|
159
160
|
drift = __se_schema_drift?(diff)
|
|
160
161
|
if drift
|
|
161
162
|
puts('Partial: schema is not up-to-date. Exit early (run full indexing).')
|
|
162
|
-
return
|
|
163
|
+
return { status: :failed, docs_total: 0, success_total: 0, failed_total: 0,
|
|
164
|
+
sample_error: 'Schema drift detected' }
|
|
163
165
|
end
|
|
164
166
|
puts('Step 2: Check Schema Status — in_sync')
|
|
165
167
|
|
|
166
168
|
__se_preflight_dependencies!(mode: pre, client: client) if pre
|
|
167
169
|
|
|
168
170
|
puts('Step 3: Partial Indexing — processing')
|
|
169
|
-
|
|
171
|
+
summaries = []
|
|
170
172
|
partitions.each do |p|
|
|
171
173
|
summary = SearchEngine::Indexer.rebuild_partition!(self, partition: p, into: nil)
|
|
174
|
+
summaries << summary
|
|
172
175
|
puts(SearchEngine::Logging::PartitionProgress.line(p, summary))
|
|
173
|
-
begin
|
|
174
|
-
all_ok &&= (summary.status == :ok)
|
|
175
|
-
rescue StandardError
|
|
176
|
-
all_ok &&= false
|
|
177
|
-
end
|
|
178
176
|
end
|
|
179
177
|
puts('Step 3: Partial Indexing — done')
|
|
180
|
-
|
|
178
|
+
|
|
179
|
+
result = __se_build_index_result(summaries)
|
|
180
|
+
__se_cascade_after_indexation!(context: :full) if result[:status] == :ok
|
|
181
|
+
result
|
|
181
182
|
end
|
|
182
183
|
|
|
183
184
|
# rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
|
@@ -320,32 +320,51 @@ module SearchEngine
|
|
|
320
320
|
__se_index_partitions_parallel!(parts, into, max_p)
|
|
321
321
|
else
|
|
322
322
|
summary = SearchEngine::Indexer.rebuild_partition!(self, partition: nil, into: into)
|
|
323
|
-
summary
|
|
323
|
+
__se_build_index_result([summary])
|
|
324
324
|
end
|
|
325
325
|
end
|
|
326
|
+
|
|
327
|
+
# Aggregate an array of Indexer::Summary structs into a single result hash.
|
|
328
|
+
# @param summaries [Array<SearchEngine::Indexer::Summary>]
|
|
329
|
+
# @return [Hash] { status:, docs_total:, success_total:, failed_total:, sample_error: }
|
|
330
|
+
def __se_build_index_result(summaries)
|
|
331
|
+
docs = 0
|
|
332
|
+
success = 0
|
|
333
|
+
failed = 0
|
|
334
|
+
sample_error = nil
|
|
335
|
+
|
|
336
|
+
Array(summaries).each do |s|
|
|
337
|
+
docs += s.docs_total.to_i
|
|
338
|
+
success += s.success_total.to_i
|
|
339
|
+
failed += s.failed_total.to_i
|
|
340
|
+
sample_error ||= __se_extract_sample_error(s)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
status = if failed.positive? && success.zero?
|
|
344
|
+
:failed
|
|
345
|
+
elsif failed.positive?
|
|
346
|
+
:partial
|
|
347
|
+
else
|
|
348
|
+
:ok
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
{ status: status, docs_total: docs, success_total: success, failed_total: failed, sample_error: sample_error }
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
private :__se_build_index_result
|
|
326
355
|
end
|
|
327
356
|
|
|
328
357
|
class_methods do
|
|
329
358
|
# Sequential processing of partition list
|
|
330
359
|
def __se_index_partitions_seq!(parts, into)
|
|
331
|
-
|
|
360
|
+
summaries = []
|
|
332
361
|
parts.each do |part|
|
|
333
362
|
summary = SearchEngine::Indexer.rebuild_partition!(self, partition: part, into: into)
|
|
363
|
+
summaries << summary
|
|
334
364
|
puts(SearchEngine::Logging::PartitionProgress.line(part, summary))
|
|
335
|
-
# Log batches individually if there are multiple batches
|
|
336
365
|
__se_log_batches_from_summary(summary.batches) if summary.batches_total.to_i > 1
|
|
337
|
-
begin
|
|
338
|
-
st = summary.status
|
|
339
|
-
if st == :failed
|
|
340
|
-
agg = :failed
|
|
341
|
-
elsif st == :partial && agg == :ok
|
|
342
|
-
agg = :partial
|
|
343
|
-
end
|
|
344
|
-
rescue StandardError
|
|
345
|
-
agg = :failed
|
|
346
|
-
end
|
|
347
366
|
end
|
|
348
|
-
|
|
367
|
+
__se_build_index_result(summaries)
|
|
349
368
|
end
|
|
350
369
|
end
|
|
351
370
|
|
|
@@ -356,42 +375,37 @@ module SearchEngine
|
|
|
356
375
|
pool = Concurrent::FixedThreadPool.new(max_p)
|
|
357
376
|
ctx = SearchEngine::Instrumentation.context
|
|
358
377
|
mtx = Mutex.new
|
|
359
|
-
|
|
378
|
+
summaries = []
|
|
379
|
+
partition_errors = []
|
|
360
380
|
begin
|
|
361
381
|
parts.each do |part|
|
|
362
382
|
pool.post do
|
|
363
383
|
SearchEngine::Instrumentation.with_context(ctx) do
|
|
364
384
|
summary = SearchEngine::Indexer.rebuild_partition!(self, partition: part, into: into)
|
|
365
385
|
mtx.synchronize do
|
|
386
|
+
summaries << summary
|
|
366
387
|
puts(SearchEngine::Logging::PartitionProgress.line(part, summary))
|
|
367
|
-
# Log batches individually if there are multiple batches
|
|
368
388
|
__se_log_batches_from_summary(summary.batches) if summary.batches_total.to_i > 1
|
|
369
|
-
begin
|
|
370
|
-
st = summary.status
|
|
371
|
-
if st == :failed
|
|
372
|
-
agg = :failed
|
|
373
|
-
elsif st == :partial && agg == :ok
|
|
374
|
-
agg = :partial
|
|
375
|
-
end
|
|
376
|
-
rescue StandardError
|
|
377
|
-
agg = :failed
|
|
378
|
-
end
|
|
379
389
|
end
|
|
380
390
|
end
|
|
381
391
|
rescue StandardError => error
|
|
382
392
|
mtx.synchronize do
|
|
383
393
|
warn(" partition=#{part.inspect} → error=#{error.class}: #{error.message.to_s[0, 200]}")
|
|
384
|
-
|
|
394
|
+
partition_errors << "#{error.class}: #{error.message.to_s[0, 200]}"
|
|
385
395
|
end
|
|
386
396
|
end
|
|
387
397
|
end
|
|
388
398
|
ensure
|
|
389
399
|
pool.shutdown
|
|
390
|
-
# Wait up to 1 hour, then force-kill and wait a bit more to ensure cleanup
|
|
391
400
|
pool.wait_for_termination(3600) || pool.kill
|
|
392
401
|
pool.wait_for_termination(60)
|
|
393
402
|
end
|
|
394
|
-
|
|
403
|
+
result = __se_build_index_result(summaries)
|
|
404
|
+
if partition_errors.any?
|
|
405
|
+
result[:status] = :failed
|
|
406
|
+
result[:sample_error] ||= partition_errors.first
|
|
407
|
+
end
|
|
408
|
+
result
|
|
395
409
|
end
|
|
396
410
|
end
|
|
397
411
|
|
data/lib/search_engine/bulk.rb
CHANGED
|
@@ -107,50 +107,60 @@ module SearchEngine
|
|
|
107
107
|
cascade_count: cascade_order.size
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
collection_results = []
|
|
110
111
|
failed_collections_total = 0
|
|
111
112
|
|
|
112
113
|
SearchEngine::Instrumentation.with_context(bulk: true, bulk_suppress_cascade: true, bulk_mode: mode.to_sym) do
|
|
113
114
|
SearchEngine::Instrumentation.instrument('search_engine.bulk.run', payload.merge(stats)) do |ctx|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
klass = safe_collection_class(name)
|
|
117
|
-
unless klass
|
|
118
|
-
failed_collections_total += 1
|
|
119
|
-
next
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
case mode.to_sym
|
|
123
|
-
when :index
|
|
124
|
-
klass.index_collection
|
|
125
|
-
else
|
|
126
|
-
klass.reindex_collection!
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Stage 2 — process collected referencers once
|
|
131
|
-
cascade_order.each do |name|
|
|
132
|
-
klass = safe_collection_class(name)
|
|
133
|
-
unless klass
|
|
134
|
-
failed_collections_total += 1
|
|
135
|
-
next
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
case mode.to_sym
|
|
139
|
-
when :index
|
|
140
|
-
klass.index_collection(pre: :ensure, force_rebuild: true)
|
|
141
|
-
else
|
|
142
|
-
klass.reindex_collection!
|
|
143
|
-
end
|
|
144
|
-
end
|
|
115
|
+
run_stage!(mode, stage1_list, :input, collection_results)
|
|
116
|
+
run_stage!(mode, cascade_order, :cascade, collection_results)
|
|
145
117
|
|
|
118
|
+
failed_collections_total = collection_results.count { |r| r[:status] != :ok }
|
|
146
119
|
ctx[:failed_collections_total] = failed_collections_total
|
|
147
120
|
end
|
|
148
121
|
end
|
|
149
122
|
|
|
150
123
|
payload[:failed_collections_total] = failed_collections_total
|
|
124
|
+
payload[:collection_results] = collection_results
|
|
151
125
|
payload.merge(stats)
|
|
152
126
|
end
|
|
153
127
|
|
|
128
|
+
def run_stage!(mode, names, stage, collection_results)
|
|
129
|
+
names.each do |name|
|
|
130
|
+
klass = safe_collection_class(name)
|
|
131
|
+
unless klass
|
|
132
|
+
collection_results << unresolved_result(name, stage)
|
|
133
|
+
next
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
result = run_single_collection(mode, klass, stage)
|
|
137
|
+
collection_results << normalize_collection_result(result, name, stage)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def run_single_collection(mode, klass, stage)
|
|
142
|
+
case mode.to_sym
|
|
143
|
+
when :index
|
|
144
|
+
stage == :cascade ? klass.index_collection(pre: :ensure, force_rebuild: true) : klass.index_collection
|
|
145
|
+
else
|
|
146
|
+
klass.reindex_collection!
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def unresolved_result(name, stage)
|
|
151
|
+
{ collection: name, stage: stage, status: :failed,
|
|
152
|
+
docs_total: 0, success_total: 0, failed_total: 0,
|
|
153
|
+
sample_error: 'Unresolved collection class' }
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def normalize_collection_result(result, name, stage)
|
|
157
|
+
if result.is_a?(Hash)
|
|
158
|
+
result.merge(stage: stage)
|
|
159
|
+
else
|
|
160
|
+
{ collection: name, stage: stage, status: :ok }
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
154
164
|
# Normalize inputs to logical collection names.
|
|
155
165
|
# @param list [Array<Symbol, String, Class>]
|
|
156
166
|
# @return [Array<String>]
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: search-engine-for-typesense
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 30.1.
|
|
4
|
+
version: 30.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nikita Shkoda
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|