Redis全文搜索教程之创建索引并关联源数据

Redis 全文搜索是依赖于 Redis 官方提供的 RediSearch 来实现的。

RediSearch 提供了一种简单快速的方法对 hash 或者 json 类型数据的任何字段建立二级索引,然后就可以对被索引的 hash 或者 json 类型数据字段进行搜索和聚合操作。

这里我们把被索引的 hash 或者 json 类型数据叫做源数据。

本文大纲如下,

图片[1]-Redis全文搜索教程之创建索引并关联源数据-不念博客

使用体验

简单场景下,用 RediSearch 来平替 Elasticsearch 的使用场景已经足够。像是 Elasticsearch 中常用的查询语法 AND 、OR 、IN 、NOT IN 、> 、< 、= 、like 等,在 RediSearch 中都是支持的。

此外 RediSearch 还支持聚合统计、停用词、文本标记和转义、同义词、标签、排序、向量查询、中文分词等。

就我个人来说,个人项目使用 RediSearch 作为全文搜索引擎已经够用了,它有占用内存低、索引建立快、查询数据性能足够高等优点。

后续发展

就目前官方对 RediSearch 的支持更新来看,

图片[2]-Redis全文搜索教程之创建索引并关联源数据-不念博客
RediSearch 官方更新频率图

最近一次提交记录在 12 月 17 号。

可以看到 RediSearch 的更新频率还是比较高的,而且是官方支持做的模块,不用担心后续无人维护。

虽然 Redis 天生支持分布式集群,但是 RediSearch 对 Redis 集群的支持还不完善,引用官方说明,

图片[3]-Redis全文搜索教程之创建索引并关联源数据-不念博客

官方针对 RediSearch 的集群支持问题,提供了一个 RediSearch 集群版本,但是这个版本只能在 Redis 企业版或者 Redis Cloud 上能使用,开源版还没有,这一点需要告诉大家。

遇到 bug

首先在使用 RediSearch 的过程中,遇到了 bug 并发现 bug 来源于 RediSearch,不要慌,也不要抱怨难用, 毕竟是开源项目,

图片[4]-Redis全文搜索教程之创建索引并关联源数据-不念博客
官方issue列表

大家可以看到 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 类型数据结构上创建二级索引,并且内部极大地简化了这一过程。(最终会出现更多数据结构)

图片[5]-Redis全文搜索教程之创建索引并关联源数据-不念博客
二级索引示意图

如果我们要使用 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.0
  • goodsIntro:文本类型,可排序,设置权重为 0.5
  • goodsId:数字类型,可排序
  • 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 在项目实战中关于索引创建与关联源数据的用法,希望大家喜欢。

© 版权声明
THE END