ElasticLunr.jsで全文検索する(日本語+英語でAND検索)
日本語と英語でAND検索できた。
成果物
GitHubPagesエラー版
* code(GitHubPagesでエラー)
* なぜかGitHubPagesが作られずエラーになる
* submoduleまわりに問題があるらしい
* submoduleをやめてcloneした
* git clone --recursive https://github.com/ytyaru/lunr-languages
* .git
ディレクトリ削除
概要
ElasticLunr.jsはLunr.jsの改良版。インデックスされるデータ量が少なく済む。
本体 | 言語ライブラリ | fork |
---|---|---|
Lunr.js | MihaiValentin/lunr-languages | - |
ElasticLunr.js | weixsong/lunr-languages | ytyaru/lunr-languages |
このうち言語ライブラリを改造して日本語と英語に対応した。
日本語ライブラリの問題
英語と混在させて検索できない。これは技術系ブログにおいて致命的。
Lunr.jsで全文検索してみた結果、this.use(lunr.multiLanguage('en', 'ja'));では日本語が有効にならない問題があった。そのため単一言語でしか検索できない。
これはElasticLunr.jsでも同じだった。それどころか多言語化ライブラリweixsong/lunr-languagesはまともにメンテナンスもされていない始末。言語コードはja
が正しいのにjp
となっていたり、tinyseg.jsがrequire.jsでインポートできるようになっていなかったり。
そこで、weixsong/lunr-languagesをフォークしてytyaru/lunr-languagesを作った。
ytyaru/lunr-languages
- tinyseg.js: require.jsでインポートできるようにする(MihaiValentin/lunr-languagesのtinyseg.jsをコピペ)
- lunr.ja.js:
- 言語コードを
jp
からja
へ修正する - 日本語と英語の混在で検索できるように修正(MihaiValentin/lunr-languages/lunr.ja.js参考)
- 言語コードを
weixsong/lunr-languages/lunr.jp.jsをみてみる。ほかの言語lunr.de.jsと比較してみると、trimmer
がない。なのでtrimmer
を実装する。Lunr.js用のMihaiValentin/lunr-languages/lunr.ja.jsをパクる。
すると以下のようなコードを追加することになった。
this.pipeline.add(
lunr.ja.trimmer,
lunr.ja.stopWordFilter,
lunr.ja.stemmer
);
lunr.tokenizer = lunr.ja.tokenizer;
/* lunr trimmer function */ lunr.ja.wordCharacters = "一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9"; lunr.ja.trimmer = lunr.trimmerSupport.generateTrimmer(lunr.ja.wordCharacters); lunr.Pipeline.registerFunction(lunr.ja.trimmer, 'trimmer-ja');
細かいことはさっぱりわからないが、なんか動いたのでよしとする。
使ってみる
require.jsを使ってほかのjsファイルをインポートする。
index.html
<script data-main="main.js" src="./lib/require/require.2.3.6.min.js"></script>
検索したい文書をobjectでつくる。任意のキーと値で。
var documents = [ { id: 0, title: "Oracle released its latest database Oracle 12g", body: "Yestaday Oracle has released its new database Oracle 12g, this would make more money for this company and lead to a nice profit report of annual year." }, ... ];
elasticlunr
を設定する。日本語の検索エンジンlunr.ja.js
を使う。multiLanguage('en', 'ja')
したかったが、有効にならない。やむなくlunr.ja.js
のほうで英語も検索できるようにすることで対応する。
main.js
require(['lib/elasticlunr/elasticlunr.0.9.6.min.js', 'lib/elasticlunr/lunr-languages/tinyseg.js', 'lib/elasticlunr/lunr-languages/min/lunr.stemmer.support.min.js', 'lib/elasticlunr/lunr-languages/lunr.multi.js', 'lib/elasticlunr/lunr-languages/lunr.ja.js' ], function(elasticlunr, tinyseg, stemmerSupport, multi, ja) { stemmerSupport(lunr); tinyseg(lunr); ja(lunr); // multi(lunr); var index = elasticlunr(function() { this.use(elasticlunr.ja); // this.use(elasticlunr.multiLanguage('en', 'ja')); this.setRef('id'); this.addField('title'); this.addField('body'); this.saveDocument(false); for (const doc of documents) { this.addDoc(doc); } });
指定したキーワードで全文検索する。対象はタイトルと本文。すべてのキーワードを含む文書のみ検索対象とする。タイトルと本文のうちいずれかひとつでもすべてのキーワードを含んでいたら対象とする。
result = index.search(keyword, { fields: { title: {boost: 2, bool: 'AND'}, body: {boost: 1, bool: 'AND'} }, bool: 'OR' });
Lunr.jsではできていた以下のことができなかった。AND
,OR
検索を指定できれば問題ない。
result = index.search(`*${keyword}*`, { // 部分一致検索にすることでムリヤリ英語混在でも検索できるようにする
課題
- インデックス作成は事前に済ませておきたい
- Elasticlunr.jsで全文検索してみた(日本語)
- Node.jsでできるらしいが、できなかった
- save-load-index
- Elasticlunr.jsで全文検索してみた(日本語)
- 修正した記事ファイルのみインデックスを作り直したい
- 指定の記事ファイルからインデックスを作りたい
- インデックスを高速に作りたい
実装 | メリット | デメリット |
---|---|---|
browser-js | すでに実装できた | ファイルから読み取れない。毎回インデックス作成するので文書量が増えたら遅すぎて死ぬ。 |
node-js | 移植が簡単なはず | ファイル読取できなかった。できても実行速度が遅いはず。 |
rust | 速いはず | 日本語用trimmerなどをrust言語で実装せねばならない。 |
静的サイトジェネレータzolaを参考にすべきか。するとelasticlunr-rs、linderaがキモっぽい。そのあたりを調べよう。
所感
先は長い。ローカルで動く日本語+英語の検索ボックスがほしいだけなのに。インデックスの自動作成まで考えるとめちゃくちゃ大変そう。
対象環境
- Raspbierry pi 4 Model B
- Raspberry Pi OS buster 10.0 2020-08-20 ※
- bash 5.0.3(1)-release
$ uname -a Linux raspberrypi 5.4.83-v7l+ #1379 SMP Mon Dec 14 13:11:54 GMT 2020 armv7l GNU/Linux