SpringBoot集成Milvus向量库实现CRUD

Milvus是一款开源向量数据库,主要用于在大模型领域做向量查询的相关操作。milvus支持的语言比较多,支持python, Java, Go,node等开发语言。本文主要介绍如何使用Java语言,采用springboot框架集成和调用Milvus数据库。

本文示例使用的milvus版本是 v2.5.4,关于如何安装部署milvus向量数据,请参考上一篇文章:lowcode.blog.csdn.net/article/det...

本文使用Java sdk操作Milvus实现数据的增删改查,需要先对milvus的基本概念有个初步的了解,便于对后面代码的更好理解。

Milvus基本概念

  • 数据库Database:与传统的数据库引擎MySQL类似,你也可以在 Milvus 中创建数据库,并为特定用户分配权限来管理它们。然后,此类用户有权管理数据库中的集合。一个 Milvus 集群最多支持 64 个数据库。Milvus 集群附带一个名为 'default' 的默认数据库。除非另有指定,否则将在 default 数据库中创建集合。
  • 集合Collection :在 Milvus 中,你可以创建多个集合来管理数据,并将数据作为实体插入到集合中。Collection 和 entity 类似于关系数据库中的 table 和 records。集合是具有固定列和变体行的二维表。每列表示一个字段,每行表示一个实体。Collection 是具有固定列和变体行的二维表。每列表示一个字段,每行表示一个实体。需要Schema来实现此类结构数据管理,要插入的每个实体都必须满足Schema中定义的约束。
  • 实体Entity :在 Milvus 中,Entity 是指 Collection 中共享相同 Schema 的数据记录,一行中每个字段的数据构成一个 Entity。因此,同一 Collection 中的 Entities 具有相同的属性(例如字段名称、数据类型和其他约束)。将 Entity 插入 Collection 时,要插入的 Entity 只有在包含 Schema 中定义的所有字段时才能成功添加。Milvus 还支持动态字段,以保持 Collection 的可扩展性。启用动态字段后,您可以将 Schema 中未定义的字段插入到 Collection 中。这些字段和值将作为键值对存储在名为 ** $meta** 的保留字段中。

添加maven依赖

创建springboot工程后,在pom.xml文件里引入milvus的sdk
io.milvus milvus-sdk-java 2.5.4

本示例使用的是milvus2.5.4最新版本,Java sdk 接口参考文档:milvus.io/api-referen...

注意使用sdk版本跟milvus版本的对应关系,milvus2.5.x版本建议使用sdk2.5.2以上版本,否则可能会出现一些诡异问题。

配置yml文件

#配置milvus向量数据库的IP和端口,后面构建MilvusClient时需要

server:

port: 8080

milvus:

host: 192.168.3.17

port: 19530

4、创建MilvusClient初始化类

import io.milvus.v2.client.ConnectConfig;

import io.milvus.v2.client.MilvusClientV2;

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;

@Bean

public MilvusClientV2 milvusClientV2() {

String uri = "http://"+host+":"+port;

ConnectConfig connectConfig = ConnectConfig.builder()

.uri(uri)

.build();

return new MilvusClientV2(connectConfig);

}

}

5、创建操作向量库的Seivce

import com.google.gson.Gson;

import com.google.gson.JsonObject;

import com.yuncheng.milvus.TestRecord;

import io.milvus.v2.client.MilvusClientV2;

import io.milvus.v2.common.DataType;

import io.milvus.v2.common.IndexParam;

import io.milvus.v2.service.collection.request.AddFieldReq;

import io.milvus.v2.service.collection.request.CreateCollectionReq;

import io.milvus.v2.service.vector.request.GetReq;

import io.milvus.v2.service.vector.request.InsertReq;

import io.milvus.v2.service.vector.request.SearchReq;

import io.milvus.v2.service.vector.request.data.FloatVec;

import io.milvus.v2.service.vector.response.GetResp;

import io.milvus.v2.service.vector.response.InsertResp;

import io.milvus.v2.service.vector.response.SearchResp;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import java.util.ArrayList;

import java.util.Collections;

import java.util.List;

@Component

public class MilvusDemoService {

private static final Logger log = LoggerFactory.getLogger(MilvusDemoService.class);

//类似于mysql中的表,定义一个名称为collection_01的集合

private static final String COLLECTION_NAME = "collection_01";

//为了测试验证方便,向量维度定义2

private static final int VECTOR_DIM = 2;

private final MilvusClientV2 client;

public MilvusDemoService(MilvusClientV2 client) {

this.client = client;

}

/**

  • 创建一个Collection
    */
    public void createCollection() {

CreateCollectionReq.CollectionSchema schema = client.createSchema();

schema.addField(AddFieldReq.builder()

.fieldName("id")

.dataType(DataType.VarChar)

.isPrimaryKey(true)

.autoID(false)

.build());

schema.addField(AddFieldReq.builder()

.fieldName("title")

.dataType(DataType.VarChar)

.maxLength(10000)

.build());

schema.addField(AddFieldReq.builder()

.fieldName("title_vector")

.dataType(DataType.FloatVector)

.dimension(VECTOR_DIM)

.build());

IndexParam indexParam = IndexParam.builder()

.fieldName("title_vector")

.metricType(IndexParam.MetricType.COSINE)

.build();

CreateCollectionReq createCollectionReq = CreateCollectionReq.builder()

.collectionName(COLLECTION_NAME)

.collectionSchema(schema)

.indexParams(Collections.singletonList(indexParam))

.build();

client.createCollection(createCollectionReq);

}

/**

  • 往collection中插入一条数据
    */
    public void insertRecord(TestRecord record) {
    JsonObject vector = new JsonObject();
    vector.addProperty("id", record.getId());
    vector.addProperty("title", record.getTitle());
    List vectorList = new ArrayList<>();
    //为了模拟测试,向量写死2个
    vectorList.add(2.8f);
    vectorList.add(3.9f);
    Gson gson = new Gson();
    vector.add("title_vector", gson.toJsonTree(vectorList));

InsertReq insertReq = InsertReq.builder()

.collectionName(COLLECTION_NAME)

.data(Collections.singletonList(vector))

.build();

InsertResp resp = client.insert(insertReq);

}

/**

  • 通过ID获取记录
    */
    public GetResp getRecord(String id) {
    GetReq getReq = GetReq.builder()
    .collectionName(COLLECTION_NAME)
    .ids(Collections.singletonList(id))
    .build();
    GetResp resp = client.get(getReq);
    return resp;
    }

/**

  • 按照向量检索,找到相似度最近的topK
    /
    public List<List<SearchResp.SearchResult>> queryVector() {
    SearchResp searchR = client.search(SearchReq.builder()
    .collectionName(COLLECTION_NAME)
    .data(Collections.singletonList(new FloatVec(new float[]{0.9f, 2.1f})))
    .topK(3)
    .outputFields(Collections.singletonList("
    "))
    .build());
    List<List<SearchResp.SearchResult>> searchResults = searchR.getSearchResults();

for (List<SearchResp.SearchResult> results : searchResults) {

for (SearchResp.SearchResult result : results) {

log.info("ID="+(String)result.getId() + ",Score="+result.getScore() + ",Result="+result.getEntity().toString());

}

}

return searchResults;

}

}

这里使用到的一个简单的pojo类

public class TestRecord {

private String id;

private String title;

public String getId() {

return id;

}

public void setId(String id) {

this.id = id;

}

public String getTitle() {

return title;

}

public void setTitle(String title) {

this.title = title;

}

}

6、创建Controller类

import com.yuncheng.milvus.service.MilvusDemoService;

import io.milvus.v2.service.vector.response.GetResp;

import io.milvus.v2.service.vector.response.SearchResp;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

import java.util.List;

@RestController

@RequestMapping("/milvus")

public class MilvusController {

private static final Logger log = LoggerFactory.getLogger(MilvusController.class);

@Autowired

private MilvusDemoService milvusDemoService;

@GetMapping("/createCollection")

public void createCollection() {

milvusDemoService.createCollection();

}

@GetMapping("/insertRecord")

public void insertRecord() throws IOException {

TestRecord record = new TestRecord();

record.setId("5");

record.setTitle("北京是中国的首都,人口有3000多万人");

milvusDemoService.insertRecord(record);

}

@GetMapping("/getRecord")

public GetResp getRecord(@RequestParam(name = "id") String id){

GetResp resp = milvusDemoService.getRecord(id);

log.info("resp = " + resp.getResults);

return resp;

}

@GetMapping("/queryVector")

public List<List<SearchResp.SearchResult>> queryVector() {

List<List<SearchResp.SearchResult>> searchResults = milvusDemoService.queryVector();

return searchResults;

}

}

测试验证CRUD

确保milvus2.5.4向量数据库正常运行,然后启动springboot工程,进行对milvus向量数据库测试验证。

7.1、创建Collection

http://localhost:8080/milvus/createCollection

执行后,登录milvus控制台webUI查看

点击collection名称,查看详细的结构定义,类似于mysql中的表结构定义:

其中,id、title、title_vector字段是程序里定义的字段,另外RowID和Timestamp字段是collection默认自带的字段。

7.2、插入数据

http://localhost:8080/milvus/insertRecord

为了测试方便,本示例写死了测试数据,往milvus中插入了5条数据

7.3、查询单条记录

http://localhost:8080/milvus/getRecord?id=2

返回JSON结果集

{

"getResults": [

{

"entity": {

"title_vector": [1, 2],

"id": "2",

"title": "张三是英国人,他喜欢吃中国火锅"

}

}

]

}

7.4、按向量检索相似度

http://localhost:8080/milvus/queryVector

返回结果集:

\[ { "entity": { "title_vector": \[0.8, 1.9\], "id": "4", "title": "王五是老师,她教学AI算法" }, "score": 0.9999797, "id": "4" }, { "entity": { "title_vector": \[1, 2\], "id": "2", "title": "张三是英国人,他喜欢吃中国火锅" }, "score": 0.99827427, "id": "2" }, { "entity": { "title_vector": \[1, 2\], "id": "1", "title": "我是中国人,我喜欢吃火锅" }, "score": 0.99827427, "id": "1" }

]

其中,score为向量相似度分值,如果score=1,则表示完全一样,score小于1,表示接近。这里为了测试方面,插入数据时,在向量字段里写死了几个固定的List值,真实的业务场景中,要通过Embedding模型计算生成,后续文章中介绍如何调用AI中的Embedding服务,生成向量化的float值。

相关推荐
叶子20242218 分钟前
守护进程实验——autoDL
人工智能·算法·机器学习
夕泠爱吃糖19 分钟前
MySQL中的部分问题(1)
数据库·mysql
陈奕昆20 分钟前
4.3 HarmonyOS NEXT AI驱动的交互创新:智能助手、实时语音与AR/MR开发实战
人工智能·交互·harmonyos
百度Geek说21 分钟前
Redis 数据恢复的月光宝盒,闪回到任意指定时间
数据库
张较瘦_40 分钟前
[论文阅读] 人工智能 | 用大语言模型抓虫:如何让网络协议实现与RFC规范对齐
论文阅读·人工智能·语言模型
qb_jiajia1 小时前
微软认证考试科目众多?该如何选择?
人工智能·microsoft·微软·云计算
pen-ai1 小时前
【统计方法】蒙特卡洛
人工智能·机器学习·概率论
秃了也弱了。1 小时前
DBSyncer:开源数据库同步利器,MySQL/Oracle/ES/SqlServer/PG/
数据库·mysql·开源
说私域1 小时前
基于开源AI智能名片链动2+1模式S2B2C商城小程序的生态农庄留存运营策略研究
人工智能·小程序·开源·零售
摘取一颗天上星️1 小时前
大模型微调技术全景图:从全量更新到参数高效适配
人工智能·深度学习·机器学习