Spring Boot集成Redis向量数据库实现相似性搜索

news2024/9/24 22:56:06

1.什么是Redis向量数据库?

Redis 是一个开源(BSD 许可)的内存数据结构存储,用作数据库、缓存、消息代理和流式处理引擎。Redis 提供数据结构,例如字符串、哈希、列表、集合、带范围查询的有序集合、位图、超对数日志、地理空间索引和流。

Redis 搜索和查询 扩展了 Redis OSS 的核心功能,并允许您将 Redis 用作向量数据库

  • 在哈希或 JSON 文档中存储向量和关联的元数据
  • 检索向量
  • 执行向量搜索

2.向量检索(Vector Search)的核心原理

向量检索(Vector Search)的核心原理是通过将文本或数据表示为高维向量,并在查询时根据向量的相似度进行搜索。在你的代码中,向量检索过程涉及以下几步:

匹配的原理:

  • 检索的核心是将文本或数据转换成向量,在高维向量空间中查找与查询最相似的向量。
  • 在存储数据时将指定的字段通过嵌入模型生成了向量。
  • 在检索时,查询文本被向量化,然后与 Redis 中存储的向量进行相似度比较,找到相似度最高的向量(即相关的文档)。

关键点:

  • 嵌入模型 将文本转换成向量。
  • 相似度计算 通过余弦相似度或欧几里得距离来度量相似性。
  • Top K 返回相似度最高的 K 个文档。

具体过程

1. 向量化数据:

当你将 JSON 中的字段存入 Redis 时,向量化工具(例如 vectorStore)会将指定的字段转换为高维向量。每个字段的内容会通过某种嵌入模型(如 Word2Vec、BERT、OpenAI Embeddings 等)转换成向量表示。每个向量表示的是该字段内容的语义特征。

2. 搜索时的向量生成:

当执行 SearchRequest.query(message) 时,系统会将输入的 message 转换为一个查询向量。这一步是通过同样的嵌入模型,将查询文本转换为与存储在 Redis 中相同维度的向量。

3. 相似度匹配:

vectorStore.similaritySearch(request) 函数使用了一个向量相似度计算方法来查找最相似的向量。这通常是通过 余弦相似度 或 欧几里得距离 来度量查询向量和存储向量之间的距离。然后返回与查询最相似的前 K 个文档,即 withTopK(topK) 所指定的 K 个最相关的结果。

4. 返回匹配的文档:

匹配的结果是根据相似度得分排序的 List<Document>。这些文档是你最初存储在 Redis 中的记录,包含了 JSON 中指定的字段。

3.环境搭建

version: '3'
services:
  redis-stack:
    image: redis/redis-stack
    ports:
      - 6379:6379

  redis-insight:
    image: redislabs/redisinsight:latest
    ports:
      - 5540:5540

Run following command:

docker-compose up -d

访问 http://localhost:5540

4.代码工程

 实验目标

实现文件数据向量化到redis,并进行相似性搜索

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.1</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>RedisVectorStore</artifactId>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <spring-ai.version>0.8.1</spring-ai.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-transformers-spring-boot-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-redis-spring-boot-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>5.1.0</version>
        </dependency>
    </dependencies>
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </pluginRepository>
    </pluginRepositories>
</project>

controller

package com.et.controller;

import com.et.service.SearchService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.et.service.SearchService;
import java.util.HashMap;
import java.util.Map;

@RestController
public class HelloWorldController {
    @Autowired
    SearchService searchService;
    @RequestMapping("/hello")
    public Map<String, Object> showHelloWorld(){
        Map<String, Object> map = new HashMap<>();
        map.put("msg", searchService.retrieve("beer"));
        return map;
    }
}

configuration

加载文件数据到并将数据向量化到redis

JsonReader loader = new JsonReader(file, KEYS);

JsonReader 和 VectorStore 实现是将 KEYS 中指定的多个字段拼接在一起,生成一个统一的文本表示,然后通过嵌入模型将这些字段的组合文本转换为一个单一的向量,那么这里就是将多个字段组合成 一个综合向量。并将其处理后存入 Redis。

package com.et.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
import org.springframework.ai.reader.JsonReader;
import org.springframework.ai.vectorstore.RedisVectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.zip.GZIPInputStream;

@Component
public class DataLoader implements ApplicationRunner {

   private static final Logger logger = LoggerFactory.getLogger(DataLoader.class);

   private static final String[] KEYS = { "name", "abv", "ibu", "description" };

   @Value("classpath:/data/beers.json.gz")
   private Resource data;

   private final RedisVectorStore vectorStore;

   private final RedisVectorStoreProperties properties;

   public DataLoader(RedisVectorStore vectorStore, RedisVectorStoreProperties properties) {
      this.vectorStore = vectorStore;
      this.properties = properties;
   }

   @Override
   public void run(ApplicationArguments args) throws Exception {
      Map<String, Object> indexInfo = vectorStore.getJedis().ftInfo(properties.getIndex());
      Long sss= (Long) indexInfo.getOrDefault("num_docs", "0");
      int numDocs=sss.intValue();
      if (numDocs > 20000) {
         logger.info("Embeddings already loaded. Skipping");
         return;
      }
      Resource file = data;
      if (data.getFilename().endsWith(".gz")) {
         GZIPInputStream inputStream = new GZIPInputStream(data.getInputStream());
         file = new InputStreamResource(inputStream, "beers.json.gz");
      }
      logger.info("Creating Embeddings...");
      // tag::loader[]
      // Create a JSON reader with fields relevant to our use case
      JsonReader loader = new JsonReader(file, KEYS);
      // Use the autowired VectorStore to insert the documents into Redis
      vectorStore.add(loader.get());
      // end::loader[]
      logger.info("Embeddings created.");
   }

}

配置redis vectorStore

package com.et.config;

import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.document.MetadataMode;
import org.springframework.ai.transformers.TransformersEmbeddingClient;
import org.springframework.ai.vectorstore.RedisVectorStore;
import org.springframework.ai.vectorstore.RedisVectorStore.RedisVectorStoreConfig;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedisConfiguration {

    @Bean
    TransformersEmbeddingClient transformersEmbeddingClient() {
        return new TransformersEmbeddingClient(MetadataMode.EMBED);
    }

    @Bean
    VectorStore vectorStore(TransformersEmbeddingClient embeddingClient, RedisVectorStoreProperties properties) {
        var config = RedisVectorStoreConfig.builder().withURI(properties.getUri()).withIndexName(properties.getIndex())
                .withPrefix(properties.getPrefix()).build();
        RedisVectorStore vectorStore = new RedisVectorStore(config, embeddingClient);
        vectorStore.afterPropertiesSet();
        return vectorStore;
    }


}

service

查询时,查询文本也会生成一个整体向量,与存储的综合向量进行匹配。

package com.et.service;


import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class SearchService {


   @Value("${topk:10}")
   private int topK;

   @Autowired
   private  VectorStore vectorStore;



   public List<Document> retrieve(String message) {
      SearchRequest request = SearchRequest.query(message).withTopK(topK);
      // Query Redis for the top K documents most relevant to the input message
      List<Document> docs = vectorStore.similaritySearch(request);

      return docs;
   }

}

只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

  • GitHub - Harries/springboot-demo: a simple springboot demo with some components for example: redis,solr,rockmq and so on.(RedisVectorStore)

5.测试

启动Spring Boot应用程序,查看日志

 . ____ _ __ _ _
 /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/ ___)| |_)| | | | | || (_| | ) ) ) )
 ' |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot :: (v3.2.1)

2024-09-24T14:03:48.217+08:00 INFO 23996 --- [ main] com.et.DemoApplication : Starting DemoApplication using Java 17.0.9 with PID 23996 (D:\IdeaProjects\ETFramework\RedisVectorStore\target\classes started by Dell in D:\IdeaProjects\ETFramework)
2024-09-24T14:03:48.221+08:00 INFO 23996 --- [ main] com.et.DemoApplication : No active profile set, falling back to 1 default profile: "default"
2024-09-24T14:03:49.186+08:00 INFO 23996 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8088 (http)
2024-09-24T14:03:49.199+08:00 INFO 23996 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2024-09-24T14:03:49.199+08:00 INFO 23996 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.17]
2024-09-24T14:03:49.289+08:00 INFO 23996 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2024-09-24T14:03:49.290+08:00 INFO 23996 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1033 ms
2024-09-24T14:03:49.406+08:00 WARN 23996 --- [ main] ai.djl.util.cuda.CudaUtils : Failed to detect GPU count: CUDA driver version is insufficient for CUDA runtime version (35)
2024-09-24T14:03:49.407+08:00 WARN 23996 --- [ main] ai.djl.util.cuda.CudaUtils : Failed to detect GPU count: CUDA driver version is insufficient for CUDA runtime version (35)
2024-09-24T14:03:49.408+08:00 INFO 23996 --- [ main] ai.djl.util.Platform : Found matching platform from: jar:file:/D:/jar_repository/ai/djl/huggingface/tokenizers/0.26.0/tokenizers-0.26.0.jar!/native/lib/tokenizers.properties
2024-09-24T14:03:49.867+08:00 INFO 23996 --- [ main] o.s.a.t.TransformersEmbeddingClient : Model input names: input_ids, attention_mask, token_type_ids
2024-09-24T14:03:49.867+08:00 INFO 23996 --- [ main] o.s.a.t.TransformersEmbeddingClient : Model output names: last_hidden_state
2024-09-24T14:03:50.346+08:00 INFO 23996 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8088 (http) with context path ''
2024-09-24T14:03:50.354+08:00 INFO 23996 --- [ main] com.et.DemoApplication : Started DemoApplication in 2.522 seconds (process running for 2.933)
2024-09-24T14:03:50.364+08:00 INFO 23996 --- [ main] com.et.config.DataLoader : Creating Embeddings...
2024-09-24T14:03:51.493+08:00 WARN 23996 --- [ main] ai.djl.util.cuda.CudaUtils : Failed to detect GPU count: CUDA driver version is insufficient for CUDA runtime version (35)
2024-09-24T14:03:51.800+08:00 INFO 23996 --- [ main] ai.djl.pytorch.engine.PtEngine : PyTorch graph executor optimizer is enabled, this may impact your inference latency and throughput. See: https://docs.djl.ai/docs/development/inference_performance_optimization.html#graph-executor-optimization
2024-09-24T14:03:51.802+08:00 INFO 23996 --- [ main] ai.djl.pytorch.engine.PtEngine : Number of inter-op threads is 6
2024-09-24T14:03:51.802+08:00 INFO 23996 --- [ main] ai.djl.pytorch.engine.PtEngine : Number of intra-op threads is 6
2024-09-24T14:04:26.212+08:00 INFO 23996 --- [nio-8088-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-09-24T14:04:26.213+08:00 INFO 23996 --- [nio-8088-exec-1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2024-09-24T14:04:26.215+08:00 INFO 23996 --- [nio-8088-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 2 ms
2024-09-24T14:09:48.846+08:00 INFO 23996 --- [ main] com.et.config.DataLoader : Embeddings created.

查看redis是否存在向量化的数据

vector

访问http://127.0.0.1:8088/hello 进行0 相似度搜索(top 10),返回得分前10的数据

search

6.引用

  • https://docs.springframework.org.cn/spring-ai/reference/api/vectordbs/redis.html
  • Vectors | Docs
  • Spring Boot集成Redis向量数据库实现相似性搜索 | Harries Blog™

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2161703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt开发-comboBox 所有槽函数介绍(2024.09)

activated(int): 触发于ComboBox中某一项被激活时&#xff0c;参数为该项的索引。 currentIndexChanged(QString)/currentIndexChanged(int): 当前选中项变化时触发&#xff0c;前者传递文本&#xff0c;后者传递索引。 currentTextChanged(QString): 当前选中项的文字变更时触…

Ubuntu 与Uboot网络共享资源

1、NFS 1.1 Ubuntu 下 NFS 服务开启 sudo apt-get install nfs-kernel-server rpcbind 等待安装完成&#xff0c;安装完成以后在用户根目录下创建一个名为“Linux”的文件夹&#xff0c;以后所有 的东西都放到这个“Linux”文件夹里面&#xff0c;在“Linux”文件夹里面新建…

Qt获取本机Mac地址、Ip地址

一、简述 今天给大家分享一个获取本机IP地址和Mac地址的方法&#xff0c;经过多次测试&#xff0c;台式机、笔记本等多个设备&#xff0c;暂时没有发现问题。 由于很多时候本地安装了虚拟机、蓝牙、无线网卡或者其他设备等&#xff0c;会有多个Mac地址&#xff0c;所以需要进…

【828华为云征文|如何轻松部署“未知表白墙”项目:华为云Flexus X实例指南】

文章目录 华为云 Flexus X 实例&#xff1a;开启高效云服务的新纪元部署【未知表白墙】项目准备工作具体操作指南服务器环境确认宝塔软件商店操作域名解析未知表白墙登录页修改管理员账号和密码未知表白墙管理页面基础设置表白管理 未知表白墙效果查看 总结 华为云 Flexus X 实…

c++924

2 #include <iostream> #include <cstring>using namespace std;class MyString { private:char *str; // 记录C风格的字符串int size; // 记录字符串的实际长度public:// 定义无参构造MyString() : size(0) {str new char[1];str[0] \0;cou…

Unity中Rigidbody 刚体组件和Rigidbody类是什么?

Rigidbody 刚体组件 Rigidbody 是 Unity 中的一个组件&#xff0c;它可以让你的游戏对象像真实世界中的物体一样移动和碰撞。想象一下&#xff0c;你有一个小球&#xff0c;你希望它像真实世界中的球一样滚动、弹跳和碰撞&#xff0c;那么你就可以给这个小球添加一个 Rigidbod…

JavaScript中的日期和时间

日期和时间 JS语言核心包括Date()构造函数&#xff0c;用来创建表示日期和时间的对象。提供了简单的API. 代码实现&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title>3.1.5 日期和时间</title></hea…

3D模型在UI设计中应用越来越多,给UI带来了什么?

当前3D模型在UI设计中应用很多&#xff0c;极大地拓展了UI设计的发挥空间&#xff0c;也拓宽了UI的应用领域&#xff0c;本文分享下UI中引入3D模型到底能带来什么价值. 3D模型在UI设计中的应用可以给用户界面带来以下几个方面的好处&#xff1a; 更真实的视觉体验&#xff1a;…

【2024W36】肖恩技术周刊(第 14 期):什么是完美副业?

周刊内容: 对一周内阅读的资讯或技术内容精品&#xff08;个人向&#xff09;进行总结&#xff0c;分类大致包含“业界资讯”、“技术博客”、“开源项目”和“工具分享”等。为减少阅读负担提高记忆留存率&#xff0c;每类下内容数一般不超过3条。 更新时间: 星期天 历史收录:…

2016年国赛高教杯数学建模C题电池剩余放电时间预测解题全过程文档及程序

2016年国赛高教杯数学建模 C题 电池剩余放电时间预测 铅酸电池作为电源被广泛用于工业、军事、日常生活中。在铅酸电池以恒定电流强度放电过程中&#xff0c;电压随放电时间单调下降&#xff0c;直到额定的最低保护电压&#xff08;Um&#xff0c;本题中为9V&#xff09;。从充…

TAPD多类别需求管理

本文档将介绍&#xff1a;什么是 TAPD 多类别需求管理&#xff0c;以及如何配置或创建新的需求类别。 一、概述 在研发管理过程中&#xff0c;团队经常会遇到规模扩张、不同特性团队间研发模式差异化大等问题。以上问题导致团队中的需求无法进行统一管理。为解决上述情况&…

54 螺旋矩阵

解题思路&#xff1a; \qquad 这道题可以直接用模拟解决&#xff0c;顺时针螺旋可以分解为依次沿“右-下-左-上”四个方向的移动&#xff0c;每次碰到“边界”时改变方向&#xff0c;边界是不可到达或已经到达过的地方&#xff0c;会随着指针移动不断收缩。 vector<int>…

Adobe Illustrator吸管工具提取的颜色与原色之间存在色差

问题原因&#xff1a; 被提取颜色的对象是外部链接图片&#xff0c;对其提取的颜色会与AI中看到的颜色不同 如下图所示&#xff0c;中间的矩形与外部矩形的内部颜色存在色差 解决办法&#xff1a; 方法一&#xff1a;将该外部图片利用屏幕截图的形式&#xff0c;粘贴到AI中。…

【Linux】fork入门级使用

目录 一、前置准备 1、进程的基本概念 2、进程标识符PID、PPID 1&#xff09;pid介绍 2&#xff09;获取pid和ppid 二、fork函数 1、fork的基本介绍 1&#xff09;fork&#xff08;&#xff09;&#xff1a; 创建子进程 2&#xff09;对于函数具体的描述 3&#xff09…

深入探索卷积神经网络(CNN)

深入探索卷积神经网络&#xff08;CNN&#xff09; 前言图像的数字表示灰度图像RGB图像 卷积神经网络&#xff08;CNN&#xff09;的架构基本组件卷积操作填充&#xff08;Padding&#xff09;步幅&#xff08;Strides&#xff09; 多通道图像的卷积池化层全连接层 CNN与全连接…

应急响应--来不来得及走流程...

免责声明&#xff1a;本文仅做分享&#xff01; 应急响应详解 概述 应急响应是现代信息安全管理中的重要一环。随着网络威胁的日益复杂化&#xff0c;企业和组织必须具备快速响应安全事件的能力&#xff0c;以最大限度地减少数据泄露、业务中断以及经济损失。本文将从应急响应…

华为全联接大会2024 | 一文回顾华为云开发者联盟重磅干货

目录 华为开发者空间预置更多工具资源&#xff0c;带来丰富场景案例 携手鲲鹏、鸿蒙、昇腾等根生态&#xff0c;使能开发者创新 学习体验、内容体系全面升级&#xff0c;助力开发者高效学习根技术 参与丰富线上体验活动&#xff0c;赢取精美礼品 在刚刚结束的华为全联接大会…

香港科技大学广州|金融科技学域博士招生宣讲会——武汉大学、华中科技大学

&#x1f514;&#x1f514;&#x1f514;明日宣讲&#x1f514;&#x1f514;&#x1f514; &#x1f490;香港科技大学广州&#xff5c;金融科技学域博士招生宣讲会 &#x1f4cd;武汉大学专场 &#x1f559;时间&#xff1a;2024年9月24日&#xff08;星期二&#xff09;1…

Vue3:provide-inject实现组件通信

目录 一.作用 1.跨层级通信 2.避免重复声明 3.封装通用服务 二.性质 1.非响应式 2.不可选项 3.高级用法 三.使用 1.爷组件 2.父组件 3.子组件 四.代码 1.爷组件代码 2.父组件代码 3.子组件代码 五.效果 Vue3中的provide-inject机制是用于在组件树中进行依赖注…

python异步处理

python中的异步处理属于比较高级的用法了&#xff0c;用来节省时间非常有用。传统的运行轨迹是阻塞的&#xff0c;就是一行代码必须完成了&#xff0c;然后才能运行下一行代码。异步运行就是我们现在有多个任务task1&#xff08;2s&#xff09;和task2&#xff08;3s&#xff0…