Amazon Web Services ブログ

Amazon ElastiCache での全文検索、完全一致検索、範囲検索、ハイブリッド検索

本記事は 2026 年 5 月 7 日に公開された “Full-text, exact-match, range, and hybrid search on Amazon ElastiCache” を翻訳したものです。

訳者註:本記事の全文検索は、現時点では言語オプションとして english のみをサポートしており、日本語テキストはスペースや句読点で区切られた単位でインデックスされます。日本語を全文検索する場合は、事前に形態素解析で語ごとにスペースで区切ったテキストをインデックスし、検索クエリも同じ形で渡すようにしてください。


Amazon ElastiCache は、別個の検索サービスを使用することなく、キャッシュ内で直接リアルタイムの全文検索、完全一致検索、数値範囲検索、ハイブリッド検索をサポートするようになりました。低レイテンシーで動的データに対するスケーラブルな検索を必要とするワークロードに対して、アプリケーションはマイクロ秒単位のレイテンシーと毎秒最大数百万回の検索操作のスループットでテラバイト規模のデータを検索できます。これらの新しい検索機能により、開発者は ElastiCache に既に保存されているデータを、単純なキーバリュー検索を超えた属性で柔軟にクエリできるようになります。

完全一致検索は、商品名、カテゴリ、ユーザー ID、注文番号など、テキスト、タグ、数値属性にわたる正確な値の一致によってドキュメントを取得します。数値範囲検索は、価格のしきい値、日付範囲、取引金額などの属性によってドキュメントをフィルタリングします。完全一致に加えて、全文検索はテキスト属性に対して、オートコンプリート (入力補完) 用のプレフィックスマッチング、タイプミス許容のためのファジーマッチング、複数語検索のための近接マッチングを実行します。これらの検索タイプをベクトル類似度と単一のハイブリッドクエリで組み合わせることができ、正確な用語と意味的な意図の両方を捉えることで、いずれかの手法を単独で使用するよりも関連性の高い結果を提供します。ベクトルワークロードにおいて、ElastiCache for Valkey は AWS 上の主要なベクトルデータベースの中で、95% 以上の再現率で最低レイテンシかつ最高スループットのベクトル検索、そして最高の価格性能比を実現します。

これらの検索機能は、ElastiCache version 9.0 for Valkey で利用可能であり、リアルタイム分析やレポーティング向けのサーバーサイド集計機能も併せて提供されています (Amazon ElastiCache での集計機能のお知らせ を参照)。ElastiCache version 9.0 for Valkey では、個々のフィールドに対するきめ細かい TTL 制御を可能にするハッシュフィールドの有効期限機能や、最大 40% 向上したパイプライン化スループットも導入されています。リリースの詳細については、Amazon ElastiCache 向け Valkey 9.0 のお知らせ をご覧ください。

この記事では、新しい検索機能を順を追って紹介し、それらがどのように連携するかを示しながら、検索およびレコメンデーションエンジンをゼロから構築していきます。

複数のアプリケーションにわたる強力なリアルタイム検索を実現

お客様からは、アプリケーションがスケールするにつれて、検索ワークフローがビジネスに求められるスループットをサポートしながら、ユーザーが期待する低レイテンシーの体験を維持する必要があるとの声が寄せられています。例えば、決済プラットフォーム、ストリーミングプラットフォーム、オンライン小売業者などは、何百万ものドキュメントを ElastiCache に保存し、マイクロ秒のレイテンシーでメタデータ属性によってデータを取得する必要があります。さらに、ワークロードが進化するにつれて、すでに ElastiCache に保存されているデータに対して新しいユースケースをサポートする豊富な検索クエリが必要だというお客様の声もあります。例えば、アプリケーションはデバイスタイプ、セッション状態、ユーザーアクティビティなどのユーザーおよびセッションコンテキストを ElastiCache に保存し、低レイテンシーの体験を提供することがよくあります。ワークロードが進化するにつれて、お客様はその同じデータをレコメンデーションシステムの基盤として活用したいと考えており、そのためにはこれらの属性をまたいだ検索が必要になります。

ElastiCache では、マイクロ秒単位の低レイテンシーと毎秒数百万クエリ (QPS) のスループットでデータを検索・取得するためのさまざまな方法が提供されるようになりました。書き込みが完了するとすぐにデータが検索可能になるため、アプリケーションは常に最新の結果をクエリできます。これらの機能は、カタログ検索、レコメンデーションエンジン、エージェント型メモリ、リアルタイムのリーダーボード、セッション検索などのユースケースを支えます。

カタログ検索: オンライン小売業者やストリーミングプラットフォームは、顧客が大規模なカタログから商品を見つけられるような検索体験を構築しています。これらのプラットフォームでは、商品名や説明文に対するテキスト検索と、ブランド、カテゴリ、価格、評価によるフィルタを単一のクエリで組み合わせることで、ファセットブラウジング体験を提供できます。プレフィックスマッチングは、ユーザーが入力するにつれて候補を読み込むタイプアヘッド検索を実現し、マイクロ秒単位で結果を返すため、即座に反応するような体験を提供します。さらに、ファジーマッチングを活用したタイプミスに強い検索を組み合わせることで、スペルミスを自動的に処理し、検索体験をより堅牢にできます。ファジーマッチングは完全一致検索よりも計算コストが高いため、ElastiCache のようなインメモリ検索エンジン上で実行することで、高速で応答性の高い体験を維持できます。

レコメンデーションエンジン: カタログが数百万アイテムに拡大するにつれて、ユーザーはデジタルプラットフォームに対して、関連性の高いコンテンツや商品を素早く表示するパーソナライズされた閲覧体験を期待しています。最新のレコメンデーションシステムは、ユーザーとアイテムをベクトル埋め込みとしてエンコードします。これらのシステムは、ベクトル検索と名前、説明、カテゴリ、在庫状況、価格帯などのフィルターを組み合わせて、数百万のアイテムからレコメンデーション候補を取得します。ハイブリッド検索は、テキスト、タグ、数値フィルターをベクトル類似度と単一のクエリで組み合わせることでこれをサポートし、取得される候補は意味的に関連性があり、ビジネス上の制約も満たします。商品ページでは、同じカテゴリと価格帯にフィルタリングしてから埋め込みの類似度でランク付けすることで、「類似アイテム」を表示できます。これを拡張して、行動履歴 (閲覧したアイテムの埋め込みの平均プーリング、アテンションベースのモデル、シーケンシャルモデルなどの手法を使用) からユーザー埋め込みを構築し、それをベクトルクエリとして渡すことで、学習した嗜好に基づいて結果をランク付けするパーソナライズされたレコメンデーションを実現できます。

エージェントメモリ: エージェントメモリは、エージェントが過去のやり取りから学習することで、会話履歴全体を再生することなく応答の関連性を向上させ、トークンコストを削減します。エージェントメモリシステムは、スコープ属性 (ユーザー、エージェント、セッション) と現在のやり取りに対する意味的関連性によってメモリを保存・取得します。ハイブリッド検索を使用することで、これらのシステムはスコープやテキストフィルターとベクトル類似性を 1 つのクエリで組み合わせます。エージェントメモリはライブの会話パス上にあるため、書き込み後すぐに読み取れる可視性が求められ、新しく保存された事実が即座に取得可能である必要があり、新しいメモリの取得と統合のために高い同時読み書き性能が求められます。ElastiCache は書き込み時にメモリを同期的にインデックス化し、マルチスレッディングを活用し、AWS 上の主要なベクトルデータベースの中で最高のスループットをマイクロ秒レベルのレイテンシーで提供します。ElastiCache と Mem0 を使用したステップバイステップの実装については、Build persistent memory for agentic AI applications with Mem0 Open Source, ElastiCache for Valkey, and Amazon Neptune Analytics をご覧ください。

ElastiCache for Valkey は、セルフマネージドのメモリレイヤーを構築したい場合や、低レイテンシでカスタマイズ可能なインメモリストアが必要な場合に適しています。フルマネージドのアプローチをお好みの場合は、Amazon Bedrock AgentCore Memory を使用してメモリを管理することもできます。

金融アプリケーションとリーダーボード: 取引プラットフォームやゲームアプリケーションでは、取引金額、タイムスタンプ、リスクスコア、プレイヤーランキングといった数値属性を持つドキュメントを保存し、低レイテンシーで取得する必要があります。ElastiCache の数値範囲クエリは、これらの属性に対する高速な検索をサポートし、時間枠、金額のしきい値、スコア帯によるフィルタリングを可能にします。ゲームアプリケーションでは、スコアの更新を即座に反映するリアルタイムなリーダーボードを維持でき、「自分の地域のトップ 100 プレイヤー」のような範囲クエリにも対応できます。

ユーザーおよびセッション管理: 各業界のアプリケーションは、セッション管理のためにセッション ID、デバイスタイプ、ユーザーハンドルといった構造化属性をキャッシュに保存しています。これらのアプリケーションは、ユーザーがログインするとセッションデータをキャッシュに書き込み、セッションのライフサイクルを通じて更新するため、即時に検索可能な高速書き込みが求められます。ElastiCache は更新を同期的にインデックス化するため、セッション属性に対する検索は遅延なく最新の状態を反映します。完全一致検索により、数百万のドキュメントから正確な識別子に基づいてアクティブなセッションや権限をサブミリ秒のレイテンシーで特定できます。

ElastiCache を使った検索・レコメンデーションエンジンの構築

これらの検索タイプを組み合わせて実演するため、エレクトロニクス、美容、家庭用品など何百万もの製品を販売する e コマースプラットフォーム AnyCompany 向けの検索およびレコメンデーションエンジンを構築します。AnyCompany は、買い物客がキーワードで製品を見つけ、ブランドや価格帯などのフィルターで絞り込み、類似性を通じて関連商品を発見できる検索体験を求めています。AnyCompany は 100 万を超える商品カタログを ElastiCache にハッシュベースのドキュメントとして保存しています (この例では、実際のタイトル、説明、ブランドを含む Amazon ESCI データセットから派生したもの)。次のコードは、このデータに対して 5 つのクエリパターンを構築します: 入力補完検索、全文一致、タイポに強いマッチング、フィルター付きブラウジング、そして類似商品のレコメンデーションです。

前提条件

この記事の例では、valkey-py クライアントライブラリと Python を使用しています。手順を実行するには、以下が必要です (所要時間の目安: 30 分):

この記事の完全なサンプルコードは、ElastiCache samples GitHub リポジトリで入手できます。

ElastiCache for Valkey クラスターのセットアップ

ElastiCache の検索用クラスターは、AWS Management Console または AWS CLI を使用して作成できます。以下の例では CLI を使用しています。検索機能は ElastiCache for Valkey バージョン 9.0 以降で利用可能です。

aws elasticache create-replication-group \
--replication-group-id AnyCompany-cache \
--replication-group-description "AnyCompany Valkey cluster" \
--engine valkey \
--engine-version 9.0 \
--transit-encryption-enabled \
--cache-node-type cache.r7g.large \
--replicas-per-node-group 0

インデックスの作成とデータのロード

商品データを検索可能にするため、products_vec_index というインデックスを作成します。タイトルと説明は、キーワード、前方一致、あいまい検索をサポートする全文検索可能な属性としてインデックス化されます。ブランドと色は、絞り込み検索のために完全一致タグとしてインデックス化されます。価格、評価、在庫は、範囲クエリやソートのためにソート可能な数値属性としてインデックス化されます。embedding は、セマンティック類似検索やレコメンデーションのためにベクトル属性としてインデックス化されます。

import gzip
import json
import struct
import urllib.request
import valkey
from valkey.commands.search.field import TextField, TagField, NumericField, VectorField
from valkey.commands.search.indexDefinition import IndexDefinition, IndexType

# : Insert your ElastiCache cluster's endpoint
VALKEY_HOST = "placeholder_cluster.cnxa6h.clustercfg.use1.cache.amazonaws.com"

client = valkey.Valkey(host=VALKEY_HOST, port=6379, decode_responses=False, ssl=True,
    ssl_cert_reqs="required")

# Create the search index with text, tag, numeric, and vector fields
try: client.execute_command("FT.DROPINDEX", "products_vec_index")
except: pass

client.ft("products_vec_index").create_index(
    fields=[
        TextField("title"),
        TextField("description"),
        TagField("brand", separator=","),
        TagField("color", separator=","),
        NumericField("price"),
        NumericField("rating"),
        NumericField("stock"),
        VectorField("embedding", "FLAT", {
            "TYPE": "FLOAT32",
            "DIM": 64,
            "DISTANCE_METRIC": "COSINE"})],
    definition=IndexDefinition(prefix=["pv:"], index_type=IndexType.HASH))

ElastiCache ストアに商品データセットを投入します。このデータセットは 130 万件の商品のサブセットで、タイトル、説明、ブランド、および Amazon ESCI Shopping Queries データセットから導出された事前計算済みの 64 次元エンベディングを含む 13.7 万件の商品で構成されています。サンプルリポジトリをクローンし、ロードスクリプトを実行してください:

git clone https://github.com/aws-samples/amazon-elasticache-samples.git
cd amazon-elasticache-samples/blogs/elasticache-valkey/fts-benchmark

# <入力が必要>: VALKEY_HOST 変数をクラスターのエンドポイントで更新して実行:
python load_products_blog.py

タイプアヘッド検索 (先行入力検索)

インデックスとデータが準備できたら、AnyCompany はインデックスに対して実行され、一致するドキュメントを返す FT.SEARCH クエリを使って検索エンジンを構築できます。ユーザーが検索バーに入力すると、アプリケーションはプレフィックスクエリを送信し、リアルタイムで候補を表示します。

from valkey.commands.search.query import Query

results = client.ft("products_vec_index").search(
    Query("wire*").return_fields("title").paging(0, 5))

# User has typed "wire" - prefix match shows suggestions
# Output:
# [{'title': 'xyz Kids Wireless Headphones'},
# ...
# ...
#  {'title': 'Santas Wire Christmas Lighting Storage Bag'}]

フレーズマッチング

ユーザーが Enter キーを押すと、アプリケーションはタイトルと説明に対して全文検索を実行します。SLOP は、単語同士がどれだけ離れていても一致と見なすかを制御し、単語がより近接している結果ほど上位にランク付けされます。

# User submits "wireless headphones"
# SLOP 2 allows up to 2 words between terms
results = client.ft("products_vec_index").search(
    Query("wireless headphones")
    .slop(2)
    .return_fields("title", "brand", "price").paging(0, 5))

# Output:
# [{'title': 'xyz Studio3 Wireless Headphones - Gray (Renewed)',
#    'brand': 'xyz', 'price': '1928.28'},
#  ...
#  {'title': 'xyz TUNE 220TWS - True Wireless in-Ear Headphone - Blue',
#   'brand': 'xyz', 'price': '1121.23'}]

タイプミスを許容するマッチング

クエリが結果を返さない場合、アプリケーションはタイプミスを修正するためにあいまい一致 (fuzzy matching) で再試行します。あいまい一致は編集距離を計算するためコストが高いので、デフォルトとしてではなくフォールバックとして使うのが最適です。

# Retry with fuzzy matching for "wireles headphoens"
results = client.ft("products_vec_index").search(
    Query("%wireles% %headphoens%")
    .return_fields("title", "brand", "price").paging(0, 5))

# Output:
# [{'title': 'xyz Comfort 35 Wireless Headphones, Noise Cancelling - Silver (Renewed)',
#   'brand': 'xyz', 'price': '1811.75'},
#  ...
#  ...
#  {'title': 'xyz SoundSport Wireless Headphones, Black + Charging Case',
#   'brand': 'xyz', 'price': '568.47'}]

フィルタリングによる閲覧

買い物客が商品を検索してフィルターを適用すると、アプリケーションはテキスト検索とタグおよび数値フィルターを単一のクエリで組み合わせます。

# ユーザーが「headphones」を検索し、価格 $50-$150、評価 4.0 以上でフィルタリング
results = client.ft("products_vec_index").search(
    Query("@title:headphones @price:[50 150] @rating:[4.0 5.0]")
    .return_fields("title", "brand", "price", "rating")
    .paging(0, 5))


# 出力:
# [{'title': 'xyz WH1000XM3 Bluetooth Wireless Noise Canceling Headphones',
#   'brand': 'xyz', 'price': '102.29', 'rating': '4.8'},
#  ...
#  ...
#  {'title': 'Bluetooth Earbuds xyz SoundLink .. in Ear Headphones',
#   'brand': 'xyz', 'price': '125.45', 'rating': '4.5'}]

類似商品のレコメンデーション

「類似商品」のレコメンデーションを実現するために、AnyCompany はテキストフィルターを使ったハイブリッド検索で結果を該当する商品タイプに絞り込み、ベクトル検索で表示中の商品との類似度に基づいてフィルター済みの結果をランク付けしています。

# Get the embedding of the product the user is currently viewing
# for example - "Kids Headphones with Microphone 2 Pack"
product_embedding = client.hget("pv:B0825SSTMN", "embedding")

# Hybrid: text pre-filter "headphones" + vector KNN for similarity ranking
results = client.ft("products_vec_index").search(
    Query("@title:headphones =>[KNN 5 @embedding $vec AS score]")
    .return_fields("title", "brand", "price", "score")
    .dialect(2),
    query_params={"vec": product_embedding})

# Output:
#  {'title': 'xyz I35 Kid Headphones with Microphone Volume Limited ...',
#   'brand': 'xyz', 'price': '155.06', 'score': '0.293'},
#  ...
#  ...
#  {'title': 'Kids Headphones with Pouch, xyz Wired ...',
#   'brand': 'xyz', 'price': '957.95', 'score': '0.351'}]

このパターンを拡張して、埋め込みベースの検索を活用したパーソナライゼーションを実現できます。閲覧したアイテム埋め込みの平均プーリング、アテンションベースのモデル、シーケンスモデルなどの手法を用いて、買い物客のインタラクション履歴からユーザー埋め込みを構築します。単一の商品埋め込みの代わりにユーザー埋め込みをベクトルクエリとして渡すことで、KNN スコアリングがフィルタリングされた集合の中から、買い物客の学習済みの嗜好に基づいて結果をランキングします。

内部の仕組みとパフォーマンス

レイテンシーとスループットを、テキストと数値のクエリタイプについて、レプリカなしの単一の cache.r7g.2xlarge ノードを含む 1 シャード構成の ElastiCache for Valkey クラスター上で計測しました。データセットには約 1 GB のデータが含まれており、上記の例で説明したテキスト、タグ、数値、ベクトル属性を持つ 130 万件の製品ドキュメントで構成されています。レイテンシーとスループットの計測には valkey-benchmark を使用しました。

クエリタイプ P50 (ms)
1 クライアント
P99 (ms)
1 クライアント
QPS
300 クライアント
テキスト検索 (完全一致) 0.135 0.255 60,000
前方一致 (タイプアヘッド検索) 0.135 0.279 57,692
数値範囲 (在庫/評価でのフィルタ) 0.175 0.199 24,087
ハイブリッドクエリ – テキスト + 数値範囲 (ファセットブラウジング) 0.135 0.295 52,632

ベクトル検索のレイテンシーとスループットのベンチマークについては、Announcing vector search for Amazon ElastiCache を参照してください。上記の例では、単一の cache.r7g.2xlarge ノードでのパフォーマンスをテストしています。レプリカ (シャードあたり最大 5 つ) とシャードを追加することで読み取りスループットをスケールし、数百万 QPS に到達できます。各レプリカは独自のインデックスを持ち、独立して検索を処理できますが、レプリカからの読み取りは結果整合性となります。データ容量よりも低レイテンシーを優先する場合は、single-slot indexes を使用して、インデックス化されたすべてのデータを 1 つのシャードに保持し、ファンアウトのオーバーヘッドを完全に回避してください。シャードを追加することで、クライアントコードを変更せずにメモリ容量を増やすことができます。

ElastiCache はデータの変更をリアルタイムで自動的にインデックス化し、エンジンは各書き込みを確認応答する前にインデックス化します。そのため、それ以降の検索では更新後のデータが返され、read-after-write 整合性が提供されます。この整合性の動作は、マルチキートランザクションや Lua スクリプトでも同様に保たれます。Valkey はマルチスレッドを活用してインデックス処理を複数のスレッドにまたがって実行するため、ElastiCache は書き込みスループットの高いワークロードにおいても検索クエリで高いパフォーマンスを発揮できます。

クリーンアップ

このウォークスルーのために ElastiCache クラスターを作成し、不要になった場合は、今後の料金が発生しないように、次の AWS CLI コマンドを使用してクラスターを削除してください。

aws elasticache delete-replication-group --replication-group-id AnyCompany-cache

まとめ

本投稿では、ElastiCache における全文検索、完全一致検索、数値範囲検索、およびハイブリッド検索について解説しました。これらの検索タイプのユースケースを取り上げ、検索およびレコメンデーションシステムの構築方法をご紹介しました。全文検索、完全一致検索、数値範囲検索、およびハイブリッド検索は、Valkey 9.0 を実行する ElastiCache のノードベースクラスターにおいて、すべての AWS 商用リージョン、AWS GovCloud (US) リージョン、および中国リージョンで追加費用なしでご利用いただけます。Valkey は、最も制約の少ないオープンソースかつベンダーニュートラルな Redis に代わる選択肢であり、ElastiCache における推奨エンジンです。始めるには、AWS Management Console、AWS SDK、または AWS CLI を使用して、新しい Valkey 9.0 以降のクラスターを作成するか、既存のクラスターをアップグレードしてください。詳細については、ElastiCache のドキュメントをご覧ください。ご質問やフィードバックは、AWS re:Post for ElastiCache までお寄せください。

著者について

Chaitanya Nuthalapati

Chaitanya Nuthalapati

Chaitanya は AWS インメモリデータベースサービスのシニアテクニカルプロダクトマネージャーで、Amazon ElastiCache for Valkey を担当しています。以前は、生成 AI、機械学習、グラフネットワークを活用したソリューションを構築していました。仕事以外の時間では、Chaitanya は趣味を集めることに忙しく、現在はテニス、スケートボード、パドルボードを楽しんでいます。

Karthik Subbarao

Karthik Subbarao

Karthik は Amazon ElastiCache のシニアソフトウェアエンジニアで、オープンソースの Valkey プロジェクトに積極的に貢献しています。分散システム、データベース、Rust、そして全般的にソフトウェア開発・技術を通じたイノベーションに情熱を持っています。

Ian Childress

Ian Childress

Ian は AWS のソフトウェア開発マネージャーで、全文検索インフラストラクチャを含む Valkey モジュールおよび統合機能を構築するチームを率いています。仕事以外では、Ian はホッケーをプレイし、Go 言語で高性能システムを書くことに没頭する飽くなき探求者です。夏になると、氷から湖へと舞台を移し、毎週末家族とウェイクサーフィンを楽しんでいます。

Eran Balan

Eran Balan

Eran は AWS のスペシャリストソリューションアーキテクトで、インメモリデータベースおよび Amazon ElastiCache を専門としています。EMEA 地域のお客様と協力し、キャッシングアーキテクチャの設計、パフォーマンスの最適化、Redis OSS から Valkey への移行など、さまざまな移行を支援しています。仕事以外では、Eran はミュージカルや演劇の観劇、ハイキング、オープンウォータースイミングを楽しんでいます。

プロジェクト全体を通じて、ビジョン、指導、そして実践的な貢献をいただいた Allen Samuels 氏に心より感謝申し上げます。


本記事は、Full-text, exact-match, range, and hybrid search on Amazon ElastiCache を翻訳したものです。翻訳は Solutions Architect の Hayato Tsutsumi が担当しました。