我基于官方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;
    }

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐