Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

mdBookを日本語検索に対応させたかった

はじめに

mdBookとはRust製のドキュメントビルダーで、Markdownのドキュメントをいい感じのHTMLなどにレンダリングできます。
Rustの公式ドキュメント・非公式ドキュメントで広く使われているのですが、現状日本語での検索ができません。
なぜ日本語で検索できないかを少し調べてみたところ、各コンポーネントは微妙に日本語対応しているものの、
全体として整合性が取れる状態にはなっていない、という感じでした。
今回、いろいろなものにパッチを当てることで、「日本語検索できるように見える」状態まで持ってくることができました。
また、完全な日本語検索に向けての課題も見えてきたので共有しておきます。

mdBookの検索

まず検索周りで使われているコンポーネントは以下のようになっています。

  • mdBook: mdBook本体
  • elasticlunr-rs: mdBookから呼んでいるelasticlunr.js用のインデックス生成クレート
  • elasticlunr.js: JS製の全文検索エンジン

さらに日本語検索にかかわるコンポーネントが以下です。

検索の流れは以下の通り。

  • mdbook buildにてmdBookがelasticlunr.js互換のインデックスファイル(searchindex.js)を生成する
  • ブラウザで読んだときにsearcher.jsが上で生成したsearchindex.jsをロードして、elasticlunr.jsで検索する

パッチを当てて日本語検索させる

mdBook

まずmdBook本体からです。

$ git clone https://github.com/rust-lang/mdBook

として2つのファイルにパッチを当てます。
1つ目は以下の通りCargo.tomlにてelasticlunr-rsにjaフィーチャを追加します。

diff --git a/Cargo.toml b/Cargo.toml
index e0a2b90..2f28447 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -46,7 +46,7 @@ staticfile = { version = "0.5", optional = true }
 ws = { version = "0.9", optional = true}

 # Search feature
-elasticlunr-rs = { version = "2.3", optional = true, default-features = false }
+elasticlunr-rs = { version = "2.3", optional = true, default-features = false, features = ["ja"] }
 ammonia = { version = "3", optional = true }

 [dev-dependencies]

2つ目はelasticlunr::Indexを生成しているところで、Language::Japaneseを指定します。

diff --git a/src/renderer/html_handlebars/search.rs b/src/renderer/html_handlebars/search.rs
index f184a59..9cc7519 100644
--- a/src/renderer/html_handlebars/search.rs
+++ b/src/renderer/html_handlebars/search.rs
@@ -2,7 +2,7 @@ use std::borrow::Cow;
 use std::collections::{HashMap, HashSet};
 use std::path::Path;

-use elasticlunr::Index;
+use elasticlunr::{Index, Language};
 use pulldown_cmark::*;

 use crate::book::{Book, BookItem};
@@ -13,7 +13,7 @@ use crate::utils;

 /// Creates all files required for search.
 pub fn create_files(search_config: &Search, destination: &Path, book: &Book) -> Result<()> {
-    let mut index = Index::new(&["title", "body", "breadcrumbs"]);
+    let mut index = Index::with_language(Language::Japanese, &["title", "body", "breadcrumbs"]);
     let mut doc_urls = Vec::with_capacity(book.sections.len());

     for item in book.iter() {

この状態でビルドしてmdbookコマンドをインストールします。
(ローカルでmdbookを使っている方は上書きしてしまうのでご注意ください)

$ cargo install --path .

ビルド

先ほど改造したmdbookを使って普通にビルドします。

$ cd XXX
$ mdbook build

lunr-languagesの導入と改造

lunr-languagesから以下のファイルを落としてきて、mdbookが生成したdocs直下に置きます。

さらにlunr.jp.jsに以下のようにパッチを当てます。

99c99
<     lunr.Pipeline.registerFunction(lunr.jp.stemmer, 'stemmer-jp');
---
>     lunr.Pipeline.registerFunction(lunr.jp.stemmer, 'stemmer-ja');
109a110,113
>
>     lunr.jp['wordCharacters'] = '一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9';
>     lunr.jp.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.jp.wordCharacters);
>     lunr.Pipeline.registerFunction(lunr.jp.trimmer, 'trimmer-ja');

さらにindex.htmlの最後の方のelasticlunr.min.jsを読んでいる部分に以下のように追記します。

        <script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="searcher.js" type="text/javascript" charset="utf-8"></script>
        <script src="lunr.stemmer.support.js" type="text/javascript" charset="utf-8"></script>
        <script src="tinyseg.js" type="text/javascript" charset="utf-8"></script>
        <script src="lunr.jp.js" type="text/javascript" charset="utf-8"></script>            

これでとりあえずindex.htmlから日本語検索できるようになりました。

検索結果の例

例えば手元にある"Rust by Example 日本語版"だと以下のようになります。

image.png

いろいろな不具合

とりあえずこの状態で日本語検索できるように見えますが、いろいろと不具合があります。

  • 検索結果のハイライトがおかしい。おそらく英語前提で半角スペース区切りを元にハイライトを決めているので。
  • 3文字以上の漢字が検索できない。例えば「構造」はOKだが「構造体」はNG、といった感じ。詳しい条件は不明。
  • 英語が検索できない。

特に3つ目はプログラミング系のドキュメントの検索としては割と致命的ですが、直すのは結構大変そうです。

完全な日本語検索に向けて

完全な日本語検索に向けて、以下のようなことをやればいいのではないかというリストを挙げておきます。

lunr-languagesのPRをマージ

lunr.jp.jsで当てたパッチは1年ほど前にPRが出ているものの放置されているものです。
すでにelasticlunr.js/lunr-languagesの作者の方はあまりアクティブではないようで、
elasticlunr.jsをOrganization管理にしたいというIssueが立っています。
正規の手順を踏むなら、できたOrgにlunr-languagesも移動、当該PRをマージしてもらう、という感じがいいでしょうか。

elasticlunr-rs と lunr-languagesの日本語名をそろえる

現状elasticlunr-rsで日本語はja、lunr-languagesではjpとなっています。
lunr.jp.jsで当てたパッチの一部はこの不整合を解消するためでした。
分かれている経緯が不明なので、どちらに寄せるべきかはよく分かりません。

(追記:2020/6/23)
某所でご指摘いただいたのですが、言語コードとしてはjaが適切なのではないか?とのことでした。
https://ja.wikipedia.org/wiki/ISO_639-1%E3%82%B3%E3%83%BC%E3%83%89%E4%B8%80%E8%A6%A7

そのあと気づいたのですが、jaに変更するのが正しいですね。
ここで指しているlunr-languagesはelasticlunr.js用にフォークしたものなのですが、
フォーク元の本家lunr-languages(こちらはlunr.js用)ではjajp両方あって、
もともとjpだったのを言語コード的に正しいjaに変更した、となっています。(jpは互換性のために残してある)
なのでelasticlunr.js用lunr-languagesも本家に追従することでこの問題を解決するのが正しそうです。

elasticlunr-rsに多言語対応を入れる

elasticlunr.jsとしては多言語(つまり英語+日本語)対応しているようですが、
elasticlunr-rs側が(Issueは立っているのですが)対応していません。
基本的には多言語時のelasticlunr.jsのインデックスの構造を調べて、そのように出力すればいいはずですが…。

mdBookに検索言語選択機能を入れる

おそらくbook.tomlの設定にいれるのがいいでしょう。
ただし日本語検索のために結構依存クレートが増えるので、フィーチャは切った方がいいかもしれません。
変更箇所は、パッチを当てたIndexの部分と、HTML/JSの生成部分です。

まとめ

というわけでmdBookで日本語検索するにはいろいろとハードルがあることが分かりました。

dalance
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away