0


【微服务】SpringBoot 整合Redis Stack 构建本地向量数据库相似性查询

一、前言

AI工具的使用让数据检索的需求场景变得越来越多,也越来越复杂,传统的关系型数据库尽管可以满足日常各类关系数据的存储,但是遇到一些文档类型,非结构化的数据时,其作用和效果就会打折扣,而且数据不仅仅是存储的需要,也需要能够满足数据分析的场景,这种情况下,就可以考虑使用向量数据库了。

二、向量数据库介绍

2.1 什么是向量数据库

向量数据库是一种优化后的数据库系统,专门设计用于高效地存储和检索高维向量数据。这些向量数据通常是从原始数据(如文本、图像、音频等)中提取的特征表示。向量数据库的核心优势在于其能够快速地进行相似性搜索,即找到与给定向量最相似的向量。

2.2 向量数据库特点

向量数据库主要具备如下特征:

  • 高效相似性搜索- 支持多种相似性度量,如欧氏距离(L2)、余弦相似度、Jaccard 相似度等。- 使用特殊的索引结构(如局部敏感哈希LSH、近似最近邻ANN等)来加速相似性搜索。
  • 高维数据处理:- 优化了高维向量数据的存储和检索,能够在大规模数据集中高效地进行相似性搜索。- 支持分布式存储和计算,能够轻松扩展到多个节点。
  • 灵活的数据模型:- 通常采用无模式或半结构化数据模型,允许灵活地存储和处理不同类型的向量数据。- 支持元数据存储,可以将向量数据与相关的元数据(如标签、时间戳等)一起存储。
  • 实时和批量处理:- 支持实时插入和查询,适用于在线推荐系统等实时应用场景。- 也支持批量数据插入和查询,适用于离线数据分析和处理。

2.3 向量数据库使用场景

在下面的一些场景中可以考虑使用向量数据库

  • 推荐系统:- 通过用户行为数据生成用户和物品的向量表示,用于推荐相似的物品或用户。- 例如,电商网站可以根据用户的浏览和购买历史生成用户向量,推荐相似的商品。
  • 图像搜索:- 将图像转换为向量表示,用于相似图像的搜索和检索。- 例如,搜索引擎可以根据上传的图片找到相似的图片。
  • 生物信息学:- 将DNA序列或其他生物数据转换为向量表示,用于基因相似性搜索和分析。- 例如,研究人员可以使用向量数据库来寻找与已知基因序列相似的新基因。
  • 自然语言处理:- 将文本转换为向量表示,用于文本相似性搜索、情感分析、机器翻译等任务。- 例如,聊天机器人可以根据用户输入的文本生成向量,找到最合适的回复。
  • 广告推荐:- 根据用户的兴趣和行为生成用户向量,推荐相关的广告。- 例如,社交媒体平台可以根据用户的兴趣生成用户向量,推荐相关的广告内容。

三、常用的向量数据库解决方案

向量数据库的选择也比较多,下面列举几种常用的向量数据库解决方案。

3.1 Milvus

3.1.1 Milvus是什么

Milvus 是一个高性能的向量数据库,由 Zilliz 开发并维护。它支持多种相似性度量(如欧氏距离、余弦相似度等),并使用高效的索引结构(如 IVF、HNSW 等)来加速相似性搜索。Milvus 可以处理大规模的向量数据集,并支持分布式部署,适用于多种应用场景。Milvus 在推荐系统、图像搜索、自然语言处理等领域表现出色,能够快速地进行相似性搜索,找到与给定向量最相似的向量。

git地址 : GitHub - milvus-io/milvus: A cloud-native vector database, storage for next generation AI applications

3.1.2 Milvus主要特点

Milvus具有如下特点:

  • 高效的相似性搜索- 多种相似性度量:支持欧氏距离(L2)、余弦相似度、内积等相似性度量。- 高效的索引结构:支持多种索引结构,如 IVF(Inverted File)、HNSW(Hierarchical Navigable Small World)等,能够显著加速相似性搜索。
  • 使用简单- 丰富的 API 和 SDK:提供多种编程语言的 SDK,包括 Python、Java、C++、Go 等,方便开发者集成。- 详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
  • 多样化的数据源支持- 多种数据源:支持多种数据源,可以与 TensorFlow、PyTorch 等深度学习框架集成,方便数据预处理和特征提取。
  • 扩展性和可伸缩性好- 分布式部署:支持分布式部署,能够处理大规模数据集,支持水平扩展。- 高可用性:支持自动故障恢复和数据备份,保证系统的高可用性。

3.2 Faiss

3.2.1 Faiss是什么

Faiss 是一个开源库,专注于大规模向量相似性搜索和聚类。它支持多种相似性度量(如欧氏距离、余弦相似度等),并使用高效的索引结构(如 IVF(Inverted File)、HNSW(Hierarchical Navigable Small World)等)来加速搜索过程。Faiss 可以在 CPU 和 GPU 上运行,适用于多种应用场景。

git地址 : GitHub - facebookresearch/faiss: A library for efficient similarity search and clustering of dense vectors.

3.2.2 Faiss主要特点

Faiss具有如下特点:

  • 高效的相似性搜索- 多种相似性度量:支持欧氏距离(L2)、余弦相似度、内积等相似性度量。- 高效的索引结构:支持多种索引结构,如 IVF(Inverted File)、HNSW(Hierarchical Navigable Small World)、PQ(Product Quantization)等,能够显著加速相似性搜索。
  • 高性能- CPU 和 GPU 支持:可以在 CPU 和 GPU 上运行,利用硬件加速提高搜索性能。- 内存优化:对内存使用进行了优化,能够在有限的内存中处理大规模数据集。
  • 灵活性好- 多种索引方法:提供了多种索引方法,可以根据具体需求选择合适的索引结构。- 自定义索引:支持自定义索引结构和搜索算法,满足特定应用的需求。
  • 易于使用- 丰富的 API:提供了 C++ 和 Python API,方便开发者集成。- 详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。

3.3 Pinecone

3.3.1 Pinecone是什么

Pinecone 是一个云原生的向量数据库,专为高效地存储和检索大规模向量数据而设计。它旨在简化向量数据的存储和检索过程,提供高性能的相似性搜索功能。Pinecone 提供了高性能的相似性搜索功能,支持实时和批量处理,适用于推荐系统、图像搜索、自然语言处理等多种应用场景。

官方地址:The vector database to build knowledgeable AI | Pinecone

3.3.2 Pinecone主要特点

Pinecone具备如下特点:

  • 高性能- 高效的相似性搜索:支持多种相似性度量,如余弦相似度、欧氏距离等,使用高效的索引结构(如 HNSW、IVF 等)来加速搜索过程。- 低延迟:优化了搜索性能,能够在毫秒级别内返回结果,适用于实时应用场景。
  • 使用简单- 简单的 API 和 SDK:提供丰富的 API 和 SDK,支持多种编程语言(如 Python、JavaScript 等),方便开发者集成。- 详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
  • 扩展性和可伸缩性好- 云原生:提供托管服务,支持自动扩展和故障恢复,保证系统的高可用性。- 水平扩展:支持水平扩展,能够处理大规模数据集,适用于企业级应用。
  • 多样化的数据源支持- 多种数据源:支持多种数据源,可以与 TensorFlow、PyTorch 等深度学习框架集成,方便数据预处理和特征提取。

3.4 Weaviate

3.4.1 Weaviate介绍

Weaviate 是一个开源的向量搜索引擎,由 Semitechnologies 开发并维护。它不仅支持高效的向量数据存储和检索,还结合了图数据模型,使其在处理复杂数据关系方面表现出色。Weaviate 适用于推荐系统、图像搜索、自然语言处理等多种应用场景。Weaviate 支持多种相似性度量(如余弦相似度、欧氏距离等),并使用高效的索引结构来加速搜索过程。Weaviate 提供了丰富的 API 和 SDK,支持多种编程语言,方便开发者集成。

3.4.2 Weaviate主要特点

Weaviate具备如下特点

  • 图数据模型- 图数据模型:支持图数据模型,可以表示和查询复杂的数据关系。- 灵活的数据模型:支持多种数据类型和格式,可以轻松适应各种用例。
  • 高效的相似性搜索- 多种相似性度量:支持余弦相似度、欧氏距离等相似性度量。- 高效的索引结构:使用高效的索引结构(如 HNSW)来加速相似性搜索。
  • 使用简单- 丰富的 API 和 SDK:提供丰富的 API 和 SDK,支持多种编程语言(如 Python、JavaScript、Go 等),方便开发者集成。- 详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
  • 扩展性和可伸缩性好- 云原生支持:提供托管服务,支持自动扩展和故障恢复,保证系统的高可用性。- 水平扩展:支持水平扩展,能够处理大规模数据集,适用于企业级应用。
  • 查询语言- 支持 GraphQL:提供强大的 GraphQL 查询功能,支持复杂的查询和过滤条件。

3.5 Redis Stack

3.5.1 Redis Stack介绍

Redis Stack 是 Redis 的一个扩展版本,它不仅包含了 Redis 的核心功能,还增加了多个模块,如 RediSearch(全文搜索)、RedisGraph(图数据库)、RedisTimeSeries(时间序列数据库)和 RedisAI(AI推理)。这些模块使得 Redis Stack 成为一个功能更加强大的数据库系统,适用于多种应用场景。

3.5.2 Redis Stack核心模块

Redis Stack核心模块主要包括下面几块

  • RediSearch- 全文搜索:提供全文搜索功能,支持复杂的查询和过滤条件。- 向量搜索:支持向量数据的存储和相似性搜索,适用于推荐系统、图像搜索等场景。- 性能优化:使用倒排索引和 BM25 算法优化搜索性能。
  • RedisGraph- 图数据库:提供图数据模型,支持节点和边的存储和查询。- Cypher 查询语言:支持 Cypher 查询语言,方便进行复杂的图数据查询。- 高性能:优化了图数据的存储和查询性能。
  • RedisTimeSeries- 时间序列数据:提供时间序列数据的存储和查询功能,适用于监控、日志分析等场景。- 压缩和聚合:支持数据压缩和聚合,节省存储空间和提高查询性能。
  • RedisAI- AI 推理:提供 AI 推理功能,支持 TensorFlow、PyTorch 等深度学习框架。- 模型管理和执行:支持模型的加载、管理和执行,适用于实时推理和预测。

3.5.3 Redis Stack主要特点

Redis Stack主要具备如下特点:

  • 高性能- 内存优化:Redis Stack 优化了内存使用,能够在有限的内存中处理大规模数据集。- 低延迟:Redis Stack 提供低延迟的数据访问,适用于实时应用场景。
  • 使用简单- 丰富的 API 和 SDK:提供丰富的 API 和 SDK,支持多种编程语言(如 Python、Java、C++、Go 等),方便开发者集成。- 详细的文档和社区支持:提供详细的文档和活跃的社区支持,帮助开发者快速上手。
  • 多样化的数据源支持- 多种数据源:支持多种数据源,可以与 TensorFlow、PyTorch 等深度学习框架集成,方便数据预处理和特征提取。
  • 扩展性和可伸缩性- 分布式部署:支持分布式部署,能够处理大规模数据集,支持水平扩展。- 高可用性:支持自动故障恢复和数据备份,保证系统的高可用性。

3.5.4 Redis Stack应用场景

Redis Stack主要有下面的应用场景

  • 全文搜索- 电子商务:支持商品搜索和推荐。- 内容管理系统:支持文章和文档的搜索和管理。
  • 图数据库- 社交网络:支持用户关系和社交图谱的存储和查询。- 推荐系统:支持基于图数据的推荐算法。
  • 时间序列数据- 监控系统:支持系统监控和日志分析。- 物联网:支持 IoT 设备的数据收集和分析。
  • AI 推理- 实时推荐:支持基于 AI 的实时推荐系统。- 图像识别:支持图像识别和分类。

四、Redis向量数据库介绍

4.1 redis向量数据库是什么

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

Redis 搜索和查询, 扩展了 Redis OSS 的核心功能,因此可以将 Redis 用作向量数据库

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

4.2 向量检索核心原理

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

4.2.1 匹配原理

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

技术关键点:

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

4.2.2 具体实现过程

在代码中操作时,主要包括下面几步:

  • 向量化数据:- 当你将 JSON 中的字段存入 Redis 时,向量化工具(例如 vectorStore)会将指定的字段转换为高维向量。每个字段的内容会通过某种嵌入模型(如 Word2Vec、BERT、OpenAI Embeddings 等)转换成向量表示。每个向量表示的是该字段内容的语义特征。
  • 搜索时向量生成:- 当执行 SearchRequest.query(message) 时,系统会将输入的 message 转换为一个查询向量。这一步是通过同样的嵌入模型,将查询文本转换为与存储在 Redis 中相同维度的向量。
  • 相似度匹配:- vectorStore.similaritySearch(request) 函数使用了一个向量相似度计算方法来查找最相似的向量。这通常是通过 余弦相似度 或 欧几里得距离 来度量查询向量和存储向量之间的距离。然后返回与查询最相似的前 K 个文档,即 withTopK(topK) 所指定的 K 个最相关的结果。
  • 返回匹配的文档:- 匹配的结果是根据相似度得分排序的 List<Document>。这些文档是你最初存储在 Redis 中的记录,包含了 JSON 中指定的字段。

五、整合springboot 实现向量数据库相似性搜索

接下来使用Redis Stack作为向量数据库,并整合springboot 实现向量数据库相似性搜索的过程。

5.1 前置准备

5.1.1 docker搭建Redis-Stack

使用下面的docker命令搭建Redis-Stack

docker run -d --name redis-stack -v /redis-data:/data -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

也可以使用下面的docker compose命令启动容器


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

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

可以通过 IP:8001 访问 Redis-Stack 的web控制台,这里在后面将会用于存储程序中的文本嵌入数据

5.1.2 导入maven依赖

在你的maven工程中导入如下依赖

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.4</version>
    <relativePath/>
</parent>

<dependencies>

    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter</artifactId>
        <version>1.0.0-M2.1</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>2.0.35</version>
    </dependency>

    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>4.12.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alibaba/dashscope-sdk-java -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>dashscope-sdk-java</artifactId>
        <version>2.16.9</version>
    </dependency>

    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/io.milvus/milvus-sdk-java -->
    <dependency>
        <groupId>io.milvus</groupId>
        <artifactId>milvus-sdk-java</artifactId>
        <version>2.4.6</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>
</repositories>

5.1.3 获取apikey

下文中将会先演示使用云厂商提供的向量数据库,所以这里使用百炼平台的,需要先获取apikey

获取apikey,可以去阿里云的百炼平台获取,然后在模型广场中向量模型那里选择一种文本向量使用

5.1.4 添加配置文件

在工程的配置文件中增加下面的配置内容

server:
  port: 8081

spring:
  ai:
    dashscope:
      api-key: 你的apikey

      embedding:
        options:
          model: text-embedding-v2

5.2 文本嵌入代码实现过程

如何用一段话去数据库查找数据?答案是向量求相似度。相似度越高,返回的数据就越靠前,比如大家熟知的搜索引擎es,你输入一段文本去查询,es会根据算分将得分最高的文档放在前面返回给你,在大模型中,嵌入模型就可以帮我们做到这件事,所以第一件事情需要将文本中的句子向量化,然后和向量数据库中的向量进行比较,找到最相似的向量。下面使用代码演示下这个过程。

5.2.1 添加一个测试接口

添加一个测试接口,参考下面的代码

package com.congge.web;

import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;

@RestController
@Slf4j
public class DocumentController {

    private final EmbeddingModel embeddingModel;

    @Autowired
    public DocumentController(EmbeddingModel embeddingModel) {
        this.embeddingModel = embeddingModel;
    }

    @GetMapping("/ai/embedding")
    public Map embed(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        EmbeddingResponse embeddingResponse = this.embeddingModel.embedForResponse(List.of(message));
        return Map.of("embedding", embeddingResponse);
    }

    //localhost:8081/embedding
    @GetMapping("/embedding")
    public Object embeddingTest() {
        // 文本嵌入
        float[] embed = embeddingModel.embed("你好,我是小码农叔叔");
        log.info("文本转换得到的向量: {}", embed);
        return embed;
    }

}

两个接口均可用于测试,选择第二个测试,请求之后,返回了很多向量的参数

5.3 ETL Pipeline

ETL是提取、转换、加载的缩写,从原始的文档到数据库需要经历提取(.doc、.ppt、.xlsx等)、转换(数据结构化、清理数据、数据分块)、写入向量数据库。这个过程可以进行多种处理,确保最后的数据适合AI问答。

SpringAI提供了ETL框架。它是搭建知识库框架的基石。

5.3.1 Spring AI ETL 介绍

在AI应用程序中,数据处理是至关重要的一部分。Spring AI提供了一个强大的ETL框架,帮助开发者高效地进行数据提取、转换和加载操作。通过这个框架,你可以轻松地将数据准备好用于AI模型的训练和推理。

5.3.2 Spring AI 处理ETL完整过程

与传统的etl过程稍有不同,springai 中etl 的完整过程如下:

  • 数据提取- 数据提取是ETL流程的第一步。在这个阶段,你需要从各种数据源中提取原始数据。Spring AI支持多种数据源,包括数据库、文件系统、API等。
  • 数据转换- 数据转换是ETL流程的第二步。在这个阶段,你需要将原始数据转换为适合AI模型使用的格式。Spring AI提供了多种数据转换工具,帮助你高效地处理数据。
  • 向量化数据- 对于AI模型来说,向量化数据是必不可少的步骤。只有经过向量化之后的数据才能在后续的检索中正常检索出结果。
  • 数据加载- 数据加载是ETL流程的最后一步。在这个阶段,你需要将转换后的数据加载到目标存储系统中,以便AI模型使用。Spring AI支持将数据加载到多种目标存储系统中。

5.3.3 Spring AI ETL 技术框架

5.3.3.1 读取器 DocumentReader

DocumentReader ,文档读取器:

  • 用于读取文档,比如PDF、Word、Excel等- 如:JsonReader(读取JSON),TextReader(读取文本),PagePdfDocumentReader(读取PDF),TikaDocumentReader(读取各种文件,大部分都可以支持.pdf,.xlsx,.docx,.pptx,.md,.json等)。- 这些reader都是DocumentReader的实现类。
5.3.3.2 文档转换器 DocumentTransformer

文档转换器,处理文档:

  • TextSplitter(文档切割成小块),ContentFormatTransformer(将文档变成键值对),SummaryMetadataEnricher(使用大模型总结文档),KeywordMetadataEnricher(使用大模型提取文档关键词)
5.3.3.3 文档写入器 DocumentWriter

文档写入器:

  • 将文档写入向量数据库或者本地文件。
  • VectorStore(向量数据库写入器),FileDocumentWriter(文件写入器)。

使用这几个组件,完整的执行流程如下:

5.4 Spring AI 文档读取操作演示

5.4.1 文件读取代码演示

TikaDocumentReader 比较全能,大部分文件都可读取,支持的文件格式可以参考官方文档。如果是比较个性化文档的场景,最好自己实现一个Reader,便于业务扩展。pom文件中导入下面的依赖。

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-tika-document-reader</artifactId>
        </dependency>

从输入流读取文件,参考下面的接口代码

@SneakyThrows
@PostMapping("read/multipart-file")
public void readMultipartFile(@RequestParam MultipartFile file) {
    // 从IO流中读取文件
    Resource resource = new InputStreamResource(file.getInputStream());
    List<Document> documents = new TikaDocumentReader(resource)
            .read();
}

上传一个本地的文档,接口测试效果,可以看到被分割成很多片段了

从本地文件读取,参考下面的接口代码

/**
 * 从本地文件读取
 * @return
 */
@GetMapping("/read/from-local-file")
public Object readLocalFile() {
    FileSystemResource resource = new FileSystemResource("E:\\工作空间\\wzlocal\\Java编码规范V1.0.pdf");
    List<Document> documents = new TikaDocumentReader(resource)
            .read();
    return documents;
}

调用一下接口,使用相同的文件,得到类似的效果

5.5 Spring AI 文档转换操作演示

在上一步的操作中,读取返回的是Document对象,而是DocumentETL Pipeline的核心对象,它包含了文档的元数据和内容。接下来就需要对文档进行转换。此时就要用到内容转换器。包括内容转换器和元数据转换器。

内容转换器

  • TokenTextSplitter:可以把内容切割成更小的块方便RAG的时候提升响应速度节省Token。
  • ContentFormatTransformer:可以把元数据的内容变成键值对字符串。

元数据转换器

  • SummaryMetadataEnricher:使用大模型总结文档。会在元数据里面增加一个summary字段。
  • KeywordMetadataEnricher:使用大模型提取文档关键词。可以在元数据里面增加一个keywords字段。

5.5.1 添加测试接口

参考下面的代码

/**
 * 分割后的文档列表  localhost:8081/doc/split
 * @param file
 * @return
 * @throws Exception
 */
@PostMapping("/doc/split")
public Object docSplit(@RequestParam MultipartFile file) throws Exception{
    InputStreamResource inputStreamResource = new InputStreamResource(file.getInputStream());
    List<Document> documentList = new TikaDocumentReader(inputStreamResource).read();
    // 分割后的文档列表
    List<Document> splitDocList = new TokenTextSplitter().split(documentList);
    List<String> contentList = splitDocList.stream().map(Document::getContent).collect(Collectors.toList());
    return contentList;
}

测试一下接口,可以看到被转换后的文档分割成多个片段

5.6 文档存储

经过前面的步骤,我们得到了一个文档列表,为了方便后续的文档搜索,需要将文档进行存储,文档存储到哪里去呢?即向量数据库。上面准备阶段搭建的Redis-Stack即是用于做向量数据库使用的。下面看代码中的操作过程。

5.6.1 导入依赖文件

导入下面的依赖坐标

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-tika-document-reader</artifactId>
    <version>1.0.0-M3</version>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-redis-store</artifactId>
    <version>1.0.0-M3</version>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>

5.6.2 配置redis信息

在配置文件中添加redis的信息

spring:
  ai:
    dashscope:
      api-key: 你的apikey

      embedding:
        options:
          model: text-embedding-v2
    vectorstore:
      redis:
        uri: redis://IP:6379

  data:
    redis:
      database: 0
      timeout: 10s
      lettuce:
        pool:
          # 连接池最大连接数
          max-active: 200
          # 连接池最大阻塞等待时间(使用负值表示没有限制)
          max-wait: -1ms
          # 连接池中的最大空闲连接
          max-idle: 10
          # 连接池中的最小空闲连接
          min-idle: 0
      repositories:
        enabled: false

      host: redis地址

5.6.3 增加一个redis的向量数据库配置类

如果在你的项目里面有用到redis,需要先禁用RedisVectorStoreAutoConfiguration。这是SpringAI自动配置RedisStack的向量数据库连接,会导致Redis的连接配置冲突。参考下面的代码。

package com.congge.config;

import lombok.AllArgsConstructor;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreAutoConfiguration;
import org.springframework.ai.autoconfigure.vectorstore.redis.RedisVectorStoreProperties;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.RedisVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.data.redis.RedisConnectionDetails;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.JedisPooled;

@Configuration
// 禁用SpringAI提供的RedisStack向量数据库的自动配置,会和Redis的配置冲突。
@EnableAutoConfiguration(exclude = {RedisVectorStoreAutoConfiguration.class})
// 读取RedisStack的配置信息
@EnableConfigurationProperties({RedisVectorStoreProperties.class})
@AllArgsConstructor
public class RedisVectorConfig {

    /**
     * 创建RedisStack向量数据库
     *
     * @param embeddingModel 嵌入模型
     * @param properties     redis-stack的配置信息
     * @return vectorStore 向量数据库
     */
    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel,
                                   RedisVectorStoreProperties properties,
                                   RedisConnectionDetails redisConnectionDetails) {
        RedisVectorStore.RedisVectorStoreConfig config = RedisVectorStore.RedisVectorStoreConfig.builder().withIndexName(properties.getIndex()).withPrefix(properties.getPrefix()).build();
        return new RedisVectorStore(config, embeddingModel,
                new JedisPooled(redisConnectionDetails.getStandalone().getHost(),
                        redisConnectionDetails.getStandalone().getPort()
                        , redisConnectionDetails.getUsername(),
                        redisConnectionDetails.getPassword()),
                properties.isInitializeSchema());
    }
}

5.6.4 增加嵌入文档接口

在上面的VectorStore配置中我们提供了EmbeddingModel,调用vectorStore.add(splitDocuments)底层会把文档给EmbeddingModel把文本变成向量然后再存入向量数据库。参考下面的代码。

/**
 * 文档保存到向量数据库   localhost:8081/doc/embedding
 * @param file
 * @return
 * @throws Exception
 */
@PostMapping("/doc/embedding")
public Object docEmbedding(@RequestParam MultipartFile file) throws Exception{
    TikaDocumentReader tikaDocumentReader = new TikaDocumentReader(new InputStreamResource(file.getInputStream()));
    List<Document> splitDocuments = new TokenTextSplitter().apply(tikaDocumentReader.read());
    // 存入向量数据库,这个过程会自动调用embeddingModel,将文本变成向量存入
    vectorStore.add(splitDocuments);
    return true;
}

调用一下接口

接口执行成功后,在Redis-Stack的控制台界面可以看到数据存入了redis中

5.6.5 增加文档检索接口

数据存入进去之后接下来就可以进行搜索了,添加下面文档检索的接口

/**
 * 关键字检索文档  localhost:8081/doc/query?keyword=面向对象
 * @param keyword
 * @return
 */
@GetMapping("/doc/query")
public List<Document> query(@RequestParam String keyword) {
    List<Document> documentList = vectorStore.similaritySearch(keyword);
    return documentList;
}

六、写在文末

本文通过较大的篇幅详细介绍了常用的向量数据库解决方案,并通过搭建redis stack环境,结合代码演示了如何基于redis stack作为向量数据库进行使用,希望对看到的同学有用,本篇到此介绍,感谢观看。


本文转载自: https://blog.csdn.net/zhangcongyi420/article/details/143866767
版权归原作者 小码农叔叔 所有, 如有侵权,请联系我们删除。

“【微服务】SpringBoot 整合Redis Stack 构建本地向量数据库相似性查询”的评论:

还没有评论