SudachiとElasticsearch
今年もあと1日で終わりますが、押し迫ってからなんとかやり残したことを片付けようと、久しぶりにQiitaの画面に向かってます。
Sudachiはリリース当初から完成度の高いモジュールで、私も本業でガリガリにチューニングしてNLPのコアエンジンとして利用させていただいています。
そんな折、Elasticsearch6系でSudachiを使う必要が生じた(今まではAPIを直に叩いていた)ので、勘所を皆様と共有しておきたいと思います。
※ 5.6系へのインストールはこちらの記事がわかりやすいと思います。
Elasticsearchのための新しい形態素解析器 「Sudachi」
Elasticsearch Plug-inのバージョン依存性
この記事をお読みになる皆様はきっとElasticsearchの達人ばかりだと思いますが、念のため事前知識を書きます。
Elasticsearch(以下es)はplug-inで様々な機能拡張が可能です。様々な国の言語のstemming処理や形態素解析がこのplug-inとして実装・提供されていますが、実はこのplug-inのバイナリはesのバージョンに依存しています。(ほんまそこまで厳しくチェックせんでもええんちゃうん、と遠い目になってしまいますが、)plug-inが参照するライブラリはインストール先のesとバージョン番号の3レベル目のビルドバージョンまで厳密に一致している必要があります。
analysis-sudachiの対応バージョン
SudachiのElasticsearch plug-inであるanalysis-sudachiは現時点で5.6系に対応しています。analysis-sudachiをesの最新版である6.1.1で使用するためには、esとその依存先であるluceneのAPI変更(これが結構頻繁で..orz)にソースコードを合わせる必要があります。この記事では変更箇所を例示してanalysis-sudachiのビルドが通りesに組み込んで動作するところまでを目指します。ただし手順は非公式なものですので、あくまで自己責任でお願いします。
analysis-sudachiをclone
リポジトリからdevelopブランチをcloneします。
https://github.com/WorksApplications/elasticsearch-sudachi.git
pom.xmlの編集
analysis-sudachiプロジェクト直下のpom.xmlを編集します。esとluceneのバージョンを上げます。
-の行がオリジナル、+の行が新しい内容です。
15行あたり
- <elasticsearch.version>5.6.1</elasticsearch.version>
+ <elasticsearch.version>6.1.1</elasticsearch.version>
211行あたり
<artifactId>lucene-analyzers-kuromoji</artifactId>
- <version>6.6.0</version>
+ <version>7.1.0</version>
241行あたり
<artifactId>lucene-test-framework</artifactId>
- <version>6.6.0</version>
+ <version>7.1.0</version>
ここでプロジェクトをupdateするとAPIの違いでコンパイルエラーになる箇所が出てくると思います。
javaソースコードの編集
次にesとluceneのAPI変更に合わせてソースコードを編集します。
41行あたり
- SudachiAnalyzer.getDefaultStopSet());
+ SudachiAnalyzer.getDefaultStopSet(), false);
185行あたり
- .loadFromStream(RESOURCE_NAME_SUDACHI_ANALYSIS_JSON, input)
+ .loadFromStream(RESOURCE_NAME_SUDACHI_ANALYSIS_JSON, input, false)
32行あたり
-import org.apache.lucene.index.Fields;
+import org.apache.lucene.index.FieldInfos;
117-123行あたり
LeafReader leafReader = leafReaderContext.reader();
- Fields fields = leafReader.fields();
+ FieldInfos fields = leafReader.getFieldInfos();
assertThat(fields.size(), is(1));
- String fieldName = fields.iterator().next();
+ String fieldName = fields.iterator().next().name;
assertThat(fieldName, is(FIELD_NAME));
- Terms terms = fields.terms(fieldName);
+ Terms terms = leafReader.terms(fieldName);
156-159行あたり
assertThat(terms.size(), is(7L));
query = queryParser.parse("京都");
- assertThat(searcher.search(query, 5).totalHits, is(0));
+ assertThat(searcher.search(query, 5).totalHits, is(0L));
query = queryParser.parse("岩波");
- assertThat(searcher.search(query, 5).totalHits, is(0));
+ assertThat(searcher.search(query, 5).totalHits, is(0L));
bug fix?
このファイルの変更はAPIをあわせるためではなく、アルゴリズムとして必要そうなのにコメントアウトされている一行を復活させるものです。この行がないと512文字を超えるフィールドで例外が発生します。
240行あたり
- //nextBaseOffset += eos;
+ nextBaseOffset += eos;
ビルドとインストール
mvn package を実行してビルドが成功すると、target/releases/ 以下にzipファイルでplug-inが作成されます。
そちらを指定して、
elasticsearch-plugin install analysis-sudachi-1.0.0-SNAPSHOT.zip
を実行してください。
辞書の配備
Elasticsearch plug-inからのリソースファイルへの参照に対してJava VMが例外を投げる場合があります。色々設定する術はあると思いますが、私はesのconfigディレクトリにsudachiの辞書を置くサブディレクトリを作ることにしました。
ubuntu: /usr/share/elasticsearch/config/sudachi/system_core.dic
Mac OS: /usr/local/opt/elasticsearch/libexec/config/sudachi/system_core.dic
index settings
私は次のような設定でanalysis-sudachiを使っています。
"analysis": {
"tokenizer": {
"sudachi_tokenizer": {
"type": "sudachi_tokenizer",
"mode": "search",
"discard_punctuation": true,
"resources_path": "/usr/share/elasticsearch/config/sudachi"
}
},
"analyzer": {
"sudachi": {
"filter": [
"lowercase",
"cjk_width",
"english_stop",
"sudachi_part_of_speech",
"sudachi_ja_stop",
"sudachi_baseform",
"sudachi_readingform"
],
"tokenizer": "sudachi_tokenizer",
"type": "custom"
}
}
}
以上です。ではでは、みなさなよいお正月を〜。