Apache Luceneで日本語検索を手軽に試す — nlp4j-local-search 0.6.1 CLI
はじめに
全文検索を少し試してみたいだけなのに、
- Elasticsearch
- OpenSearch
- Apache Solr
- Docker
などの環境を用意するのは、少し大げさに感じることがあります。
そこで開発しているのが nlp4j-local-search です。
nlp4j-local-search は、Apache Lucene をローカル環境から直接利用するためのPythonライブラリです。
内部ではJavaとApache Luceneを利用していますが、利用者は基本的にPython側のAPIやCLIだけを使います。
Version 0.6.1 では、次のような対話型CLIも利用できるようになりました。
nlp4j-local-search --lang ja
この記事では、Wikipediaから作成した日本語のサンプルデータを使い、
インストール
↓
サンプルデータ取得
↓
ロード
↓
日本語全文検索
↓
フィールド検索
↓
日付検索
↓
集計・データ探索
までをコマンドラインだけで試してみます。
Pythonコードを書く必要はありません。
nlp4j-local-search とは
PyPI:
https://pypi.org/project/nlp4j-local-search/0.6.1/
GitHub:
https://github.com/oyahiroki/nlp4j-local-search
nlp4j-local-search の特徴は、Apache Luceneを検索サーバーとしてではなく、ローカルの検索ライブラリとして利用するところです。
イメージとしては次のようになります。
Python / CLI
|
v
nlp4j-local-search
|
v
Java
|
v
Apache Lucene
検索サーバーを別プロセスで起動する必要はありません。
そのため、
- NLPの実験
- 検索機能のプロトタイプ
- Lucene Queryの確認
- JSONLデータの探索
- 小規模な検索アプリケーション
- Jupyter / Colabでの実験
などに向いています。
もちろん、Elasticsearch、OpenSearch、Solrにはそれぞれ強力な機能があります。
nlp4j-local-search はそれらを置き換えるものというより、
「Apache Luceneをもっと小さな単位で、手軽に試したい」
というケースを狙っています。
1. インストール
今回は Version 0.6.1 を使用します。
pip install -q nlp4j-local-search==0.6.1
インストールできたら、
nlp4j-local-search --help
を実行してみます。
2. 日本語のサンプルデータを取得する
今回使用するサンプルデータはこちらです。
https://nlp4j-2.sakura.ne.jp/data/wiki/jawiki-20260801-pages-articles_compact_manga.jsonl.gz
Wikipedia日本語版のdumpをもとに作成した、漫画関連の記事を収録したJSONLデータです。
gzip圧縮されたまま利用できます。
Linux、macOS、WSLなどであれば、例えば wget で取得できます。
wget https://nlp4j-2.sakura.ne.jp/data/wiki/jawiki-20260801-pages-articles_compact_manga.jsonl.gz
curlなら、
curl -O https://nlp4j-2.sakura.ne.jp/data/wiki/jawiki-20260801-pages-articles_compact_manga.jsonl.gz
でも構いません。
展開する必要はありません。
jawiki-20260801-pages-articles_compact_manga.jsonl.gz
をそのまま nlp4j-local-search に渡せます。
データは以下のようなものになっています。
{"id":"222","title_s":"日本の漫画家","text_ja":"日本の漫画家では、日本における漫画家について解説する。","timestamp_dt":"2026-05-18T19:39:59Z","category_s":["日本の漫画家"]}
{"id":"224","title_s":"日本の漫画作品一覧","text_ja":"日本の漫画作品一覧は、ウィキペディア日本語版に記事の存在する日本の漫画作品の五十音順の一覧である。","timestamp_dt":"2023-10-09T14:23:39Z","category_s":["漫画作品一覧","日本の漫画"]}
{"id":"225","title_s":"うる星やつら","text_ja":"『うる星やつら』は、高橋留美子による日本の漫画。『週刊少年サンデー』において、1978年39号から1987年8号まで連載された。略称は「うる星」。第26回小学館漫画賞少年少女部門受賞作。2020年11月時点で累計発行部数は3500万部を突破している。","timestamp_dt":"2026-06-01T20:04:48Z","category_s":["うる星やつら","漫画作品 う","高橋留美子の漫画作品","1978年の漫画","週刊少年サンデーの漫画作品","小学館漫画賞少年少女部門の受賞作品","SF漫画作品","ギャグ漫画","恋愛漫画","ボーイ・ミーツ・ガール作品","鬼を題材とした漫画作品","地球外生命体を題材とした漫画作品","高等学校を舞台とした漫画作品","漫画のキャラクターゲーム","星雲賞受賞作品","日本で開発されたコンピュータゲーム"]}
3. CLIを起動する
日本語検索を行うため、--lang ja を指定します。
nlp4j-local-search --lang ja
すると、次のような対話モードになります。
nlp4j-local-search
Language: ja
Auto analyze: False
Type 'help' or '?' for help.
>>
--lang は必須です。
今回は日本語なので、
--lang ja
を指定しています。
4. helpを表示する
まず、
>> help
または、
>> ?
と入力すると、利用可能なコマンドを確認できます。
主なコマンドは次の通りです。
load(...)
search(...)
fields
aggregatable_fields
count
view(...)
help
exit
Pythonの関数呼び出しに近い見た目にしてあります。
5. Wikipediaデータをロードする
ダウンロードしたファイルを読み込みます。
>> load("jawiki-20260801-pages-articles_compact_manga.jsonl.gz")
.jsonl.gz は直接読み込めるため、事前に展開する必要はありません。
ロードが完了すると、例えば次のように表示されます。
Loaded 30,956 documents in 4.21 seconds (7,353 docs/sec).
件数や時間は環境によって異なります。
ここまでで、JSONLの文書がApache Luceneのインデックスに登録され、検索可能になります。
6. 日本語で検索してみる
まず普通に日本語を入力してみます。
>> search("高橋留美子")
検索結果にはLuceneのscoreと本文が表示されます。
[... ] score=...
『...』は、高橋留美子による日本の漫画。...
[... ] score=...
...
結果内容やscoreはデータやバージョンによって変わる可能性があります。
検索件数を指定することもできます。
>> search("高橋留美子", 5)
これで上位5件を取得します。
7. 日本語検索について
nlp4j-local-search は、日本語の全文検索に対応しています。
単なる、
"京都" in text
のような文字列部分一致とは異なり、LuceneのAnalyzerを利用した全文検索を行います。
例えば日本語では、
東京都
と、
京都
のように文字列の一部が偶然重なるケースがあります。
全文検索では、こうした文字単位の単純な部分一致ではなく、検索用に解析された語を利用できます。
日本語文書をLuceneで検索したい場合、この違いは重要です。
8. Lucene Query Syntaxを使う
CLIの search() にはLucene Query Parser形式のクエリを指定できます。
つまり、単純なキーワード検索だけでなく、
- フィールド指定
- AND / OR
- フレーズ検索
- 範囲検索
なども利用できます。
フレーズ検索
例えば、
週刊少年サンデー
をフレーズとして検索してみます。
>> search('text_ja:"週刊少年サンデー"', 5)
text_ja は日本語テキスト用のフィールドです。
9. 特定のフィールドを検索する
今回のデータには、例えば次のようなフィールドがあります。
id
title_s
text_ja
category_s
timestamp_dt
フィールド一覧は、
>> fields
で確認できます。
例えばカテゴリが、
恋愛漫画
である文書を検索する場合、
>> search("category_s:恋愛漫画", 10)
と書けます。
通常の全文検索とフィールド検索を組み合わせることもできます。
>> search('text_ja:"高橋留美子" AND category_s:恋愛漫画', 10)
Lucene Query Syntaxをそのまま試せるので、Luceneを学習するときにも便利です。
10. AND検索を試す
例えば、
高橋留美子
と、
恋愛漫画
という条件を組み合わせる場合、
>> search('text_ja:"高橋留美子" AND category_s:恋愛漫画', 10)
と指定できます。
このように、
全文検索
+
構造化フィールド
を一つのクエリにまとめられます。
11. 日付で検索する
今回のWikipediaデータには更新日時も含めています。
例えば、
timestamp_dt
というフィールドがあります。
2026年以降の文書を探す場合、
>> search("timestamp_dt:[2026-01-01 TO *]", 10)
と指定できます。
これはLuceneのRange Queryです。
[2026-01-01 TO *]
は、
2026-01-01 以上
を意味します。
年だけで検索したい場合には、派生フィールドも利用できます。
例えば、
timestamp_year_i
に対して、
>> search("timestamp_year_i:[2020 TO 2026]", 20)
のような検索も可能です。
12. 文書数を確認する
現在ロードされている文書数は、
>> count
で確認できます。
例えば、
30,956
のように表示されます。
13. 集計可能なフィールドを見る
次のコマンドを実行してみます。
>> aggregatable_fields
これは、集計や view() に利用できるフィールドの一覧を表示します。
例えば、
category_s
title_s
timestamp_year_i
timestamp_month_i
...
などを確認できます。
14. view() でデータの中身を眺める
検索だけでなく、データセットの中にどのような値が多いのかを見ることもできます。
例えば、
>> view("category_s", 20)
とすると、category_s の上位20件を表示できます。
例えば、
Rank Value Count
---- ------------------- --------
1 日本の漫画家 7007
2 存命人物 7005
3 生年未記載 2644
4 継続中の作品 1857
5 恋愛漫画 1639
...
のようなイメージです。
単に検索結果を見るだけではなく、
このデータセットには、どんなカテゴリが多いのか?
を簡単に確認できます。
15. 件数の少ない値を除外する
view() の3番目の引数には最低出現件数を指定できます。
>> view("category_s", 20, 3)
この場合、3文書以上存在する値だけが対象になります。
大量のカテゴリを含むデータでは便利です。
16. 検索結果の特徴を view() で見る
view() にはLucene Queryを渡すこともできます。
例えば、
>> view("category_s", 'text_ja:"高橋留美子"', 20, 3)
とします。
これは、
-
text_ja:"高橋留美子"で文書を検索 - その検索結果に含まれる
category_sを集計 - 全文書との比率を比較
という処理になります。
単なる検索では、
どの文書がヒットしたか
を確認します。
一方 view() を使うと、
その検索結果には、どのようなカテゴリが特徴的なのか
を見ることができます。
全文検索と簡単なデータ分析を同じインデックス上で行えるのは、Luceneをテキスト分析用途で使う面白さの一つだと思います。
17. ファイル名はTab補完できる
load() ではファイル名のTab補完も利用できます。
例えば、
>> load("jaw<Tab>
と入力すると、候補が一意であれば、
>> load("jawiki-20260801-pages-articles_compact_manga.jsonl.gz")
のように補完できます。
少し長いデータファイル名を入力する場合に便利です。
18. 終了する
CLIを終了する場合は、
>> exit
または、
>> quit
を入力します。
>> exit
bye
19. 一連の操作をまとめる
今回の操作をまとめると、実質これだけです。
まずインストールします。
pip install -q nlp4j-local-search==0.6.1
サンプルデータを取得します。
wget https://nlp4j-2.sakura.ne.jp/data/wiki/jawiki-20260801-pages-articles_compact_manga.jsonl.gz
CLIを起動します。
nlp4j-local-search --lang ja
データをロードします。
>> load("jawiki-20260801-pages-articles_compact_manga.jsonl.gz")
普通に検索します。
>> search("高橋留美子", 5)
フレーズ検索します。
>> search('text_ja:"週刊少年サンデー"', 5)
フィールド検索します。
>> search("category_s:恋愛漫画", 5)
条件を組み合わせます。
>> search('text_ja:"高橋留美子" AND category_s:恋愛漫画', 10)
日付で検索します。
>> search("timestamp_dt:[2026-01-01 TO *]", 10)
カテゴリを眺めます。
>> view("category_s", 20)
検索結果の特徴を確認します。
>> view("category_s", 'text_ja:"高橋留美子"', 20, 3)
終了します。
>> exit
20. なぜLuceneを直接使うのか
Apache Luceneは、長年利用されている全文検索ライブラリです。
Elasticsearch、OpenSearch、Solrなどを通してLuceneを利用した経験がある人も多いと思います。
一方、
少量のJSONLを検索したい
数万件程度のデータで全文検索を試したい
Lucene Query Syntaxを確認したい
NLP処理の途中で検索インデックスを作りたい
という用途では、検索サーバーを構築するほどではないこともあります。
そこで、
JSONL
↓
Python
↓
Apache Lucene
をできるだけ短くつなぐことを nlp4j-local-search では目指しています。
例えば今回も、
pip install
wget
nlp4j-local-search
だけで日本語WikipediaデータをLuceneで検索できます。
まとめ
今回は nlp4j-local-search 0.6.1 のCLIを使って、日本語全文検索を試してみました。
特徴をまとめると、
- Apache Luceneを利用
- 日本語全文検索に対応
- Pythonから利用可能
- CLIからも利用可能
- JSONL / JSONL.gzを直接ロード可能
- Lucene Query Syntaxを利用可能
- フィールド検索が可能
- 日付・数値のRange Queryが可能
-
view()でフィールド値の集計・探索が可能 - Elasticsearch / OpenSearch / Solr / Dockerを起動しなくても試せる
という構成になっています。
特に、
pip install
↓
wget
↓
load()
↓
search()
だけでApache Luceneによる日本語検索を試せるので、
「Luceneを使った検索を少し試してみたい」
というときの実験環境として使いやすくなってきたと思います。
今後も、検索・分析をローカル環境で手軽に試せる機能を追加していく予定です。
Links
PyPI:
https://pypi.org/project/nlp4j-local-search/0.6.1/
GitHub:
https://github.com/oyahiroki/nlp4j-local-search
Sample data:
https://nlp4j-2.sakura.ne.jp/data/wiki/jawiki-20260801-pages-articles_compact_manga.jsonl.gz