Valkey: 検索とJSON

Valkeyの検索モジュールでJSONドキュメントを保存・検索する

👋 Stackhero ドキュメントへようこそ!

Stackhero では、すぐに利用可能な Valkey クラウド ソリューションを提供しています。主な特長は以下の通りです:

  • Valkey Admin Web UI を標準搭載。
  • メッセージサイズ・転送量が無制限
  • ワンクリックで簡単にアップデート可能。
  • プライベート専用インフラによる最適なパフォーマンスと強固なセキュリティ

時間を節約し、運用をシンプルに:Stackhero の Valkey クラウドホスティング ソリューションは、わずか5分でお試しいただけます!

Stackhero上のValkeyインスタンスには、Valkeyプロジェクトがメンテナンスしている主要なモジュールが含まれています。これらのモジュールにより、Valkeyは単なるキャッシュ以上の存在となります。ネイティブなJSONドキュメントの保存、データのインデックス作成、タグ・数値・テキスト・ベクトルクエリの実行が、別途データベースを管理することなく可能です。

3つのモジュールが利用可能で、いずれもご契約プランに含まれており、Valkeyと同じBSD 3-Clauseライセンスで提供されています:

  • JSONJSON.* コマンド):JSONPath対応で構造化されたJSONドキュメントの保存・更新が可能です。個々のフィールドを直接読み書きできるため、小さな変更のたびにドキュメント全体を書き換える必要はありません。
  • SearchFT.* コマンド):セカンダリインデックス、タグ、数値、全文検索、ベクトル類似検索を実行できます。
  • BloomBF.* コマンド):Bloomフィルターを使い、大規模データセットに対して「すでに見たことがあるか?」を最小限のメモリで効率的に判定できます。

モジュールはデフォルトで無効化されているため、既存サービスは有効化するまで現在の動作を維持します。

  1. Stackheroダッシュボードでサービスを開きます。
  2. サービスの設定画面に移動します。
  3. Modules セクションで有効化したいモジュールを選択します。
  4. 変更を保存します。

Valkeyは選択したモジュールをロードするために再起動します。通常、数秒で完了します。

モジュール(例:JSONドキュメントやBloomフィルター)を使ってデータを保存している場合は、そのモジュールを有効のままにしてください。Valkeyは、作成元のモジュールがないとそのデータを読み込めません。モジュールを無効化すると、サービスが正常に再起動できなくなります。

現在ロードされているモジュールは、いつでも以下のコマンドで確認できます:

valkey-cli -u "rediss://default:<yourPassword>@<XXXXXX>.stackhero-network.com:<PORT_TLS>" MODULE LIST

構造化ドキュメントをネイティブなJSONとして、文字列化せずに保存できます:

JSON.SET product:1 $ '{"name":"Espresso machine","brand":"Bianca","price":459,"stock":12}'

単一フィールドの読み取りや更新は、高速かつアトミックにサーバー側で処理されます:

JSON.GET product:1 $.price
# "[459]"

JSON.NUMINCRBY product:1 $.stock -1
# "[11]"

この方法により、アプリケーション側で毎回ドキュメント全体を取得・パースする必要がなくなります。例えば、在庫カウンターのデクリメントは1回のアトミック操作で済み、競合状態や余計なコードも不要です。

JSON.* コマンドセットは RedisJSON のAPIと互換性があるため、ほとんどのValkeyおよびRedisクライアントがそのまま利用できます。

インデックスは一度作成すれば、指定したプレフィックスに一致するすべてのキーに対してValkeyが自動で最新状態を維持します。インデックス作成前に書き込まれたキーも対象です:

FT.CREATE productsIndex
  ON HASH
  PREFIX 1 product:
  SCHEMA
    name TEXT
    brand TAG
    price NUMERIC

インデックス化されたデータは、表現力の高い検索コマンドでクエリできます:

# タグの完全一致
FT.SEARCH productsIndex "@brand:{Bianca}"

# 数値範囲
FT.SEARCH productsIndex "@price:[0 500]"

# 複合クエリ
FT.SEARCH productsIndex "@brand:{Bianca} @price:[0 500]"

# 名前による全文検索
FT.SEARCH productsIndex "espresso"

同じインデックスに対して FT.AGGREGATE で集計処理も可能です。FT.INFO コマンドでは、インデックスの状態やバックグラウンドインデックス作成の進捗を確認できます。

JSONドキュメントもインデックス化可能です。JSONモジュールが有効な場合、スキーマで ON JSON やJSONPath式を利用できます。

ValkeyはRedisと同じプロトコルを使用しているため、どのRedisクライアントでも利用可能です。以下は公式 node-redisクライアント を使った例です:

import { createClient, SCHEMA_FIELD_TYPE } from 'redis';

const client = createClient({ url: process.env.STACKHERO_VALKEY_URL_TLS });
await client.connect();

// 起動時に一度だけインデックスを作成
try {
  await client.ft.create(
    'productsIndex',
    {
      name: SCHEMA_FIELD_TYPE.TEXT,
      brand: SCHEMA_FIELD_TYPE.TAG,
      price: SCHEMA_FIELD_TYPE.NUMERIC
    },
    { ON: 'HASH', PREFIX: 'product:' }
  );
} catch (error) {
  if (!error.message.includes('Index already exists')) {
    throw error;
  }
}

await client.hSet('product:1', { name: 'Espresso machine', brand: 'Bianca', price: '459' });

const results = await client.ft.search('productsIndex', '@brand:{Bianca} @price:[0 500]');
console.log(results.total, results.documents);

await client.quit();

redis-pyクライアント を使ってValkeyの検索機能を利用できます:

import os
import redis
from redis.commands.search.field import TextField, TagField, NumericField
from redis.commands.search.index_definition import IndexDefinition, IndexType
from redis.commands.search.query import Query

r = redis.from_url(os.environ['STACKHERO_VALKEY_URL_TLS'], decode_responses=True)

# 起動時に一度だけインデックスを作成
try:
    r.ft('productsIndex').create_index(
        (TextField('name'), TagField('brand'), NumericField('price')),
        definition=IndexDefinition(prefix=['product:'], index_type=IndexType.HASH),
    )
except redis.ResponseError as error:
    if 'Index already exists' not in str(error):
        raise

r.hset('product:1', mapping={'name': 'Espresso machine', 'brand': 'Bianca', 'price': 459})

results = r.ft('productsIndex').search(Query('@brand:{Bianca} @price:[0 500]'))
print(results.total, results.docs)
  • 検索モジュールは厳選されたコマンドセットを提供しますFT.CREATEFT.SEARCHFT.AGGREGATEFT.INFOFT.DROPINDEXFT._LIST。これらでタグ・数値・テキスト・ベクトルクエリを効率的にカバーします。アプリケーションでスペルチェックや同義語、サジェスト辞書など高度な全文検索機能が必要な場合は、Redisクエリエンジンがより多機能です。
  • インデックスはデータベース単位で作成され、各データベースごとに動作します。 例えば、データベース3で作成したインデックスはデータベース3からのみ参照・検索可能です。これはValkeyがRedisのデータベース選択よりも拡張されている点で、Redisはデータベース0のみインデックス作成をサポートしています。
  • インデックスはプランのメモリを消費します。データ同様、インデックス作成後はPrometheusメトリクスused_memoryで監視できます。
  • 既存キーのインデックス作成はバックグラウンドで実行されます。 FT.CREATE実行直後は、インデックス構築中のため数秒間検索結果が不完全な場合があります。
  • すべてのモジュールコマンドが有効です。 すべてのモジュールの全コマンドがサービス上で制限なく利用できます。

ベクトル類似検索、セマンティック検索、RAG(retrieval-augmented generation)を試したい場合は、ベクトル検索ガイドをご覧ください。