search-engine-for-typesense 30.1.6.3 → 30.1.6.4
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/cleanup.rb +9 -11
- data/lib/search_engine/base/index_maintenance/lifecycle.rb +60 -35
- data/lib/search_engine/base/index_maintenance/schema.rb +22 -12
- data/lib/search_engine/base/index_maintenance.rb +18 -64
- data/lib/search_engine/cascade.rb +14 -9
- data/lib/search_engine/cli/doctor.rb +2 -4
- data/lib/search_engine/indexer/bulk_import.rb +13 -55
- data/lib/search_engine/logging/batch_line.rb +89 -0
- data/lib/search_engine/logging/color.rb +18 -1
- data/lib/search_engine/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46b318e4be1f0f8d12e5f3ead35ad4945c472773f8f7117427d1ffd2a0f44732
|
|
4
|
+
data.tar.gz: 4db7bc9cf0e081ff21b87c8b089627fe02e651020ad8a9df7fc0a3edb0cc7dea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cccc05756309485db9c5c49a880e45aa09a3f8f7e6afade62fd2d064eb3ccca286c96791aed830da01090ee10c60b6cf5c602269b55859b3466f76d3d22bd704
|
|
7
|
+
data.tar.gz: 112cbcd585dfeec20a848c9b153024fcb48526b4f56127a75ef45300693baf6ca666899f607f436e32889d50d2e7f1ad1b29d1daf6af6c5f59b1611db8d9b683
|
|
@@ -45,13 +45,13 @@ module SearchEngine
|
|
|
45
45
|
def cleanup(into: nil, partition: nil, clear_cache: false)
|
|
46
46
|
logical = respond_to?(:collection) ? collection.to_s : name.to_s
|
|
47
47
|
puts
|
|
48
|
-
puts(%(>>>>>> Cleanup Collection "#{logical}"))
|
|
48
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Cleanup Collection "#{logical}")))
|
|
49
49
|
|
|
50
50
|
filters = SearchEngine::StaleRules.compile_filters(self, partition: partition)
|
|
51
51
|
filters.compact!
|
|
52
52
|
filters.reject! { |f| f.to_s.strip.empty? }
|
|
53
53
|
if filters.empty?
|
|
54
|
-
puts('Cleanup — skip (no stale configuration)')
|
|
54
|
+
puts(SearchEngine::Logging::Color.dim('Cleanup — skip (no stale configuration)'))
|
|
55
55
|
return 0
|
|
56
56
|
end
|
|
57
57
|
|
|
@@ -65,25 +65,23 @@ module SearchEngine
|
|
|
65
65
|
partition: partition
|
|
66
66
|
)
|
|
67
67
|
|
|
68
|
-
puts("Cleanup — deleted=#{deleted}")
|
|
68
|
+
puts("Cleanup — #{SearchEngine::Logging::Color.apply("deleted=#{deleted}", :green)}")
|
|
69
69
|
deleted
|
|
70
70
|
rescue StandardError => error
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
)
|
|
71
|
+
err_msg = "Cleanup — error=#{error.class}: #{error.message.to_s[0, 200]}"
|
|
72
|
+
warn(SearchEngine::Logging::Color.apply(err_msg, :red))
|
|
74
73
|
0
|
|
75
74
|
ensure
|
|
76
75
|
if clear_cache
|
|
77
76
|
begin
|
|
78
|
-
puts(
|
|
77
|
+
puts("Cleanup — #{SearchEngine::Logging::Color.bold('cache clear')}")
|
|
79
78
|
SearchEngine::Cache.clear
|
|
80
79
|
rescue StandardError => error
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)
|
|
80
|
+
err_msg = "Cleanup — cache clear error=#{error.class}: #{error.message.to_s[0, 200]}"
|
|
81
|
+
warn(SearchEngine::Logging::Color.apply(err_msg, :red))
|
|
84
82
|
end
|
|
85
83
|
end
|
|
86
|
-
puts(%(>>>>>> Cleanup Done))
|
|
84
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Cleanup Done)))
|
|
87
85
|
end
|
|
88
86
|
|
|
89
87
|
private
|
|
@@ -17,7 +17,7 @@ module SearchEngine
|
|
|
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
|
-
puts(%(>>>>>> Indexing Collection "#{logical}"))
|
|
20
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Indexing Collection "#{logical}")))
|
|
21
21
|
client_obj = client || SearchEngine.client
|
|
22
22
|
|
|
23
23
|
result = if partition.nil?
|
|
@@ -56,7 +56,12 @@ module SearchEngine
|
|
|
56
56
|
|
|
57
57
|
diff = SearchEngine::Schema.diff(self, client: client)[:diff] || {}
|
|
58
58
|
missing = __se_schema_missing?(diff)
|
|
59
|
-
|
|
59
|
+
presence = if missing
|
|
60
|
+
SearchEngine::Logging::Color.apply('missing', :yellow)
|
|
61
|
+
else
|
|
62
|
+
SearchEngine::Logging::Color.apply('present', :green)
|
|
63
|
+
end
|
|
64
|
+
puts("Step 1: Presence — #{SearchEngine::Logging::Color.bold('processing')} → #{presence}")
|
|
60
65
|
|
|
61
66
|
applied, indexed_inside_apply = __se_full_apply_if_missing(client, missing)
|
|
62
67
|
drift = __se_full_check_drift(diff, missing, force_rebuild)
|
|
@@ -76,43 +81,48 @@ module SearchEngine
|
|
|
76
81
|
applied = false
|
|
77
82
|
indexed_inside_apply = false
|
|
78
83
|
if missing
|
|
79
|
-
puts(
|
|
84
|
+
puts("Step 2: Create+Apply Schema — #{SearchEngine::Logging::Color.bold('processing')}")
|
|
80
85
|
SearchEngine::Schema.apply!(self, client: client) do |new_physical|
|
|
81
86
|
indexed_inside_apply = __se_index_partitions!(into: new_physical)
|
|
82
87
|
end
|
|
83
88
|
applied = true
|
|
84
|
-
puts(
|
|
89
|
+
puts("Step 2: Create+Apply Schema — #{SearchEngine::Logging::Color.apply('done', :green)}")
|
|
85
90
|
else
|
|
86
|
-
puts('Step 2: Create+Apply Schema — skip (collection present)')
|
|
91
|
+
puts(SearchEngine::Logging::Color.dim('Step 2: Create+Apply Schema — skip (collection present)'))
|
|
87
92
|
end
|
|
88
93
|
[applied, indexed_inside_apply]
|
|
89
94
|
end
|
|
90
95
|
|
|
91
96
|
def __se_full_check_drift(diff, missing, force_rebuild)
|
|
92
97
|
unless missing
|
|
93
|
-
puts(
|
|
98
|
+
puts("Step 3: Check Schema Status — #{SearchEngine::Logging::Color.bold('processing')}")
|
|
94
99
|
drift = __se_schema_drift?(diff)
|
|
95
100
|
if force_rebuild && !drift
|
|
96
|
-
puts(
|
|
101
|
+
puts("Step 3: Check Schema Status — #{SearchEngine::Logging::Color.apply('force_rebuild', :yellow)}")
|
|
97
102
|
return true
|
|
98
103
|
end
|
|
99
|
-
|
|
104
|
+
schema_status = if drift
|
|
105
|
+
SearchEngine::Logging::Color.apply('drift', :yellow)
|
|
106
|
+
else
|
|
107
|
+
SearchEngine::Logging::Color.apply('in_sync', :green)
|
|
108
|
+
end
|
|
109
|
+
puts("Step 3: Check Schema Status — #{schema_status}")
|
|
100
110
|
return drift
|
|
101
111
|
end
|
|
102
|
-
puts('Step 3: Check Schema Status — skip (just created)')
|
|
112
|
+
puts(SearchEngine::Logging::Color.dim('Step 3: Check Schema Status — skip (just created)'))
|
|
103
113
|
false
|
|
104
114
|
end
|
|
105
115
|
|
|
106
116
|
def __se_full_apply_if_drift(client, drift, applied, indexed_inside_apply, force_rebuild)
|
|
107
117
|
if drift
|
|
108
|
-
puts(
|
|
118
|
+
puts("Step 4: Apply New Schema — #{SearchEngine::Logging::Color.bold('processing')}")
|
|
109
119
|
SearchEngine::Schema.apply!(self, client: client, force_rebuild: force_rebuild) do |new_physical|
|
|
110
120
|
indexed_inside_apply = __se_index_partitions!(into: new_physical)
|
|
111
121
|
end
|
|
112
122
|
applied = true
|
|
113
|
-
puts(
|
|
123
|
+
puts("Step 4: Apply New Schema — #{SearchEngine::Logging::Color.apply('done', :green)}")
|
|
114
124
|
else
|
|
115
|
-
puts('Step 4: Apply New Schema — skip')
|
|
125
|
+
puts(SearchEngine::Logging::Color.dim('Step 4: Apply New Schema — skip'))
|
|
116
126
|
end
|
|
117
127
|
[applied, indexed_inside_apply]
|
|
118
128
|
end
|
|
@@ -120,12 +130,12 @@ module SearchEngine
|
|
|
120
130
|
def __se_full_indexation(applied, indexed_inside_apply)
|
|
121
131
|
result = nil
|
|
122
132
|
if applied && indexed_inside_apply
|
|
123
|
-
puts('Step 5: Indexing — skip (performed during schema apply)')
|
|
133
|
+
puts(SearchEngine::Logging::Color.dim('Step 5: Indexing — skip (performed during schema apply)'))
|
|
124
134
|
result = indexed_inside_apply if indexed_inside_apply.is_a?(Hash)
|
|
125
135
|
else
|
|
126
|
-
puts(
|
|
136
|
+
puts("Step 5: Indexing — #{SearchEngine::Logging::Color.bold('processing')}")
|
|
127
137
|
result = __se_index_partitions!(into: nil)
|
|
128
|
-
puts(
|
|
138
|
+
puts("Step 5: Indexing — #{SearchEngine::Logging::Color.apply('done', :green)}")
|
|
129
139
|
end
|
|
130
140
|
|
|
131
141
|
cascade_ok = result.is_a?(Hash) ? result[:status] == :ok : false
|
|
@@ -135,11 +145,12 @@ module SearchEngine
|
|
|
135
145
|
|
|
136
146
|
def __se_full_retention(applied, logical, client)
|
|
137
147
|
if applied
|
|
138
|
-
puts('Step 6: Retention Cleanup — skip (handled by schema apply)')
|
|
148
|
+
puts(SearchEngine::Logging::Color.dim('Step 6: Retention Cleanup — skip (handled by schema apply)'))
|
|
139
149
|
else
|
|
140
|
-
puts(
|
|
150
|
+
puts("Step 6: Retention Cleanup — #{SearchEngine::Logging::Color.bold('processing')}")
|
|
141
151
|
dropped = __se_retention_cleanup!(logical: logical, client: client)
|
|
142
|
-
|
|
152
|
+
dropped_str = SearchEngine::Logging::Color.apply("dropped=#{dropped.inspect}", :green)
|
|
153
|
+
puts("Step 6: Retention Cleanup — #{dropped_str}")
|
|
143
154
|
end
|
|
144
155
|
end
|
|
145
156
|
|
|
@@ -149,32 +160,43 @@ module SearchEngine
|
|
|
149
160
|
diff = diff_res[:diff] || {}
|
|
150
161
|
|
|
151
162
|
missing = __se_schema_missing?(diff)
|
|
152
|
-
|
|
163
|
+
presence = if missing
|
|
164
|
+
SearchEngine::Logging::Color.apply('missing', :yellow)
|
|
165
|
+
else
|
|
166
|
+
SearchEngine::Logging::Color.apply('present', :green)
|
|
167
|
+
end
|
|
168
|
+
puts("Step 1: Presence — #{SearchEngine::Logging::Color.bold('processing')} → #{presence}")
|
|
153
169
|
if missing
|
|
154
|
-
|
|
170
|
+
msg = SearchEngine::Logging::Color.apply(
|
|
171
|
+
'Step 1: Partial — collection is not present. Quit early.', :yellow
|
|
172
|
+
)
|
|
173
|
+
puts(msg)
|
|
155
174
|
return { status: :failed, docs_total: 0, success_total: 0, failed_total: 0,
|
|
156
175
|
sample_error: 'Collection not present' }
|
|
157
176
|
end
|
|
158
177
|
|
|
159
|
-
puts(
|
|
178
|
+
puts("Step 2: Check Schema Status — #{SearchEngine::Logging::Color.bold('processing')}")
|
|
160
179
|
drift = __se_schema_drift?(diff)
|
|
161
180
|
if drift
|
|
162
|
-
|
|
181
|
+
msg = SearchEngine::Logging::Color.apply(
|
|
182
|
+
'Step 2: Partial — schema is not up-to-date. Exit early (run full indexing).', :yellow
|
|
183
|
+
)
|
|
184
|
+
puts(msg)
|
|
163
185
|
return { status: :failed, docs_total: 0, success_total: 0, failed_total: 0,
|
|
164
186
|
sample_error: 'Schema drift detected' }
|
|
165
187
|
end
|
|
166
|
-
puts(
|
|
188
|
+
puts("Step 2: Check Schema Status — #{SearchEngine::Logging::Color.apply('in_sync', :green)}")
|
|
167
189
|
|
|
168
190
|
__se_preflight_dependencies!(mode: pre, client: client) if pre
|
|
169
191
|
|
|
170
|
-
puts(
|
|
192
|
+
puts("Step 3: Partial Indexing — #{SearchEngine::Logging::Color.bold('processing')}")
|
|
171
193
|
summaries = []
|
|
172
194
|
partitions.each do |p|
|
|
173
195
|
summary = SearchEngine::Indexer.rebuild_partition!(self, partition: p, into: nil)
|
|
174
196
|
summaries << summary
|
|
175
197
|
puts(SearchEngine::Logging::PartitionProgress.line(p, summary))
|
|
176
198
|
end
|
|
177
|
-
puts(
|
|
199
|
+
puts("Step 3: Partial Indexing — #{SearchEngine::Logging::Color.apply('done', :green)}")
|
|
178
200
|
|
|
179
201
|
result = __se_build_index_result(summaries)
|
|
180
202
|
__se_cascade_after_indexation!(context: :full) if result[:status] == :ok
|
|
@@ -185,34 +207,34 @@ module SearchEngine
|
|
|
185
207
|
def __se_cascade_after_indexation!(context: :full)
|
|
186
208
|
if SearchEngine::Instrumentation.context&.[](:bulk_suppress_cascade)
|
|
187
209
|
puts
|
|
188
|
-
puts('>>>>>> Cascade Referencers — suppressed (bulk)')
|
|
210
|
+
puts(SearchEngine::Logging::Color.dim('>>>>>> Cascade Referencers — suppressed (bulk)'))
|
|
189
211
|
return
|
|
190
212
|
end
|
|
191
213
|
puts
|
|
192
|
-
puts(%(>>>>>> Cascade Referencers))
|
|
214
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Cascade Referencers)))
|
|
193
215
|
results = SearchEngine::Cascade.cascade_reindex!(source: self, ids: nil, context: context)
|
|
194
216
|
outcomes = Array(results[:outcomes])
|
|
195
217
|
if outcomes.empty?
|
|
196
|
-
puts(' none')
|
|
218
|
+
puts(SearchEngine::Logging::Color.dim(' none'))
|
|
197
219
|
else
|
|
198
220
|
outcomes.each do |o|
|
|
199
221
|
coll = o[:collection] || o['collection']
|
|
200
222
|
mode = (o[:mode] || o['mode']).to_s
|
|
201
223
|
case mode
|
|
202
224
|
when 'partial'
|
|
203
|
-
puts(%( Referencer "#{coll}" → partial reindex))
|
|
225
|
+
puts(%( Referencer "#{coll}" → #{SearchEngine::Logging::Color.apply('partial reindex', :green)}))
|
|
204
226
|
when 'full'
|
|
205
|
-
puts(%( Referencer "#{coll}" → full reindex))
|
|
227
|
+
puts(%( Referencer "#{coll}" → #{SearchEngine::Logging::Color.apply('full reindex', :green)}))
|
|
206
228
|
when 'skipped_unregistered'
|
|
207
|
-
puts(%( Referencer "#{coll}" → skipped (unregistered)))
|
|
229
|
+
puts(SearchEngine::Logging::Color.dim(%( Referencer "#{coll}" → skipped (unregistered))))
|
|
208
230
|
when 'skipped_cycle'
|
|
209
|
-
puts(%( Referencer "#{coll}" → skipped (cycle)))
|
|
231
|
+
puts(SearchEngine::Logging::Color.dim(%( Referencer "#{coll}" → skipped (cycle))))
|
|
210
232
|
else
|
|
211
233
|
puts(%( Referencer "#{coll}" → #{mode}))
|
|
212
234
|
end
|
|
213
235
|
end
|
|
214
236
|
end
|
|
215
|
-
puts('>>>>>> Cascade Done')
|
|
237
|
+
puts(SearchEngine::Logging::Color.header('>>>>>> Cascade Done'))
|
|
216
238
|
rescue StandardError => error
|
|
217
239
|
base = "Cascade — error=#{error.class}: #{error.message.to_s[0, 200]}"
|
|
218
240
|
if error.respond_to?(:status) || error.respond_to?(:body)
|
|
@@ -233,9 +255,12 @@ module SearchEngine
|
|
|
233
255
|
rescue StandardError
|
|
234
256
|
nil
|
|
235
257
|
end
|
|
236
|
-
|
|
258
|
+
err_parts = [base]
|
|
259
|
+
err_parts << "status=#{status}" if status
|
|
260
|
+
err_parts << "body=#{body_preview}" if body_preview
|
|
261
|
+
warn(SearchEngine::Logging::Color.apply(err_parts.compact.join(' '), :red))
|
|
237
262
|
else
|
|
238
|
-
warn(base)
|
|
263
|
+
warn(SearchEngine::Logging::Color.apply(base, :red))
|
|
239
264
|
end
|
|
240
265
|
end
|
|
241
266
|
# rubocop:enable Metrics/PerceivedComplexity, Metrics/AbcSize
|
|
@@ -28,13 +28,18 @@ module SearchEngine
|
|
|
28
28
|
def update_collection!
|
|
29
29
|
client = SearchEngine.client
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
status_word = SearchEngine::Logging::Color.bold('analyzing diff for in-place update...')
|
|
32
|
+
puts "Update Collection — #{status_word}"
|
|
32
33
|
updated = SearchEngine::Schema.update!(self, client: client)
|
|
33
34
|
|
|
34
35
|
if updated
|
|
35
|
-
|
|
36
|
+
status_word = SearchEngine::Logging::Color.apply('schema updated in-place (PATCH)', :green)
|
|
37
|
+
puts "Update Collection — #{status_word}"
|
|
36
38
|
else
|
|
37
|
-
|
|
39
|
+
msg = SearchEngine::Logging::Color.dim(
|
|
40
|
+
'Update Collection — in-place update not possible (no changes or incompatible)'
|
|
41
|
+
)
|
|
42
|
+
puts(msg)
|
|
38
43
|
end
|
|
39
44
|
updated
|
|
40
45
|
end
|
|
@@ -53,17 +58,19 @@ module SearchEngine
|
|
|
53
58
|
end
|
|
54
59
|
|
|
55
60
|
if physical.nil?
|
|
56
|
-
puts('Drop Collection — skip (not present)')
|
|
61
|
+
puts(SearchEngine::Logging::Color.dim('Drop Collection — skip (not present)'))
|
|
57
62
|
return
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
puts
|
|
61
|
-
|
|
62
|
-
puts(
|
|
66
|
+
header = SearchEngine::Logging::Color.header(%(>>>>>> Dropping Collection "#{logical}"))
|
|
67
|
+
puts(header)
|
|
68
|
+
status_word = SearchEngine::Logging::Color.bold('processing')
|
|
69
|
+
puts("Drop Collection — #{status_word} (logical=#{logical} physical=#{physical})")
|
|
63
70
|
# Use an extended timeout to accommodate large collection drops
|
|
64
71
|
client.delete_collection(physical, timeout_ms: 60_000)
|
|
65
|
-
puts(
|
|
66
|
-
puts(%(>>>>>> Dropped Collection "#{logical}"))
|
|
72
|
+
puts("Drop Collection — #{SearchEngine::Logging::Color.apply('done', :green)}")
|
|
73
|
+
puts(SearchEngine::Logging::Color.header(%(>>>>>> Dropped Collection "#{logical}")))
|
|
67
74
|
nil
|
|
68
75
|
end
|
|
69
76
|
|
|
@@ -80,16 +87,19 @@ module SearchEngine
|
|
|
80
87
|
end
|
|
81
88
|
|
|
82
89
|
if physical
|
|
83
|
-
|
|
90
|
+
status_word = SearchEngine::Logging::Color.apply('dropping existing', :yellow)
|
|
91
|
+
puts("Recreate Collection — #{status_word} (logical=#{logical} physical=#{physical})")
|
|
84
92
|
client.delete_collection(physical)
|
|
85
93
|
else
|
|
86
|
-
|
|
94
|
+
msg = SearchEngine::Logging::Color.dim('Recreate Collection — no existing collection (skip drop)')
|
|
95
|
+
puts(msg)
|
|
87
96
|
end
|
|
88
97
|
|
|
89
98
|
schema = SearchEngine::Schema.compile(self)
|
|
90
|
-
|
|
99
|
+
status_word = SearchEngine::Logging::Color.bold('creating collection with schema')
|
|
100
|
+
puts("Recreate Collection — #{status_word} (logical=#{logical})")
|
|
91
101
|
client.create_collection(schema)
|
|
92
|
-
puts(
|
|
102
|
+
puts("Recreate Collection — #{SearchEngine::Logging::Color.apply('done', :green)}")
|
|
93
103
|
nil
|
|
94
104
|
end
|
|
95
105
|
|
|
@@ -5,6 +5,7 @@ require 'search_engine/base/index_maintenance/cleanup'
|
|
|
5
5
|
require 'search_engine/base/index_maintenance/lifecycle'
|
|
6
6
|
require 'search_engine/base/index_maintenance/schema'
|
|
7
7
|
require 'search_engine/logging/color'
|
|
8
|
+
require 'search_engine/logging/batch_line'
|
|
8
9
|
|
|
9
10
|
module SearchEngine
|
|
10
11
|
class Base
|
|
@@ -41,7 +42,10 @@ module SearchEngine
|
|
|
41
42
|
|
|
42
43
|
indent = ' ' * depth
|
|
43
44
|
puts if depth.zero?
|
|
44
|
-
|
|
45
|
+
header = SearchEngine::Logging::Color.header(
|
|
46
|
+
%(#{indent}>>>>>> Preflight Dependencies (mode: #{mode}, collection: "#{current}"))
|
|
47
|
+
)
|
|
48
|
+
puts(header)
|
|
45
49
|
|
|
46
50
|
deps.each do |cfg|
|
|
47
51
|
dep_coll = (cfg[:collection] || cfg['collection']).to_s
|
|
@@ -50,7 +54,7 @@ module SearchEngine
|
|
|
50
54
|
dep_klass = __se_resolve_dep_class(dep_coll)
|
|
51
55
|
|
|
52
56
|
if dep_klass.nil?
|
|
53
|
-
puts(%(#{indent} "#{dep_coll}" → skipped (unregistered)))
|
|
57
|
+
puts(SearchEngine::Logging::Color.dim(%(#{indent} "#{dep_coll}" → skipped (unregistered))))
|
|
54
58
|
visited.add(dep_coll)
|
|
55
59
|
next
|
|
56
60
|
end
|
|
@@ -72,7 +76,7 @@ module SearchEngine
|
|
|
72
76
|
visited.add(dep_coll)
|
|
73
77
|
end
|
|
74
78
|
|
|
75
|
-
puts(%(#{indent}>>>>>> Preflight Done (collection: "#{current}")))
|
|
79
|
+
puts(SearchEngine::Logging::Color.header(%(#{indent}>>>>>> Preflight Done (collection: "#{current}"))))
|
|
76
80
|
end
|
|
77
81
|
|
|
78
82
|
# @return [String] current collection logical name; empty string when unavailable
|
|
@@ -165,27 +169,29 @@ module SearchEngine
|
|
|
165
169
|
case mode.to_s
|
|
166
170
|
when 'ensure'
|
|
167
171
|
if missing
|
|
168
|
-
|
|
172
|
+
status_word = SearchEngine::Logging::Color.apply('ensure (missing)', :yellow)
|
|
173
|
+
puts(%(#{indent}"#{dep_coll}" → #{status_word} → index_collection))
|
|
169
174
|
# Avoid nested preflight to prevent redundant recursion cycles
|
|
170
175
|
SearchEngine::Instrumentation.with_context(bulk_suppress_cascade: true) do
|
|
171
176
|
dep_klass.index_collection(client: client)
|
|
172
177
|
end
|
|
173
178
|
else
|
|
174
|
-
puts(%(#{indent}"#{dep_coll}" → present (skip)))
|
|
179
|
+
puts(SearchEngine::Logging::Color.dim(%(#{indent}"#{dep_coll}" → present (skip))))
|
|
175
180
|
end
|
|
176
181
|
when 'index'
|
|
177
182
|
if missing || drift
|
|
178
183
|
reason = missing ? 'missing' : 'drift'
|
|
179
|
-
|
|
184
|
+
status_word = SearchEngine::Logging::Color.apply("index (#{reason})", :yellow)
|
|
185
|
+
puts(%(#{indent}"#{dep_coll}" → #{status_word} → index_collection))
|
|
180
186
|
# Avoid nested preflight to prevent redundant recursion cycles
|
|
181
187
|
SearchEngine::Instrumentation.with_context(bulk_suppress_cascade: true) do
|
|
182
188
|
dep_klass.index_collection(client: client)
|
|
183
189
|
end
|
|
184
190
|
else
|
|
185
|
-
puts(%(#{indent}"#{dep_coll}" → in_sync (skip)))
|
|
191
|
+
puts(SearchEngine::Logging::Color.dim(%(#{indent}"#{dep_coll}" → in_sync (skip))))
|
|
186
192
|
end
|
|
187
193
|
else
|
|
188
|
-
puts(%(#{indent}"#{dep_coll}" → skipped (unknown mode: #{mode})))
|
|
194
|
+
puts(SearchEngine::Logging::Color.dim(%(#{indent}"#{dep_coll}" → skipped (unknown mode: #{mode}))))
|
|
189
195
|
end
|
|
190
196
|
end
|
|
191
197
|
|
|
@@ -193,61 +199,10 @@ module SearchEngine
|
|
|
193
199
|
return unless batches.is_a?(Array)
|
|
194
200
|
|
|
195
201
|
batches.each_with_index do |batch_stats, idx|
|
|
196
|
-
|
|
197
|
-
batch_status = __se_batch_status_from_stats(batch_stats)
|
|
198
|
-
status_color = SearchEngine::Logging::Color.for_status(batch_status)
|
|
199
|
-
|
|
200
|
-
prefix = batch_number == 1 ? ' single → ' : ' '
|
|
201
|
-
line = +prefix
|
|
202
|
-
line << SearchEngine::Logging::Color.apply("status=#{batch_status}", status_color) << ' '
|
|
203
|
-
docs_count = batch_stats[:docs_count] || batch_stats['docs_count'] || 0
|
|
204
|
-
line << "docs=#{docs_count}" << ' '
|
|
205
|
-
success_count = (batch_stats[:success_count] || batch_stats['success_count'] || 0).to_i
|
|
206
|
-
success_str = "success=#{success_count}"
|
|
207
|
-
line << (
|
|
208
|
-
success_count.positive? ? SearchEngine::Logging::Color.bold(success_str) : success_str
|
|
209
|
-
) << ' '
|
|
210
|
-
failed_count = (batch_stats[:failure_count] || batch_stats['failure_count'] || 0).to_i
|
|
211
|
-
failed_str = "failed=#{failed_count}"
|
|
212
|
-
line << (
|
|
213
|
-
failed_count.positive? ? SearchEngine::Logging::Color.apply(failed_str, :red) : failed_str
|
|
214
|
-
) << ' '
|
|
215
|
-
line << "batch=#{batch_number} "
|
|
216
|
-
duration_ms = batch_stats[:duration_ms] || batch_stats['duration_ms'] || 0.0
|
|
217
|
-
line << "duration_ms=#{duration_ms}"
|
|
218
|
-
|
|
219
|
-
# Extract sample error from batch stats
|
|
220
|
-
sample_err = __se_extract_batch_sample_error(batch_stats)
|
|
221
|
-
line << " sample_error=#{sample_err.inspect}" if sample_err
|
|
222
|
-
|
|
223
|
-
puts(line)
|
|
202
|
+
puts(SearchEngine::Logging::BatchLine.format(batch_stats, idx + 1, indifferent: true))
|
|
224
203
|
end
|
|
225
204
|
end
|
|
226
205
|
|
|
227
|
-
def __se_batch_status_from_stats(stats)
|
|
228
|
-
success_count = (stats[:success_count] || stats['success_count'] || 0).to_i
|
|
229
|
-
failure_count = (stats[:failure_count] || stats['failure_count'] || 0).to_i
|
|
230
|
-
|
|
231
|
-
if failure_count.positive? && success_count.positive?
|
|
232
|
-
:partial
|
|
233
|
-
elsif failure_count.positive?
|
|
234
|
-
:failed
|
|
235
|
-
else
|
|
236
|
-
:ok
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
def __se_extract_batch_sample_error(stats)
|
|
241
|
-
samples = stats[:errors_sample] || stats['errors_sample']
|
|
242
|
-
return nil unless samples.is_a?(Array) && samples.any?
|
|
243
|
-
|
|
244
|
-
samples.each do |msg|
|
|
245
|
-
s = msg.to_s
|
|
246
|
-
return s unless s.strip.empty?
|
|
247
|
-
end
|
|
248
|
-
nil
|
|
249
|
-
end
|
|
250
|
-
|
|
251
206
|
private :__se_current_collection_name,
|
|
252
207
|
:__se_fetch_joins_config,
|
|
253
208
|
:__se_belongs_to_dependencies,
|
|
@@ -257,9 +212,7 @@ module SearchEngine
|
|
|
257
212
|
:__se_diff_for,
|
|
258
213
|
:__se_dependency_status,
|
|
259
214
|
:__se_handle_preflight_action,
|
|
260
|
-
:__se_log_batches_from_summary
|
|
261
|
-
:__se_batch_status_from_stats,
|
|
262
|
-
:__se_extract_batch_sample_error
|
|
215
|
+
:__se_log_batches_from_summary
|
|
263
216
|
end
|
|
264
217
|
|
|
265
218
|
class_methods do
|
|
@@ -390,7 +343,8 @@ module SearchEngine
|
|
|
390
343
|
end
|
|
391
344
|
rescue StandardError => error
|
|
392
345
|
mtx.synchronize do
|
|
393
|
-
|
|
346
|
+
err_msg = " partition=#{part.inspect} → error=#{error.class}: #{error.message.to_s[0, 200]}"
|
|
347
|
+
warn(SearchEngine::Logging::Color.apply(err_msg, :red))
|
|
394
348
|
partition_errors << "#{error.class}: #{error.message.to_s[0, 200]}"
|
|
395
349
|
end
|
|
396
350
|
end
|
|
@@ -145,7 +145,7 @@ into: nil
|
|
|
145
145
|
# when no partitions are configured.
|
|
146
146
|
# @param ref_klass [Class]
|
|
147
147
|
# @return [void]
|
|
148
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
|
148
|
+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize
|
|
149
149
|
def __se_full_reindex_for_referrer(ref_klass, client:, alias_cache:)
|
|
150
150
|
logical = ref_klass.respond_to?(:collection) ? ref_klass.collection.to_s : ref_klass.name.to_s
|
|
151
151
|
physical = resolve_physical_collection_name(logical, client: client, cache: alias_cache)
|
|
@@ -184,12 +184,13 @@ into: nil
|
|
|
184
184
|
|
|
185
185
|
if parts.empty?
|
|
186
186
|
coll_display = physical && physical != logical ? "#{logical} (physical: #{physical})" : logical
|
|
187
|
-
puts(%( Referencer "#{coll_display}" — partitions=0 → skip))
|
|
187
|
+
puts(SearchEngine::Logging::Color.dim(%( Referencer "#{coll_display}" — partitions=0 → skip)))
|
|
188
188
|
return false
|
|
189
189
|
end
|
|
190
190
|
|
|
191
191
|
coll_display = physical && physical != logical ? "#{logical} (physical: #{physical})" : logical
|
|
192
|
-
|
|
192
|
+
parts_str = SearchEngine::Logging::Color.bold("partitions=#{parts.size}")
|
|
193
|
+
puts(%( Referencer "#{coll_display}" — #{parts_str} parallel=#{compiled.max_parallel}))
|
|
193
194
|
mp = compiled.max_parallel.to_i
|
|
194
195
|
if mp > 1 && parts.size > 1
|
|
195
196
|
require 'concurrent-ruby'
|
|
@@ -211,13 +212,13 @@ into: nil
|
|
|
211
212
|
|
|
212
213
|
else
|
|
213
214
|
coll_display = physical && physical != logical ? "#{logical} (physical: #{physical})" : logical
|
|
214
|
-
puts(%( Referencer "#{coll_display}" — single))
|
|
215
|
+
puts(%( Referencer "#{coll_display}" — #{SearchEngine::Logging::Color.bold('single')}))
|
|
215
216
|
SearchEngine::Indexer.rebuild_partition!(ref_klass, partition: nil, into: nil)
|
|
216
217
|
executed = true
|
|
217
218
|
end
|
|
218
219
|
executed
|
|
219
220
|
end
|
|
220
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
|
221
|
+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/AbcSize
|
|
221
222
|
|
|
222
223
|
# Resolve logical alias to physical name with optional per-run memoization.
|
|
223
224
|
# @param logical [String]
|
|
@@ -333,27 +334,31 @@ into: nil
|
|
|
333
334
|
def reindex_referencer_with_fresh_schema!(ref_klass, logical, physical, client:, force_rebuild: false)
|
|
334
335
|
coll_display = physical && physical != logical ? "#{logical} (physical: #{physical})" : logical
|
|
335
336
|
action = force_rebuild ? 'force_rebuild index_collection' : 'index_collection'
|
|
336
|
-
|
|
337
|
+
status_word = SearchEngine::Logging::Color.apply("schema rebuild required, running #{action}", :yellow)
|
|
338
|
+
puts(%( Referencer "#{coll_display}" — #{status_word}))
|
|
337
339
|
|
|
338
340
|
SearchEngine::Instrumentation.with_context(bulk_suppress_cascade: true) do
|
|
339
341
|
ref_klass.index_collection(client: client, pre: :ensure, force_rebuild: force_rebuild)
|
|
340
342
|
end
|
|
341
343
|
true
|
|
342
344
|
rescue StandardError => error
|
|
343
|
-
|
|
345
|
+
err_line = %( Referencer "#{logical}" — schema rebuild failed: #{error.message})
|
|
346
|
+
puts(SearchEngine::Logging::Color.apply(err_line, :red))
|
|
344
347
|
false
|
|
345
348
|
end
|
|
346
349
|
|
|
347
350
|
def reindex_referencer_with_drop!(ref_klass, logical, physical)
|
|
348
351
|
coll_display = physical && physical != logical ? "#{logical} (physical: #{physical})" : logical
|
|
349
|
-
|
|
352
|
+
status_word = SearchEngine::Logging::Color.apply('force reindex (drop+index)', :yellow)
|
|
353
|
+
puts(%( Referencer "#{coll_display}" — #{status_word}))
|
|
350
354
|
|
|
351
355
|
SearchEngine::Instrumentation.with_context(bulk_suppress_cascade: true) do
|
|
352
356
|
ref_klass.reindex_collection!
|
|
353
357
|
end
|
|
354
358
|
true
|
|
355
359
|
rescue StandardError => error
|
|
356
|
-
|
|
360
|
+
err_line = %( Referencer "#{logical}" — force reindex failed: #{error.message})
|
|
361
|
+
puts(SearchEngine::Logging::Color.apply(err_line, :red))
|
|
357
362
|
false
|
|
358
363
|
end
|
|
359
364
|
|
|
@@ -553,10 +553,8 @@ module SearchEngine
|
|
|
553
553
|
started = monotonic_ms
|
|
554
554
|
callable.call
|
|
555
555
|
rescue StandardError => error
|
|
556
|
-
puts "error: #{error.
|
|
557
|
-
puts
|
|
558
|
-
puts '--------------------------------'
|
|
559
|
-
puts '--------------------------------'
|
|
556
|
+
puts SearchEngine::Cli::Support.fmt_err("#{error.class}: #{error.message}")
|
|
557
|
+
puts SearchEngine::Cli::Support.fmt_err(error.backtrace.first(5).join("\n"))
|
|
560
558
|
failure(callable.name, started, error, hint: 'Unexpected error', doc: nil)
|
|
561
559
|
end
|
|
562
560
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'search_engine/logging/color'
|
|
4
|
+
require 'search_engine/logging/batch_line'
|
|
4
5
|
|
|
5
6
|
module SearchEngine
|
|
6
7
|
class Indexer
|
|
@@ -141,7 +142,7 @@ module SearchEngine
|
|
|
141
142
|
shared_state = initialize_shared_state
|
|
142
143
|
producer_error = nil
|
|
143
144
|
|
|
144
|
-
puts(' Starting parallel batch processing...') if log_batches
|
|
145
|
+
puts(SearchEngine::Logging::Color.dim(' Starting parallel batch processing...')) if log_batches
|
|
145
146
|
started_at = monotonic_ms
|
|
146
147
|
|
|
147
148
|
# Producer thread: fetch batches lazily and push to queue
|
|
@@ -154,15 +155,17 @@ module SearchEngine
|
|
|
154
155
|
next unless log_batches && (batch_count % 10).zero?
|
|
155
156
|
|
|
156
157
|
elapsed = (monotonic_ms - started_at).round(1)
|
|
157
|
-
if total_batches_estimate
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
158
|
+
progress = if total_batches_estimate
|
|
159
|
+
" Processed #{batch_count}/#{total_batches_estimate} batches... (#{elapsed}ms)"
|
|
160
|
+
else
|
|
161
|
+
" Processed #{batch_count} batches... (#{elapsed}ms)"
|
|
162
|
+
end
|
|
163
|
+
puts(SearchEngine::Logging::Color.dim(progress))
|
|
162
164
|
end
|
|
163
165
|
rescue StandardError => error
|
|
164
166
|
producer_error = error
|
|
165
|
-
|
|
167
|
+
err_msg = " Producer failed at batch #{batch_count}: #{error.class}: #{error.message.to_s[0, 200]}"
|
|
168
|
+
warn(SearchEngine::Logging::Color.apply(err_msg, :red))
|
|
166
169
|
ensure
|
|
167
170
|
# Signal completion to all workers
|
|
168
171
|
max_parallel.times { batch_queue.push(sentinel) }
|
|
@@ -299,7 +302,8 @@ module SearchEngine
|
|
|
299
302
|
failure_stat = failure_stats(thread_idx, docs_count, 0, error)
|
|
300
303
|
|
|
301
304
|
shared_state[:mtx].synchronize do
|
|
302
|
-
|
|
305
|
+
err_msg = " batch_index=#{thread_idx} → error=#{error.class}: #{error.message.to_s[0, 200]}"
|
|
306
|
+
warn(SearchEngine::Logging::Color.apply(err_msg, :red))
|
|
303
307
|
aggregate_stats([failure_stat], shared_state, batch_size, log_batches)
|
|
304
308
|
end
|
|
305
309
|
end
|
|
@@ -572,53 +576,7 @@ module SearchEngine
|
|
|
572
576
|
end
|
|
573
577
|
|
|
574
578
|
def log_batch(stats, batch_number)
|
|
575
|
-
|
|
576
|
-
status_color = SearchEngine::Logging::Color.for_status(batch_status)
|
|
577
|
-
|
|
578
|
-
prefix = batch_number == 1 ? ' single → ' : ' '
|
|
579
|
-
line = +prefix
|
|
580
|
-
line << SearchEngine::Logging::Color.apply("status=#{batch_status}", status_color) << ' '
|
|
581
|
-
line << "docs=#{stats[:docs_count]}" << ' '
|
|
582
|
-
success_count = stats[:success_count].to_i
|
|
583
|
-
success_str = "success=#{success_count}"
|
|
584
|
-
line << (
|
|
585
|
-
success_count.positive? ? SearchEngine::Logging::Color.bold(success_str) : success_str
|
|
586
|
-
) << ' '
|
|
587
|
-
failed_count = stats[:failure_count].to_i
|
|
588
|
-
failed_str = "failed=#{failed_count}"
|
|
589
|
-
line << (failed_count.positive? ? SearchEngine::Logging::Color.apply(failed_str, :red) : failed_str) << ' '
|
|
590
|
-
line << "batch=#{batch_number} "
|
|
591
|
-
line << "duration_ms=#{stats[:duration_ms]}"
|
|
592
|
-
|
|
593
|
-
# Extract sample error from batch stats
|
|
594
|
-
sample_err = extract_batch_sample_error(stats)
|
|
595
|
-
line << " sample_error=#{sample_err.inspect}" if sample_err
|
|
596
|
-
|
|
597
|
-
puts(line)
|
|
598
|
-
end
|
|
599
|
-
|
|
600
|
-
def extract_batch_sample_error(stats)
|
|
601
|
-
samples = stats[:errors_sample] || stats['errors_sample']
|
|
602
|
-
return nil unless samples.is_a?(Array) && samples.any?
|
|
603
|
-
|
|
604
|
-
samples.each do |msg|
|
|
605
|
-
s = msg.to_s
|
|
606
|
-
return s unless s.strip.empty?
|
|
607
|
-
end
|
|
608
|
-
nil
|
|
609
|
-
end
|
|
610
|
-
|
|
611
|
-
def batch_status_from_stats(stats)
|
|
612
|
-
success_count = stats[:success_count].to_i
|
|
613
|
-
failure_count = stats[:failure_count].to_i
|
|
614
|
-
|
|
615
|
-
if failure_count.positive? && success_count.positive?
|
|
616
|
-
:partial
|
|
617
|
-
elsif failure_count.positive?
|
|
618
|
-
:failed
|
|
619
|
-
else
|
|
620
|
-
:ok
|
|
621
|
-
end
|
|
579
|
+
puts(SearchEngine::Logging::BatchLine.format(stats, batch_number))
|
|
622
580
|
end
|
|
623
581
|
end
|
|
624
582
|
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SearchEngine
|
|
4
|
+
module Logging
|
|
5
|
+
# Shared formatter for per-batch log lines during indexation.
|
|
6
|
+
#
|
|
7
|
+
# Produces the aligned `single →` / continuation lines used by both
|
|
8
|
+
# {SearchEngine::Base::IndexMaintenance} and {SearchEngine::Indexer::BulkImport}.
|
|
9
|
+
#
|
|
10
|
+
# @since M8
|
|
11
|
+
module BatchLine
|
|
12
|
+
FIRST_PREFIX = ' single → '
|
|
13
|
+
CONTINUATION_PREFIX = ' '
|
|
14
|
+
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# Format a single batch log line.
|
|
18
|
+
#
|
|
19
|
+
# @param stats [Hash] batch statistics with :docs_count, :success_count, :failure_count, :duration_ms, :errors_sample
|
|
20
|
+
# @param batch_number [Integer] 1-based batch index
|
|
21
|
+
# @param indifferent [Boolean] when true, also check string keys (for summary hashes)
|
|
22
|
+
# @return [String]
|
|
23
|
+
def format(stats, batch_number, indifferent: false)
|
|
24
|
+
require 'search_engine/logging/color'
|
|
25
|
+
|
|
26
|
+
batch_status = status_from_counts(stats, indifferent: indifferent)
|
|
27
|
+
status_color = SearchEngine::Logging::Color.for_status(batch_status)
|
|
28
|
+
|
|
29
|
+
prefix = batch_number == 1 ? FIRST_PREFIX : CONTINUATION_PREFIX
|
|
30
|
+
line = +prefix
|
|
31
|
+
line << SearchEngine::Logging::Color.apply("status=#{batch_status}", status_color) << ' '
|
|
32
|
+
line << "docs=#{fetch_stat(stats, :docs_count, indifferent: indifferent, default: 0)}" << ' '
|
|
33
|
+
|
|
34
|
+
success_count = fetch_stat(stats, :success_count, indifferent: indifferent, default: 0).to_i
|
|
35
|
+
success_str = "success=#{success_count}"
|
|
36
|
+
line << (success_count.positive? ? SearchEngine::Logging::Color.bold(success_str) : success_str) << ' '
|
|
37
|
+
|
|
38
|
+
failed_count = fetch_stat(stats, :failure_count, indifferent: indifferent, default: 0).to_i
|
|
39
|
+
failed_str = "failed=#{failed_count}"
|
|
40
|
+
line << (failed_count.positive? ? SearchEngine::Logging::Color.apply(failed_str, :red) : failed_str) << ' '
|
|
41
|
+
|
|
42
|
+
line << "batch=#{batch_number} "
|
|
43
|
+
line << "duration_ms=#{fetch_stat(stats, :duration_ms, indifferent: indifferent, default: 0.0)}"
|
|
44
|
+
|
|
45
|
+
sample_err = extract_sample_error(stats, indifferent: indifferent)
|
|
46
|
+
line << " sample_error=#{sample_err.inspect}" if sample_err
|
|
47
|
+
|
|
48
|
+
line
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Derive batch status from success/failure counts.
|
|
52
|
+
# @return [Symbol] :ok, :partial, or :failed
|
|
53
|
+
def status_from_counts(stats, indifferent: false)
|
|
54
|
+
success = fetch_stat(stats, :success_count, indifferent: indifferent, default: 0).to_i
|
|
55
|
+
failure = fetch_stat(stats, :failure_count, indifferent: indifferent, default: 0).to_i
|
|
56
|
+
|
|
57
|
+
if failure.positive? && success.positive?
|
|
58
|
+
:partial
|
|
59
|
+
elsif failure.positive?
|
|
60
|
+
:failed
|
|
61
|
+
else
|
|
62
|
+
:ok
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Extract first non-blank sample error message.
|
|
67
|
+
# @return [String, nil]
|
|
68
|
+
def extract_sample_error(stats, indifferent: false)
|
|
69
|
+
samples = fetch_stat(stats, :errors_sample, indifferent: indifferent, default: nil)
|
|
70
|
+
return nil unless samples.is_a?(Array) && samples.any?
|
|
71
|
+
|
|
72
|
+
samples.each do |msg|
|
|
73
|
+
s = msg.to_s
|
|
74
|
+
return s unless s.strip.empty?
|
|
75
|
+
end
|
|
76
|
+
nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @api private
|
|
80
|
+
def fetch_stat(stats, key, indifferent: false, default: nil)
|
|
81
|
+
val = stats[key]
|
|
82
|
+
val = stats[key.to_s] if val.nil? && indifferent
|
|
83
|
+
val.nil? ? default : val
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private_class_method :fetch_stat
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -12,7 +12,7 @@ module SearchEngine
|
|
|
12
12
|
module_function
|
|
13
13
|
|
|
14
14
|
# @param str [String]
|
|
15
|
-
# @param color [Symbol] one of :green, :yellow, :red
|
|
15
|
+
# @param color [Symbol] one of :green, :yellow, :red, :cyan
|
|
16
16
|
# @return [String]
|
|
17
17
|
def apply(str, color)
|
|
18
18
|
return str unless enabled?
|
|
@@ -21,6 +21,7 @@ module SearchEngine
|
|
|
21
21
|
when :green then 32
|
|
22
22
|
when :yellow then 33
|
|
23
23
|
when :red then 31
|
|
24
|
+
when :cyan then 36
|
|
24
25
|
else 0
|
|
25
26
|
end
|
|
26
27
|
return str if code.zero?
|
|
@@ -37,6 +38,22 @@ module SearchEngine
|
|
|
37
38
|
"\e[1m#{str}\e[0m"
|
|
38
39
|
end
|
|
39
40
|
|
|
41
|
+
# Apply dim styling to de-emphasize text (skip/no-op states).
|
|
42
|
+
# @param str [String]
|
|
43
|
+
# @return [String]
|
|
44
|
+
def dim(str)
|
|
45
|
+
return str unless enabled?
|
|
46
|
+
|
|
47
|
+
"\e[2m#{str}\e[0m"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Bold text for section headers (>>>>>> lines).
|
|
51
|
+
# @param str [String]
|
|
52
|
+
# @return [String]
|
|
53
|
+
def header(str)
|
|
54
|
+
bold(str)
|
|
55
|
+
end
|
|
56
|
+
|
|
40
57
|
# Map indexation status to a color.
|
|
41
58
|
# @param status [#to_s]
|
|
42
59
|
# @return [Symbol] color name
|
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.6.
|
|
4
|
+
version: 30.1.6.4
|
|
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-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|
|
@@ -153,6 +153,7 @@ files:
|
|
|
153
153
|
- lib/search_engine/instrumentation.rb
|
|
154
154
|
- lib/search_engine/joins/guard.rb
|
|
155
155
|
- lib/search_engine/joins/resolver.rb
|
|
156
|
+
- lib/search_engine/logging/batch_line.rb
|
|
156
157
|
- lib/search_engine/logging/color.rb
|
|
157
158
|
- lib/search_engine/logging/format_helpers.rb
|
|
158
159
|
- lib/search_engine/logging/partition_progress.rb
|