こんにちは、BtoB領域で商品情報や検索のバックエンドを担当している中野です! 前回の投稿 に引き続き検索ネタです!
検索エンジンで使う日本語の形態素解析にはいくつか選択肢がありますが、Sudachiを利用されているシステムも多いと思います。先日、Elasticsearch/OpenSearch向けのSudachiプラグインである elasticsearch-sudachi にコントリビュートしましたので、その内容と学んだことを共有いたします。
背景
我々のチームが管理している検索システムでは、検索エンジンをElasticsearchからOpenSearchに移行を進めています。執筆時点のOpenSearchの最新バージョンは3.4なのですが、着手当時はSudachiプラグインがOpenSearch 3系に未対応でした。
少し状況の補足ですが、インフラは Amazon OpenSearch Service を利用していて、この環境では3系のインスタンスを起動すると自動的にSudachiプラグインもインストールされている状態になっています(AWSが独自にSudachiプラグインをフォークしたものがインストールされているのかもしれません)。
ですので3系対応のコントリビュートをしなくても一応は移行作業を進められるのですが、普段お世話になっているOSSに貢献したい気持ち(と、好奇心!)からやってみようと決意しました。
やったこと
とはいえ何から始めればいいのか悩ましかったです。Sudachiプラグインでは、サポートしている Elasticsearch/OpenSearch のすべてのバージョンでテストを走らせるジョブ が実装されています。まずはこれにOpenSearch 3系を追加してテストを実行し、上がってくるエラーを潰していく方法をとりました。
細かい修正が多いため網羅はしませんが、要点だけまとめると次の3点でした。
- GitHub ActionsジョブへのOpenSearch 3系追加(OpenSearch 3系はJava 21で実行)
- Gradleの更新(Java 17/21の両対応)
- OpenSearchのバージョン差異を吸収するスクリプトの実装
以降では「3」のバージョン差異を埋める仕組みについて解説します。
複数のOpenSearchバージョンをサポートする仕組み
OpenSearch 3系はメジャーバージョンアップなので、内部API(REST APIではなくプラグインが利用するAPI)の変更が入っています。Sudachiプラグインは単一のコードベースで複数の検索エンジンやバージョンをサポートしています。これはバージョン間の差異を埋めるための互換性レイヤやGradleカスタムプラグインによって実現されています。
互換性レイヤ
Sudachiプラグインには src/main/ext ディレクトリがあり、ここにバージョン固有のコードが分離されています。下図のように、配下のディレクトリ名が検索エンジンやバージョンを表しています。
src/main/ext/ ├── es-8.10-ge/ # Elasticsearch 8.10以上 ├── os-2.00-ge/ # OpenSearch 2.0以上 ├── os-3.00-ge/ # OpenSearch 3.0以上 └── os-3.00-lt/ # OpenSearch 3.0未満 ※ ge は "greater or equal(以上)"、lt は "less than(未満)" を表しています
Gradleカスタムプラグイン
こちら に実装されているカスタムプラグインによって、ビルド時に -PengineVersion=os:3.0.0 のように指定された検索エンジンの種類やバージョン番号をもとに、さきほどの ext ディレクトリに配置されているコードがソースセットに追加されます。
たとえばビルド時の引数で -PengineVersion=os:3.0.0 を指定した場合、 os-3.00-ge 配下のコードがソースセットに追加されビルドされます。
例: configPath() の実装
前述の互換性レイヤとGradleカスタムプラグインを利用してバージョン差異を埋める具体例をひとつ紹介します。
設定ファイルの配置場所を返す内部APIが、2系で Environment.configFile() だったのが、3系で Environment.configDir() にリネームされました。
この変更に対応するため、 os-3.00-lt/config-path.kt では次のようになっており、
fun Environment.configPath(): java.nio.file.Path = this.configFile()
一方で os-3.00-ge/config-path.kt では、
fun Environment.configPath(): java.nio.file.Path = this.configDir()
となっていて configFile() と configDir() を、 configPath() で統一して利用できるようにしています。これによってプラグインのメインコードではバージョン差異を気にせず configPath() を使えばよいことになります。
おわりに
この記事では、Sudachiプラグインにコントリビュートした経験について書きました。特に興味深い点として複数バージョンをサポートするための仕組みを紹介しました。普段のWebアプリケーション開発では考えることのない課題を意識することになり、とても新鮮な体験でした。
Sudachiプラグインという便利なOSSを公開し、メンテナンスされている皆様にこの場を借りてお礼申し上げます! また機会があればコントリビュートしたいと思います。
また、ここで紹介した内容はすべてコントリビュートする前には知らなかった知識です。AIを利用することで、仕組みの理解から修正まで素早く進められました。AIの助けがなければ 通常業務の合間に 今回のようなコントリビュートをするのは不可能でした。内部を全く知らないOSSであっても、諦めずにキャッチアップしてPRを出せる素晴らしい時代になったと感じました!
仲間を募集中です!
サービス開発もOSSへのコントリビュートも爆速でやっていきたい仲間を募集しています! この記事を読んでアスクルの開発に興味を持たれた方はぜひお気軽にご連絡ください。