Milvus Collections
·
我基于官方api做了一些简单抽象,方便做功能验证和写MVP案例,完整代码见仓库地址: https://github.com/liyubin117/ai-demo.git
欢迎star :)
Collections 和实体类似于关系数据库中的表和记录。
Collection 是一个二维表,具有固定的列和变化的行。每列代表一个字段,每行代表一个实体。

DDL
创建
public static void createCollection(String db, String collection) {
CreateCollectionReq customizedSetupReq1 = CreateCollectionReq.builder()
.databaseName(db)
.collectionName(collection)
.collectionSchema(createSchema())
.indexParams(indexParams()) // 若索引,则在创建时会自动加载
.numShards(3) // 设置分片数
.property(Constant.MMAP_ENABLED, "true") // 启用mmap,默认true。允许 Milvus 将原始字段数据映射到内存中,而不是完全加载它们。这样可以减少内存占用,提高 Collections 的容量
.property(Constant.TTL_SECONDS, "86400") // 一旦 TTL 超时,Milvus 就会删除 Collection 中的实体。删除是异步的,这表明在删除完成之前,搜索和查询仍然可以进行
.consistencyLevel(ConsistencyLevel.EVENTUALLY) // 为集合中的搜索和查询设置一致性级别,按一致性严格程度从高到低依次是STRONG SESSION BOUNDED EVENTUALLY,严格程度取决于客户端设定的保证时间与服务端的服务时间的gap
.build();
initClient().createCollection(customizedSetupReq1);
}
加载
加载集合是在集合中进行相似性搜索和查询的前提
加载 Collections 时,Milvus 会将索引文件和所有字段的原始数据加载到内存中,以便快速响应搜索和查询。在载入 Collections 后插入的实体会自动编入索引并载入
public static void loadCollection(String db, String collection) {
LoadCollectionReq req = LoadCollectionReq.builder()
.databaseName(db)
.collectionName(collection)
// 加载特定字段在内测中,生产慎用,如果选择加载特定字段,只有load_fields 中包含的字段才能用作搜索和查询中的过滤器和输出字段。您应始终在load_fields 中包含主字段和至少一个向量字段的名称
.loadFields(Arrays.asList("my_id", "my_vector"))
.build();
initClient().loadCollection(req);
}
释放加载
及时腾出内存
public static void releaseLoad(String db, String collection) {
ReleaseCollectionReq releaseCollectionReq = ReleaseCollectionReq.builder()
.databaseName(db)
.collectionName(collection)
.build();
initClient().releaseCollection(releaseCollectionReq);
}
重命名
public static void renameCollection(String srcName, String destName) {
RenameCollectionReq renameCollectionReq = RenameCollectionReq.builder()
.collectionName(srcName)
.newCollectionName(destName)
.build();
initClient().renameCollection(renameCollectionReq);
}
修改属性
public static void alterCollection(String db, String collection, String key, String value) {
Map<String, String> properties = Collections.singletonMap(key, value);
AlterCollectionReq alterCollectionReq = AlterCollectionReq.builder()
.databaseName(db)
.collectionName(collection)
.properties(properties)
.build();
initClient().alterCollection(alterCollectionReq);
}
删除
public static void dropCollection(String db, String collection) {
DropCollectionReq dropQuickSetupParam = DropCollectionReq.builder()
.databaseName(db)
.collectionName(collection)
.build();
initClient().dropCollection(dropQuickSetupParam);
}
加载分区
public static void loadCollectionPartition(String db, String collection, String part) {
LoadPartitionsReq loadPartitionsReq = LoadPartitionsReq.builder()
.databaseName(db)
.collectionName(collection)
.partitionNames(Collections.singletonList(part))
.build();
initClient().loadPartitions(loadPartitionsReq);
}
释放分区加载
public static void releasePartitionLoad(String db, String collection, String part) {
ReleasePartitionsReq releasePartitionsReq = ReleasePartitionsReq.builder()
.databaseName(db)
.collectionName(collection)
.partitionNames(Collections.singletonList(part))
.build();
initClient().releasePartitions(releasePartitionsReq);
}
创建别名
别名是一个 Collection 的二级可变名称。使用别名提供了一个抽象层,可以在不修改应用程序代码的情况下动态切换 Collections。这对于生产环境中的无缝数据更新、A/B 测试和其他操作符特别有用
别名的关键属性:
- 一个 Collection 可以有多个别名。
- 一个别名一次只能指向一个 Collections。
- 处理请求时,Milvus 会首先检查是否存在提供名称的 Collection。如果不存在,它就会检查该名称是否是某个 Collection 的别名,即优先把名称看做是 Collection 名
public static void createAlias(String db, String collection, String alias) {
CreateAliasReq createAliasReq = CreateAliasReq.builder()
.databaseName(db)
.collectionName(collection)
.alias(alias)
.build();
initClient().createAlias(createAliasReq);
}
删除别名
public static void dropAlias(String alias) {
DropAliasReq dropAliasReq = DropAliasReq.builder()
.alias(alias)
.build();
initClient().dropAlias(dropAliasReq);
}
DML
insert
在插入数据之前,需要根据 Schema 将数据组织到字典列表中,每个字典代表一个实体,并包含 Schema 中定义的所有字段。如果 Collection 启用了动态字段,每个字典还可以包含 Schema 中未定义的字段。也可插入到指定分区。
public static void insert(String db, String collection, String part) {
List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
JsonObject row1 = new JsonObject();
row1.add("embedding", gson.toJsonTree(new float[]{0.1f, 0.2f, 0.3f, 0.4f}));
row1.addProperty("category", "book");
boolean[] boolArray = new boolean[]{true, false, false, true, true, false, true, true, false, true, false, false, true, true, false, true};
row1.add("binary_vector", gson.toJsonTree(convertBoolArrayToBytes(boolArray)));
row1.addProperty("text", "information retrieval is a field of study.");
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(1L, 0.5f);
sparse.put(100L, 0.3f);
sparse.put(500L, 0.8f);
row1.add("sparse_vector", gson.toJsonTree(sparse));
rows.add(row1);
JsonObject row2 = new JsonObject();
row2.add("embedding", gson.toJsonTree(new float[]{0.2f, 0.3f, 0.4f, 0.5f}));
row2.addProperty("category", "toy");
boolArray = new boolean[]{false, true, false, true, false, true, false, false, true, true, false, false, true, true, false, true};
row2.add("binary_vector", gson.toJsonTree(convertBoolArrayToBytes(boolArray)));
row2.addProperty("text", "information retrieval focuses on finding relevant information in large datasets.");
sparse = new TreeMap<>();
sparse.put(10L, 0.1f);
sparse.put(200L, 0.7f);
sparse.put(1000L, 0.9f);
row2.add("sparse_vector", gson.toJsonTree(sparse));
rows.add(row2);
InsertResp insertResp = initClient().insert(InsertReq.builder()
.databaseName(db)
.collectionName(collection)
.partitionName(part)
.data(rows)
.build());
System.out.printf("After insert, Generated IDs: %s\n", insertResp.getPrimaryKeys());
}
upsert
插入新实体或更新现有实体,具体取决于 upsert 请求中提供的主键是否存在于 Collections 中。upsert 可在覆盖或合并模式下工作。
public static void insert(String db, String collection, String part) {
List<JsonObject> rows = new ArrayList<>();
Gson gson = new Gson();
JsonObject row1 = new JsonObject();
row1.add("embedding", gson.toJsonTree(new float[]{0.1f, 0.2f, 0.3f, 0.4f}));
row1.addProperty("category", "book");
boolean[] boolArray = new boolean[]{true, false, false, true, true, false, true, true, false, true, false, false, true, true, false, true};
row1.add("binary_vector", gson.toJsonTree(convertBoolArrayToBytes(boolArray)));
row1.addProperty("text", "information retrieval is a field of study.");
SortedMap<Long, Float> sparse = new TreeMap<>();
sparse.put(1L, 0.5f);
sparse.put(100L, 0.3f);
sparse.put(500L, 0.8f);
row1.add("sparse_vector", gson.toJsonTree(sparse));
rows.add(row1);
JsonObject row2 = new JsonObject();
row2.add("embedding", gson.toJsonTree(new float[]{0.2f, 0.3f, 0.4f, 0.5f}));
row2.addProperty("category", "toy");
boolArray = new boolean[]{false, true, false, true, false, true, false, false, true, true, false, false, true, true, false, true};
row2.add("binary_vector", gson.toJsonTree(convertBoolArrayToBytes(boolArray)));
row2.addProperty("text", "information retrieval focuses on finding relevant information in large datasets.");
sparse = new TreeMap<>();
sparse.put(10L, 0.1f);
sparse.put(200L, 0.7f);
sparse.put(1000L, 0.9f);
row2.add("sparse_vector", gson.toJsonTree(sparse));
rows.add(row2);
InsertResp insertResp = initClient().insert(InsertReq.builder()
.databaseName(db)
.collectionName(collection)
.partitionName(part)
.data(rows)
.build());
System.out.printf("After insert, Generated IDs: %s\n", insertResp.getPrimaryKeys());
}
delete
可通过筛选条件、主键、分区删除实体
public static DeleteResp delete(String db, String collection, String filter, List<Object> ids, String part) {
DeleteReq.DeleteReqBuilder reqBuilder = DeleteReq.builder()
.databaseName(db)
.collectionName(collection);
if (StringUtils.isNotEmpty(filter)) reqBuilder.filter(filter); // 过滤条件,比如"color in ['red_7025', 'purple_4976]"
if (ids != null && !ids.isEmpty()) reqBuilder.ids(ids); // 一些要删除的主键的值
if (StringUtils.isNotEmpty(filter) && ids != null && !ids.isEmpty() && StringUtils.isNotEmpty(part)) {
reqBuilder.partitionName(part); // 也可删除指定分区的filter或ids数据
}
return initClient().delete(reqBuilder.build());
}
查询
获取加载状态
public static Boolean getCollectionLoadState(String db, String collection, String part) {
GetLoadStateReq.GetLoadStateReqBuilder reqBuilder = GetLoadStateReq.builder()
.databaseName(db)
.collectionName(collection)
;
if (StringUtils.isNotEmpty(part)) {
reqBuilder.partitionName(part);
}
return initClient().getLoadState(reqBuilder.build());
}
描述
public static DescribeCollectionResp descCollection(String db, String collection) {
DescribeCollectionReq request = DescribeCollectionReq.builder()
.databaseName(db)
.collectionName(collection)
.build();
return initClient().describeCollection(request);
}
获取分区
public static List<String> getCollectionPartitions(String db, String collection) {
ListPartitionsReq req = ListPartitionsReq.builder()
.databaseName(db)
.collectionName(collection)
.build();
return initClient().listPartitions(req);
}
分区是否存在
public static boolean isCollectionPartitionExisted(String db, String collection, String part) {
HasPartitionReq req = HasPartitionReq.builder()
.databaseName(db)
.collectionName(collection)
.partitionName(part)
.build();
return initClient().hasPartition(req);
}
获取别名
public static List<String> getAliases(String db, String collection) {
ListAliasesReq listAliasesReq = ListAliasesReq.builder()
.databaseName(db)
.collectionName(collection)
.build();
return initClient().listAliases(listAliasesReq).getAlias();
}
Search(ANNS + Filter)
public static SearchResp search(String db, String collection, String key, String annsField, BaseVector queryVector, String filter) {
Map<String, Object> searchParams = new HashMap<>();
searchParams.put("nprobe", 10); // 用于控制相似最近邻搜索的精度
searchParams.put("drop_ratio_search", 0.2); // 用于控制在近似最近邻搜索(ANN)过程中跳过部分数据的比例,以提高搜索效率,但可能会略微降低精度
SearchReq. SearchReqBuilder reqBuilder = SearchReq.builder()
.databaseName(db)
.collectionName(collection)
.searchParams(searchParams)
.topK(2);
if (StringUtils.isNotEmpty(annsField) && queryVector != null) {
reqBuilder.data(Collections.singletonList(queryVector))
.annsField(annsField)
.outputFields(Arrays.asList(key, annsField));
}
if (StringUtils.isNotEmpty(filter)) reqBuilder.filter(filter);
SearchResp searchR = initClient().search(reqBuilder.build());
return searchR;
}
更多推荐




所有评论(0)