wp2txt 2.3.1 → 2.3.3
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/.dockerignore +3 -0
- data/.github/workflows/publish-image.yml +140 -0
- data/.gitignore +1 -0
- data/CHANGELOG.md +92 -281
- data/DEVELOPMENT.md +20 -4
- data/DEVELOPMENT_ja.md +9 -3
- data/README.md +13 -14
- data/README_ja.md +8 -8
- data/Rakefile +23 -17
- data/bin/wp2txt +4 -2
- data/bin/wp2txt-mcp +4 -3
- data/docs/{RESEARCH.md → INDEXES.md} +69 -64
- data/lib/wp2txt/constants.rb +5 -0
- data/lib/wp2txt/corpus.rb +96 -79
- data/lib/wp2txt/corpus_jobs.rb +15 -13
- data/lib/wp2txt/fts_index.rb +12 -3
- data/lib/wp2txt/metadata_index.rb +3 -3
- data/lib/wp2txt/output_path.rb +72 -12
- data/lib/wp2txt/stream_processor.rb +19 -9
- data/lib/wp2txt/version.rb +1 -1
- data/spec/docs_sync_spec.rb +27 -5
- data/spec/fts_index_spec.rb +40 -0
- data/spec/p1_correctness_spec.rb +357 -0
- data/spec/stream_processor_spec.rb +3 -1
- data/spec/titles_output_path_spec.rb +49 -1
- data/wp2txt.gemspec +1 -1
- metadata +5 -10
- data/image/wp2txt-logo.svg +0 -16
- data/image/wp2txt.svg +0 -31
- data/scripts/benchmark_regex.rb +0 -161
- data/scripts/fetch_html_entities.rb +0 -94
- data/scripts/fetch_language_metadata.rb +0 -180
- data/scripts/fetch_mediawiki_data.rb +0 -334
- data/scripts/fetch_template_data.rb +0 -186
- data/scripts/profile_memory.rb +0 -139
data/README.md
CHANGED
|
@@ -84,7 +84,7 @@ docker run -it -v /path/to/localdata:/data ghcr.io/yohasebe/wp2txt
|
|
|
84
84
|
|
|
85
85
|
The `wp2txt` command is available inside the container. Use `/data` for input/output files.
|
|
86
86
|
|
|
87
|
-
Images are published to GitHub Container Registry (`ghcr.io/yohasebe/wp2txt`). The former Docker Hub repository
|
|
87
|
+
Images are published to GitHub Container Registry (`ghcr.io/yohasebe/wp2txt`). The former Docker Hub repository has been removed — pull from GHCR.
|
|
88
88
|
|
|
89
89
|
**MCP server (no Ruby required on the host):**
|
|
90
90
|
|
|
@@ -255,12 +255,12 @@ By default, citation templates are removed. Use `--extract-citations` to extract
|
|
|
255
255
|
|
|
256
256
|
Supported: `{{cite book}}`, `{{cite web}}`, `{{cite news}}`, `{{cite journal}}`, `{{Citation}}`, etc.
|
|
257
257
|
|
|
258
|
-
##
|
|
258
|
+
## Offline Indexes, Queries, and the MCP Server
|
|
259
259
|
|
|
260
|
-
Beyond text extraction, wp2txt can turn a dump into a **local, version-pinned
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
Beyond text extraction, wp2txt can turn a dump into a **local, version-pinned database**:
|
|
261
|
+
SQLite indexes over categories, section headings, redirects, and (optionally) the full
|
|
262
|
+
article text, plus interlanguage links for cross-edition comparison — all queryable
|
|
263
|
+
offline, and usable from an LLM client via an MCP server.
|
|
264
264
|
|
|
265
265
|
```console
|
|
266
266
|
$ wp2txt --build-index --fulltext --lang=ja # build the indexes
|
|
@@ -270,13 +270,12 @@ $ wp2txt --import-langlinks -L ja --langlinks-langs en,de,fr,zh,ko
|
|
|
270
270
|
$ wp2txt-mcp --lang=ja # stdio MCP server for LLM agents
|
|
271
271
|
```
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
These queries scan every article — `0 matches` means the term is absent from that dump
|
|
274
|
+
version — and extractions record the dump version and query in a `.meta.json` sidecar,
|
|
275
|
+
so results can be reproduced later.
|
|
276
276
|
|
|
277
|
-
**→ See
|
|
278
|
-
|
|
279
|
-
MCP tool list, and design principles.
|
|
277
|
+
**→ See [docs/INDEXES.md](docs/INDEXES.md)** for index building, offline queries,
|
|
278
|
+
full-text search, interlanguage links, cross-language SQL, and the full MCP tool list.
|
|
280
279
|
|
|
281
280
|
## Command Line Options
|
|
282
281
|
|
|
@@ -354,11 +353,11 @@ MCP tool list, and design principles.
|
|
|
354
353
|
### Research infrastructure options
|
|
355
354
|
|
|
356
355
|
--build-index Build the metadata index (add --fulltext for FTS)
|
|
357
|
-
--find-articles / --search
|
|
356
|
+
--find-articles / --search Offline queries over a whole edition (see docs/INDEXES.md)
|
|
358
357
|
--import-langlinks Import interlanguage links (version-matched)
|
|
359
358
|
--fts-optimize Optimize an existing full-text index
|
|
360
359
|
|
|
361
|
-
See
|
|
360
|
+
See [docs/INDEXES.md](docs/INDEXES.md) for details.
|
|
362
361
|
|
|
363
362
|
## Configuration File
|
|
364
363
|
|
data/README_ja.md
CHANGED
|
@@ -80,7 +80,7 @@ docker run -it -v /path/to/localdata:/data ghcr.io/yohasebe/wp2txt
|
|
|
80
80
|
|
|
81
81
|
`wp2txt`コマンドはコンテナ内で使用可能です。入出力には`/data`ディレクトリを使用してください。
|
|
82
82
|
|
|
83
|
-
イメージはGitHub Container Registry(`ghcr.io/yohasebe/wp2txt`)で公開されています。旧 Docker Hub
|
|
83
|
+
イメージはGitHub Container Registry(`ghcr.io/yohasebe/wp2txt`)で公開されています。旧 Docker Hub リポジトリは削除済みです — GHCR から pull してください。
|
|
84
84
|
|
|
85
85
|
## 基本的な使い方
|
|
86
86
|
|
|
@@ -333,9 +333,9 @@ defaults:
|
|
|
333
333
|
|
|
334
334
|
コマンドラインオプションは設定ファイルの設定を上書きします。
|
|
335
335
|
|
|
336
|
-
##
|
|
336
|
+
## オフライン索引・クエリ・MCPサーバー
|
|
337
337
|
|
|
338
|
-
テキスト抽出に加えて、wp2txt
|
|
338
|
+
テキスト抽出に加えて、wp2txtはダンプを**ローカルな版固定データベース**に変換できます。
|
|
339
339
|
カテゴリ・節見出し・リダイレクト・(オプションで)記事全文のSQLite索引、言語版横断比較のための
|
|
340
340
|
言語間リンク(langlinks)— すべてオフラインで悉皆的にクエリでき、MCPサーバー経由で
|
|
341
341
|
LLMエージェントにも公開できます。
|
|
@@ -348,12 +348,12 @@ $ wp2txt --import-langlinks -L ja --langlinks-langs en,de,fr,zh,ko
|
|
|
348
348
|
$ wp2txt-mcp --lang=ja # LLMエージェント向けMCPサーバー
|
|
349
349
|
```
|
|
350
350
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
351
|
+
これらのクエリは全記事を走査します(`0件` は当該ダンプ版にその語が存在しないことを
|
|
352
|
+
意味します)。抽出結果には dump 版とクエリを記録した `.meta.json` サイドカーが付き、
|
|
353
|
+
あとから再現できます。
|
|
354
354
|
|
|
355
|
-
**→ 詳細は [
|
|
356
|
-
|
|
355
|
+
**→ 詳細は [docs/INDEXES.md](docs/INDEXES.md)(英語)を参照**:
|
|
356
|
+
索引構築、オフラインクエリ、全文検索、言語間リンク、言語版横断SQL、MCPツール一覧。
|
|
357
357
|
|
|
358
358
|
## パフォーマンス
|
|
359
359
|
|
data/Rakefile
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
|
+
require "open3"
|
|
4
5
|
require "rspec/core"
|
|
5
6
|
require "rspec/core/rake_task"
|
|
6
7
|
require_relative "./lib/wp2txt/version"
|
|
@@ -28,20 +29,25 @@ end
|
|
|
28
29
|
|
|
29
30
|
Rake::Task["build"].enhance([:normalize_permissions])
|
|
30
31
|
|
|
32
|
+
# Pre-release gate: verify the built gem's payload against spec.files and scan
|
|
33
|
+
# it for names, content, and modes that must never ship (code-security protocol).
|
|
34
|
+
Rake::Task["build"].enhance { sh "ruby", "scripts/verify_gem.rb" }
|
|
35
|
+
|
|
31
36
|
# =============================================================================
|
|
32
37
|
# Docker
|
|
33
38
|
# =============================================================================
|
|
34
39
|
|
|
35
40
|
# Paths that must never reach a published image. The image is built from the
|
|
36
41
|
# working tree, so anything ignored locally (private notes, scratch files)
|
|
37
|
-
# would otherwise ride along
|
|
38
|
-
IMAGE_FORBIDDEN_PATHS = %w[/wp2txt/research-notes /wp2txt/tmp /wp2txt/.git /wp2txt/CLAUDE.md /wp2txt/.claude].freeze
|
|
42
|
+
# would otherwise ride along.
|
|
43
|
+
IMAGE_FORBIDDEN_PATHS = %w[/wp2txt/research-notes /wp2txt/tmp /wp2txt/.git /wp2txt/CLAUDE.md /wp2txt/.claude /wp2txt/.private-doc-tokens].freeze
|
|
39
44
|
|
|
40
45
|
desc "Verify a built image contains no private material (run before pushing)"
|
|
41
46
|
task :verify_image, [:tag] do |_t, args|
|
|
42
47
|
tag = args[:tag] || "wp2txt-verify:local"
|
|
43
48
|
checks = IMAGE_FORBIDDEN_PATHS.map { |p| "test -e #{p} && echo LEAK:#{p}" }.join("; ")
|
|
44
|
-
out =
|
|
49
|
+
out, status = Open3.capture2e("docker", "run", "--rm", tag, "sh", "-c", "#{checks}; true")
|
|
50
|
+
abort "Image verification failed for #{tag}: #{out}" unless status.success?
|
|
45
51
|
leaks = out.lines.grep(/^LEAK:/).map(&:strip)
|
|
46
52
|
abort "Image #{tag} contains private paths:\n #{leaks.join("\n ")}" unless leaks.empty?
|
|
47
53
|
|
|
@@ -54,18 +60,18 @@ task :check_image do
|
|
|
54
60
|
Rake::Task[:verify_image].invoke
|
|
55
61
|
end
|
|
56
62
|
|
|
57
|
-
desc "
|
|
58
|
-
task push
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
63
|
+
desc "Explain how images are published (they are built and pushed by CI)"
|
|
64
|
+
task :push do
|
|
65
|
+
abort <<~MESSAGE
|
|
66
|
+
Images are published by GitHub Actions, not from here.
|
|
67
|
+
|
|
68
|
+
A local build sends this working tree as the build context, so untracked
|
|
69
|
+
files ride along; a runner starts from a clean checkout, where they do not
|
|
70
|
+
exist. Push a v* tag and .github/workflows/publish-image.yml takes over:
|
|
71
|
+
|
|
72
|
+
rake release # tags and pushes (also publishes the gem)
|
|
73
|
+
|
|
74
|
+
To rehearse without publishing, run the workflow from the Actions tab with
|
|
75
|
+
"push" left off. To check a local build, run `rake check_image`.
|
|
76
|
+
MESSAGE
|
|
71
77
|
end
|
data/bin/wp2txt
CHANGED
|
@@ -217,7 +217,8 @@ class WpApp
|
|
|
217
217
|
next unless title_match
|
|
218
218
|
|
|
219
219
|
title = title_match[1]
|
|
220
|
-
next if title.nil? || title.empty?
|
|
220
|
+
next if title.nil? || title.empty?
|
|
221
|
+
next unless Wp2txt.namespace_id(page_xml[%r{<ns>(-?\d+)</ns>}, 1]).zero?
|
|
221
222
|
|
|
222
223
|
# Extract text content
|
|
223
224
|
text_match = TEXT_REGEX.match(page_xml)
|
|
@@ -609,7 +610,8 @@ class WpApp
|
|
|
609
610
|
next unless title_match
|
|
610
611
|
|
|
611
612
|
title = title_match[1]
|
|
612
|
-
next if title.nil? || title.empty?
|
|
613
|
+
next if title.nil? || title.empty?
|
|
614
|
+
next unless Wp2txt.namespace_id(page_xml[%r{<ns>(-?\d+)</ns>}, 1]).zero?
|
|
613
615
|
|
|
614
616
|
text_match = TEXT_REGEX.match(page_xml)
|
|
615
617
|
next unless text_match
|
data/bin/wp2txt-mcp
CHANGED
|
@@ -73,7 +73,7 @@ def respond(&block)
|
|
|
73
73
|
result = block.call
|
|
74
74
|
MCP::Tool::Response.new([{ type: "text", text: JSON.generate(result) }])
|
|
75
75
|
rescue ArgumentError => e
|
|
76
|
-
MCP::Tool::Response.new([{ type: "text", text: JSON.generate({ error: e.message }) }], error: true)
|
|
76
|
+
MCP::Tool::Response.new([{ type: "text", text: JSON.generate({ error: e.message, code: (e.code if e.respond_to?(:code)) }.compact) }], error: true)
|
|
77
77
|
rescue StandardError => e
|
|
78
78
|
MCP::Tool::Response.new([{ type: "text", text: JSON.generate({ error: "#{e.class}: #{e.message}" }) }], error: true)
|
|
79
79
|
end
|
|
@@ -312,7 +312,8 @@ def extract_params(output_path:, content:, titles: nil, sections: nil, alias_set
|
|
|
312
312
|
content: content, titles: titles,
|
|
313
313
|
sections: sections, alias_set: alias_set, category: category, depth: depth,
|
|
314
314
|
categories: categories, category_match: category_match,
|
|
315
|
-
title_match: title_match, limit: limit, chunk_size: chunk_size, chunk_overlap: chunk_overlap
|
|
315
|
+
title_match: title_match, limit: limit, chunk_size: chunk_size, chunk_overlap: chunk_overlap,
|
|
316
|
+
overwrite: overwrite }
|
|
316
317
|
end
|
|
317
318
|
|
|
318
319
|
server.define_tool(
|
|
@@ -341,7 +342,7 @@ end
|
|
|
341
342
|
|
|
342
343
|
server.define_tool(
|
|
343
344
|
name: "cancel_job",
|
|
344
|
-
description: "Request cancellation of a running job (takes effect at the next batch boundary;
|
|
345
|
+
description: "Request cancellation of a running job (takes effect at the next batch boundary; staged output is removed).",
|
|
345
346
|
input_schema: { properties: { job_id: { type: "string" } }, required: ["job_id"] }
|
|
346
347
|
) do |job_id:, server_context:|
|
|
347
348
|
respond { job_manager.cancel(job_id) || { error: "unknown job: #{job_id}" } }
|
|
@@ -1,40 +1,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Offline Indexes, Queries, and the MCP Server
|
|
2
2
|
|
|
3
|
-
This guide covers
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
This guide covers wp2txt's index-based features: local indexes over Wikipedia dumps,
|
|
4
|
+
offline queries across a whole edition, full-text search, interlanguage links,
|
|
5
|
+
cross-language SQL, and the MCP server for connecting an LLM client.
|
|
6
6
|
|
|
7
7
|
For plain-text extraction (the classic wp2txt), see the [README](../README.md).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
wp2txt takes the opposite approach: build local indexes over an official dump file, so that
|
|
17
|
-
every query is
|
|
18
|
-
|
|
19
|
-
- **exhaustive** — it scans every article, not search-ranked results;
|
|
20
|
-
- **version-pinned** — results are tied to one dump (e.g. `jawiki-20260701`) and
|
|
21
|
-
reproducible later;
|
|
22
|
-
- **agent-operable** — the MCP server exposes the whole layer to LLM agents, with
|
|
23
|
-
guardrails and provenance records designed for autonomous use.
|
|
9
|
+
Everything below runs against a downloaded dump file: queries cover every article of the
|
|
10
|
+
edition rather than a page of search results, results are tied to one dump (e.g.
|
|
11
|
+
`jawiki-20260701`) and can be reproduced later, and nothing goes over the network once
|
|
12
|
+
the dump is downloaded. Typical uses: "which of the 1.5M articles have a plot section",
|
|
13
|
+
"how many film articles mention X, and which ones don't", "how do the section structures
|
|
14
|
+
of the same article differ between the English and Japanese editions".
|
|
24
15
|
|
|
25
16
|
## 1. Building the indexes
|
|
26
17
|
|
|
27
18
|
```console
|
|
28
|
-
#
|
|
19
|
+
# Metadata index (categories, section headings, redirects, category hierarchy)
|
|
29
20
|
$ wp2txt --build-index --lang=ja
|
|
30
21
|
|
|
31
|
-
#
|
|
22
|
+
# Metadata index + FTS5 full-text index over the cleaned article text
|
|
32
23
|
$ wp2txt --build-index --fulltext --lang=ja
|
|
33
24
|
```
|
|
34
25
|
|
|
35
26
|
The dump is downloaded automatically if needed and everything is cached under
|
|
36
27
|
`~/.wp2txt/cache/`. Ballpark figures (Apple Silicon laptop): Japanese Wikipedia ~15 min /
|
|
37
|
-
~1.7 GB for the metadata index, ~1.5 h / ~12 GB with full text; English roughly
|
|
28
|
+
~1.7 GB for the metadata index, ~1.5 h / ~12 GB with full text; English roughly 2.5–3× the
|
|
38
29
|
time, ~10 GB / ~12 GB respectively.
|
|
39
30
|
|
|
40
31
|
The full-text tokenizer is selected per language: character trigrams for Japanese,
|
|
@@ -58,8 +49,8 @@ $ wp2txt --find-articles --in-category "Films" -D 2 -j json --limit 100 --lang=e
|
|
|
58
49
|
$ wp2txt --search "タイムループ" --in-category "映画作品" -D 3 --lang=ja
|
|
59
50
|
```
|
|
60
51
|
|
|
61
|
-
Search totals
|
|
62
|
-
that dump version —
|
|
52
|
+
Search totals count every match in the edition, so `0 matches` means the term is absent
|
|
53
|
+
from that dump version — a result you can state and re-verify later.
|
|
63
54
|
|
|
64
55
|
## 3. Interlanguage links (langlinks)
|
|
65
56
|
|
|
@@ -114,7 +105,7 @@ $ claude mcp add wp2txt -- docker run -i --rm -v wp2txt:/root/.wp2txt ghcr.io/yo
|
|
|
114
105
|
|
|
115
106
|
| Tool | Purpose |
|
|
116
107
|
|------|---------|
|
|
117
|
-
| `dump_info` | Dump identity,
|
|
108
|
+
| `dump_info` | Dump identity, installed indexes, corpus statistics, langlinks provenance |
|
|
118
109
|
| `get_article` / `get_sections` / `list_headings` / `get_categories` | Single-article access (redirect-aware) |
|
|
119
110
|
| `find_articles` | Exhaustive filtered listing (category recursion, category AND / pattern match, section headings, title match) |
|
|
120
111
|
| `category_tree` / `section_stats` | Scope exploration and heading-frequency discovery |
|
|
@@ -126,17 +117,23 @@ $ claude mcp add wp2txt -- docker run -i --rm -v wp2txt:/root/.wp2txt ghcr.io/yo
|
|
|
126
117
|
| `extract_corpus` | Filtered or explicit-title extraction to JSONL + reproducibility sidecar; optional RAG chunking |
|
|
127
118
|
| `start_extract_job` / `job_status` / `cancel_job` / `list_jobs` | Background jobs for large extractions |
|
|
128
119
|
|
|
129
|
-
###
|
|
130
|
-
|
|
131
|
-
- **
|
|
132
|
-
|
|
133
|
-
- **
|
|
134
|
-
|
|
135
|
-
- **
|
|
136
|
-
the query, and any alias sets
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
120
|
+
### What happens when your assistant uses these tools
|
|
121
|
+
|
|
122
|
+
- **Filtering and counting run over the whole dump**, and your assistant reads the result
|
|
123
|
+
rather than tallying articles itself.
|
|
124
|
+
- **Large results are written to a file**; the reply carries a summary and a short sample.
|
|
125
|
+
Your corpus lands on disk intact instead of being paraphrased through the chat.
|
|
126
|
+
- **Extractions are traceable.** Extractions and file-writing queries leave a `.meta.json`
|
|
127
|
+
next to the output recording the dump version, the query, and any alias sets used, so
|
|
128
|
+
you can reproduce or cite the result later.
|
|
129
|
+
- **The tools cannot change your data.** Queries run read-only and are stopped after 30
|
|
130
|
+
seconds, saved alias sets are re-checked before being stored, and files can only be
|
|
131
|
+
written under the server's output directory — worth knowing if you plan to let an
|
|
132
|
+
assistant work unattended.
|
|
133
|
+
Output confinement assumes a dedicated, trusted output directory: symlink paths are
|
|
134
|
+
rejected, both output and sidecar are exclusively reserved unless overwrite is requested,
|
|
135
|
+
and unique temporary files are renamed on success; concurrent replacement of parent
|
|
136
|
+
directories is outside this guarantee.
|
|
140
137
|
|
|
141
138
|
## 5. Cross-language SQL
|
|
142
139
|
|
|
@@ -157,12 +154,12 @@ query_sql(
|
|
|
157
154
|
```
|
|
158
155
|
|
|
159
156
|
Attached databases appear as `{lang}_meta` (and `{lang}_fts` when that language has a
|
|
160
|
-
full-text
|
|
161
|
-
|
|
162
|
-
|
|
157
|
+
full-text index) and share the main database's schema. You pass language codes, not paths,
|
|
158
|
+
and the SQL you write cannot attach anything itself. The response lists what was attached,
|
|
159
|
+
including each edition's dump name, and flags it when the dates differ.
|
|
163
160
|
|
|
164
|
-
|
|
165
|
-
|
|
161
|
+
This runs entirely offline against pinned dump versions, so a cross-edition comparison can
|
|
162
|
+
be re-run later and produce the same numbers.
|
|
166
163
|
|
|
167
164
|
## 6. Large results, explicit sets, and reproducibility
|
|
168
165
|
|
|
@@ -182,26 +179,34 @@ extract_corpus(titles: ["東京物語", "羅生門", ...], content: "summary",
|
|
|
182
179
|
redirect hop, and reports unmatched titles in `not_found` — closing the loop
|
|
183
180
|
*SQL decides the set → the tool materializes it → the LLM reads it*.
|
|
184
181
|
|
|
185
|
-
## 7.
|
|
186
|
-
|
|
187
|
-
Section headings vary ("Plot" vs "Synopsis"; 「あらすじ」 vs
|
|
188
|
-
no per-language dictionaries.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
|
|
182
|
+
## 7. Section alias sets
|
|
183
|
+
|
|
184
|
+
Section headings vary by article and by language ("Plot" vs "Synopsis"; 「あらすじ」 vs
|
|
185
|
+
「ストーリー」), and wp2txt ships no per-language dictionaries. Three tools manage named
|
|
186
|
+
groups of equivalent headings instead:
|
|
187
|
+
|
|
188
|
+
- `section_stats` lists the headings actually used in a scope, with counts.
|
|
189
|
+
- `section_cooccurrence` reports how often two headings appear in the same article.
|
|
190
|
+
Headings that mean the same thing rarely co-occur, so a high ratio is evidence that
|
|
191
|
+
they are *different* sections (「概要」 and 「あらすじ」 co-occur often — not synonyms).
|
|
192
|
+
- `save_alias_set` stores a named group. The group is re-checked against co-occurrence
|
|
193
|
+
before saving; a failing group is not stored and the call returns `saved: false`
|
|
194
|
+
rather than an error. Pass `force` to override, and use `list_alias_sets` to see
|
|
195
|
+
what is stored.
|
|
196
|
+
|
|
197
|
+
Queries then accept `alias_set: "name"` in place of a heading list, and extractions
|
|
198
|
+
record the group's exact contents in their `.meta.json`. The bundled `discover_aliases`
|
|
199
|
+
prompt walks an LLM client through building and saving a set.
|
|
200
|
+
|
|
201
|
+
## 8. Limitations to keep in mind
|
|
202
|
+
|
|
203
|
+
- Searches run over the **cleaned text**: content that extraction replaces with a marker
|
|
204
|
+
(`[MATH]`, `[CODE]`, `[TABLE]`, …) cannot be matched. A count is a count of the cleaned
|
|
205
|
+
text, not of the raw wikitext.
|
|
206
|
+
- Japanese, Chinese, and Korean indexes cannot match queries shorter than 3 characters.
|
|
207
|
+
Word-based languages match exact forms only — `run` does not find `running`. Plan your
|
|
208
|
+
search terms accordingly, especially when you intend to report a zero result.
|
|
209
|
+
- Categories and interlanguage links are read from the dump as editors wrote them.
|
|
210
|
+
Categories added by a template rather than written in the article text are not visible
|
|
211
|
+
to wp2txt, which can make a category look much smaller than it is on the website —
|
|
212
|
+
check against the article text if a count looks wrong.
|
data/lib/wp2txt/constants.rb
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Wp2txt
|
|
4
|
+
# Missing namespace elements use the metadata scanner's historical ns=0 default.
|
|
5
|
+
def self.namespace_id(value)
|
|
6
|
+
(value || "0").to_i
|
|
7
|
+
end
|
|
8
|
+
|
|
4
9
|
# =========================================================================
|
|
5
10
|
# Custom Exception Classes
|
|
6
11
|
# =========================================================================
|