こんにちは。ウォンテッドリーの Enabling チームでバックエンドエンジニアをしている市古(@sora_ichigo_x)です。
現在、Enabling チームでは技術的な取り組みを社外にも発信すべく、メンバーが週替わりで技術ブログをリレー形式で執筆しています。前回は私、市古による「おすすめ Claude Code 設定・運用まとめ」でした。今回は Elasticsearch の話をします。
Elasticsearch は高度な検索と分析を実現する検索エンジンとして広く活用されています。
しかし、その仕組みをブラックボックスに感じているエンジニアは多いのではないでしょうか。
このブログでは Elasticsearch の全文検索に入門し、同義語検索のような応用的なユースケースの仕組みまで理解することを目指します。
なお、このブログは執筆時点(2025年6月17日)で最新である Elasticsearch 8.19 までの内容をもとに書かれています。
目次
Elasticsearch を使った同義語検索の仕組みを理解する前に全体像を掴んでおくには、2つの知識を押さえておく必要があります。
- Elasticsearch 自体の知識
- テキスト分析の知識
Elasticsearch
オープンソースの検索エンジン
Elasticsearch は全文検索やログ分析などに強みを持つオープンソースの検索エンジンです。
内部的には Apache Lucene というライブラリをベースに実行されており、Elasticsearch はこの Lucene をラップする形で、API やクラスタリング、インデックス管理などを提供しています。

Elasticsearch:公式分散検索および分析エンジン | Elastic
構造化・非構造化を問わず様々なデータを高速に検索できるため、Web サービスの検索機能の裏側に使われることが多いです。
まずは基本概念を覚えよう
Elasticsearch では、クラスタがノード(物理サーバー)の集合で、インデックスが検索対象の単位です。インデックスはシャードに分割され、各シャードにJSON形式のドキュメント(データ)が分散保持されます。例えば、実運用ではこんな形で利用されます。
- クラスタ:
- Wantedly の「スカウト検索」機能で使う Elasticsearch クラスタ(Nノード構成)
- インデックス:
- candidates(候補者データ)
- ドキュメント:
-
{ "id": 123, "name": "山田太郎", "skills": ["Go", "React"]}(候補者のプロフィール1件)
-
- シャード:
- candidates インデックスをNつのプライマリーシャードとMつずつのレプリカシャードに構成 → 計N+Mシャードが各ノードに分散配置される
あるインデックスに検索クエリが送られると、Elasticsearch はそれを全シャードにブロードキャストし(①)、各シャードが部分的な結果を返します(②)。最後にそれらを統合して、ユーザーに最終結果を返します(③)。
Elasticsearch の難しさとして、インデックスやシャードなど覚える概念の多さが挙げられますが、これらの用語の理解が浅いままではドキュメントの理解が得られないので、大変ですがまずは基本概念の理解に時間をかけましょう。elasticsearch-head などの GUI で実際のクラスタの構成を見るのもおすすめです。
テキスト分析 (Text Analysis)
文字列を検索しやすい形 (トークン) に変換するプロセスのこと
Elasticsearch では、検索対象の文字列や検索キーワードをそのまま扱うのではなく検索しやすい形に変換します。そして、この検索しやすい形のことをトークンと呼び、変換処理のことをテキスト分析(Text Analysis)と呼びます。
全文検索を単なる完全一致検索ではなく、部分一致やステミング、同義語検索を実現するためにはテキスト分析の存在が必須となります。

Text analysis | Elastic Docs
3ステップで構成される
テキスト分析は3つのステップから構成され、それぞれ異なる役割を担っています。
- Character filters:テキスト中の記号やHTMLタグなどを変換・除去する
例. "The cat & the dog" → "The cat and the dog" - Tokenizer:テキストを単語単位(トークン)に分割する
例. "The cat and the dog" → ["The", "cat", "and", "the", "dog"] - Token filters:トークンに対して小文字化、語幹化、ストップワード除去、同義語展開などを行う
例. ["The", "cat", "and", "the", "dog"] → ["cat", "dog"]
例えば、Elasticsearch で日本語を扱う場合は kuromoji_tokenizer という日本語の単語分割に対応した Analysis plugins を入れる必要があります(実際にはストップワードなども英語と異なるため他にも token filterがいくつか用意されています)。また、同義語検索を実現する場合には Synonym token filter という token filter を入れる必要があります。
このように Elasticsearch を活用する上でドキュメントを読み進めるとアナライザだけでなく character filters や tokenizer、token filters という用語が頻出するため、テキスト分析の仕組みと用語は事前に理解しておくと良いでしょう。
インデックス時と検索時の2つの時点で実行される
テキスト分析は文書がインデックスされた時と検索クエリを受け取った時の2つの時点で実行されています。
公式ドキュメント
ほとんどの場合、一致率向上・誤一致防止のためインデックス時と検索時には 同じアナライザを使用します。一方で、テキスト分析の結果が運用や時間経過によって変わりうる場合はインデックス時にあえてテキスト分析をスキップした方が良いケースもあります。
後述しますが、このブログのテーマである同義語検索は辞書ルールの変更頻度によっては、まさにインデックス時にテキスト分析をスキップした方が良いケースです。
同義語検索
辞書ルールで1つの検索キーワードに対して複数トークンをヒットさせる
前提として、全文検索は文中のテキストフィールドを単語レベルで正確に一致するかどうかに基づいて検索します。
具体的には、検索対象の文字列を事前にトークンに分割した上で、さらに検索キーワードも同様にトークン化して、両者のマッチングを行います。つまり「エンジニア」と検索したときに、インデックス内に同じ単語があればヒットしますが「開発者」や「技術者」といった意味の近い語はヒットしません。
このように、全文検索は文字レベルの一致が基本であるため、意味の近さを考慮した検索を実現するためには、辞書ルールで1つの検索キーワードに対して複数トークンをヒットさせるか (同義語検索)、セマンティック検索を利用する必要があります。
このブログでは前者の同義語検索に焦点を当てて解説していきます。
同義語検索を実現する方法は2つある
同義語検索の仕組みは基本的にはシンプルで、あるキーワード(例: エンジニア)に対して、他のキーワード(例: 開発者、技術者)もヒットするように辞書ルールをあらかじめ用意して、それに従って検索を行っています。
なお、この辞書ルールの一覧のことをこのブログでは便宜上「同義語リスト」と呼びます。
エンジニア => 開発者、技術者この仕組みを考えると、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 を利用する方が現実的です。主な比較観点としては、同義語リストの可読性と管理性、パフォーマンスです。
- 可読性と管理性の向上:
- クエリに毎回同義語をすべてORで書く必要がなくなり、ロジックがシンプルになる
- 辞書を1箇所で管理できるため、将来的なルールの追加・変更がしやすい
- パフォーマンス
- 同義語が多数ある場合にORクエリで処理するとクエリの肥大化により実行コストが上がる
- Synonym token filter であればテキスト分析で処理されるため、検索時の負荷が抑えられる
synonym を使う場合、さらに2つの意思決定をする必要がある
具体的には、次の2つを決める必要があります。
- 同義語リストの展開タイミングをいつにするか
- 同義語リストの状態管理をどう実現するか
1. 同義語リストの展開タイミングをいつにするか
synonym による同義語リストの展開は、テキスト分析のトークンフィルターで行います。
そしてテキスト分析ということは、インデックス時と検索時の2つの時点で実行できます。改めてこの性質をおさらいしておきましょう。
- インデックス時:文書がインデックスされたタイミングで全ての text フィールドの値を分析
- 検索時:全文検索を実行するタイミングでクエリ文字列を分析
また、インデックスのマッピング (スキーマ定義のこと)に 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 は同義語フィルターなしに設定します。
両方で展開したい場合は、両方に同義語フィルターを含めます。
現実的な設計判断では、同義語リストのメンテナンス性・再インデックスのコスト・検索時のレスポンス性能といった運用面の要素を重視すべきです。
2. 同義語リストの状態管理をどう実現するか
synonym を使う場合、もう1つ重要になるのが「同義語リストそのものをどう保管・更新・反映させるか」という問題です。2025年6月時点の Elasticsearch 8.19 では、主に以下3つの管理方法が存在します。
- Synonym API (synonym_set)
Elasticsearch 8.10 以降で使える新しい手法です。Synonym API を用いて、同義語セットをクラスタ内に登録・更新し、テキストアナライザの設定中の synonym_set フィールドでトークンフィルタに参照させる形で動作します。 - Synonym ファイル (synonym_path)
synonym_set 以前から広く使われてきた方法で、全ノードに同義語リストファイルを物理配置し、テキストアナライザの設定中の synonym_path フィールドにパスを指定して参照します。 - インライン定義
テキストアナライザの設定に同義語リストを直接書き込む方法です。
推奨構成
ここまでで Elasticsearch で同義語検索を実現する際に考慮すべきポイントが整理できました。
では実際のプロダクション環境では、どのような構成を採用すべきなのでしょうか。
私が考える2025年6月時点での推奨構成は次の通りです。
中身は 2019年に Elasticsearch の公式ブログ (The same, but different: Boosting the power of Elasticsearch with synonyms) にある内容がベースになっています。
- 基本方針
- 同義語のルールが少数である場合を除き、原則 OR クエリではなく Synonym token filter を使う
- 同義語リストの状態管理方法
- Elasticsearch 8.10.0 以上の場合:Synonym API (synonym_set)
- Elasticsearch 8.10.0 未満の場合:Synonym ファイル (synonym_path)
- インライン定義は使わない
- 同義語リストの展開タイミング
- 検索時のみ synonym 展開する
同義語リストの展開タイミングについて補足しておくと、実運用では「同義語の更新をどれくらいの頻度で行うか」「検索結果の一貫性をどう担保したいか」が分かれ目になります。
インデックス時に Synonym token filter を使うと、文書の登録時にキーワードが拡張・置換され、その状態で転置インデックスに保存されます。この方式にはいくつかのデメリットがあります。
- インデックスサイズが増える:すべての同義語を持つトークンが保存されるため、ストレージコストが上がる
- スコアリングが歪む:用語頻度に基づくスコア計算において、本来は低頻度な単語も水増しされ、精度が落ちる可能性がある
- 同義語ルールの変更に再インデックスが必要:一度登録した文書には変更が反映されないため、辞書の修正コストが高い
唯一のメリットは、クエリ時に同義語展開処理を行わないため、検索パフォーマンスが若干向上する可能性があるという点ですが、現代のインフラやデータ規模ではこの利点は小さくなっています。
一方で、検索時に synonym フィルターを適用する場合、上述の問題はほとんど発生しません。
かつては、検索時の辞書変更にもインデックスの re-open 操作が必要でしたが、Elasticsearch 7.3 以降では reload_search_analyzers によりゼロダウンタイムで反映可能になっており、運用上のデメリットもほぼ解消されています。また、Elasticsearch 7.3 未満でも少々手間ですがインデックスを再作成するなどの手段を用いればゼロダウンタイムでの反映は可能です。
結果的に、検索時に同義語リストを展開するメリットの方がインデックス時に展開するより上回る形になっています。
まとめ
このブログでは Elsaticsearch とテキスト分析の基礎知識から始め、同義語検索を実現する上での設計について紹介しました。
- 基本方針
- 同義語のルールが少数である場合を除き、原則 OR クエリではなく Synonym token filter を使う
- 同義語リストの状態管理方法
- Elasticsearch 8.10.0 以上の場合:Synonym API (synonym_set)
- Elasticsearch 8.10.0 未満の場合:Synonym ファイル (synonym_path)
- インライン定義は使わない
- 同義語リストの展開タイミング
- 検索時のみ synonym 展開する
文量の関係で Elasticsearch の仕様やマッピング・クエリのスキーマレベルで解説できていないため、実際にはこのブログを参考に公式ドキュメントに一通り目を通すと良いでしょう。
参考
- ElasticSearch Get Started https://www.elastic.co/docs/get-started/
- Text analysis https://www.elastic.co/docs/manage-data/data-store/text-analysis
- ElasticSearch Text analysis https://www.elastic.co/docs/manage-data/data-store/text-analysis/anatomy-of-an-analyzer
- Concept https://www.elastic.co/docs/manage-data/data-store/text-analysis/index-search-analysis
- Index and search analysis (timing) https://www.elastic.co/docs/manage-data/data-store/text-analysis/stemming
- Stemming https://www.elastic.co/docs/manage-data/data-store/text-analysis/token-graphs
- Token grpahs https://www.elastic.co/docs/solutions/search/full-text/how-full-text-works
- ElasticSearch Full-Text Search https://www.elastic.co/docs/solutions/search/semantic-search
- ElasticSearch Semantic Search https://www.elastic.co/docs/solutions/search/full-text/search-with-synonyms
- ElasticSearch Search with synonyms https://www.elastic.co/docs/solutions/search/full-text/search-with-synonyms