SpringBoot集成Milvus|(实现向量的存储和查询)

SpringBoot集成Milvus|(实现向量的存储和查询)


章节
第一章链接: SpringBoot集成Milvus|(实现向量的存储和查询)

前言

随着互联网不断发展,电子邮件、论文、物联网传感数据、社交媒体照片、蛋白质分子结构等非结构化数据已经变得越来越普遍。如果想要使用计算机来处理这些数据,需要使用 embedding 技术将这些数据转化为向量。随后,Milvus 会存储这些向量,并为其建立索引。Milvus 能够根据两个向量之间的距离来分析他们的相关性。如果两个向量十分相似,这说明向量所代表的源数据也十分相似

一、Milvus介绍

Milvus 是一款云原生向量数据库,它具备高可用、高性能、易拓展的特点,用于海量向量数据的实时召回。
Milvus 基于 FAISS、Annoy、HNSW 等向量搜索库构建,核心是解决稠密向量相似度检索的问题。在向量检索库的基础上,Milvus 支持数据分区分片、数据持久化、增量数据摄取、标量向量混合查询、time travel 等功能,同时大幅优化了向量检索的性能,可满足任何向量检索场景的应用需求。通常,建议用户使用 Kubernetes 部署 Milvus,以获得最佳可用性和弹性。

二、Milvus数据库安装

1.Milvus安装环境准备(centos7)

1、服务器需要部署docker以及docker-compose
2、新建一个工作目录
3、获取Milvus的安装启动YAML文件
4、获取文件wget https://github.com/milvus-io/milvus/releases/download/v2.1.4/milvus-standalone-docker-compose.yml -O docker-compose.yml
5、启动docker镜像 sudo docker-compose up -d
6、启动后容器情况
在这里插入图片描述
7、停止Milvus容器
sudo docker-compose down

2.Milvus客户端安装

1、在docker仓库中找到Milvus对应版本的客户端镜像
2、拉取镜像;docker pull zilliz/attu:v2.1.0
3、运行attu
docker run -itd --restart=always -p 13000:3000 -e HOST_URL=http://{ip}:13000 -e MILVUS_URL={ip}:19530 zilliz/attu:v2.1.0
ip为部署ip
4、浏览器登陆访问:http://{ip}::13000

3.attu新建Milvus集合

新建集合名称为content3,字段id是主键,content存的是内容对应的文本id,content_intro存的是内容向量
请添加图片描述
新建pattion
content3_partion为新建的分区请添加图片描述

三、Milvus集成

1.依赖引入

java操作Milvus的组件工具

    <!-- milvus向量数据库  -->
   <dependency>
       <groupId>io.milvus</groupId>
       <artifactId>milvus-sdk-java</artifactId>
       <version>2.2.4</version>
       <exclusions>
           <exclusion>
               <groupId>org.apache.logging.log4j</groupId>
               <artifactId>log4j-slf4j-impl</artifactId>
           </exclusion>
       </exclusions>
   </dependency>

2.客户端初始化

初始化客户端使用实例,放入容器

@Configuration
public class MilvusConfig {

    @Value("${milvus.host}")
    private String host;
    @Value("${milvus.port}")
    private Integer port;

    @Bean
    public MilvusServiceClient milvusServiceClient() {
        return new MilvusServiceClient(
                ConnectParam.newBuilder()
                        .withHost(host)
                        .withPort(port)
                        .build());
    }
}

3.代码创建集合示例

代码初始化集合结构

public class PushMaterielsConst {
    /**
     * 集合名称(库名)
     */
    public static final String COLLECTION_NAME = "content3";

    public static final String PARTITION_NAME = "content3_partion";

    /**
     * 分片数量
     */
    public static final Integer SHARDS_NUM = 1;
    /**
     * 分区数量
     */
    public static final Integer PARTITION_NUM = 1;

    /**
     * 分区前缀
     */
    public static final String PARTITION_PREFIX = "shards_";
    /**
     * 向量值长度
     */
    public static final Integer FEATURE_DIM = 256;

    public static final Boolean TRUE = true;

    /**
     * 字段
     */
    public static class Field {
        /**
         * 主键idID
         */
        public static final String ID = "id";
        /**
         * 文本id
         */
        public static final String CONTENT = "content";
        /**
         * 向量值
         */
        public static final String CONTENT_INTRO = "content_intro";
    }


}

创建结合代码

 public void creatCollection() {
        FieldType fieldType1 = FieldType.newBuilder()
                .withName(PushMaterielsConst.Field.ID)
                .withDataType(DataType.Int64)
                .withPrimaryKey(true)
                .withAutoID(false)
                .build();
        FieldType fieldType2 = FieldType.newBuilder()
                .withName(PushMaterielsConst.Field.CONTENT)
                .withDataType(DataType.VarChar)
                .withMaxLength(255)
                .build();
        FieldType fieldType3 = FieldType.newBuilder()
                .withName(PushMaterielsConst.Field.CONTENT_INTRO)
                .withDataType(DataType.FloatVector)
                .withDimension(2)
                .build();
        CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
                .withCollectionName(PushMaterielsConst.COLLECTION_NAME)
                .withDescription("user content search")
                .withShardsNum(2)
                .addFieldType(fieldType1)
                .addFieldType(fieldType2)
                .addFieldType(fieldType3)
                .build();
        MilvusServiceClient milvusClient = getClient();
        R<RpcStatus> response = milvusClient.createCollection(createCollectionReq);
        log.info(PushMaterielsConst.COLLECTION_NAME + "是否成功创建集合——>>" + response.getStatus());
    }

插入数据代码

  /**
     * 插入数据
     *
     * @param milvusParamVoList
     * @return
     */
    public void insertPrepare(List<MilvusParamBo> milvusParamVoList) {
        List<String> content_array = new ArrayList<>();
        List<List<Float>> vector_array = new ArrayList<>();
        for (MilvusParamBo vo : milvusParamVoList) {
            content_array.add(vo.getContentId());
            vector_array.add(vo.getVector());
        }

        List<InsertParam.Field> fields = new ArrayList<>();
        fields.add(new InsertParam.Field(PushMaterielsConst.Field.CONTENT, content_array));
        fields.add(new InsertParam.Field(PushMaterielsConst.Field.CONTENT_INTRO, vector_array));

        InsertParam insertParam = InsertParam.newBuilder()
                .withCollectionName(PushMaterielsConst.COLLECTION_NAME)
                .withPartitionName(PushMaterielsConst.PARTITION_NAME)
                .withFields(fields)
                .build();
        R<MutationResult> insert = milvusClient.insert(insertParam);
        Integer status = insert.getStatus();
        System.out.println("数据插入状态:" + status);
    }

查询数据代码

    public List<String> searchTallestSimilarity(List<Float> arcsoftToFloat) {
        log.info("----Milvus数据加载到内存------");
        loadCollection();
        List<List<Float>> list = new ArrayList<>();
        list.add(arcsoftToFloat);
        SearchParam searchParam = SearchParam.newBuilder()
                //集合名称
                .withCollectionName(PushMaterielsConst.COLLECTION_NAME)
                .withPartitionNames(Arrays.asList(PushMaterielsConst.PARTITION_NAME))
                //计算方式
                // 欧氏距离 (L2)
                // 内积 (IP)
                .withMetricType(MetricType.L2)
                .withOutFields(Arrays.asList(PushMaterielsConst.Field.CONTENT))
                //返回多少条结果
                .withTopK(5)
                //搜索的向量值
                .withVectors(list)
                //搜索的Field
                .withVectorFieldName(PushMaterielsConst.Field.CONTENT_INTRO)
                .withParams(SEARCH_PARAM)
                .build();

        log.info("----Milvus向量搜索------");
        R<SearchResults> search = milvusClient.search(searchParam);
        log.info("----搜索数据处理------");
        if (search.getData() == null) return null;
        List<String> returnList = Lists.newArrayList();
        SearchResultsWrapper wrapper = new SearchResultsWrapper(search.getData().getResults());
        for (int i = 0; i < list.size(); i++) {
            List<?> fieldData = wrapper.getFieldData(PushMaterielsConst.Field.CONTENT, i);
            for (int j = 0; j < fieldData.size(); j++) {
                returnList.add((String) fieldData.get(j));
            }
        }
        return returnList;
    }
    public void loadCollection() {
        log.info("----Milvus加载到内存------");
        R<RpcStatus> response = milvusClient.loadPartitions(
                LoadPartitionsParam
                        .newBuilder()
                        //集合名称
                        .withCollectionName(PushMaterielsConst.COLLECTION_NAME)
                        //需要加载的分区名称
                        .withPartitionNames(Lists.newArrayList(PushMaterielsConst.PARTITION_NAME))
                        .build());
        log.info("Milvus数据加载到内存状态:{}", response.getStatus());
    }

按照多条件匹配查询数据

      public List<MilvesSearchDate> searchTallestSimilarityWithExpr(List<Float> arcsoftToFloat, ContentParamVo paramVo,
                                                                  String collectionName, String partionName) {
        Integer top = paramVo.getTop();
        if (top == null || top < 1) {
            top = 5;
        }
        loadCollection(collectionName, partionName);

        // 查询参数封装
        //1、向量查询参数
        List<List<Float>> list = new ArrayList<>();
        list.add(arcsoftToFloat);
        SearchParam.Builder builder = SearchParam.newBuilder()
                //集合名称
                .withCollectionName(collectionName)
                .withPartitionNames(Arrays.asList(partionName))
                //计算方式
                // 欧氏距离 (L2)
                // 内积 (IP)
                .withMetricType(MetricType.L2)
                .withOutFields(Arrays.asList(MilvusFileId.PARTID))
                //返回多少条结果
                .withTopK(top)
                //搜索的向量值
                .withVectors(list)
                //搜索的Field
                .withVectorFieldName(MilvusFileId.EMBEDDING)
                .withParams(SEARCH_PARAM);
//                .withExpr();
        //2、类别查询参数
        List<String> categorys = paramVo.getCategoryId();
        if (categorys != null && categorys.size() > 0) {
            String categoryExpr = StringUtils.join(MilvusFileId.CATEGORY, " in [", listToString(categorys), "]");
            log.info("过滤条件:{}", categoryExpr);
            builder.withExpr(categoryExpr);
        }
        //3、文章id查询参数
        List<String> docs = paramVo.getDocId();
        if (docs != null && docs.size() > 0) {
            String docExpr = StringUtils.join(MilvusFileId.DOCID, " in [", listToString(docs), "]");
            log.info("过滤条件:{}", docExpr);
            builder.withExpr(docExpr);
        }
        log.info("----Milvus向量搜索------");
        R<SearchResults> search = milvusClient.search(builder.build());
        log.info("----搜索数据处理------");
        if (search.getData() == null) {
            log.error("搜索数据返回为空");
            return null;
        }
        List<MilvesSearchDate> returnList = Lists.newArrayList();
        SearchResultsWrapper wrapper = new SearchResultsWrapper(search.getData().getResults());
        try {
            for (int i = 0; i < list.size(); i++) {
                List<SearchResultsWrapper.IDScore> scores = wrapper.getIDScore(i);
                List<?> fieldData = wrapper.getFieldData(MilvusFileId.PARTID, i);
                for (int j = 0; j < fieldData.size(); j++) {
                    returnList.add(MilvesSearchDate.builder()
                            .partId((String) fieldData.get(j))
                            .score(scores.get(j).getScore())
                            .build());
                }
            }
        } catch (Exception e) {
            log.error("解析向量匹配数据异常:{}", e.getMessage());
        }
        return returnList;
    }

总结

上述为springboot集成Milvus数据库的实现,查询的时候存在问题,如果查询的写了前五条数据,实际只查询到3条的话,剩下的两天就会乱给(给的数据和查询文本不相干)

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
功能设计 ​ 系统功能模块较为简单,主要功能就是**新增人脸**和**人脸搜索**两个功能,其中新增人脸使用页面上传和压缩包批量上传两个方式,压缩包上传时文件名称为用户名,下面主要说明人脸搜索的功能流程 ##### Milvues ​ 在介绍前需要说明一下Mulvus ​ Milvus 向量数据库能够帮助用户轻松应对海量非结构化数据(图片 / 视频 / 语音 / 文本)检索。单节点 Milvus 可以在秒内完成十亿级的向量搜索 ​ 因此虹软的SDK只能提取向量及对比的功能,在大规模人脸识别中,需要搜索引擎对于人脸数据进行初步筛选到一个较小的范围后在利用虹软的SDK进行测试,值得一提的是,博主多次测试后Milvues返回的匹配率足以满足人脸匹配的要求,Milvus的安装部署和使用文档参考 https://milvus.io/cn/docs/v2.0.x ​ **特别说明的是**虹软提取的数组是一个经过归一后的1032长度的byte数组,我们需要对数组进行转换,去除前8位的版本号,并将1024长度的byte转为256长度的float向量,这部分可以利用Arrays提供的方法进行转换,代码中也有相应的工具类 ##### 人脸上传(单张) ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
Milvus 是一个开源的向量相似度搜索引擎,而Spring Boot 是一个用于构建基于 Java 的独立、生产级的应用程序的框架。 Milvus Spring Boot 是将 MilvusSpring Boot 框架结合使用的一种方式。借助 Spring Boot,我们可以更方便地构建基于 Milvus 的应用程序。 首先,我们可以使用 Spring Boot 的依赖管理功能,将 Milvus 的 Java 客户端库添加到项目中。这样,我们就可以在我们的应用程序中直接使用 Milvus 的功能,如向量的插入、查询和删除等。 其次,Spring Boot 提供了强大的配置管理功能,我们可以轻松地将 Milvus 的连接配置信息添加到应用程序的配置文件中,例如指定 Milvus 的 IP 地址、端口号和连接池大小等。这样,我们就可以灵活地管理 Milvus 与其他组件的连接。 另外,Spring Boot 还提供了便捷的 RESTful API 开发功能。我们可以利用这一特性,将 Milvus 的搜索引擎功能以接口的形式暴露给客户端,使得客户端可以通过 HTTP 请求来进行向量的检索。这样,我们可以轻松地建立一个灵活、高性能的分布式向量搜索系统。 总的来说,Milvus Spring Boot 结合了 Milvus 的强大功能和 Spring Boot 的便捷开发特性,使得我们可以更快速、灵活地搭建起一个高性能的向量搜索应用程序。它在大数据、人工智能等领域有广泛的应用前景,可以应对各种复杂的向量查询需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值