TikaのLanguageDetector

Tikaには文字列を渡すと、その言語を渡してくれる機能がある。使うためにはまずpom.xmlに以下の依存関係を追加する。

                <dependency>
                        <groupId>org.apache.tika</groupId>
                        <artifactId>tika-langdetect</artifactId>
                        <version>1.20</version>
                </dependency>

あとは、LanguageDetectorを生成して利用する。

import java.io.IOException;

import org.apache.tika.langdetect.OptimaizeLangDetector;
import org.apache.tika.language.detect.LanguageDetector;
import org.apache.tika.language.detect.LanguageResult;

public class LanguageDetectorExample {

    public String detectLanguage(String text) throws IOException {
        LanguageDetector detector = new OptimaizeLangDetector().loadModels();
        LanguageResult result = detector.detect(text);
        return result.getLanguage();
    }
}

上記はTikaにあるexampleコードだが、OptimaizeLangDetectorをnewして、loadModels()でLanguageDetectorを取得する。あとはLanguageDetectorに言語判定したいテキストを渡せば言語情報が返ってくる。

まぁ、とはいえ、そこそこ判定が外れる気もする…。

_sourceからデータを消しておく

クエリー時に_sourceに含めないという設定もあるけど、インデックス時に_sourceに入れないでおくという方法もある。Fess 13ではこの機能を使ってハイブリットな言語用インデックスで検索する予定ではある。

で、使うためにはElasticsearchのサイトにもあるような感じで

$ curl -X PUT "localhost:9200/fess" -H 'Content-Type: application/json' -d'
{
"mappings": {
"_doc": {
"_source": {
"excludes": [
"content_*",
"title_*"
]
}
}
}
}
'

のようにすれば、title_〜とcontent_〜のプロパティは_sourceに保存されなくなる。

でも、Fessではcontent_lengthは除外したくないので、

$ curl -X PUT "localhost:9200/fess" -H 'Content-Type: application/json' -d'
{
"mappings": {
"_doc": {
"_source": {
"includes": [
"content_length"
],
"excludes": [
"content_*",
"title_*"
]
}
}
}
}
'

としたところ、全部が消えることになり、期待する動きとは違っていた…。つまり、includes > excludesの順に処理するっぽい。というわけで、Fessではexcludesにワイルドカードを使わずに明示して対応した。

Fess 13に向けて

Fess 13ではElasticsearch 7系を採用する予定だけど、Elasticsearch 6から7への変更はBreaking changesに書いてある。まだ、7.0.0-beta1がリリースされている段階だが、Elasticsearch 6のときにはrc1がリリースされた頃からFess 12に対応したため、ちょっと時間がかかったりで、6.1がリリースされたときにFess 12.0がリリースされたので、Elasticsearchとはマイナーバージョンがずれる感じになってしまった。ということで、今回はちょっと早めのbeta1から対応を始めている。

とはいえ、Breaking changesにあるように変更はいろいろとある。今回はFessで遭遇した変更点を上げておく

typeの削除が始まる

Elasticsearch 8で完全になくなると思うが、7では消し始めていかないとエラーになったりする箇所がある。なので、使っていれば積極的に消していく必要がある。Java APIとかだと、client.prepareDelete(index, type, id)みたいに指定できてしまう箇所とかもあるけど、これらはclient.prepareDelete().setIndex(index).setId(id) みたいにtypeを消したりとどんど消していったほうが良い。_docとかをtypeに指定しておけば良いかもしれないが、修正漏れとか出たりするので思い切って消す方針で進めるのが良いかも。

hits.totalがオブジェクトになる

ここにあるように、件数が整数で返ってきたのがオブジェクトになるので、JSONのパースとかしている箇所では注意が必要。オブジェクトになったことに合わせて、track_total_hitsなど、件数表示の指定などがあったり、件数の考え方に確認しておく必要がある。

Transport Clientは非推奨

Elasticsearchとの通信にはHTTP(9200)とTransport(9300)の2種類があったが、Transportの方は非推奨になっている。8では削除される。なので、TransportClientは利用せず、HTTPでElasticsearchと通信するようにしておくのが良い。Fessでは普通にRESTに書き直すのは変更が大きすぎてつらすぎるため、elasticsearch-httpclientを作成して、TransportClientを単純置き換えで、移行した。

楽観的排他制御の変更

_versionでoptimistic concurrency controlを実装していたが、(たぶん)Elasticsearch 6の後半あたりで、_seq_noと_primary_termを利用するように変更されている。Elasticsearch 7では_seq_noと_primary_termを利用する必要があるので、これに置き換える必要がある。

スクロールのコンテキストの上限

スクロールで検索するとサーチコンテキストがオープンされるのだが、search.max_open_scroll_contextが500に設定され、オープンするコンテキストの上限が500になった。今までは上限なしだった。というわけで、スクロール検索している箇所では、スクロールの終了時にはscroll_idは削除しないと、指定時間だけ保持されることになり、上限になってエラーになる。サーチコンテキストは不要なら明示的に削除していく必要がある。

というあたりのところが、Fessでは問題なった箇所かな…。Elasticsearch 7とは関係なく、言語判定部分などみなしたい箇所があるので、fessインデックスのマッピングなどは見直すことになると思います。Elasticsearch 7が出た後の近いうちにはFess 13をリリースできるようにしたいところではありますね。

cluster.routing.use_adaptive_replica_selection

Elasticsearch 6までは複数のレプリカがある場合、レプリカの使われ方はラウンドロビンで順番に使われます。たとえば、
・シャード1P, シャード1R1 シャード1R2
・シャード2P, シャード2R1 シャード2R2
のように2シャードの3レプリカの場合は、1回目の検索で1P&2P, 2回めの検索で1R1&2R1, 3回目の検索で1R2&2R2みたいな感じで使われることになります(実際には非同期で処理されるのでシャード1と2ではそのときのが選択されて場合によってはPとRはずれる気はするけど)。という感じで、デフォルトはラウンドロビンということ。

Elasticsearch 7からはラウンドロビンでなくなり、それを6でも試すなら、cluster.routing.use_adaptive_replica_selectionをtrueにする。7ではtrueになっている。

curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
"transient": {
"cluster.routing.use_adaptive_replica_selection": true
}
}
'