Redis 全文搜索 (RediSearch):索引构建与复杂查询

大家好,欢迎来到今天的RediSearch全文搜索讲座。今天我们要聊的是RediSearch中最核心的部分:索引构建和复杂查询。准备好了吗?让我们开始吧!

RediSearch:不只是个缓存,还是个搜索高手

Redis,我们都知道,是个速度飞快的键值存储。但RediSearch呢?它给Redis装上了一个强大的全文搜索引擎,让Redis不仅仅能存东西,还能高效地搜索!想象一下,你有一个庞大的商品数据库,以前搜东西慢得像蜗牛,现在用RediSearch,嗖的一下就出来了,是不是很爽?

索引:搜索的基石

要让RediSearch搜索得快,首先得建立索引。索引就像书的目录,没它,你只能一页一页地翻,有了它,直接定位到你想看的那页。

1. 创建索引:FT.CREATE 命令

创建索引的核心命令是 FT.CREATE。这个命令有很多参数,我们一点点来看。

FT.CREATE {index_name}
  [ON {HASH | JSON}]
  [PREFIX {count} {prefix} ...]
  [FILTER {expression}]
  SCHEMA {field} [TEXT | NUMERIC | GEO | TAG | VECTOR] [options ...]
  • {index_name}: 索引的名字,随便你起,但别太离谱。
  • ON {HASH | JSON}: 数据类型,是Redis的Hash还是JSON。
  • PREFIX {count} {prefix} ...: 指定哪些key需要被索引。count是前缀的数量,prefix是具体的前缀。
  • FILTER {expression}: 过滤条件,只有满足条件的key才会被索引。
  • SCHEMA {field} [TEXT | NUMERIC | GEO | TAG | VECTOR] [options ...]:最关键的部分,定义索引的字段和类型。

举个栗子:索引商品数据

假设我们有这样的商品数据(存储在Redis Hash中):

HMSET product:1 id 1 name "Awesome T-Shirt" description "A super cool t-shirt for everyone" price 29.99 category "Clothing" tags "fashion,casual,summer"
HMSET product:2 id 2 name "Fancy Pants" description "Elegant pants for special occasions" price 59.99 category "Clothing" tags "formal,party"
HMSET product:3 id 3 name "Running Shoes" description "Comfortable shoes for running" price 79.99 category "Shoes" tags "sports,running,fitness"

我们要创建一个索引来搜索这些商品,可以这样做:

FT.CREATE product_idx
  ON HASH
  PREFIX 1 product:
  SCHEMA
    name TEXT WEIGHT 5.0
    description TEXT
    price NUMERIC SORTABLE
    category TAG SEPARATOR ","
    tags TAG

解释一下:

  • product_idx: 索引的名字。
  • ON HASH: 数据存储在Hash中。
  • PREFIX 1 product:: 只索引以 product: 开头的key。
  • SCHEMA: 定义字段和类型。
    • name TEXT WEIGHT 5.0: name 字段是文本类型,WEIGHT 5.0 表示这个字段在搜索结果中的权重是5.0(权重越高,搜索结果越靠前)。
    • description TEXT: description 字段是文本类型,默认权重是1.0。
    • price NUMERIC SORTABLE: price 字段是数值类型,SORTABLE 表示可以根据价格排序。
    • category TAG SEPARATOR ",": category 字段是标签类型,分隔符是逗号。
    • tags TAG: tags 字段是标签类型,默认分隔符是逗号。

字段类型详解

  • TEXT: 文本类型,用于全文搜索。
  • NUMERIC: 数值类型,用于数值范围搜索和排序。
  • GEO: 地理位置类型,用于地理位置搜索。
  • TAG: 标签类型,用于精确匹配,性能比TEXT好。
  • VECTOR: 向量类型,用于相似性搜索(需要指定算法和维度)。

options 详解

  • WEIGHT: 权重,只有TEXT类型才能设置。
  • SORTABLE: 可排序,NUMERIC和TAG类型可以设置。
  • NOINDEX: 不索引,但可以用于FILTER。
  • SEPARATOR: 分隔符,TAG类型可以设置。
  • WITHSUFFIXTRIE: 用于自动补全。

2. 添加数据:HMSET (JSON.SET)

数据添加,就是往Redis里存数据。如果ONHASH,就用 HMSET 命令;如果ONJSON,就用 JSON.SET 命令。

# HASH
HMSET product:4 id 4 name "Warm Socks" description "Cozy socks for winter" price 9.99 category "Clothing" tags "winter,cozy"

# JSON
JSON.SET product:5 $ '{"id": 5, "name": "Stylish Hat", "description": "A fashionable hat", "price": 19.99, "category": "Clothing", "tags": ["fashion", "hat"]}'

3. 更新索引:FT.DROPINDEX 和 FT.CREATE

如果你想修改索引结构,比如添加一个字段,你需要先删除旧的索引,然后重新创建。

FT.DROPINDEX product_idx
FT.CREATE product_idx ... (新的索引定义)

复杂查询:让搜索更精准

索引建好了,接下来就是查询。FT.SEARCH 命令是查询的核心。

FT.SEARCH {index_name} {query} [options ...]
  • {index_name}: 索引的名字。
  • {query}: 查询语句,这是最复杂的部分。
  • [options ...]:一些可选的参数,比如排序、分页等。

查询语法:一门新的语言

RediSearch的查询语法自成一派,有点像SQL,但更简洁。

1. 简单文本搜索

FT.SEARCH product_idx "t-shirt"

这个命令会搜索 namedescription 字段中包含 "t-shirt" 的商品。

2. 指定字段搜索

FT.SEARCH product_idx "@name:t-shirt"

只在 name 字段中搜索 "t-shirt"。

3. AND 和 OR

FT.SEARCH product_idx "t-shirt socks"  # AND
FT.SEARCH product_idx "t-shirt | socks" # OR
  • t-shirt socks: 搜索同时包含 "t-shirt" 和 "socks" 的商品(AND)。
  • t-shirt | socks: 搜索包含 "t-shirt" 或者 "socks" 的商品(OR)。

4. NOT

FT.SEARCH product_idx "t-shirt -socks"

搜索包含 "t-shirt" 但不包含 "socks" 的商品。

5. 短语搜索

FT.SEARCH product_idx ""super cool t-shirt""

搜索包含 "super cool t-shirt" 这个短语的商品。

6. 前缀搜索

FT.SEARCH product_idx "t-sh*"

搜索 namedescription 字段中以 "t-sh" 开头的词。

7. 模糊搜索

FT.SEARCH product_idx "%t-sirt%"

允许一个拼写错误,搜索 namedescription 字段中与 "t-shirt" 相似的词。

8. 数值范围搜索

FT.SEARCH product_idx "@price:[10 50]"

搜索价格在 10 到 50 之间的商品。

9. 标签搜索

FT.SEARCH product_idx "@category:{Clothing}"

搜索 category 字段为 "Clothing" 的商品。

FT.SEARCH product_idx "@tags:{fashion|summer}"

搜索 tags 字段包含 "fashion" 或者 "summer" 的商品。

10. 排序

FT.SEARCH product_idx "t-shirt" SORTBY price ASC
FT.SEARCH product_idx "t-shirt" SORTBY price DESC

根据价格升序或降序排序。

11. 分页

FT.SEARCH product_idx "t-shirt" LIMIT 0 10  # 从第0条开始,返回10条
FT.SEARCH product_idx "t-shirt" LIMIT 10 10 # 从第10条开始,返回10条

12. 返回指定字段

FT.SEARCH product_idx "t-shirt" RETURN 2 id name

只返回 idname 字段。

13. 高级查询:FILTER 和 GEO

  • FILTER: 在查询结果的基础上进行过滤。
  • GEO: 用于地理位置搜索。

FILTER 示例

假设我们有一个 discount 字段,表示折扣力度(0-100)。

FT.SEARCH product_idx "t-shirt" FILTER discount > 50

搜索包含 "t-shirt" 且 discount 大于 50 的商品。(注意:discount 需要在SCHEMA中定义为NUMERIC NOINDEX)

GEO 示例

假设我们有一个 location 字段,存储经纬度(格式:longitude,latitude)。

FT.CREATE store_idx
  ON HASH
  PREFIX 1 store:
  SCHEMA
    name TEXT
    location GEO

HMSET store:1 name "Store A" location "116.4074,39.9042"
HMSET store:2 name "Store B" location "114.4977,30.5166"

FT.SEARCH store_idx "* @location:[-122.4194,37.7749 10 km]"

搜索距离经纬度 (-122.4194,37.7749) 10公里以内的店铺。

代码示例:Python + Redis + RediSearch

import redis

# 连接 Redis
r = redis.Redis(host='localhost', port=6379, db=0)

# 创建索引 (假设已经创建)
index_name = "product_idx"

# 查询
query = "t-shirt"
result = r.execute_command("FT.SEARCH", index_name, query)

# 处理结果
total_results = result[0]
print(f"Total results: {total_results}")

for i in range(1, len(result), 2):
    doc_id = result[i]
    fields = result[i+1]
    product = {}
    for j in range(0, len(fields), 2):
        product[fields[j].decode()] = fields[j+1].decode()
    print(f"Product ID: {doc_id.decode()}")
    print(f"Product Name: {product['name']}")
    print(f"Product Price: {product['price']}")
    print("-" * 20)

性能优化:让搜索飞起来

  • 选择合适的字段类型: TAG 类型比 TEXT 类型更快,如果只需要精确匹配,尽量用 TAG
  • 使用 WEIGHT 调整权重: 把重要的字段权重调高,让搜索结果更符合预期。
  • *避免使用 `查询**:*` 查询会扫描所有数据,性能很差。
  • 合理使用 FILTER: FILTER 可以在查询结果的基础上进行过滤,但也会增加一些开销。
  • 监控 Redis 和 RediSearch 的性能: 使用 INFO 命令可以查看 Redis 和 RediSearch 的性能指标。

一些小技巧

  • 使用 RediSearch 的客户端库: 有很多 RediSearch 的客户端库,比如 Python 的 redisearch,可以更方便地操作 RediSearch。
  • 多看文档: RediSearch 的官方文档非常详细,遇到问题可以查阅文档。
  • 实践出真知: 多写代码,多尝试不同的查询,才能真正掌握 RediSearch。

总结

RediSearch 确实是一个强大的工具,但要用好它,需要理解索引的构建、查询的语法和性能优化的技巧。希望今天的讲座能帮助你更好地使用 RediSearch,让你的搜索功能更上一层楼!

最后的彩蛋:常见问题解答

  • RediSearch 会占用多少内存? 取决于你的数据量和索引的大小。
  • RediSearch 支持中文吗? 支持,但需要进行一些配置,比如指定分词器。
  • RediSearch 和 Elasticsearch 有什么区别? RediSearch 基于 Redis,速度更快,但功能相对简单。Elasticsearch 是一个独立的搜索引擎,功能更强大,但配置更复杂。
  • 什么时候应该使用 RediSearch? 当你需要快速的全文搜索,并且已经在使用 Redis 时,RediSearch 是一个不错的选择。

好了,今天的讲座就到这里。 感谢大家的参与! 希望大家多多实践,早日成为 RediSearch 高手!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注