OSS 活動を通して貢献できた Meilisearch を紹介したい

この記事は[スタンバイ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/stanby) の11日目の記事です。

こんにちは。[求人検索サービス](https://jp.stanby.com/)を提供する[株式会社スタンバイ](https://stanby.co.jp/)でプロダクト開発部長をしている大須賀です。
普段の仕事は開発組織運営などのマネジメントが中心です。一般的にマネージャは、業務として直接的に開発に携わることが少なくなり、Individual Contributor (IC) としてスペシャリストを目指すエンジニアから敬遠されがちです。確かにその通りかもしれませんが、幸運なことに私の場合、仕事をではマネージャとして、OSS 活動ではエンジニアとして、今年一年、充実したキャリアを積むことができたと思っています。

そこで今回は、会社でマネージャをしながらも、OSS の活動でエンジニアとして貢献できた OSS 全文検索サーバー [Meilisearch](https://www.meilisearch.com/) を紹介したいと思います。

私が Meilisearch にかかわり始めたのは、2022年5月に [miiton](https://github.com/miiton) 氏のコントリビューションによって、私が開発している形態素解析器 [Lindera](https://github.com/lindera-morphology/lindera) が Meilisearch の日本語 Segmenter として組み込まれたことがきっかけです。
OSS 開発者として、自分が開発したソフトウェアが製品に組み込まれることは、この上なく嬉しいものです。そのことが私自身の活動の励みにもなり、その頃から、形態素解析器のパフォーマンス改善や Meilisearch の日本語サポート周辺の課題解決を積極的に手伝うようになりました。（もちろん業務外の時間で。）
その私の活動が Meilisearch のエンジニアの目に留まったのか、今年の5月に Meilisearch の言語サポートのメインコントリビューター (Meilistars) のひとりとして認められ、インタビュー記事 ([Minoru Osuka: POV of a main language contributor](https://blog.meilisearch.com/meilistar-interview-minoru-osuka/)) まで書いてもらいました。
Meilisearch のコントリビューターのひとりとして、日本国内への普及に少しでも貢献できればと思います。

# Meilisearch とは

![meilisearch-logo-light.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58291/2eeb4b0c-3abb-26a4-2bed-03c3250a95c9.png)

Meilisearch はフランス パリの検索エンジンスタートアップ Meilisearch 社が開発する [Rust](https://www.rust-lang.org/) 製の全文検索サーバーです。読み方はメイリサーチです。
2023年2月に初の安定板である[バージョン 1.0.0](https://blog.meilisearch.com/whats-new-in-v1-0/) がリリースされ、[Gigazine でも紹介記事](https://gigazine.net/news/20230430-meilisearch-search-engine/)が書かれたり、既にいろいろな方のブログ記事などで取り上げられているので、ご存じの方も多いかと思います。
現在は最新版の[バージョン 1.5.0](https://blog.meilisearch.com/meilisearch-1-5/) がリリースされています。
以下に Meilisearch の特徴のいくつかをいくつか上げたいと思います。

## 高速 (Lightning fast)
Meilisearch のベンチマークは他のブログ記事などにも書かれているように数万件 (数十MB) のドキュメントを数十秒でインデクシングし、Query Auto-Completion のような Typeahead search を行ってもミリ秒単位で応答しサクサク動くとのことです。フットプリントも小さく、メモリ使用量も少ないのも特徴です。

## 導入が簡単 (Plug 'n play)
デフォルトで賢い初期設定がされているので、デプロイしてすぐにデータを投入して利用を開始できます。
これは検索エンジンを手軽に使いたいという人にはとても嬉しいことではないでしょうか。[Elasticsearch](https://www.elastic.co/elasticsearch) や [Solr](https://solr.apache.org/)、[Vespa](https://vespa.ai/) では、まずスキーマをどうするかなど、インデクシングを始める前に考えなければならないことが数多くあります。とりあえずすぐに全文検索機能をサービスに導入してキーワード検索を実現したい、というユーザーには非常に魅力的ではないでしょうか。

## 豊富な機能 (Ultra relevant)
あらゆるユースケースに対応できる検索のための機能を備えています。上記のようにプリセットされた設定でも満足できるかもしれませんが、必要であれば、カスタマイズも可能です。[Filtering](https://www.meilisearch.com/docs/learn/fine_tuning_results/filtering) や [Relevancy](https://www.meilisearch.com/docs/learn/core_concepts/relevancy) の設定をすることが可能なので、好みに合わせて検索結果の調整も可能です。

## 実装が容易 (Easy to integrate) 
Meiliserch は HTTP をベースとした RESTful API を提供していますが、アプリケーションへの実装が容易にできるよう、様々な開発言語のための SDK が用意されています。
こちらの [integration-guides](https://github.com/meilisearch/integration-guides) を見ていただくとわかるようにメジャーな開発言語向けの SDK は揃っています。


# できること

さて、そんな Meilisearch ですが、どんな用途に向いているかというと、既に多くのユースケースがあります。

## 全文検索 (Full text search)
もっとも一般的な使い方です。ウェブサイト、ブログ、ナレッジベースなど、情報が多くなるにつれて目的の情報にたどり着くのが困難になるケースがありますが、手軽に全文検索を用いたサイト内検索を実現できます。[ファセット検索](https://www.meilisearch.com/docs/learn/fine_tuning_results/faceted_search)にも対応しているのでお手軽にカテゴリ毎にドキュメントをまとめるなどもできます。
- [オンラインドキュメントと日本語全文検索](https://voluntas.medium.com/%E3%82%AA%E3%83%B3%E3%83%A9%E3%82%A4%E3%83%B3%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%A8%E6%97%A5%E6%9C%AC%E8%AA%9E%E5%85%A8%E6%96%87%E6%A4%9C%E7%B4%A2-30cc38d7b1c3)
実際に Sphinx のテーマに Meilisearch から検索する JavaScript を組み込んで、ドキュメントを全文検索できるようにしています。
- [MisskeyでMeilisearchを導入するやり方](https://nanasi-apps.xyz/misskey-meilisearch)
[Misskey](https://misskey-hub.net/) という OSS の分散型 SNS プラットフォームへ組み込んで投稿を検索できるようにするといったこともできます。

## 位置情報検索 (Geo search)
緯度経度で検索したり、ある地点からの距離で絞り込んだりできるので、実世界の情報を利用して地図上にピンを立てるといった地図サービスのようなものも実現できます。
- [Example of Meiliseach in Japanese](https://meilisearch-example-jp.miiton.dev/)
こちらは位置情報も含んだ日本語検索のデモンストレーションになります。
位置情報だけでなく、ファセットも盛り込んだものになっているので、Meilisearch の機能を存分に体験できます。

## セマンティック検索 (Semantic search)
2023年7月にリリースされた[バージョン 1.3.0](https://blog.meilisearch.com/v1-3-release/) からベクトルデータのサポートが実験的に導入されたことで、ベクトル検索を行えるようになりました。テキストを形態素解析器でトークン化するのではなく、Embedding model を使用してベクトルで表現し、ベクトル間の距離を計算することで、類似するベクトルを検索する手法です。
キーワード検索とは異なり、ドキュメントやクエリの意図や意味といったもので検索を行います。
[OpenAI](https://openai.com/) の [ChatGPT](https://chat.openai.com/) などの大規模言語モデル (LLM：Large Language Models) が注目されていますが、その LLM と AI エージェントの開発を助ける [LangChain](https://www.langchain.com/) と一緒に Meilisearch を使うことでセマンティック検索を構築することも可能です。
- [A step-by-step tutorial to building semantic search with LangChain](https://blog.meilisearch.com/langchain-semantic-search-tutorial/)
こちらは、実際に LLM と LangChain、Meilisearch を組み合わせてセマンティック検索を構築するための手順が説明されたオフィシャルブログの記事です。


# まだ苦手なこと
どんな用途にも使えそうな Meilisearch ですが、まだ苦手なことはありそうです。

## 完全なカスタマイズ
Meilisearch は賢い初期設定がされており、豊富な機能を備え、カスタマイズ可能です。しかしながら、Elasticsearch や Solr、Vespa のようにフィールド単位でのきめ細かなスキーマ設定やアナライザの設定ができません。
検索のすべてを掌握し、コントロールする必要があるなら Elasticsearch や Solr、Vespa などの検索サーバーを利用する方が良いでしょう。
でも、それはそれでとても大変ですよね？サクッと全文検索できれば良いというライトユーザーにはとても面倒な作業の連続になるでしょう。意外と Meilisearch で事足りてしまうかもしれません。

## ランキング学習
ルールベース ([Relevancy](https://www.meilisearch.com/docs/learn/core_concepts/relevancy)) を用いたランキングの調整を行うことはできますが、残念ながら機械学習を用いたランキング学習 (LTR: Learning to Rank) といった、ランキングの手法をとることはまだできません。完全なカスタマイズと同様、Elasticsearch や Solr、Vespa といった検索サーバーを利用する方が良いでしょう。
ただ、Meilisearch のターゲットであるライトな検索エンジンユーザーには、LTR は運用コストを考えると重すぎるかな？とも思います。


# 日本語の対応状況
日本語の対応状況はどうでしょうか？
先ほども書かせてもらいましたが、2022年5月にリリースされた [バージョン 0.27.0](https://github.com/meilisearch/meilisearch/releases/tag/v0.27.0) から私がメンテナンスをしている OSS の日本語形態素解析器 [Lindera](https://github.com/lindera-morphology/lindera) を日本語の Segmenter として採用しており、日本語のドキュメントを扱うことができるようになっています。
ところが、Meilisearch のインデックス設定やドキュメントの構造、検索 API のパラメータには Segmenter を指定するような項目は見当たりません。
- [Documents](https://www.meilisearch.com/docs/learn/core_concepts/documents)
- [All index settings](https://www.meilisearch.com/docs/learn/configuration/settings)
- [Search](https://www.meilisearch.com/docs/reference/api/search)

なんと Meilisearch は Elasticsearch や Solr、Vespa のように Tokenizer 設定をする必要はありません。Meilisearch の Tokenizer は入力されたドキュメントのテキストから、どの言語で書かれたかを自動検出し、検出できた言語用の Segmenter を使ってテキストを単語に分割します。
簡単ですね。何も難しいことはありませんし、何も悩むことはありません。よしなにやってくれます。

## 言語自動検出の対応状況

上記のように文字列から言語検出を行っていますが、カンの良い人は気づいたかもしれません。

漢字のみクエリの場合、日本語のつもりでも自動言語検出機能によって中国語と判定されてしまうという問題があります。
先程も紹介しましたが、Meilisearch ではフィールド毎にどの Segmenter を使用するか指定はできません。そのフィールドに入ってきたテキストを基に言語検出を行い、自動で Segmenter を切り替えます。
インデックスするテキストが「ここは東京都渋谷区」というテキストであれば、日本語固有の片仮名や平仮名が文字として含まれているので、日本語であると検出され、Tokenizer は日本語用の Segmenter (Lindera) を使って「ここ / は / 東京 / 都 / 渋谷 / 区」とトークン化します。
しかし、クエリの場合はどうでしょう？検索の時は比較的短い単語だけのクエリで検索することが多いと思います。
一般的に、「東京」というクエリで先程のテキストが検索できることを期待しますが、漢字だけの単語「東京」と言うクエリは、中国語であると検出されてしまうため、中国語用の Segmenter ([Jieba](https://github.com/messense/jieba-rs)) が適用され、「東 / 京」とトークン化されてしまい、ユーザーの意図した形で検索を行うことができないのです。

| テキスト | 言語検出 | Segmenter | トークン |
| --- | --- | --- | --- |
| ここは東京都渋谷区 | Japanese | Lindera | ここ / は / <b>東京</b> / 都 / 渋谷 / 区 |
| 東京 | Mandarin | Jieba | <b>東</b> / <b>京</b> |

Meilisearch の Tokenizer はテキストがどの言語で書かれているかの検出には [Whatlang](https://github.com/greyblake/whatlang-rs) を使用していますが、漢字のみのテキストでは日本語なのか中国語なのかを正確に検出することは難しそうです。
次のベン図を見ていただければわかると思います。

![191637391-b30a766d-28df-4bce-af8a-c0712b0a520b.png](https://user-images.githubusercontent.com/468745/191637391-b30a766d-28df-4bce-af8a-c0712b0a520b.png)

漢字は日本語だけでなく中国語でも使われています。[日本語の常用漢字の殆どは中国語 (GB 2312、Big5) でも利用されている](https://github.com/orgs/meilisearch/discussions/532#discussioncomment-3705382)文字であるため、文字単位で何語であるかを判断できません。漢字のみの日本語テキストを Whatlang が中国語として検出してしまうのも仕方ないなと思ってしまいます。

これでは、日本語のテキストを検索することはできないじゃないかと思うかもしれませんが、Meilisearch は日本語を扱うためのビルドがされたバイナリを含む[コンテナイメージを Docker Hub で配布](https://hub.docker.com/r/getmeili/meilisearch/tags?page=1&name=japanese)していますので、日本語をメインとする検索サービスを構築する方は、こちらを利用することを強くお勧めします。

## 形態素解析辞書の対応状況

あと、日本語形態素解析器の辞書ですが、分散型 SNS プラットフォームへ組み込む取り組みの中で、コンテキスト (文脈) の有無で形態素解析の結果が異なるという問題が話題となりました。
 - [MeiliSearch設定されたサーバーの検索で外部サーバーの投稿が結果に出ない場合がある #10845](https://github.com/misskey-dev/misskey/issues/10845)

コンテキストの有無でトークン化が変わる例を次に上げます。

| 辞書 | テキスト | トークン |   |
| --- | --- | --- | --- |
| IPADIC | doc: 京都大学に行く<br>query: 京都大学 | doc: <b>京都大</b> / <b>学</b> / に / 行く<br>query: <b>京都大学</b>| <b>×</b> トークン不一致<br>検索できない |
| UniDic | doc: 京都大学に行く<br>query: 京都大学 | doc: <b>京都</b> / <b>大学</b> / に / 行く<br>query: <b>京都</b> / <b>大学</b> | <b>○</b> トークン一致<br>検索できる |

このように IPADIC では、形態素辞書に登録されている情報により、「京都大学」という文字列が文脈の有無によってトークン化のされ方が変わってしまいます。これでは「京都大学に行く」というテキストは「京都大学」というクエリで検索できませんが、[UniDic](https://clrd.ninjal.ac.jp/unidic/) では文脈の有無によってズレが生じることなく、一貫性のあるトークン化が行えます。
UniDic にすると辞書のサイズが大きくなってしまうというデメリットがありますが、情報検索においては、このような一貫性のあるトークン化が行えることのほうが重要であると個人的にも考えていますので、Meilisearch の[デフォルトの辞書を IPADIC から UniDic へ切り替え](https://github.com/meilisearch/charabia/pull/218)させてもらいました。

この記事執筆時点において、デフォルトの IPADIC を内包する [Lucene](https://lucene.apache.org/) Kuromoji を日本語トークナイザとしている Elasticsearch や Solr では、上記のような文脈の有無によってトークン化にズレが生じ、検索できないケースなどがありますが、Meilisearch では UniDic をデフォルトで内包しているため、一貫性のあるトークン化が Elasticsearch や Solr よりも期待でき、より良い検索体験を実現できているのではないでしょうか。(※ ただし、内包される辞書が大きくなってしまい、バイナリサイズが大きくなってしまいます)
この一貫性のあるトークン化の重要性については[情報検索のための単語分割一貫性の定量的評価](https://www.anlp.jp/proceedings/annual_meeting/2016/pdf_dir/D6-5.pdf)を読んでいただければと思います。


# まとめ

Meilisearch について、なんとなく理解していただけたでしょうか？
生まれたばかりで、今後の成長が楽しみなプロダクトにコントリビューターとして関われたのはエンジニアとしてとても良い経験ができました。

まだまだ日本語を扱うには課題が多い Meilisearch ですが、日本語のサポートについては Meilisearch の開発者だけでなく、私達のような多くのコミュニティの開発によって支えられています。特に言語周りの処理については、その言語を母国語とするソフトウェアエンジニアの力必要となります。
Meilisearch の日本語サポートについて興味がありましたら、ぜひこちらのディスカッションに参加していただけたらと思います。

- [Japanese Language support #532](https://github.com/orgs/meilisearch/discussions/532)

そして、今年一年お世話になった、OSS 開発コミュニティの皆さんに改めて感謝いたします。


# 最後に

![service_stanby_logo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58291/5ad64f1b-1e13-04cb-9ba9-b0a87a35bc4a.png)

私達スタンバイでは、事業成長に伴い、一緒にサービス作りと事業を支えてくれるエンジニアを募集しています。
[スタンバイ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/stanby) や、[Stanby Tech Blog](https://techblog.stanby.co.jp/) の記事を読んで、弊社に興味もっていただけたら幸いです。
カジュアル面談などで、弊社について詳しくご説明させていただきますので、一度お話してみませんか？
ぜひお声がけください！

- [カジュアル面談のお申込み](https://hrmos.co/pages/stanby-inc/jobs/100100463)

- [株式会社スタンバイ エンジニア の求人一覧](https://hrmos.co/pages/stanby-inc/jobs?category=1445664202436182016)