redisearch+springboot的简单使用

安装

推荐使用docker安装,使用以下命令即可安装并启动redisearch

docker run -p 6379:6379 redislabs/redisearch:latest

通过下载redisearch二进制文件,将二进制文件加载进redis模块的方式好像对于开源的redis版本不能使用 ,要商业版的才可以。(因为我使用开源redis用了一天,就提示我不能使用了)

使用RediSearch来搜索数据之前,我们得先创建下索引: 

FT.CREATE {index}
  [ON {data_type}]
     [PREFIX {count} {prefix} [{prefix} ..]
     [LANGUAGE {default_lang}]
  SCHEMA {identifier} [AS {attribute}]
      [TEXT | NUMERIC | GEO | TAG ] [CASESENSITIVE]
      [SORTABLE] [NOINDEX]] ...
  • 使用FT.CREATE命令可以建立索引,语法中的参数意义如下;
    • index:索引名称;
    • data_type:建立索引的数据类型,目前支持JSON或者HASH两种;
    • PREFIX:通过它可以给索引加上前缀
    • LANGUAGE:指定TEXT类型属性的默认语言,使用chinese可以设置为中文;
    • identifier:指定属性名称;
    • attribute:指定属性别名;
    • TEXT | NUMERIC | GEO | TAG:这些都是属性可选的类型;
    • SORTABLE:指定属性可以进行排序。

看一下官方文档是怎么使用的https://redis.io/docs/stack/search/quick_start/

创建索引

使用FT.CREATE命令创建具有字段和权重的索引(默认权重为 1.0):

  • 创建索引myIdx
  • 索引类型为hash
  • 索引加上前缀doc:
  • 字段title,数据类型:text;weight  5.0 权重
  • 字段body,url,数据类型:text;
127.0.0.1:6379> FT.CREATE myIdx ON HASH PREFIX 1 doc: SCHEMA title TEXT WEIGHT 5.0 body TEXT url TEXT
OK

此时,任何带有前缀的键的现有哈希文档都会doc:自动添加到索引中。

添加文件

创建索引后,任何带有doc:前缀的新哈希文档都会在创建时自动建立索引。

使用HSET命令创建一个新的哈希文档并将其添加到索引中:

127.0.0.1:6379> HSET doc:1 title "hello world" body "lorem ipsum" url "http://redis.io"
(integer) 3

搜索索引

要在索引中搜索包含特定单词的文档,请使用以下FT.SEARCH命令:

127.0.0.1:6379> FT.SEARCH myIdx "hello world" LIMIT 0 10
1) (integer) 1
2) "doc:1"
3) 1) "title"
   2) "hello world"
   3) "body"
   4) "lorem ipsum"
   5) "url"
   6) "http://redis.io"

删除索引

要删除索引而不删除关联的哈希文档

127.0.0.1:6379> FT.DROPINDEX myIdx
OK

更多命令使用还可以看看这篇文章https://zhuanlan.zhihu.com/p/478548947

用javaAPI的方式使用redisearch(索引类型使用的是hash)

导入maven坐标

<dependency>
    <groupId>com.redislabs</groupId>
    <artifactId>jredisearch</artifactId>
    <version>1.8.1</version>
</dependency>
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.10</version>
</dependency>
<dependency>
    <groupId>com.hankcs</groupId>
    <artifactId>hanlp</artifactId>
    <version>portable-1.7.8</version>
</dependency>

配置类,注入Client对象

@Configuration
public class RSclientConfig {
    @Bean
    public Client setClient(){
        /*
            Client 是主要的 RediSearch 客户端类,包装了连接管理和所有 RediSearch 命令
            连接redisearch
            "store"索引(相当于表)
         */
        Client client = new Client("store", host, port);
        return client;
    }
}

rediSearch工具类

import io.redisearch.*;
import io.redisearch.aggregation.AggregationBuilder;
import io.redisearch.aggregation.SortedField;
import io.redisearch.aggregation.reducers.Reducers;
import io.redisearch.client.AddOptions;
import io.redisearch.client.Client;
import io.redisearch.querybuilder.GeoValue;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
public class RedisSearchUtilsV2 {
    @Autowired
    private Client client;

    /**
     * 删除索引
     */
    public void dropIndex(){
        // 只会删除索引,不会删除索引相关的文档,true:表示如果索引不存在,直接返回false,不报错
        client.dropIndex(true);
    }

    /**
     * 删除文档数据
     */
    public void deleteDocuments(String ...docs){
        // 传入一组(将删除的文档ID)
        client.deleteDocuments(true,docs);
    }

    /**修改文档**/
    public void updateDocument(String docId, double score, Map map){
        client.updateDocument(docId,score,map);
    }

    /**添加数据8**/
    public boolean addHashData(){
        try{
            // FullText类型(全文查询)
            Schema.Field storeName = new Schema.Field("storeName", Schema.FieldType.FullText,false);
            Schema.Field storeIntroduce = new Schema.Field("storeIntroduce", Schema.FieldType.FullText,false);
            // tag类型
            Schema.Field tag = new Schema.Field("tag", Schema.FieldType.Tag,false);
            // geo类型,经纬度
            Schema.Field location = new Schema.Field("location", Schema.FieldType.Geo,false);
            // number类型(此类型可以范围查询),true:允许排序
            Schema.Field start = new Schema.Field("start", Schema.FieldType.Numeric,true);

            // 定义一个索引模式(相当于student的表结构)
            Schema schema = new Schema()
                    .addField(storeName)
                    .addField(storeIntroduce)
                    .addField(tag)
                    //.addField(location)// 加上Geo类型后,无法查询出数据,暂时不知道什么原因
                    .addField(start);


            // 创建索引(如果存在相同的索引则会创建失败)
            client.createIndex(schema, Client.IndexOptions.Default());

            // 创建一些map数据,准备存入student索引
            Map<String, Object> fields1 = createDocument("粥铺", "我家小米粥好喝", "满减"
                    ,new GeoValue(106.555697,29.613248,5000, GeoValue.Unit.METERS),300);

            Map<String, Object> fields2 = createDocument("米铺", "我家米便宜好吃", "香米"
                    ,new GeoValue(106.555661,29.613695,100, GeoValue.Unit.METERS),100);

            Map<String, Object> fields3 = createDocument("米通信", "电子批发城", "电子"
                    ,new GeoValue(106.555922,29.613429,100, GeoValue.Unit.METERS),120);

            Map<String, Object> fields4 = createDocument("熊猫家居", "只为让你有更好的舒适生活", "家居"
                    ,new GeoValue(106.555922,29.613429,100, GeoValue.Unit.METERS),220);

            Map<String, Object> fields5 = createDocument("河马家居", "为你私人定制", "家居"
                    ,new GeoValue(106.555571,29.612973,100, GeoValue.Unit.METERS),60);


            // 创建选项,设置语言为中文,否则无法进行中文分词查询
            AddOptions options = new AddOptions();
            options.setLanguage("chinese");

        /*
            创建文档(相当于表的每一行数据)id:"doc1"   fields:fields1  score:1

                id:唯一值,否则创建失败,且必须为string;
                fields:需要存放的数据(默认为Map类型);
                score:分数(权重0~1)
         */
            Document doc1 = new Document("doc1", fields1,1);
            Document doc2 = new Document("doc2", fields2,0.7);
            Document doc3 = new Document("doc3", fields3,0.5);
            Document doc4 = new Document("doc4", fields4,0.3);
            Document doc5 = new Document("doc5", fields5,0.1);

            // 添加文档,将设置项加进去
            client.addDocument(doc1,options);
            client.addDocument(doc2,options);
            client.addDocument(doc3,options);
            client.addDocument(doc4,options);
            client.addDocument(doc5,options);
        }catch (Exception e){
            e.printStackTrace();
            log.info("文档添加失败!");
            return false;
        }
        log.info("文档添加成功!");
        return true;
    }

    /**根据关键字全文查询
     *  @method limit(0,3) 从位置0开始查询出3条
     *  @method setWithScores() 按分数大小查询(权重),从大到小排
     *  @method setLanguage("chinese") 默认不支持中文分词,所以要设置语言
     *  @method highlightFields("title","body") 设置哪些字段需要高亮显示
     */
    public SearchResult searchByKey(String queryString) {

        // 创建查询语句
        Query query = new Query(queryString)
                .limit(0,3)
                .setWithScores()
                .highlightFields("storeName","storeIntroduce")
                .setLanguage("chinese");

        // 提交查询
        SearchResult result = client.search(query);
        // 输出
        show(result);
        return result;
    }

    /**根据数值范围+key查询
     *
     * @method addFilter()
     *      添加过滤器
     * @method Query.NumericFilter(field,min,max)
     *      数值过滤器
     *      查询字段field范围(min,max)
     * **/
    public SearchResult searchByNumberRange(String key, String field, int min, int max){

        Query query = new Query(key)
                .addFilter(new Query.NumericFilter(field,min,max))
                .limit(0,3)
                .setLanguage("chinese");
        // 提交查询
        SearchResult result = client.search(query);
        // 输出
        show(result);
        return result;
    }

    /**位置范围+key查询
     * new Query.GeoFilter("location",lon,lat,radius,unit)
     *      经纬度过滤器
     * @param key 搜索关键词
     * @param lon 经度
     * @param lat 纬度
     * @param radius 半径
     * @param unit 度量单位
     * **/
    public SearchResult searchByGeoRange(String key, double lon, double lat, double radius, String unit){
        Query query = new Query(key)
                .addFilter(new Query.GeoFilter("location",lon,lat,radius,unit))
                .limit(0,3)
                .setLanguage("chinese");
        // 提交查询
        SearchResult result = client.search(query);

        show(result);
        return result;
    }

    /**
     * 聚合查询
     * @param query
     * @param start
     * @param state
     * @param avgprice
     * @param k
     * @return
     */
    public AggregationResult aggregate(String query, String start, String state, String avgprice, String k){
        AggregationBuilder builder =new AggregationBuilder(query)
                .apply("@".concat(start).concat("/1000"), k)
                .groupBy("@".concat(state), Reducers.avg("@".concat(k)).as(avgprice))
                .filter("@".concat(avgprice).concat(">=2"))
                .sortBy(Integer.MAX_VALUE, SortedField.asc("@".concat(state)));
        return client.aggregate(builder);
    }

    private static Map<String, Object> createDocument(String storeName, String storeIntroduce,
                                                      String tag, GeoValue location, Integer start){

        Map<String, Object> fields = new HashMap<>();

        fields.put("storeName", storeName);
        fields.put("storeIntroduce", storeIntroduce);
        fields.put("tag", tag);
        fields.put("location", location);
        fields.put("start", start);

        return fields;
    }

    public static void show(SearchResult searchResult){
        // 输出
        searchResult.docs.stream().forEach(System.out::println);
    }
}

测试类

@SpringBootTest
class RedisSearchApplicationTests {

    @Autowired
    private RedisSearchUtilsV2 redisSearchUtilsV2;

    @Autowired
    private RedisSearchUtils redisSearchUtils;

    @Test
    void drop(){
        // 删除索引
        redisSearchUtilsV2.dropIndex();
    }

    @Test
    void test(){
        // 添加数据
        redisSearchUtilsV2.addHashData();
    }

    // 全文搜索
    @Test
    void Test1() {
        // 根据key查询数据
        redisSearchUtilsV2.searchByKey("家居");
    }

    // key+number范围查询
    @Test
    void test3(){
        // 查询包含内容家居,字段"start"值在100~400的数据
        redisSearchUtilsV2.searchByNumberRange("家居","start",100,400);
    }

    // 搜索某一经纬度点,半径5000m范围内的数据
    @Test
    void test4(){
        redisSearchUtilsV2.searchByGeoRange("家居",106.555697,29.613248,5000, "m");
    }

}

查询效果

 接下来学习redisearch+redisJSON结合使用(这种方式的效率更高)

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IABQL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值