Redis 全文搜索是依赖于 Redis 官方提供的 RediSearch 来实现的。
RediSearch 提供了一种简单快速的方法对 hash 或者 json 类型数据的任何字段建立二级索引,然后就可以对被索引的 hash 或者 json 类型数据字段进行搜索和聚合操作。
这里我们把被索引的 hash 或者 json 类型数据叫做源数据。
本文大纲如下,
![Redis全文搜索教程之创建索引并关联源数据 图片[1]-Redis全文搜索教程之创建索引并关联源数据-不念博客](https://www.bunian.cn/wp-content/uploads/2023/12/640-1-15.png)
使用体验
简单场景下,用 RediSearch 来平替 Elasticsearch 的使用场景已经足够。像是 Elasticsearch 中常用的查询语法 AND 、OR 、IN 、NOT IN 、> 、< 、= 、like 等,在 RediSearch 中都是支持的。
此外 RediSearch 还支持聚合统计、停用词、文本标记和转义、同义词、标签、排序、向量查询、中文分词等。
就我个人来说,个人项目使用 RediSearch 作为全文搜索引擎已经够用了,它有占用内存低、索引建立快、查询数据性能足够高等优点。
后续发展
就目前官方对 RediSearch 的支持更新来看,
![Redis全文搜索教程之创建索引并关联源数据 图片[2]-Redis全文搜索教程之创建索引并关联源数据-不念博客](https://www.bunian.cn/wp-content/uploads/2023/12/640-2-15.png)
最近一次提交记录在 12 月 17 号。
可以看到 RediSearch 的更新频率还是比较高的,而且是官方支持做的模块,不用担心后续无人维护。
虽然 Redis 天生支持分布式集群,但是 RediSearch 对 Redis 集群的支持还不完善,引用官方说明,
![Redis全文搜索教程之创建索引并关联源数据 图片[3]-Redis全文搜索教程之创建索引并关联源数据-不念博客](https://www.bunian.cn/wp-content/uploads/2023/12/640-3-14.png)
官方针对 RediSearch 的集群支持问题,提供了一个 RediSearch 集群版本,但是这个版本只能在 Redis 企业版或者 Redis Cloud 上能使用,开源版还没有,这一点需要告诉大家。
遇到 bug
首先在使用 RediSearch 的过程中,遇到了 bug 并发现 bug 来源于 RediSearch,不要慌,也不要抱怨难用, 毕竟是开源项目,
![Redis全文搜索教程之创建索引并关联源数据 图片[4]-Redis全文搜索教程之创建索引并关联源数据-不念博客](https://www.bunian.cn/wp-content/uploads/2023/12/640-4-15.png)
大家可以看到 issue 列表中有很多 bug 没有解决。
不过本着开源共进的精神,希望大家发现了 bug 后,第一时间在 RediSearch 官方 github 上提个 issue,方便官方发现并解决问题。
RediSearch Github 仓库地址:https://github.com/RediSearch/RediSearch
下面我给大家用 newbee-mall-pro 项目作为样本,给大家介绍下如何创建一个索引并关联源数据。
newbee-mall-pro 项目地址:https://github.com/wayn111/newbee-mall-pro
添加源数据
在 newbee-mall-pro 项目中,已经将商品数据以 hash 类型存入了 Redis 中,
其中,我们对于 key 名称的定义规则是按照 newbee_mall:goods: + 商品ID
。
这里我们的 key 名称定义规则很重要,RediSearch 创建索引会基于 key 名称前缀来生成。
hash 类型的 value 包含属性如下,
goodsId
: 商品 ID,唯一属性,由数据库商品表主键生成goodsName
: 商品名称goodsIntro
: 商品简介goodsCategoryId
: 商品分类 ID,唯一属性,由数据库商品分类表主键生成goodsSellStatus
: 商品上架状态,0 代表下架,1 代表上架sellingPrice
: 商品售价originalPrice
: 商品原价tag
: 商品标签
在 newbee-mall-pro 中,添加源数据的方法已经写好了,代码逻辑在 JedisSearch.addGoodsListIndex()
方法里,
public boolean addGoodsListIndex(String keyPrefix, List<Goods> list) {
int chunk = 200;
List<List<Goods>> partition = ListUtil.partition(list, chunk);
AbstractPipeline pipelined = client.pipelined();
for (List<Goods> goodsList : partition) {
for (Goods goods : goodsList) {
RsGoodsDTO target = new RsGoodsDTO();
MyBeanUtil.copyProperties(goods, target);
Map<String, String> hash = MyBeanUtil.toMap(target);
// 支持中文
hash.put("_language", Constants.GOODS_IDX_LANGUAGE);
pipelined.hset(keyPrefix + goods.getGoodsId(), hash);
}
}
pipelined.sync();
return true;
}
上诉代码中,其实就是把 list 商品列表以 hash 类型的数据结构写进 Redis 中,并且为了加快写入速度,使用了 Redis 提供的管道操作。
需要注意的就是 hash 类型中新增了一个 _language
字段,用于指定 RediSearch 对于源数据关联的索引,要使用中文分词查询。
建立索引
RediSearch 通过提供一种简单且自动的方式在 Redis hash 类型数据结构上创建二级索引,并且内部极大地简化了这一过程。(最终会出现更多数据结构)
![Redis全文搜索教程之创建索引并关联源数据 图片[5]-Redis全文搜索教程之创建索引并关联源数据-不念博客](https://www.bunian.cn/wp-content/uploads/2023/12/640-5-13.png)
如果我们要使用 RediSearch 查询商品 hash 结构里的 goodsName 字段,那么必须要对该字段建立索引。
Jedis 新建索引
所以这里,我给大家介绍下在 newbee-mall-pro 项目中,是如何建立索引的,代码逻辑在 GoodsServiceImpl.syncRs()
方法中,
// 定义索引结构
public boolean syncRs() {
jedisSearch.dropIndex("idx:goods");
Schema schema = new Schema()
.addSortableTextField("goodsName", 1.0)
.addSortableTextField("goodsIntro", 0.5)
.addSortableNumericField("goodsId")
.addSortableNumericField("goodsCategoryId")
.addSortableNumericField("goodsSellStatus")
.addSortableNumericField("sellingPrice")
.addSortableNumericField("originalPrice")
.addSortableTagField("tag", "|");
jedisSearch.createIndex(Constants.GOODS_IDX_NAME, Constants.GOODS_IDX_PREFIX, schema);
}
上述代码中,我们对商品 hash 结构里的下方字段都建立了索引。
goodsName
:文本类型,可排序,设置权重为 1.0goodsIntro
:文本类型,可排序,设置权重为 0.5goodsId
:数字类型,可排序goodsCategoryId
:数字类型,可排序goodsSellStatus
:数字类型,可排序sellingPrice
:数字类型,可排序originalPrice
:数字类型,可排序tag
:标签类型,可排序,设置分隔符为字符串|
在 RediSerach 中可以添加的字段类型有 text、numberic、tag 等,可以设置是否排序。
并且还可以设置权重系数,表示该字段已加权。这对于在搜索操作期间为特定字段分配不同的重要性级别非常有用,通常就是在条件筛选完成后的打分排序阶段用于提升或者降低排名。
Redis 中的新建索引语法
当我们把上面的 Jedis 新建索引的代码转换为 Redis 中的语法后,如下
> FT.CREATE idx:goods ON hash PREFIX 1 "newbeemall:goods:" \
SCHEMA goodsName TEXT SORTABLE WEIGHT 1.0 \
SCHEMA goodsIntro TEXT SORTABLE WEIGHT 0.5 \
goodsId NUMERIC SORTABLE \
goodsCategoryId NUMERIC SORTABLE \
goodsSellStatus NUMERIC SORTABLE \
sellingPrice NUMERIC SORTABLE \
originalPrice NUMERIC SORTABLE \
tag TAG SORTABLE SEPARATOR "|"
现在我给大家详细介绍下这条命令:
FT.CREATE
:RediSearch 中索引创建语法。idx:goods
:指定索引名称,索引名称将在所有键名称中使用,因此请保持简短。ON hash
:指定索引关联的结构类型。需要注意的是,在 RediSearch 2.0 中仅支持哈希结构,随着 RediSearch 更新,后续有望支持更多数据结构。PREFIX 1 "newbeemall:goods:"
:指定索引的关联源数据的 key 前缀,可以指定多个前缀。SCHEMA ...
:字段定义,用于定义字段名称、类型、是否排序、权重等。可以定义多个字段。
如果你想了解更多关于 ft.search 的语法以及字段定义相关的只是,可以打开官方文档,
https://redis.io/docs/interact/search-and-query/basic-constructs/schema-definition
Redis 中查询索引定义
在 RediSearch 中要查询已经存在的索引详情也是很简单的,官方提供了 ft.info 索引名称
的语法,用来打印索引详情。> FT.INFO idx:goods
最后聊两句
本文给大家用我的开源项目 newbee-mall-pro 作为样本,给大家细致的介绍了一番 RediSearch 在项目实战中关于索引创建与关联源数据的用法,希望大家喜欢。