安装
推荐使用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:
前缀的新哈希文档都会在创建时自动建立索引。
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结合使用(这种方式的效率更高)