本記事は 2026 年 2 月 12 日 に公開された「 Matching your Ingestion Strategy with your OpenSearch Query Patterns 」を翻訳したものです。 Amazon OpenSearch Service クラスターで適切なインデックス戦略を選択すると、効率性を維持しながら低レイテンシーで正確な結果を得られます。アクセスパターンに複雑なクエリが必要な場合は、インデックス戦略を見直すことをお勧めします。 本記事では、OpenSearch でカスタムインデックスアナライザーを作成し、 Edge n-gram トークナイザー を使ってワイルドカードを使わずにプレフィックスクエリをマッチさせ、オートコンプリート機能を効率的に実装する方法を紹介します。 インデックスアナライザーとは インデックスアナライザー は、ドキュメントの取り込み時にテキストフィールドを解析します。アナライザーが出力するトークンを使ってクエリをマッチングします。 デフォルトでは、OpenSearch は 標準インデックスアナライザー でデータをインデックスします。標準インデックスアナライザーは、スペースでトークンを分割し、小文字に変換し、ほとんどの句読点を除去します。ログ分析などのユースケースでは、標準インデックスアナライザーだけで十分な場合もあります。 標準インデックスアナライザー 標準インデックスアナライザーの動作を見てみましょう。 _analyze API を使って、標準インデックスアナライザーが「Standard Index Analyzer.」という文をどのようにトークン化するかテストします。 注意 : 本記事のコマンドはすべて、OpenSearch Dashboard の DevTools で実行できます。 GET /_analyze { "analyzer": "standard", "text": "Standard Index Analyzer." } #======== #Results #======== { "tokens": [ { "token": "standard", "start_offset": 0, "end_offset": 8, "type": "<ALPHANUM>", "position": 0 }, { "token": "index", "start_offset": 9, "end_offset": 14, "type": "<ALPHANUM>", "position": 1 }, { "token": "analyzer", "start_offset": 15, "end_offset": 23, "type": "<ALPHANUM>", "position": 2 } ] } 各単語が小文字に変換され、ピリオド (句読点) が除去されていることがわかります。 独自のインデックスアナライザーを作成する OpenSearch には、さまざまなアクセスパターンに対応する多数の組み込みアナライザーが用意されています。また、特定の検索ニーズに合わせたカスタムアナライザーも構築できます。次の例では、住所リストに対して部分一致を返すカスタムアナライザーを設定します。このアナライザーは オートコンプリート 機能向けに設計されており、ユーザーが住所全体を入力しなくても素早く住所を見つけられます。オートコンプリートにより、マッチしたプレフィックスに基づいて検索語を補完できます。 まず、 standard_index_test というインデックスを作成します。 PUT standard_index_test { "mappings": { "properties": { "text_entry": { "type": "text", "analyzer": "standard" } } } } 標準アナライザーはデフォルトのアナライザーであるため、analyzer に standard を指定する必要はありません。 テストのために、作成した standard_index_test にデータを一括追加します。 POST _bulk {"index":{"_index":"standard_index_test"}} {"text_entry": "123 Amazon Street Seattle, Wa 12345 "} {"index":{"_index":"standard_index_test"}} {"text_entry": "456 OpenSearch Drive Anytown, Ny 78910"} {"index":{"_index":"standard_index_test"}} {"text_entry": "789 Palm way Ocean Ave, Ca 33345"} {"index":{"_index":"standard_index_test"}} {"text_entry": "987 Openworld Street, Tx 48981"} 「ope」というテキストでデータをクエリします。 GET standard_index_test/_search { "query": { "match": { "text_entry": { "query": "ope" } } } } #======== #Results #======== { "took": 2, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 0, "relation": "eq" }, "max_score": null, "hits": [] # No matches } } 「ope」で検索しても結果が返りません。理由を確認するために、標準インデックスアナライザーがテキストをどのようにトークン化しているか詳しく見てみましょう。標準インデックスアナライザーで住所「456 OpenSearch Drive Anytown, Ny 78910」をテストします。 POST standard_index_test/_analyze { "analyzer": "standard", "text": "456 OpenSearch Drive Anytown, Ny 78910" } #======== #Results #======== "tokens": "456" "opensearch" "drive" "anytown" "ny" "78910" 標準インデックスアナライザーは住所を 456 、 opensearch 、 drive などの個別のトークンに分割しています。つまり、個別のトークン ( 456 や opensearch など) を検索しない限り、 o 、 op 、 ope 、さらには open でも結果は返りません。1 つの方法として、インデックスには標準インデックスアナライザーを使いつつ、 ワイルドカード を使う方法があります。 GET standard_index_test/_search { "query": { "wildcard": { "text_entry": "ope*" } } } ワイルドカードクエリは「456 OpenSearch Drive Anytown, Ny 78910」にマッチしますが、ワイルドカードクエリはリソース消費が大きく低速になる可能性があります。OpenSearch で ope* をクエリすると、 転置インデックス のルックアップ最適化をバイパスして、インデックス内の各トークンを反復処理します。メモリ使用量が増加し、パフォーマンスが低下します。クエリと検索のパフォーマンスを向上させるには、アクセスパターンに適したインデックスアナライザーを使用します。 Edge n-gram Edge n-gram トークナイザー は、単語のプレフィックスをトークン化することで、ワイルドカードを使わずに部分一致を実現します。たとえば、入力語 coffee は c 、 co 、 cof などすべてのプレフィックスに展開されます。最小長 ( min_gram ) と最大長 ( max_gram ) の間のプレフィックスに制限できます。 min_gram=3 、 max_gram=5 の場合、「coffee」は cof 、 coff 、 coffe に展開されます。 Edge n-gram を使用するカスタムインデックスアナライザーで custom_index という新しいインデックスを作成します。最小トークン長 ( min_gram ) を 3 文字、最大トークン長 ( max_gram ) を 20 文字に設定します。 min_gram と max_gram はそれぞれ返されるトークンの最小長と最大長を設定します。アクセスパターンに基づいて min_gram と max_gram を選択してください。この例では「ope」という語で検索するため、 o や op のような語は検索しないので最小長を 3 未満にする必要はありません。 min_gram を低く設定しすぎるとレイテンシーが高くなる可能性があります。同様に、個別のトークンが 20 文字を超えることはないため、最大長を 20 より大きくする必要はありません。最大長を 20 に設定することで、将来より長いトークン長の住所を取り込む場合にも余裕を持たせています。なお、ここで作成するインデックスはオートコンプリート機能に特化したものであり、一般的な検索インデックスには不要な場合があります。 PUT custom_index { "mappings": { "properties": { "text_entry": { "type": "text", "analyzer": "autocomplete", "search_analyzer": "standard" } } }, "settings": { "analysis": { "filter": { "edge_ngram_filter": { "type": "edge_ngram", "min_gram": 3, "max_gram": 20 } }, "analyzer": { "autocomplete": { "type": "custom", "tokenizer": "standard", "filter": [ "lowercase", "edge_ngram_filter" ] } } } } } 上記のコードでは、 autocomplete というカスタムアナライザーを持つ custom_index というインデックスを作成しました。このアナライザーは以下の処理を行います。 標準トークナイザーでテキストをトークンに分割する lowercase フィルターですべてのトークンを小文字に変換する edge_ngram の最小値と最大値に基づいてトークンをさらに小さなチャンクに分割する 検索アナライザーには標準アナライザーを設定し、検索時のクエリ処理を軽減しています。取り込み時にカスタムアナライザーでテキストを分割済みのため、検索時に同じ処理を繰り返す必要はありません。カスタムアナライザーが「Lexington Avenue」というテキストをどのように解析するかテストします。 GET custom_index/_analyze { "analyzer": "autocomplete", "text": "Lexington Avenue" } #======== #Results #======== # Minimum token length is 3 so we won't see l, or le "tokens": "lex" "lexi" "lexin" "lexing" "lexingt" "lexingto" "lexington" "ave" "aven" "avenu" "avenue" トークンが小文字に変換され、部分一致に対応していることがわかります。アナライザーがテキストをどのようにトークン化するか確認できたので、データを一括追加します。 POST _bulk {"index":{"_index":"custom_index"}} {"text_entry": "123 Amazon Street Seattle, Wa 12345 "} {"index":{"_index":"custom_index"}} {"text_entry": "456 OpenSearch Drive Anytown, Ny 78910"} {"index":{"_index":"custom_index"}} {"text_entry": "789 Palm way Ocean Ave, Ca 33345"} {"index":{"_index":"custom_index"}} {"text_entry": "987 Openworld Street, Tx 48981"} テストしてみましょう。 GET custom_index/_search { "query": { "match": { "text_entry": { "query": "ope" } } } } #======== #Results #======== "hits": [ { "_index": "custom_index", "_id": "aYCEIJgB4vgFQw3LmByc", "_score": 0.9733556, "_source": { "text_entry": "456 OpenSearch Drive Anytown, Ny 78910" } }, { "_index": "custom_index", "_id": "a4CEIJgB4vgFQw3LmByc", "_score": 0.4095239, "_source": { "text_entry": "987 Openworld Street, Tx 48981" } } ] カスタム n-gram アナライザーを設定して、住所リスト内の部分一致を実現できました。 なお、非標準のインデックスアナライザーの使用と計算コストの高いクエリの記述にはトレードオフがあります。アナライザーは、特に非効率に使用した場合、インデックスのスループットに影響を与え、全体のインデックスサイズを増加させる可能性があります。たとえば、 custom_index の作成時に検索アナライザーを標準アナライザーに設定しました。取り込み時と検索時の両方で n-gram を使用していれば、クラスターのパフォーマンスに不要な負荷がかかっていたでしょう。さらに、 min_gram と max_gram をアクセスパターンに合った値に設定し、検索ユースケースに必要以上の n-gram を作成しないようにしました。適切な設定により、取り込みスループットに影響を与えず、検索の最適化によるメリットを得られました。 まとめ 本記事では、オートコンプリートクエリを簡素化し高速化するために、OpenSearch のデータインデックス方法を変更しました。今回のケースでは、Edge n-gram を使用することで、ワイルドカードクエリによるクラスターパフォーマンスへの影響なしに、住所の一部をマッチさせて正確な結果を得られました。 本番環境にデプロイする前に、必ずクラスターをテストしてください。インデックスと検索の両面からクラスターを最適化するには、アクセスパターンの理解が不可欠です。本記事のガイドラインを出発点として活用してください。インデックスを作成する前にアクセスパターンを確認し、テスト環境でさまざまなインデックスアナライザーを試して、クエリの簡素化やクラスター全体のパフォーマンス向上に役立つか確認してください。OpenSearch クラスターの一般的な最適化手法については、 Get started with Amazon OpenSearch Service: T-shirt-size your domain の記事を参照してください。 著者について Rakan Kandah Rakan は、AWS のソリューションアーキテクトです。余暇にはギターの演奏や読書を楽しんでいます。 この記事は Kiro が翻訳を担当し、Solutions Architect の 榎本 貴之 がレビューしました。