Redis®*: 搜索与 JSON

使用 Redis 查询引擎高效存储和查询 JSON 文档

👋 欢迎来到 Stackhero 文档!

Stackhero 提供即开即用的 Redis cloud 解决方案,带来多项优势,包括:

  • 集成 Redis Commander 网页界面
  • 无限制的消息大小与传输。
  • 只需一键即可轻松完成更新
  • 依托专属私有基础设施,实现卓越性能与强大安全性

节省时间简化您的工作流程:只需 5 分钟即可体验 Stackhero 的 Redis cloud hosting 方案!

您在 Stackhero 上的 Redis 实例包含了 Redis Open Source 提供的所有模块。这些模块让 Redis 不仅仅是一个缓存:您可以直接存储真实的 JSON 文档,对其建立索引,并运行全文、本地数值和标签查询。无需为您的技术栈再添加其他数据库。

以下四个模块均包含在您的方案中:

  • JSONJSON.* 命令):为 Redis 增加了原生 JSON 类型,并支持 JSONPath。您可以读取或更新单个字段,无需重写整个文档。
  • SearchFT.* 命令),也称为 Redis 查询引擎:提供全文搜索、二级索引、聚合和向量搜索能力。
  • BloomBF.*CF.*CMS.*TOPK.*TDIGEST.* 命令):概率型数据结构,可在大规模数据集上用极少内存高效回答“这个我见过吗?”等问题。
  • Time seriesTS.* 命令):提供时间序列类型,支持数据保留、降采样和聚合功能。

Vector setsVADDVSIM)直接内置于 Redis 内核。这些功能支持高效的向量相似度搜索(如 embeddings),始终可用,无需任何配置。

模块默认处于禁用状态。这确保现有服务在您主动启用新功能前,保持原有行为。

  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

现在,您可以执行如下查询:

# 对 name 字段进行全文搜索,并限定价格区间
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 监控索引进度。
  • 所有模块命令均可完整使用。 每个已启用模块的所有命令都可在您的服务中直接使用。

如需了解更多关于向量相似度、语义搜索或检索增强生成(RAG)的内容,请参阅 向量搜索指南