Redis®*: 搜尋與 JSON

使用 Redis 查詢引擎高效儲存與查詢 JSON 文件

👋 歡迎來到 Stackhero 文件中心!

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

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

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

您的 Stackhero Redis 實例已包含 Redis Open Source 所有可用模組。這些模組讓 Redis 不僅僅是快取:您可以直接儲存真正的 JSON 文件、建立索引,並執行全文、數值與標籤查詢。無需再額外新增其他資料庫到您的架構中。

以下四個模組皆包含於您的方案中:

  • JSONJSON.* 指令):新增原生 JSON 類型並支援 JSONPath。您可以讀取或更新單一欄位,無需重寫整份文件。
  • SearchFT.* 指令),也稱為 Redis 查詢引擎:提供全文搜尋、次級索引、聚合與向量搜尋功能。
  • BloomBF.*CF.*CMS.*TOPK.*TDIGEST.* 指令):機率型資料結構,能以極低記憶體成本,快速回答「這個我看過嗎?」等問題,適用於大規模資料集。
  • Time seriesTS.* 指令):提供時間序列類型,具備保留、降採樣與聚合等功能。

Vector setsVADDVSIM)則直接內建於 Redis 本身。這讓您能高效進行嵌入向量相似度搜尋,且在您的服務中始終可用,無需額外設定。

模組預設為停用狀態。這可確保現有服務在您選擇啟用新功能前,維持原有行為。

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

您的 Redis 服務將會重新啟動並載入所選模組。這個過程通常只需幾秒鐘。

一旦您透過某個模組(如 JSON 文件、Bloom filter 或 time series)儲存資料,請務必保持該模組啟用。Redis 無法存取由已停用模組建立的資料,且停用後將導致服務無法啟動。Vector sets 屬於 Redis 核心功能,始終可用。

若要檢查目前已載入哪些模組,您可以執行:

redis-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,"tags":["coffee","kitchen"],"stock":12}'

這種方式讓您可以在伺服器端原子性地讀取或更新單一欄位:

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

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

JSON.ARRAPPEND product:1 $.tags '"gift"'

這樣的流程能簡化開發並減少錯誤。例如,庫存數量遞減時,無需在應用程式端讀取、解析與重寫整份文件,而是直接在伺服器端安全且即時地完成。

完整指令列表請參閱 Redis JSON 文件

您只需宣告一次索引,Redis 會自動為所有符合指定前綴的 key 維護索引,包括建立索引前就已存在的 key。

FT.CREATE productsIndex
  ON JSON
  PREFIX 1 product:
  SCHEMA
    $.name AS name TEXT
    $.brand AS brand TAG
    $.price AS price NUMERIC SORTABLE
    $.tags[*] AS tags TAG

現在您可以執行如下查詢:

# 針對名稱進行全文搜尋,並設定價格範圍
FT.SEARCH productsIndex "@name:(espresso) @price:[0 500]"

# 品牌精確比對,並依價格排序
FT.SEARCH productsIndex "@brand:{Bianca}" SORTBY price ASC

# 前綴查詢,適用於自動完成場景
FT.SEARCH productsIndex "@name:(espr*)"

Redis 也能直接在索引上執行聚合運算,讓您無需匯出資料即可進行分析查詢:

FT.AGGREGATE productsIndex "*"
  GROUPBY 1 @brand
  REDUCE COUNT 0 AS products
  REDUCE AVG 1 @price AS averagePrice
  SORTBY 2 @products DESC

如果您是以純 hash 儲存資料而非 JSON,可以在 schema 中使用 ON HASH 與標準欄位名稱。

import { createClient, SCHEMA_FIELD_TYPE } from 'redis';

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

// 啟動時建立索引
try {
  await client.ft.create(
    'productsIndex',
    {
      '$.name': { type: SCHEMA_FIELD_TYPE.TEXT, AS: 'name' },
      '$.brand': { type: SCHEMA_FIELD_TYPE.TAG, AS: 'brand' },
      '$.price': { type: SCHEMA_FIELD_TYPE.NUMERIC, AS: 'price', SORTABLE: true }
    },
    { ON: 'JSON', PREFIX: 'product:' }
  );
}
catch (error) {
  if (!error.message.includes('Index already exists')) {
    throw error;
  }
}

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

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

await client.quit();
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_REDIS_URL_TLS'], decode_responses=True)

# 啟動時建立索引
try:
    r.ft('productsIndex').create_index(
        (
            TextField('$.name', as_name='name'),
            TagField('$.brand', as_name='brand'),
            NumericField('$.price', as_name='price', sortable=True),
        ),
        definition=IndexDefinition(prefix=['product:'], index_type=IndexType.JSON),
    )
except redis.ResponseError as error:
    if 'Index already exists' not in str(error):
        raise

r.json().set('product:1', '$', {
    'name': 'Espresso machine',
    'brand': 'Bianca',
    'price': 459,
})

results = r.ft('productsIndex').search(Query('@name:(espresso) @price:[0 500]'))
print(results.total, results.docs)
  • Search 索引僅適用於資料庫 0。 在其他資料庫執行 FT.CREATE 會出現錯誤。如果您的應用程式使用多個邏輯資料庫,請將可被索引的資料放在資料庫 0,或以 key 前綴區分不同資料集。
  • 索引會佔用您方案的記憶體,就像資料一樣。 大型文字或向量索引可能佔用不少空間,建立後可透過 Prometheus 指標 監控 used_memory
  • 針對現有 key 建立索引會在背景執行。 剛執行 FT.CREATE 後,搜尋結果可能暫時不完整。您可用 FT.INFO productsIndex 監控索引進度。
  • 所有模組指令皆可完整使用。 每個已啟用模組的所有指令都可在您的服務中直接使用。

若想進一步了解向量相似度、語意搜尋或檢索增強生成(retrieval-augmented generation),請參閱 向量搜尋指南