Elasticsearch:公式分散検索および分析エンジン | Elastic
Elasticsearchは、分散型でRESTfulなオープンソースの検索/分析エンジンです。スピードや水平スケール性、信頼性、管理の手軽さにすぐれ、高い評価とシェアを獲得しています。無料トライアルでお試しください。...
https://www.elastic.co/jp/elasticsearch
Photo by Mick Haupt on Unsplash
こんにちは。ウォンテッドリーの Enabling チームでバックエンドエンジニアをしている市古(@sora_ichigo_x)です。
現在、Enabling チームでは技術的な取り組みを社外にも発信すべく、メンバーが週替わりで技術ブログをリレー形式で執筆しています。前回は私、市古による「おすすめ Claude Code 設定・運用まとめ」でした。今回は Elasticsearch の話をします。
Elasticsearch は高度な検索と分析を実現する検索エンジンとして広く活用されています。
しかし、その仕組みをブラックボックスに感じているエンジニアは多いのではないでしょうか。
このブログでは Elasticsearch の全文検索に入門し、同義語検索のような応用的なユースケースの仕組みまで理解することを目指します。
なお、このブログは執筆時点(2025年6月17日)で最新である Elasticsearch 8.19 までの内容をもとに書かれています。
Elasticsearch
オープンソースの検索エンジン
まずは基本概念を覚えよう
テキスト分析 (Text Analysis)
文字列を検索しやすい形 (トークン) に変換するプロセスのこと
3ステップで構成される
インデックス時と検索時の2つの時点で実行される
同義語検索
辞書ルールで1つの検索キーワードに対して複数トークンをヒットさせる
同義語検索を実現する方法は2つある
synonym を使う場合、さらに2つの意思決定をする必要がある
1. 同義語リストの展開タイミングをいつにするか
2. 同義語リストの状態管理をどう実現するか
推奨構成
まとめ
参考
Elasticsearch を使った同義語検索の仕組みを理解する前に全体像を掴んでおくには、2つの知識を押さえておく必要があります。
Elasticsearch は全文検索やログ分析などに強みを持つオープンソースの検索エンジンです。
内部的には Apache Lucene というライブラリをベースに実行されており、Elasticsearch はこの Lucene をラップする形で、API やクラスタリング、インデックス管理などを提供しています。
構造化・非構造化を問わず様々なデータを高速に検索できるため、Web サービスの検索機能の裏側に使われることが多いです。
Elasticsearch では、クラスタがノード(物理サーバー)の集合で、インデックスが検索対象の単位です。インデックスはシャードに分割され、各シャードにJSON形式のドキュメント(データ)が分散保持されます。例えば、実運用ではこんな形で利用されます。
{ "id": 123, "name": "山田太郎", "skills": ["Go", "React"]}
(候補者のプロフィール1件)あるインデックスに検索クエリが送られると、Elasticsearch はそれを全シャードにブロードキャストし(①)、各シャードが部分的な結果を返します(②)。最後にそれらを統合して、ユーザーに最終結果を返します(③)。
Elasticsearch の難しさとして、インデックスやシャードなど覚える概念の多さが挙げられますが、これらの用語の理解が浅いままではドキュメントの理解が得られないので、大変ですがまずは基本概念の理解に時間をかけましょう。elasticsearch-head などの GUI で実際のクラスタの構成を見るのもおすすめです。
Elasticsearch では、検索対象の文字列や検索キーワードをそのまま扱うのではなく検索しやすい形に変換します。そして、この検索しやすい形のことをトークンと呼び、変換処理のことをテキスト分析(Text Analysis)と呼びます。
全文検索を単なる完全一致検索ではなく、部分一致やステミング、同義語検索を実現するためにはテキスト分析の存在が必須となります。
テキスト分析は3つのステップから構成され、それぞれ異なる役割を担っています。
例えば、Elasticsearch で日本語を扱う場合は kuromoji_tokenizer という日本語の単語分割に対応した Analysis plugins を入れる必要があります(実際にはストップワードなども英語と異なるため他にも token filterがいくつか用意されています)。また、同義語検索を実現する場合には Synonym token filter という token filter を入れる必要があります。
このように Elasticsearch を活用する上でドキュメントを読み進めるとアナライザだけでなく character filters や tokenizer、token filters という用語が頻出するため、テキスト分析の仕組みと用語は事前に理解しておくと良いでしょう。
テキスト分析は文書がインデックスされた時と検索クエリを受け取った時の2つの時点で実行されています。
公式ドキュメント
ほとんどの場合、一致率向上・誤一致防止のためインデックス時と検索時には 同じアナライザを使用します。一方で、テキスト分析の結果が運用や時間経過によって変わりうる場合はインデックス時にあえてテキスト分析をスキップした方が良いケースもあります。
後述しますが、このブログのテーマである同義語検索は辞書ルールの変更頻度によっては、まさにインデックス時にテキスト分析をスキップした方が良いケースです。
前提として、全文検索は文中のテキストフィールドを単語レベルで正確に一致するかどうかに基づいて検索します。
具体的には、検索対象の文字列を事前にトークンに分割した上で、さらに検索キーワードも同様にトークン化して、両者のマッチングを行います。つまり「エンジニア」と検索したときに、インデックス内に同じ単語があればヒットしますが「開発者」や「技術者」といった意味の近い語はヒットしません。
このように、全文検索は文字レベルの一致が基本であるため、意味の近さを考慮した検索を実現するためには、辞書ルールで1つの検索キーワードに対して複数トークンをヒットさせるか (同義語検索)、セマンティック検索を利用する必要があります。
このブログでは前者の同義語検索に焦点を当てて解説していきます。
同義語検索の仕組みは基本的にはシンプルで、あるキーワード(例: エンジニア)に対して、他のキーワード(例: 開発者、技術者)もヒットするように辞書ルールをあらかじめ用意して、それに従って検索を行っています。
なお、この辞書ルールの一覧のことをこのブログでは便宜上「同義語リスト」と呼びます。
エンジニア => 開発者、技術者
この仕組みを考えると、Elsaticsearch で同義語検索を実現する方法は主に2つに挙げられます。
1つ目が OR クエリを利用する方法、2つ目が Synonym token filter を利用する方法です。
ORクエリと Synonym token filter のどちらを利用するかは、基本的には同義語リストのサイズや複雑度合いを見て決めると良いでしょう。
特定のユースケース特化で、数個の辞書ルールしかない場合(そんなケースがどれほどあるか分かりませんが...)は わざわざ Synonym token filter を導入せずとも OR クエリで軽量に実装した方が素早く、より少量の変更で要件を満たせます。
{
"query": {
"bool": {
"should": [
{ "match": { "title": "開発者" } },
{ "match": { "title": "エンジニア" } }
{ "match": { "title": "技術者" } }
]
}
}
}
一方で数十個以上の辞書ルールがある場合や、今後の拡張・保守性を考慮する場合は、Synonym token filter を利用する方が現実的です。主な比較観点としては、同義語リストの可読性と管理性、パフォーマンスです。
具体的には、次の2つを決める必要があります。
synonym による同義語リストの展開は、テキスト分析のトークンフィルターで行います。
そしてテキスト分析ということは、インデックス時と検索時の2つの時点で実行できます。改めてこの性質をおさらいしておきましょう。
また、インデックスのマッピング (スキーマ定義のこと)に analyzer と search_analyzer へと別々の設定を割り当てることで、同義語展開のタイミングを制御できます。
{
"settings": {
"analysis": {
"analyzer": {
"synonym_index_analyzer": {
"tokenizer": "standard",
"filter": ["lowercase", "my_synonym_filter"]
},
"synonym_search_analyzer": {
"tokenizer": "standard",
"filter": ["lowercase", "my_synonym_filter"]
}
},
"filter": {
"my_synonym_filter": {
"type": "synonym",
"synonyms": [
"quick, fast",
"jumps, leaps"
]
}
}
}
},
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "synonym_index_analyzer",
"search_analyzer": "synonym_search_analyzer"
}
}
}
}
インデックス時のみ分析したい場合は、analyzer に同義語フィルターを含め、search_analyzer は同義語フィルターなしに設定します。
検索時のみ展開したい場合は、 逆に search_analyzer に同義語フィルターを含め、analyzer は同義語フィルターなしに設定します。
両方で展開したい場合は、両方に同義語フィルターを含めます。
現実的な設計判断では、同義語リストのメンテナンス性・再インデックスのコスト・検索時のレスポンス性能といった運用面の要素を重視すべきです。
synonym を使う場合、もう1つ重要になるのが「同義語リストそのものをどう保管・更新・反映させるか」という問題です。2025年6月時点の Elasticsearch 8.19 では、主に以下3つの管理方法が存在します。
ここまでで Elasticsearch で同義語検索を実現する際に考慮すべきポイントが整理できました。
では実際のプロダクション環境では、どのような構成を採用すべきなのでしょうか。
私が考える2025年6月時点での推奨構成は次の通りです。
中身は 2019年に Elasticsearch の公式ブログ (The same, but different: Boosting the power of Elasticsearch with synonyms) にある内容がベースになっています。
同義語リストの展開タイミングについて補足しておくと、実運用では「同義語の更新をどれくらいの頻度で行うか」「検索結果の一貫性をどう担保したいか」が分かれ目になります。
インデックス時に Synonym token filter を使うと、文書の登録時にキーワードが拡張・置換され、その状態で転置インデックスに保存されます。この方式にはいくつかのデメリットがあります。
唯一のメリットは、クエリ時に同義語展開処理を行わないため、検索パフォーマンスが若干向上する可能性があるという点ですが、現代のインフラやデータ規模ではこの利点は小さくなっています。
一方で、検索時に synonym フィルターを適用する場合、上述の問題はほとんど発生しません。
かつては、検索時の辞書変更にもインデックスの re-open 操作が必要でしたが、Elasticsearch 7.3 以降では reload_search_analyzers によりゼロダウンタイムで反映可能になっており、運用上のデメリットもほぼ解消されています。また、Elasticsearch 7.3 未満でも少々手間ですがインデックスを再作成するなどの手段を用いればゼロダウンタイムでの反映は可能です。
結果的に、検索時に同義語リストを展開するメリットの方がインデックス時に展開するより上回る形になっています。
このブログでは Elsaticsearch とテキスト分析の基礎知識から始め、同義語検索を実現する上での設計について紹介しました。
文量の関係で Elasticsearch の仕様やマッピング・クエリのスキーマレベルで解説できていないため、実際にはこのブログを参考に公式ドキュメントに一通り目を通すと良いでしょう。