wp2txt 2.1.2 → 2.2.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/CHANGELOG.md +17 -0
- data/Gemfile +2 -0
- data/README.md +79 -1
- data/bin/wp2txt +8 -0
- data/bin/wp2txt-mcp +396 -0
- data/lib/wp2txt/cli.rb +67 -0
- data/lib/wp2txt/corpus.rb +722 -0
- data/lib/wp2txt/corpus_jobs.rb +106 -0
- data/lib/wp2txt/fts_index.rb +445 -0
- data/lib/wp2txt/index_cache.rb +17 -0
- data/lib/wp2txt/index_commands.rb +344 -0
- data/lib/wp2txt/metadata_index.rb +672 -0
- data/lib/wp2txt/multistream.rb +9 -5
- data/lib/wp2txt/version.rb +1 -1
- data/spec/corpus_spec.rb +503 -0
- data/spec/fts_index_spec.rb +245 -0
- data/spec/metadata_index_spec.rb +208 -0
- data/spec/support/multistream_fixture.rb +66 -0
- metadata +16 -1
data/lib/wp2txt/cli.rb
CHANGED
|
@@ -118,6 +118,30 @@ module Wp2txt
|
|
|
118
118
|
opt :show_matched_sections, "Include matched_sections field in JSON output (shows actual headings)",
|
|
119
119
|
default: false
|
|
120
120
|
|
|
121
|
+
# Metadata index options (offline exhaustive queries)
|
|
122
|
+
opt :build_index, "Build local metadata index (categories, sections) from a multistream dump",
|
|
123
|
+
default: false, short: :none
|
|
124
|
+
opt :find_articles, "List article titles matching index filters (requires built index)",
|
|
125
|
+
default: false, short: :none
|
|
126
|
+
opt :in_category, "Filter by category for --find-articles (recursion depth via --depth)",
|
|
127
|
+
type: String, short: :none
|
|
128
|
+
opt :has_section, "Filter by section heading for --find-articles (alias-aware)",
|
|
129
|
+
type: String, short: :none
|
|
130
|
+
opt :title_match, "Filter by title substring for --find-articles",
|
|
131
|
+
type: String, short: :none
|
|
132
|
+
opt :limit, "Maximum number of titles to output with --find-articles (0 = no limit)",
|
|
133
|
+
default: 0, type: Integer, short: :none
|
|
134
|
+
opt :fulltext, "Also build the full-text (FTS5) index with --build-index",
|
|
135
|
+
default: false, short: :none
|
|
136
|
+
opt :fts_tokenizer, "FTS tokenizer: unicode61, trigram, or porter (default: auto by language)",
|
|
137
|
+
type: String, short: :none
|
|
138
|
+
opt :search, "Full-text search query (requires --build-index --fulltext beforehand)",
|
|
139
|
+
type: String, short: :none
|
|
140
|
+
opt :skip_fts_optimize, "Skip the final optimize step of the full-text build (much faster build; queries slightly slower until --fts-optimize is run)",
|
|
141
|
+
default: false, short: :none
|
|
142
|
+
opt :fts_optimize, "Optimize an existing full-text index (merge segments; idempotent)",
|
|
143
|
+
default: false, short: :none
|
|
144
|
+
|
|
121
145
|
opt :file_size, "Approximate size (in MB) of each output file (0 for single file)",
|
|
122
146
|
default: 10, short: "-f"
|
|
123
147
|
opt :num_procs, "Number of parallel processes (auto-detected based on CPU/memory)",
|
|
@@ -294,6 +318,49 @@ module Wp2txt
|
|
|
294
318
|
if opts[:show_matched_sections] && opts[:format].to_s.downcase != "json"
|
|
295
319
|
Optimist.die "--show-matched-sections requires --format json"
|
|
296
320
|
end
|
|
321
|
+
|
|
322
|
+
# Metadata index options
|
|
323
|
+
if opts[:build_index] && opts[:find_articles]
|
|
324
|
+
Optimist.die "--build-index and --find-articles cannot be combined (build first, then query)"
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
%i[in_category has_section title_match].each do |key|
|
|
328
|
+
if opts[key] && !opts[:find_articles] && !opts[:search]
|
|
329
|
+
Optimist.die "--#{key.to_s.tr('_', '-')} requires --find-articles or --search"
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
Optimist.die :limit, "must be 0 or greater" if opts[:limit].negative?
|
|
334
|
+
|
|
335
|
+
if opts[:fulltext] && !opts[:build_index]
|
|
336
|
+
Optimist.die "--fulltext requires --build-index"
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
if opts[:fts_tokenizer] && !%w[unicode61 trigram porter].include?(opts[:fts_tokenizer])
|
|
340
|
+
Optimist.die :fts_tokenizer, "must be unicode61, trigram, or porter"
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
if opts[:skip_fts_optimize] && !opts[:fulltext]
|
|
344
|
+
Optimist.die "--skip-fts-optimize requires --build-index --fulltext"
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
if opts[:fts_optimize] && (opts[:build_index] || opts[:find_articles] || opts[:search])
|
|
348
|
+
Optimist.die "--fts-optimize is a standalone mode (cannot combine with --build-index/--find-articles/--search)"
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
if opts[:search] && (opts[:build_index] || opts[:find_articles])
|
|
352
|
+
Optimist.die "--search cannot be combined with --build-index/--find-articles"
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
if opts[:build_index] || opts[:find_articles] || opts[:search] || opts[:fts_optimize]
|
|
356
|
+
conflicts = []
|
|
357
|
+
conflicts << "--articles" if opts[:articles]
|
|
358
|
+
conflicts << "--from-category" if opts[:from_category]
|
|
359
|
+
conflicts << "--section-stats" if opts[:section_stats]
|
|
360
|
+
unless conflicts.empty?
|
|
361
|
+
Optimist.die "--build-index/--find-articles/--search cannot be combined with #{conflicts.join(', ')}"
|
|
362
|
+
end
|
|
363
|
+
end
|
|
297
364
|
end
|
|
298
365
|
|
|
299
366
|
# Parse article list from comma-separated string
|