redisearch+redisJSON联合使用

上一篇学习了redisearch+hash的使用redisearch+springboot的简单使用 

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:通过它可以选择需要建立索引的数据前缀,比如PREFIX 1 "product:"表示为键中以product:为前缀的数据建立索引;
  • LANGUAGE:指定TEXT类型属性的默认语言,使用chinese可以设置为中文;
  • identifier:指定属性名称;
  • attribute:指定属性别名;
  • TEXT | NUMERIC | GEO | TAG:这些都是属性可选的类型;
  • SORTABLE:指定属性可以进行排序。

创建索引

FT.CREATE 当您使用该命令创建JSON索引后,存储在数据库中的任何现有和未来的 JSON 文档会自动与之关联。

使用以下语法创建 JSON 索引:

FT.CREATE {index_name} ON JSON SCHEMA {json_path} AS {attribute} {type}

 每个 JSONPath 表达式的结果(即json中的字段)都和索引中称为 attribute(以前hash中称为  field)的逻辑名称相关联。

例如,此命令创建一个索引,该索引代表 与JSON 文档中的name、description 、brandName和price相关联进行索引,设置索引语言为chinese:

> FT.CREATE itemIdx ON JSON LANGUAGE chinese SCHEMA $.name AS name TEXT $.description as description TEXT $.brandName AS brandName TAG $.price AS price NUMERIC SORTABLE
"OK"

添加 JSON 文档

可以使用任何 RedisJSON 写入命令,例如JSON.SETJSON.ARRAPPEND来创建或修改 JSON 文档。

因为索引与JSON文档是关联同步的,所以只要JSON.SET命令返回,文档就会在索引上可用。任何与索引内容匹配的后续查询都将返回该文档。

现在有这样2个JSON文档数据:

{
  "name": "锤子8plus",
  "description": "锤子手机-5G-128G",
  "brandName":"锤子",
  "price": 3299
}
{
  "name": "华为Mate40 pro",
  "description": "HuaWei-5G-128G",
  "brandName":"华为",
  "price": 6499
}

使用JSON.SET命令添加文档 (a1、a2表示文档的ID,唯一)

> JSON.SET a1 $ '{"name": "锤子8plus","description": "锤子手机-5G-128G","brandName":"锤子","price": 3299}'
"OK"
> JSON.SET a2 $ '{"name": "华为Mate40 pro","description": "HuaWei-5G-128G","brandName":"华为","price": 6499}'
"OK"

1.建立完索引后,我们就可以使用FT.SEARCH对数据进行查看了,比如使用*可以查询全部; 

FT.SEARCH itemIdx *

 2.由于我们设置了price字段为SORTABLE,我们可以以price降序返回商品信息;

FT.SEARCH itemIdx * SORTBY price DESC

3.还可以返回指定字段的数据;

FT.SEARCH itemIdx * RETURN 2 name price

 表示返回2个字段,分别是name、price

 4.我们把brandName设置为了TAG类型,我们可以使用如下语句查询品牌为华为或锤子的商品;

查询TAG类型,要用{} 

 FT.SEARCH itemIdx '@brandName:{华为 | 锤子}'

5.由于priceNUMERIC类型,我们可以使用如下语句查询价格在3000~4000的商品; 

FT.SEARCH itemIdx '@price:[3000 4000]'

6.还可以使用像like一样的模糊查询,例:查询name以华为开头的

FT.SEARCH item '@name:华为*'

7.在FT.SEARCH中直接指定搜索关键词,可以对所有TEXT类型的属性进行全局搜索,支持中文搜索

FT.SEARCH itemIdx '手机'

 8.也可以指定搜索的字段,比如搜索description中带有5G字段的商品;

FT.SEARCH itemIdx '@description:5G'

 9.还可以搜索description中带有锤子和5G字段的商品;用空格表示AND

FT.SEARCH itemIdx '@description:(锤子 5G)'

 10.还可以搜索description中带有锤子或5G字段的商品;用 | 代表或

FT.SEARCH itemIdx '@description:(锤子 | 5G)'

 11.还可以多字段,多条件查询,例:搜索name中带有华为字段,并且price在5000~7000的商品

FT.SEARCH itemIdx '@name:(华为) @price:[5000,7000]'

 12.还可以使用HIGHLIGHT来进行高亮显示效果(默认对搜索关键字加上<b></b>)

HIGHLIGHT必须跟在return后面,return 1 name表示返回一个name字段;对name字段高亮显示。测试了一下,高亮只对TEXT类型有效。

 FT.SEARCH itemIdx '@name:(华为)' return 1 name HIGHLIGHT

 13.如果高亮不想使用<b></b>,还可以用TAGS来设置自己想要的标签

FT.SEARCH itemIdx '@name:(华为)' return 1 name HIGHLIGHT TAGS '<p>' '</p>'

14.我们还可用limit来限制搜索返回的数量

 FT.SEARCH itemIdx '@description:(5G)' limit 0 2

 15.通过FT.DROPINDEX命令可以删除索引,如果不加DD只会删除索引,与索引关联的JSON文档不会删除;如果加入DD选项的话,会连数据一起删除;

FT.DROPINDEX itemIdx DD

 

使用JSON的方式与HASH相比,JSON更快。在对数据做修改时,hash需要删除整个文档再将修改后的文档添加,而json可以直接对文档中的某一字段修改。

用redisInsight可视化工具可以看到有这样一份文档

 现在想要对文档中的price修改为3499;

在RedisJSON中,获取JSON对象中的属性时需要以 .开头

> JSON.SET a1 .price 3499
"OK"

 可以看到数据已经改变了。

 还有更多的使用方式可以查看官网的介绍https://redis.io/docs/stack/search/indexing_json/#highlight-search-terms

### 实现 Spring Boot 中的余弦相似度计算 要在 Spring Boot 项目中实现或使用余弦相似度计算,可以通过 Redis 向量数据库的支持来完成。以下是具体的说明: #### 集成 Redis 并配置向量索引 为了支持向量数据存储以及相似性搜索功能,可以利用 Redis 的模块 `RedisJSON` 和 `RediSearch` 来创建向量字段并定义其维度和距离度量方式(如余弦相似度)。通过设置参数 `DISTANCE_METRIC COSINE` 可指定采用余弦相似度作为衡量标准[^1]。 ```java @Configuration public class RedisConfig { @Bean public LettuceConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(); } @Bean public StringRedisTemplate stringRedisTemplate(LettuceConnectionFactory connectionFactory) { return new StringRedisTemplate(connectionFactory); } } ``` 上述代码片段展示了如何在 Spring Boot 应用程序中初始化 Redis 连接工厂,并绑定到模板类以便后续操作。 #### 数据建模与插入 当准备好了基础环境之后,则需设计好待存入的数据结构形式及其对应的键名空间规划;接着按照官方文档指引编写命令脚本或者调用 API 方法将目标对象序列化后上传至服务器端保存起来的同时为其附加必要的元属性标签用于辅助检索过滤条件匹配过程。 例如下面这个例子演示了怎样把一条记录连同它所关联的一个高维特征表示一起写入数据库当中去: ```java @Service public class VectorService { private final StringRedisTemplate template; public VectorService(StringRedisTemplate template){ this.template = template; } public void saveVectorData(String id, double[] vector){ Map<String,Object> dataMap=new HashMap<>(); dataMap.put("id",id); // 将浮点数组转换为字符串列表形式适合 JSON 存储格式要求 List<Double> vecList=Arrays.stream(vector).boxed().collect(Collectors.toList()); dataMap.put("vector",vecList); template.opsForValue().set(id,new JSONObject(dataMap).toString()); // 假设已经预先设置了FT.CREATE index_name ON JSON SCHEMA $.vector AS VECTOR FLAT DIMENSION d TYPE FLOAT DISTANCE_METRIC cosine template.execute((RedisCallback<Void>)connection->{ connection.sendCommand(Command.HSET,id.getBytes(),new byte[][]{":vector".getBytes()},SerializationUtils.serialize(vecList)); return null; }); } } ``` 这里需要注意的是实际部署运行前还需要额外执行一次建立全文搜索引擎索引的动作以激活矢量查询能力。 #### 查询逻辑构建 最后一步就是开发应用程序接口供外部调用来发起基于内容推荐请求时能够高效准确地返回最相近的结果集项们啦! 下面给出了一种可能版本的样子仅供参考学习之用而已哦~ ```java @RestController @RequestMapping("/api/recommendations") public class RecommendationController { private final StringRedisTemplate template; public RecommendationController(StringRedisTemplate template){ this.template=template; } @GetMapping("/{itemId}") public ResponseEntity<List<Map.Entry<String,Double>>> getRecommendations(@PathVariable String itemId,@RequestParam(defaultValue="5") int topK){ Optional<byte[]> optItemVecBytes=Optional.ofNullable(template.getConnectionFactory() .getConnection() .stringCommands() .get((itemId+":vector").getBytes())); if(!optItemVecBytes.isPresent())return ResponseEntity.notFound().build(); List<Double> itemVec=(List<Double>)SerializationUtils.deserialize(optItemVecBytes.get()); Set<Tuple> results=template.opsForGeo().search( GeoReference.fromCoordinate(new DefaultPoint(itemVec.subList(0,itemVec.size()/2),itemVec.subList(itemVec.size()/2,itemVec.size()))), new Circle(new Point(0d,0d),Distance.ZERO), Limit.limit(topK)) .stream() .map(tup -> Maps.immutableEntry(tup.getKey(), tup.getScore())) .collect(Collectors.toSet()); return ResponseEntity.ok(new ArrayList<>(results)); } } ``` 以上仅作为一个简化版示意案例分享给大家了解思路方法途径而已,在真实生产环境中还需考虑更多细节因素比如性能优化等方面的工作呢!
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

IABQL

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

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

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

打赏作者

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

抵扣说明:

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

余额充值