Valkey: 搜尋與 JSON

使用 Valkey 搜尋模組儲存與查詢 JSON 文件

👋 歡迎來到 Stackhero 文件中心!

Stackhero 提供即時可用的 Valkey cloud 解決方案,帶來多項優勢,包括:

  • 內建 Valkey Admin 網頁管理介面
  • 無限制的訊息大小與傳輸量。
  • 一鍵輕鬆完成更新
  • 專屬私有基礎架構提供最佳效能與強大安全性

節省時間簡化您的工作流程:只需 5 分鐘即可體驗 Stackhero 的 Valkey cloud hosting 解決方案!

您在 Stackhero 上的 Valkey 實例已包含由 Valkey 專案維護的核心模組。這些模組讓 Valkey 不僅僅是快取:您可以原生儲存 JSON 文件、建立資料索引,並執行標籤、數值、文字與向量查詢,無需額外管理獨立的資料庫。

目前有三個模組可用,皆已包含於您的方案中,並採用與 Valkey 相同的 BSD 3-Clause 寬鬆授權:

  • JSONJSON.* 指令):支援 JSONPath,可儲存與更新結構化 JSON 文件。您可以直接讀取或修改單一欄位,針對小幅變更無需重寫整份文件。
  • SearchFT.* 指令):可進行次級索引、標籤、數值、全文查詢,以及向量相似度搜尋。
  • BloomBF.* 指令):利用 Bloom filter 高效判斷「這個資料是否已經出現過?」適用於大量資料集,且記憶體用量極低。

模組預設為關閉,因此現有服務會維持原有行為,直到您主動啟用。

  1. Stackhero 控制台 開啟您的服務。
  2. 前往服務設定頁面。
  3. Modules 區段,勾選您要啟用的模組。
  4. 儲存變更。

Valkey 會重新啟動以載入所選模組,通常僅需數秒。

若您已使用某個模組儲存資料(例如 JSON 文件或 Bloom filter),請務必保持該模組啟用。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]"

這種方式讓您無需每次更新都在應用程式端抓取與解析整份文件。例如,庫存數量遞減現在只需一個原子操作,避免競爭條件與額外程式碼。

JSON.* 指令集與 RedisJSON API 相容,因此大多數 Valkey 與 Redis 客戶端皆可直接使用。

只需建立一次索引,Valkey 會自動維護所有符合前綴的 key,即使是在建立索引前已存在的 key 也會納入:

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 模組後,您可在 schema 中使用 ON JSON 與 JSONPath 表達式。

Valkey 採用與 Redis 相同的協定,因此任何 Redis 客戶端皆可相容。您可搭配官方 node-redis client 使用下列範例:

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 client 操作 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 資料庫選擇 的延伸,後者僅支援在資料庫 0 建立索引。
  • 索引會佔用您方案的記憶體,與資料本身相同。建立索引後,您可於 Prometheus 指標 監控 used_memory
  • 現有 key 的索引會於背景執行。 執行 FT.CREATE 後,搜尋結果可能在數秒內尚未完整,待索引建置完成即可。
  • 所有模組指令皆已啟用。 您的服務可使用所有模組的所有指令,無任何限制。

若您想進一步探索向量相似度、語意搜尋或檢索增強生成(RAG),請參閱 向量搜尋指南