Richken blog

Rust製の検索エンジン Tantivy は日本語でも使えた

Rust製の検索エンジン Tantivy を使って日本語検索してみた。

Tantivy ではTokenizerを自由に変更できるため、期待した結果を得ることができた。

Sonic ではTokenizerを変えたりすることができなかったため、日本語検索が上手く機能させることができず残念な思いをしたけれど、これでRust製の検索エンジンを使うことができるので嬉しい。

Tantivyの使い方

これまた、READMEで紹介されている通りに行うだけ。

とりあえず動かしてみる

cargo new(または cargo init) しても良いのだけど、面倒なので git clone する。

git clone --depth 1 https://github.com/tantivy-search/tantivy rust-search-tantivy
cd rust-search-tantivy

それから、

cargo run --example basic_search

他にもいろいろな例があるので、動かしてみると、使い方がわかる。

tantivy-cli で遊んでみる

tantivy-cli の README で紹介されている通りに実行していく。

cargo install tantivy-cli

curl -L -O http://fulmicoton.com/tantivy-files/wiki-articles-1000.json

tantivy new -i ./wikipedia-index
cat wiki-articles-1000.json | tantivy index -i ./wikipedia-index
du -sh wikipedia-index/ # この例では、index のサイズ(11MB)は元データ (10MB)とあまり変わらないみたい。
tantivy search -i wikipedia-index -q "barack obama"
# 以下のようにするとHTTPでAPIを叩いて検索できるようになる。素晴らしい。
tantivy serve -i wikipedia-index

日本語 Tokenizer

以下の2つのTokenizerが紹介されていた。

※ 💀更新されていない。こちらのISSUEによると、上手く動かないらしい。 ※ こちらの記事によれば、linderakuromoji-rs(crates.io に publish されていない) の開発を引き継いでいるとのことなので。

lindera を使うべし。

設定方法

Cargo.tomlに以下を追加して

[dependencies]
lindera-tantivy = { git = "https://github.com/ken0x0a/lindera-tantivy", branch = "chore/tantivy0.13" }
# lindera-tantivy = "1" # これだと tantivy 0.13.0 だと動かない

これがマージされてリリースされるまでは、自分のforkを使う必要あり。

Tokenizer を Schemaで設定して Index に登録する。

// 任意の名前
const TOKENIZER_KUROMOJI: &str = "ja_tokenizer";

// Schema で tokenizer を選択する。
let mut schema_builder = Schema::builder();
schema_builder.add_text_field(
    "name",
    TextOptions::default()
        .set_indexing_options(
            TextFieldIndexing::default()
                .set_tokenizer(TOKENIZER_KUROMOJI)
                .set_index_option(IndexRecordOption::WithFreqs), // IndexRecordOption も必要に応じて変える
        )
        .set_stored(), // 適宜、つけても消しても
);
let schema = schema_builder.build();


// Index に tokenizer を登録する
let index = Index::open_or_create(directory, schema)?; // `create_in_dir` などなど index を作れば何でも良い
// let index = Index::open_in_dir(&index_path)?; // ReadOnly
index
    .tokenizers()
    .register(TOKENIZER_KUROMOJI, lindera_tantivy::tokenizer::LinderaTokenizer::new("decompose", ""));

おまけ

index に登録された document 数の確認方法

let index = open_index(&index_path)?;
let meta = index.load_metas()?; // https://docs.rs/tantivy/0.13.0/tantivy/struct.Index.html#method.load_metas
let mut total_doc_count = 0;
for seg in meta.segments {
    println!(
        "{}: {:>4} {:>4}",
        seg.id().uuid_string(),
        seg.num_docs(),
        seg.num_deleted_docs()
    );
    total_doc_count += seg.num_docs();
}

println!("\nTotal {} docs", total_doc_count);

他のマルチバイト文字 について

Tantivy では日本語の他のマルチバイト文字(中国語や韓国語)を使うためのTokenizerについてもREADMEで紹介されている。