Springboot整合Milvus向量库

news2024/11/27 0:39:38

1. Milvus的Maven依赖, 配置如下

        <dependency>
            <groupId>io.milvus</groupId>
            <artifactId>milvus-sdk-java</artifactId>
            <version>2.3.4</version>
            <exclusions>
                <exclusion>
                    <artifactId>log4j-slf4j-impl</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

PS: 请注意!引入的版本要看你部署的milvus服务的版本是多少,然后milvus官网上会有milvus服务对应的java sdk版本的版本号,版本号一定要对应的上  这样相应的版本api文档接口才可以用

milvus官方文档:Milvus v2.3.x documentation

然后2.3.4版本的java sdk的milvus还需要引用google 的protobuf包,不然会报错提示找不到此包

此包也要注意对应milvus的版本  这里官网没说明,我自行尝试可用的是3.24.1版本对应milvus的2.3.4版本的, 配置如下:

        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.24.1</version>
        </dependency>

2. 向量库的配置类 获取向量库服务地址 登录用户密码等

import io.milvus.client.MilvusServiceClient;
import io.milvus.param.ConnectParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MilvusConfig {

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

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

    @Value("${milvus.username}")
    private String username;

    @Value("${milvus.password}")
    private String password;

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

application.yml配置文件里配置相应的数据信息

3. 根据milvus 2.3.5 java SDK提供的API接口  调测相关使用的接口 

如:创建集合,创建索引,加载集合到内存,插入向量数据,查询向量数据并返回结果 删除集合

import java.util.List;

/**
 * milvus向量数据库相关业务接口
 *
 * @author Jx
 * @version 2024-3-18
 */
public interface IMilvusService {
    Boolean hasCollect(String collectionName);

    void create(String collectionName, String desc);

    Boolean insert(String name, List<Long> textIds, List<List<Float>> vectorList);
    List<Long> search(String name, int topK, List<List<Float>> vectorList);

    void dropCollect(String name);

    void createIndex(String name);

    void dropVectors(String name, List<Long> indexIds);
}

实现类

import com.beust.jcommander.internal.Lists;
import com.geb.config.FaceArchive;
import com.geb.service.IMilvusService;
import io.milvus.client.MilvusServiceClient;
import io.milvus.common.clientenum.ConsistencyLevelEnum;
import io.milvus.grpc.DataType;
import io.milvus.grpc.GetLoadStateResponse;
import io.milvus.grpc.MutationResult;
import io.milvus.grpc.SearchResults;
import io.milvus.param.IndexType;
import io.milvus.param.MetricType;
import io.milvus.param.R;
import io.milvus.param.RpcStatus;
import io.milvus.param.collection.*;
import io.milvus.param.dml.DeleteParam;
import io.milvus.param.dml.InsertParam;
import io.milvus.param.dml.SearchParam;
import io.milvus.param.highlevel.collection.ListCollectionsParam;
import io.milvus.param.highlevel.collection.response.ListCollectionsResponse;
import io.milvus.param.highlevel.dml.DeleteIdsParam;
import io.milvus.param.highlevel.dml.response.DeleteResponse;
import io.milvus.param.index.CreateIndexParam;
import io.milvus.response.SearchResultsWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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


@Slf4j
@Service
public class MilvusServiceImpl implements IMilvusService {

    @Autowired
    private MilvusServiceClient milvusServiceClient;


    final IndexType INDEX_TYPE = IndexType.IVF_FLAT;   // IndexType
    final String INDEX_PARAM = "{\"nlist\":1024}";     // ExtraParam

    /**
     * 创建集合的字段
     * text_id  对应的文本id
     * vector  向量字段
     * tag  标签
     */
    private final String TEXTID = "text_id";
    private final String VECTOR = "vector";
    private final String TAG = "tag";

    private final int dimension = 1024;



    /**
     * 创建集合  指定集合名称
     */
    @Override
    public void create(String collectionName, String desc){
        log.info("Miluvs create collectionName:{}, desc:{}", collectionName, desc);
        boolean has = hasCollect(collectionName);
        log.info("Miluvs hasCollect:{}", has);
        // 不存在此集合才进行创建集合
        if(!has){
            //  创建集合 设置索引 加载集合到内存中
            FieldType fieldType1 = FieldType.newBuilder()
                    .withName(TEXTID)
                    .withDataType(DataType.Int64)
                    .withPrimaryKey(true)
                    .withAutoID(false)
                    .build();
            FieldType fieldType2 = FieldType.newBuilder()
                    .withName(VECTOR)  // 设置向量名称
                    .withDataType(DataType.FloatVector)  // 设置向量类型
                    .withDimension(dimension) // 设置向量维度
                    .build();
            FieldType fieldType3 = FieldType.newBuilder()
                    .withName(TAG)
                    .withDataType(DataType.Int64)
                    .build();
            CreateCollectionParam createCollectionReq = CreateCollectionParam.newBuilder()
                    .withCollectionName(collectionName)
                    .withDescription(desc)
                    .withShardsNum(2)
                    .addFieldType(fieldType1)
                    .addFieldType(fieldType2)
                    .addFieldType(fieldType3)
                    .withEnableDynamicField(true)
                    .withConsistencyLevel(ConsistencyLevelEnum.BOUNDED)
                    .build();
            R<RpcStatus> response = milvusServiceClient.createCollection(createCollectionReq);
            if (response.getStatus() != R.Status.Success.getCode()) {
                log.info("milvus create fail message:{}", response.getMessage());
            }else{
                // 创建集合索引并加载集合到内存  插入数据和搜索的前置操作!!
                createIndex(collectionName);
            }
        }
    }


    /**
     * 创建集合索引 -- 加在向量字段上
     * @param collectionName
     */
    public void createIndex(String collectionName){
        milvusServiceClient.createIndex(
                CreateIndexParam.newBuilder()
                        .withCollectionName(collectionName)
                        .withFieldName(VECTOR)
                        .withIndexType(INDEX_TYPE)
                        .withMetricType(MetricType.L2)
                        .withExtraParam(INDEX_PARAM)
                        .withSyncMode(Boolean.FALSE)
                        .build()
        );
        // 加载所创建的集合
        loadCollection(collectionName);

    }


    /**
     * 加载集合
     * @param collectionName
     */
    public void loadCollection(String collectionName){
        milvusServiceClient.loadCollection(
                LoadCollectionParam.newBuilder()
                        .withCollectionName(collectionName)
                        .build()
        );
        // You can check the loading status
        GetLoadStateParam param = GetLoadStateParam.newBuilder()
                .withCollectionName(collectionName)
                .build();
        R<GetLoadStateResponse> stateResponse = milvusServiceClient.getLoadState(param);
        if (stateResponse.getStatus() != R.Status.Success.getCode()) {
            System.out.println(stateResponse.getMessage());
        }
    }


    /**
     * 集合是否存在
     * @return
     */
    @Override
    public Boolean hasCollect(String collectionName){
        R<Boolean> hasResult = milvusServiceClient.hasCollection(
                HasCollectionParam.newBuilder()
                        .withCollectionName(collectionName)
                        .build());
        if (hasResult.getStatus() == R.Status.Success.getCode()) {
            return hasResult.getData();
        }
        return false;
    }



    /**
     * 向量库中插入数据
     */
    @Override
    public Boolean insert(String name, List<Long> textIds, List<List<Float>> vectorList){
        log.info("milvus insert name:{}, textIds:{}, vectorList:{}", name, textIds, vectorList);
        List<Long> tagList = new ArrayList<>();
        for (Long textId : textIds) {
            tagList.add(0L);
        }
        List<InsertParam.Field> fieldsInsert = new ArrayList<>();
        fieldsInsert.add(new InsertParam.Field(TEXTID, textIds));  // 文本对应的ids数据list
        fieldsInsert.add(new InsertParam.Field(VECTOR, vectorList));  // 转换后的向量数据list
        fieldsInsert.add(new InsertParam.Field(TAG, tagList));  // 标签占位符  给个0
        InsertParam param = InsertParam.newBuilder()
                .withCollectionName(name)
                .withFields(fieldsInsert)
                .build();
        R<MutationResult> response = milvusServiceClient.insert(param);
        if (response.getStatus() != R.Status.Success.getCode()) {
            log.info("milvus insert vector fail! message:{}", response.getMessage());
            return false;
        }else{
            return true;
        }

    }



    /**
     * 删除集合
     * @param collectionName
     */
    @Override
    public void dropCollect(String collectionName){
        milvusServiceClient.dropCollection(
                DropCollectionParam.newBuilder()
                        .withCollectionName(collectionName)
                        .build()
        );
    }


    /**
     * 根据ids删除向量
     * @param collectionName
     * @param indexIds
     */
    @Override
    public void dropVectors(String collectionName, List<Long> indexIds){
        String expr =  TEXTID + " in " + indexIds;
        DeleteParam param = DeleteParam.newBuilder()
                .withCollectionName(collectionName)
                .withExpr(expr)
                .build();
        R<MutationResult> response = milvusServiceClient.delete(param);
        if (response.getStatus() != R.Status.Success.getCode()) {
            System.out.println(response.getMessage());
        }
    }



    /**
     * 向量搜索 - 向量库中用具体向量搜索 - 返回indexIds
     */
    @Override
    public List<Long> search(String collectionName, int topK , List<List<Float>> vectorList){
        // 构建查询条件  进行向量字段查询   待测试1024维度向量
        SearchParam searchParam = io.milvus.param.dml.SearchParam.newBuilder()
                .withCollectionName(collectionName)
                .withVectorFieldName(VECTOR)
                .withOutFields(Lists.newArrayList("*"))
                .withVectors(vectorList)
                .withTopK(topK)
                .build();
        R<SearchResults> searchResults = milvusServiceClient.search(searchParam);
        if (searchResults.getStatus() != R.Status.Success.getCode()) {
            log.info(searchResults.getMessage());
        }
        List<Long> textIdList = new ArrayList<>() ;
        SearchResultsWrapper wrapper = new SearchResultsWrapper(searchResults.getData().getResults());
        for (int i = 0; i < vectorList.size(); ++i) {
            List<SearchResultsWrapper.IDScore> scores = wrapper.getIDScore(i);
            for (SearchResultsWrapper.IDScore score:scores) {
                Map<String, Object> filedsMap = score.getFieldValues();
                textIdList.add(Long.valueOf(String.valueOf(filedsMap.get(TEXTID))));
            }
        }
        return textIdList;
    }




    /**
     * 删除集合中的 id对应的向量
     */
    public void deleteEmbedingById(){
        List<String> ids = Lists.newArrayList("441966745769900131","441966745769900133");
        DeleteIdsParam param = DeleteIdsParam.newBuilder()
                .withCollectionName(FaceArchive.COLLECTION_NAME_MILVUS_TESTONE)
                .withPrimaryIds(ids)
                .build();
        R<DeleteResponse> response = milvusServiceClient.delete(param);
        if (response.getStatus() != R.Status.Success.getCode()) {
            System.out.println(response.getMessage());
        }

        for (Object deleteId : response.getData().getDeleteIds()) {
            System.out.println(deleteId);
        }

    }



    // 测试用的向量数据类型
    public List<List<Float>> getListVector(){
        List<Float> vectorData = new ArrayList<>();
        for (int i = 0; i < 1; i++) {
            vectorData.add((float) Math.random());
        }
        List<List<Float>> vectors = new ArrayList<>();
        vectors.add(vectorData);

        return vectors;
    }
}

以上,跟业务进行结合  直接调用操作向量库的API接口即可~

PS:milvus  集成在springboot项目中踩的坑:

#首先就是milvus和protobuf的版本要对应上  可以查下官网api提供的服务端的milvus版本对应的java sdk milvus版本    然后根据milvus sdk版本再找到对应的protobuf版本

#其次  根据官网文档api创建完集合后是无法自动加载集合的  需要手动为集合创建一个索引  比如IVF类型的索引  再进行集合加载到内存  然后才可以对该集合查询插入数据等操作

插入过程中: 所有字段值都不能为空 且 所有字段值条数都需一样  也就是统一字段条数  一致!!

#还有就是,创建集合时候  确定好向量字段的维度,

后面插入向量数据以及查询向量数据的数据维度要与创建向量字段的维度相同!!

注意! milvus向量库只负责向量的操作存储及查询这些,并不负责文本or视频音频转为向量数据的过程,此过程需要专门模型转换进行数据处理为向量数据才可用milvus向量数据库操作!

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

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

相关文章

【蓝桥备赛】异或和——树状数组、DFS

题目链接 异或和 思路分析 树上每个点都有一个点权&#xff0c;对树上的更新操作是修改指定点的点权&#xff0c;查询操作是查询指定点为根结点的子树点权异或和。 这里的这些操作都和树状数组的单点修改和区间查询非常相似&#xff0c;即我们在修改一个点时&#xff0c;同时…

浅谈物联网高速公路智慧配电室系统构建方案

关键词&#xff1a;高速公路&#xff1b;智慧供配电&#xff1b;电力监控&#xff1b;配电室智能运维托管&#xff1b;安全隐患 0、引言 随着高速公路事业的不断发展和路网的不断延伸&#xff0c;传统的管理方式已难以满足日益增长的需求&#xff0c;动态管理和安全隐患预警成…

当面试官问你插入排序算法,你敢说自己会吗?

算法学习的重要性 在程序员的世界里&#xff0c;算法就如同一座桥梁&#xff0c;连接着问题与解决方案&#xff0c;是实现优秀程序的关键。 掌握算法&#xff0c;就能够在面对各种问题时&#xff0c;找到最合适的解决方法&#xff0c;以最少的时间和空间&#xff0c;实现最优的…

【airtest】自动化入门教程(四)Poco元素定位

目录 一、基础操作 1、通过属性名等方式 2、通过属性组合 3、子节点方式 4、子节点加属性组合方式 5、孙节点offspring 6、兄弟节点sibling 7、父节点parent 8、正则表达式 9、直到某个元素出现 10、直到某个元素消失 二、通过局部坐标定位 1、使用局部坐标系的cli…

计算多个元素的累乘结果累乘器start默认初始为1 math.prod()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算多个元素的累乘结果 累乘器start默认初始为1 math.prod() [太阳]选择题 请问题目中的代码最后输出什么? import math list1 [1, 2, 3] print("【显示】list1 ",list1) pri…

留学生在美国大学利用AI工具到底算不算作弊呢?

自2022年以来&#xff0c;美国大学就开启了一场AI作弊与反作弊大战 战场小至测验&#xff0c;大至申请 这场战争并没有一方胜利&#xff0c;作弊者心思费尽 校方反作弊弄得教授们苦不堪言 那么作为中国留学生该如何避免这场战役呢&#xff1f; 毕竟还是学业要紧呢…… 故事…

[Leetcode笔记] 动态规划相关

前言 写题目写到了一些和动态规划相关的内容&#xff0c;所以在这里记录一下 LCR 089 题解思路 总的来说&#xff0c;就是用一个数组去存储当前的最优解&#xff0c;然后从0开始一路向上顺推过去&#xff0c;求得最后一位的最优解。 class Solution { public:int rob(vect…

【HTML】标签学习(下.2)

&#xff08;大家好哇&#xff0c;今天我们将继续来学习HTML&#xff08;下.2&#xff09;的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 二.列表标签 2.1 无序列表(重点) 2.2有序列表(理解) 2.3 自定义列表(重点…

数据结构算法系列----广度优先搜索(bfs)

目录 一、什么是bfs 二、bfs和dfs的差异 搜索顺序&#xff1a; 数据结构&#xff1a; 搜索方式&#xff1a; 三、bfs解决的主要问题 四、例题 一、什么是bfs BFS&#xff08;广度优先搜索&#xff09;是一种图搜索算法&#xff0c;用于在图或树数据结构中进行遍历。BFS从…

管理科学笔记

1.线性规划 画出区域&#xff0c;代入点计算最大最小值 2.最小生成树 a.断线法&#xff0c;从大的开始断 b.选择法&#xff0c;从小的开始选 3.匈牙利法 维度数量直线覆盖所有的0 4.一直选最当前路线最短路径 5.线性规划 6.决策论

反射——获取Class对象的三种方法,构造器、成员变量、方法

作用 学习如何获取类的信息&#xff0c;操作它们 获取class对象的三种方法 package com.zz.reflection;import com.zz.Interface.studentMannger.ClassManager;//目标&#xff1a;获取class对象 public class Test1Class {public static void main(String args[]) throws Class…

【Qt】使用Qt实现Web服务器(九):EventSource+JSON实现工业界面数据刷新

1、效果 效果如下,实时刷新温度、湿度 2、源码 2.1 index.html <html><body> <!-- 页面布局,本人对HTML标签不熟悉,凑合看吧 --> <div><label for

微信怎么恢复好友?7个方法助你轻松寻回失联好友

在数字化社交日益盛行的今天&#xff0c;微信作为我们日常生活中不可或缺的沟通工具&#xff0c;承载着与亲朋好友、同事伙伴之间的深厚情谊。然而&#xff0c;有时由于误操作或其他原因&#xff0c;我们可能会不小心删除了某些重要的微信好友&#xff0c;这时&#xff0c;如何…

【二叉树】Leetcode 105. 从前序与中序遍历序列构造二叉树【中等】

从前序与中序遍历序列构造二叉树 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例1&#xff1a; 输入: preorder [3,9,20,15,7], inorder …

刷题之动态规划-子数组

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;开始刷动态规划的子数组类型相关的题目 动态规划5个步骤 状态表示 &#xff1a;dp数组中每一个下标对应值的含义是什么>dp[i]表示什么状态转移方程&#xff1a; dp[i] 等于什么1 和 2 是动态规划的核心步骤&#xff0c;…

深度学习理论基础(五)卷积神经网络CNN

目录 前述&#xff1a;卷积神经网络基础1.卷积网络流程2.卷积网络核心3.卷积下采样4.卷积上采样--转置卷积 一、卷积神经网络层1.卷积层&#xff08;1&#xff09;内部参数&#xff1a;卷积核权重&#xff08;2&#xff09;内部参数&#xff1a;偏置&#xff08;3&#xff09;外…

dm8 开启归档模式

dm8 开启归档模式 1 命令行 [dmdbatest1 dm8]$ disql sysdba/Dameng123localhost:5237服务器[localhost:5237]:处于普通打开状态 登录使用时间 : 3.198(ms) disql V8 SQL> select name,status$,arch_mode from v$database;行号 NAME STATUS$ ARCH_MODE ----------…

DETR【Transformer+目标检测】

End-to-End Object Detection with Transformers 2024 NVIDIA GTC&#xff0c;发布了地表最强的GPU B200&#xff0c;同时&#xff0c;黄仁勋对谈《Attention is All You Need》论文其中的7位作者&#xff0c;座谈的目的无非就是诉说&#xff0c;Transformer才是今天人工智能成…

【JavaWeb】百度地图API SDK导入

百度地图开放平台 | 百度地图API SDK | 地图开发 (baidu.com) 登录注册&#xff0c;创建应用&#xff0c;获取AK 地理编码 | 百度地图API SDK (baidu.com) 需要的接口一&#xff1a;获取店铺/用户 所在地址的经纬度坐标 轻量级路线规划 | 百度地图API SDK (baidu.com) 需要的…