From 5bb7e80cd69a1409c605ea77546fbd48c4c489db Mon Sep 17 00:00:00 2001 From: yhmo Date: Thu, 24 Sep 2026 14:58:28 +0800 Subject: [PATCH] feat: add V2 alias/partition/index examples, hybrid search function chains, ListCompactionTasks, deprecate function request DTOs - deprecate AddCollectionFunctionRequest and DropCollectionFunctionRequest in favor of AddFunctionFieldRequest/DropFunctionFieldRequest (pymilvus parity) - add examples/src/v2 alias.cpp, partition.cpp and index.cpp demonstrating the alias, partition and index management APIs - general.cpp: demonstrate delete-by-ids, delete-by-filter, truncate and rename - add_field.cpp: demonstrate AlterCollectionFieldProperties on an added field - db.cpp: demonstrate AlterDatabaseProperties and DropDatabaseProperties - index.cpp: insert a small row batch and flush before building indexes so describeIndex() reports meaningful indexed/total rows - HybridSearchRequest: add FunctionChains()/SetFunctionChains/ WithFunctionChains()/AddFunctionChain(); Validate() rejects function chains combined with a rerank and requires either one; the pre-callback converts each chain into the proto (pymilvus hybrid_search function_chains parity) - ListCompactionTasks: new ListCompactionTasksRequest + MilvusClientV2:: ListCompactionTasks facade that lists all retained compaction tasks of a collection via GetCompactionStateWithPlans (pymilvus list_compaction_tasks) - bump pinned milvus-proto to ae7fea6 so GetCompactionPlansRequest carries db_name/collection_name (also adds GetPersistentSegmentInfoRequest.states) - DqlUtils: guard the rerank param mapping for a null rerank function - CompactionPlan: expose the full CompactionMergeInfo surface (plan_id, trigger_id, collection_id, partition_id, channel, type, state, failure_reason, targets) and CompactionType/CompactionTaskState enums; GetCompactionPlansResponse gains CollectionName() (pymilvus Plan parity) - extract shared ValidateFunctionChains() used by SearchRequest and HybridSearchRequest so both enforce the limit/offset op-param checks - reuse MilvusConnection::GetCompactionPlans for ListCompactionTasks instead of a duplicate GetCompactionStateWithPlans wrapper - add unit tests and mocked tests covering the new request members, validation, collection_name, the full plan fields, and hybrid function chains on the wire request - examples/README.md: fix partition example to reference GetPartitionStatistics - test: switch system-test MinIO image to milvusdb/minio:RELEASE.2024-05-28T17-19-04Z (quay.io/minio image is not pullable in CI, failing the coverage job) Signed-off-by: yhmo --- cmake/MilvusProtoGen.cmake | 2 +- examples/README.md | 3 + examples/src/v2/add_field.cpp | 11 ++ examples/src/v2/alias.cpp | 120 ++++++++++++ examples/src/v2/db.cpp | 10 + examples/src/v2/general.cpp | 46 ++++- examples/src/v2/index.cpp | 173 +++++++++++++++++ examples/src/v2/partition.cpp | 145 ++++++++++++++ src/impl/MilvusClientV2Impl.cpp | 85 ++++++-- src/impl/MilvusClientV2Impl.h | 3 + src/impl/request/dql/HybridSearchRequest.cpp | 40 +++- src/impl/request/dql/SearchRequest.cpp | 49 +---- .../utility/ListCompactionTasksRequest.cpp | 53 +++++ .../utility/GetCompactionPlansResponse.cpp | 10 + src/impl/types/CompactionPlan.cpp | 95 +++++++++ src/impl/utils/DqlUtils.cpp | 62 +++++- src/impl/utils/DqlUtils.h | 8 + src/include/milvus/MilvusClientV2.h | 11 ++ .../collection/AddCollectionFunctionRequest.h | 3 + .../DropCollectionFunctionRequest.h | 3 + .../milvus/request/dql/HybridSearchRequest.h | 30 +++ .../utility/ListCompactionTasksRequest.h | 82 ++++++++ .../utility/GetCompactionPlansResponse.h | 16 ++ src/include/milvus/types/CompactionPlan.h | 181 +++++++++++++++++- test/it/v2/TestDQL.cpp | 36 ++++ test/it/v2/TestUtility.cpp | 67 +++++++ test/st/milvus_container.py | 2 +- test/ut/request/TestDqlRequests.cpp | 102 ++++++++++ test/ut/request/TestUtilityRequests.cpp | 18 ++ test/ut/response/TestUtilityResponses.cpp | 4 + test/ut/types/TestCompactionPlan.cpp | 29 +++ 31 files changed, 1428 insertions(+), 71 deletions(-) create mode 100644 examples/src/v2/alias.cpp create mode 100644 examples/src/v2/index.cpp create mode 100644 examples/src/v2/partition.cpp create mode 100644 src/impl/request/utility/ListCompactionTasksRequest.cpp create mode 100644 src/include/milvus/request/utility/ListCompactionTasksRequest.h diff --git a/cmake/MilvusProtoGen.cmake b/cmake/MilvusProtoGen.cmake index 29d3fd72..68b9cdbf 100644 --- a/cmake/MilvusProtoGen.cmake +++ b/cmake/MilvusProtoGen.cmake @@ -16,7 +16,7 @@ include_guard(GLOBAL) -set(PROTO_COMMIT c39cddab3fac51c1a1228136ce5e95b131bd2ce6) +set(PROTO_COMMIT ae7fea6ab2f4e958f2feef0f0edb9a0d23fa7e0c) include(FetchContent) diff --git a/examples/README.md b/examples/README.md index 4d9e98fd..3f78a7a3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -39,6 +39,7 @@ Legacy examples for MilvusClient (maintenance mode): - `./cmake_build/examples/v1/sdk_vector_sparse_v1`: example to show the usage of SparseVector field. Recommended examples for MilvusClientV2: +- `./cmake_build/examples/v2/sdk_alias_v2`: example to show the usage of aliases, including createAlias(), listAliases(), describeAlias(), alterAlias() and dropAlias(). - `./cmake_build/examples/v2/sdk_array_v2`: example to show the usage of Array field. - `./cmake_build/examples/v2/sdk_cdc_v2`: example to show the usage of CDC interfaces including DumpMessages(). - `./cmake_build/examples/v2/sdk_db_v2`: example to show the usage of databases. @@ -54,6 +55,7 @@ Recommended examples for MilvusClientV2: - `./cmake_build/examples/v2/sdk_geometry_field_v2`: a general example to show the usage of Geometry field. - `./cmake_build/examples/v2/sdk_group_by_v2`: a general example to show the usage of grouping search. - `./cmake_build/examples/v2/sdk_hybrid_search_v2`: example to show the usage of hybrid search interface. +- `./cmake_build/examples/v2/sdk_index_v2`: example to show the usage of index interfaces, including createIndex(), listIndexes(), describeIndex(), alterIndexProperties(), dropIndexProperties() and dropIndex(). - `./cmake_build/examples/v2/sdk_iterator_query_v2`: example to show the usage of query iterator. - `./cmake_build/examples/v2/sdk_iterator_search_v2`: example to show the usage of search iterator. - `./cmake_build/examples/v2/sdk_json_v2`: example to show the usage of JSON field. @@ -64,6 +66,7 @@ Recommended examples for MilvusClientV2: - `./cmake_build/examples/v2/sdk_optimize_v2`: example to show the usage of Optimize(). - `./cmake_build/examples/v2/sdk_order_by_v2`: example to show the usage of ORDER BY for search and query. - `./cmake_build/examples/v2/sdk_partition_key_v2`: example to show the usage of partition key. +- `./cmake_build/examples/v2/sdk_partition_v2`: example to show the usage of partitions, including createPartition(), hasPartition(), loadPartitions(), GetPartitionStatistics(), releasePartitions() and dropPartition(). - `./cmake_build/examples/v2/sdk_rbac_v2`: example to show the usage of RBAC. - `./cmake_build/examples/v2/sdk_rerank_function_v2`: example to show the usage of rerank function. - `./cmake_build/examples/v2/sdk_run_analyzer_v2`: example to show the usage of run_analyzer(). diff --git a/examples/src/v2/add_field.cpp b/examples/src/v2/add_field.cpp index 41f253b2..eef5c1fc 100644 --- a/examples/src/v2/add_field.cpp +++ b/examples/src/v2/add_field.cpp @@ -138,6 +138,17 @@ main(int argc, char* argv[]) { QueryById(client, 100); } + { + // alter the max length of the added field + status = client->AlterCollectionFieldProperties(milvus::AlterCollectionFieldPropertiesRequest() + .WithCollectionName(collection_name) + .WithFieldName(field_text) + .AddProperty("max_length", "200")); + util::CheckStatus("alter field 'text' max_length", status); + std::cout << "Field 'text' max_length altered to 200" << std::endl; + DescribeCollection(client); + } + { const std::string text_value = "this is a new row"; InsertRow(client, 500, &text_value); diff --git a/examples/src/v2/alias.cpp b/examples/src/v2/alias.cpp new file mode 100644 index 00000000..958caca1 --- /dev/null +++ b/examples/src/v2/alias.cpp @@ -0,0 +1,120 @@ +// Licensed to the LF AI & Data foundation under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "ExampleUtils.h" +#include "milvus/MilvusClientV2.h" + +namespace { +const std::string collection_name = "CPP_V2_ALIAS"; +const std::string second_collection_name = collection_name + "_2"; +const std::string alias_name = "cpp_v2_alias"; +const std::string field_id = "id"; +const std::string field_vector = "vector"; +const uint32_t dimension = 4; + +void +CreateCollection(milvus::MilvusClientV2Ptr& client, const std::string& name) { + milvus::CollectionSchemaPtr collection_schema = std::make_shared(); + collection_schema->AddField({field_id, milvus::DataType::INT64, "", true, true}); + collection_schema->AddField( + milvus::FieldSchema(field_vector, milvus::DataType::FLOAT_VECTOR).WithDimension(dimension)); + + // drop a leftover collection from a previous run + client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(name)); + + auto status = client->CreateCollection( + milvus::CreateCollectionRequest().WithCollectionName(name).WithCollectionSchema(collection_schema)); + util::CheckStatus("create collection: " + name, status); +} + +void +CreateAlias(milvus::MilvusClientV2Ptr& client) { + auto status = + client->CreateAlias(milvus::CreateAliasRequest().WithCollectionName(collection_name).WithAlias(alias_name)); + util::CheckStatus("create alias: " + alias_name, status); +} + +void +ListAliases(milvus::MilvusClientV2Ptr& client) { + milvus::ListAliasesResponse response; + auto status = client->ListAliases(milvus::ListAliasesRequest().WithCollectionName(collection_name), response); + util::CheckStatus("list aliases of: " + collection_name, status); + std::cout << "Aliases of " << collection_name << ": "; + util::PrintList(response.Aliases()); +} + +void +DescribeAlias(milvus::MilvusClientV2Ptr& client) { + milvus::DescribeAliasResponse response; + auto status = client->DescribeAlias(milvus::DescribeAliasRequest().WithAlias(alias_name), response); + util::CheckStatus("describe alias: " + alias_name, status); + std::cout << "Alias '" << response.Desc().Name() << "' -> collection '" << response.Desc().CollectionName() + << "' in database '" << response.Desc().DatabaseName() << "'" << std::endl; +} + +void +AlterAlias(milvus::MilvusClientV2Ptr& client) { + // create a second collection and repoint the alias to it + CreateCollection(client, second_collection_name); + auto status = client->AlterAlias( + milvus::AlterAliasRequest().WithCollectionName(second_collection_name).WithAlias(alias_name)); + util::CheckStatus("alter alias: " + alias_name, status); +} + +void +DropAlias(milvus::MilvusClientV2Ptr& client) { + auto status = client->DropAlias(milvus::DropAliasRequest().WithAlias(alias_name)); + util::CheckStatus("drop alias: " + alias_name, status); +} + +} // namespace + +int +main(int argc, char* argv[]) { + printf("Example start...\n"); + + auto client = milvus::MilvusClientV2::Create(); + + milvus::ConnectParam connect_param{"http://localhost:19530", "root:Milvus"}; + auto status = client->Connect(connect_param); + util::CheckStatus("connect milvus server", status); + + // drop leftovers from a previous run; an alias blocks dropping its target collection, + // so the alias (if any) must be dropped before either collection. + client->DropAlias(milvus::DropAliasRequest().WithAlias(alias_name)); + client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(second_collection_name)); + client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(collection_name)); + + CreateCollection(client, collection_name); + CreateAlias(client); + ListAliases(client); + DescribeAlias(client); + AlterAlias(client); + DescribeAlias(client); + DropAlias(client); + + // cleanup + client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(second_collection_name)); + client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(collection_name)); + + client->Disconnect(); + return 0; +} diff --git a/examples/src/v2/db.cpp b/examples/src/v2/db.cpp index 7ce47323..287faee9 100644 --- a/examples/src/v2/db.cpp +++ b/examples/src/v2/db.cpp @@ -54,6 +54,16 @@ main(int argc, char* argv[]) { std::cout << "database.replica.number = " << resp_desc_db.Desc().Properties().at("database.replica.number") << std::endl; + status = client->AlterDatabaseProperties(milvus::AlterDatabasePropertiesRequest() + .WithDatabaseName(my_db_name) + .AddProperty("database.max.collections", "20")); + util::CheckStatus("alter database properties: " + my_db_name, status); + + status = client->DropDatabaseProperties(milvus::DropDatabasePropertiesRequest() + .WithDatabaseName(my_db_name) + .AddPropertyKey("database.max.collections")); + util::CheckStatus("drop database properties: " + my_db_name, status); + status = client->UseDatabase(my_db_name); util::CheckStatus("switch database:" + my_db_name, status); std::string current_db_name; diff --git a/examples/src/v2/general.cpp b/examples/src/v2/general.cpp index 9d44a26c..0297828e 100644 --- a/examples/src/v2/general.cpp +++ b/examples/src/v2/general.cpp @@ -142,6 +142,9 @@ main(int argc, char* argv[]) { // for this collection and load the collection status = client->DropCollection( milvus::DropCollectionRequest().WithCollectionName(collection_name).WithDatabaseName(db_name)); + // a leftover from an interrupted rename demo must also be dropped or the rename fails on rerun + client->DropCollection( + milvus::DropCollectionRequest().WithCollectionName(collection_name + "_renamed").WithDatabaseName(db_name)); status = client->CreateCollection( milvus::CreateCollectionRequest() .WithDatabaseName(db_name) @@ -387,6 +390,47 @@ main(int argc, char* argv[]) { } } + { + // delete a batch of rows by their primary keys + std::vector ids_to_delete(insert_ids.begin(), insert_ids.begin() + 100); + milvus::DeleteResponse resp_delete; + status = client->Delete( + milvus::DeleteRequest().WithCollectionName(collection_name).WithIDs(std::move(ids_to_delete)), resp_delete); + util::CheckStatus("delete rows by primary keys", status); + } + + { + // delete rows by a filter expression + milvus::DeleteResponse resp_delete; + status = client->Delete( + milvus::DeleteRequest().WithCollectionName(collection_name).WithFilter(field_age + " > 90"), resp_delete); + util::CheckStatus("delete rows by filter: " + field_age + " > 90", status); + } + + { + // truncate the collection, removing all rows but keeping the schema and partitions + status = client->TruncateCollection(milvus::TruncateCollectionRequest().WithCollectionName(collection_name)); + util::CheckStatus("truncate collection: " + collection_name, status); + } + + { + // rename the collection, then rename it back so the rest of the example keeps using the original name + const std::string new_name = collection_name + "_renamed"; + status = client->RenameCollection( + milvus::RenameCollectionRequest().WithCollectionName(collection_name).WithNewCollectionName(new_name)); + util::CheckStatus("rename collection: " + collection_name + " -> " + new_name, status); + + milvus::DescribeCollectionResponse desc_response; + status = + client->DescribeCollection(milvus::DescribeCollectionRequest().WithCollectionName(new_name), desc_response); + util::CheckStatus("describe renamed collection: " + new_name, status); + std::cout << "Describe renamed collection: " << desc_response.Desc().CollectionName() << std::endl; + + status = client->RenameCollection( + milvus::RenameCollectionRequest().WithCollectionName(new_name).WithNewCollectionName(collection_name)); + util::CheckStatus("rename collection: " + new_name + " -> " + collection_name, status); + } + // describe the collection, load state is LOADED DescribeCollection(client); @@ -405,7 +449,7 @@ main(int argc, char* argv[]) { // create index again { milvus::IndexParam index_vector(field_face, "vector_index_name", milvus::IndexType::HNSW, - milvus::MetricType::L2); + milvus::MetricType::L2); index_vector.AddExtraParam("M", "32"); index_vector.AddExtraParam("efConstruction", "100"); diff --git a/examples/src/v2/index.cpp b/examples/src/v2/index.cpp new file mode 100644 index 00000000..cb9d3057 --- /dev/null +++ b/examples/src/v2/index.cpp @@ -0,0 +1,173 @@ +// Licensed to the LF AI & Data foundation under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "ExampleUtils.h" +#include "milvus/MilvusClientV2.h" + +namespace { +const std::string collection_name = "CPP_V2_INDEX"; +const std::string field_id = "id"; +const std::string field_vector = "vector"; +const std::string field_name = "name"; +const uint32_t dimension = 128; +const std::string vector_index_name = "idx_vector"; +const std::string scalar_index_name = "idx_scalar"; + +void +CreateCollection(milvus::MilvusClientV2Ptr& client) { + milvus::CollectionSchemaPtr collection_schema = std::make_shared(); + collection_schema->AddField({field_id, milvus::DataType::INT64, "", true, true}); + collection_schema->AddField( + milvus::FieldSchema(field_vector, milvus::DataType::FLOAT_VECTOR).WithDimension(dimension)); + milvus::FieldSchema name_schema{field_name, milvus::DataType::VARCHAR}; + name_schema.SetMaxLength(256); + collection_schema->AddField(name_schema); + + auto status = client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(collection_name)); + status = client->CreateCollection( + milvus::CreateCollectionRequest().WithCollectionName(collection_name).WithCollectionSchema(collection_schema)); + util::CheckStatus("create collection: " + collection_name, status); +} + +void +InsertRows(milvus::MilvusClientV2Ptr& client) { + milvus::EntityRows rows; + for (auto i = 0; i < 100; ++i) { + milvus::EntityRow row; + row[field_name] = "name_" + std::to_string(i); + row[field_vector] = util::GenerateFloatVector(dimension); + rows.emplace_back(std::move(row)); + } + + milvus::InsertResponse response; + auto status = client->Insert( + milvus::InsertRequest().WithCollectionName(collection_name).WithRowsData(std::move(rows)), response); + util::CheckStatus("insert rows", status); + + // persist the rows so the index build covers real data and describeIndex() reports + // non-zero indexed/total rows + status = client->Flush(milvus::FlushRequest().AddCollectionName(collection_name)); + util::CheckStatus("flush collection", status); +} + +void +CreateIndexes(milvus::MilvusClientV2Ptr& client) { + milvus::IndexParam index_vector(field_vector, vector_index_name, milvus::IndexType::IVF_FLAT, + milvus::MetricType::L2); + index_vector.AddExtraParam(milvus::NLIST, "128"); + + milvus::IndexParam index_scalar(field_name, scalar_index_name, milvus::IndexType::INVERTED); + + auto status = client->CreateIndex(milvus::CreateIndexRequest() + .WithCollectionName(collection_name) + .WithSync(true) + .AddIndexParam(std::move(index_vector)) + .AddIndexParam(std::move(index_scalar))); + util::CheckStatus("create indexes on collection: " + collection_name, status); +} + +void +ListIndexes(milvus::MilvusClientV2Ptr& client) { + milvus::ListIndexesResponse response; + auto status = client->ListIndexes(milvus::ListIndexesRequest().WithCollectionName(collection_name), response); + util::CheckStatus("list indexes of collection: " + collection_name, status); + std::cout << "Index names: "; + util::PrintList(response.IndexNames()); +} + +void +PrintIndexDesc(const milvus::DescribeIndexResponse& response) { + for (const auto& desc : response.Descs()) { + std::cout << " Index '" << desc.IndexName() << "' on field '" << desc.FieldName() + << "': type=" << std::to_string(desc.IndexType()) << ", metric=" << std::to_string(desc.MetricType()) + << ", state=" << std::to_string(desc.StateCode()) << ", rows=" << desc.IndexedRows() << "/" + << desc.TotalRows() << std::endl; + } +} + +void +DescribeIndexes(milvus::MilvusClientV2Ptr& client) { + milvus::DescribeIndexResponse response; + auto status = client->DescribeIndex( + milvus::DescribeIndexRequest().WithCollectionName(collection_name).WithIndexName(vector_index_name), response); + util::CheckStatus("describe index by index name: " + vector_index_name, status); + PrintIndexDesc(response); + + status = client->DescribeIndex( + milvus::DescribeIndexRequest().WithCollectionName(collection_name).WithFieldName(field_name), response); + util::CheckStatus("describe index by field name: " + field_name, status); + PrintIndexDesc(response); +} + +void +AlterIndexProperties(milvus::MilvusClientV2Ptr& client) { + auto status = client->AlterIndexProperties(milvus::AlterIndexPropertiesRequest() + .WithCollectionName(collection_name) + .WithIndexName(vector_index_name) + .AddProperty(milvus::MMAP_ENABLED, "true")); + util::CheckStatus("alter index properties of: " + vector_index_name, status); +} + +void +DropIndexProperties(milvus::MilvusClientV2Ptr& client) { + auto status = client->DropIndexProperties(milvus::DropIndexPropertiesRequest() + .WithCollectionName(collection_name) + .WithIndexName(vector_index_name) + .AddPropertyKey(milvus::MMAP_ENABLED)); + util::CheckStatus("drop index properties of: " + vector_index_name, status); +} + +void +DropIndexes(milvus::MilvusClientV2Ptr& client) { + auto status = client->DropIndex( + milvus::DropIndexRequest().WithCollectionName(collection_name).WithIndexName(vector_index_name)); + util::CheckStatus("drop index: " + vector_index_name, status); + + status = client->DropIndex( + milvus::DropIndexRequest().WithCollectionName(collection_name).WithIndexName(scalar_index_name)); + util::CheckStatus("drop index: " + scalar_index_name, status); +} + +} // namespace + +int +main(int argc, char* argv[]) { + printf("Example start...\n"); + + auto client = milvus::MilvusClientV2::Create(); + + milvus::ConnectParam connect_param{"http://localhost:19530", "root:Milvus"}; + auto status = client->Connect(connect_param); + util::CheckStatus("connect milvus server", status); + + CreateCollection(client); + InsertRows(client); + CreateIndexes(client); + ListIndexes(client); + DescribeIndexes(client); + AlterIndexProperties(client); + DropIndexProperties(client); + DropIndexes(client); + + client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(collection_name)); + client->Disconnect(); + return 0; +} diff --git a/examples/src/v2/partition.cpp b/examples/src/v2/partition.cpp new file mode 100644 index 00000000..754d65e2 --- /dev/null +++ b/examples/src/v2/partition.cpp @@ -0,0 +1,145 @@ +// Licensed to the LF AI & Data foundation under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include "ExampleUtils.h" +#include "milvus/MilvusClientV2.h" + +namespace { +const std::string collection_name = "CPP_V2_PARTITION"; +const std::string partition_name = "p1"; +const std::string field_id = "id"; +const std::string field_vector = "vector"; +const uint32_t dimension = 4; + +void +CreateCollection(milvus::MilvusClientV2Ptr& client) { + milvus::CollectionSchemaPtr collection_schema = std::make_shared(); + collection_schema->AddField({field_id, milvus::DataType::INT64, "", true, true}); + collection_schema->AddField( + milvus::FieldSchema(field_vector, milvus::DataType::FLOAT_VECTOR).WithDimension(dimension)); + + auto status = client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(collection_name)); + status = client->CreateCollection( + milvus::CreateCollectionRequest().WithCollectionName(collection_name).WithCollectionSchema(collection_schema)); + util::CheckStatus("create collection: " + collection_name, status); + + // a collection must have an index before its partitions can be loaded + milvus::IndexParam index_vector(field_vector, "", milvus::IndexType::FLAT, milvus::MetricType::L2); + status = client->CreateIndex( + milvus::CreateIndexRequest().WithCollectionName(collection_name).AddIndexParam(std::move(index_vector))); + util::CheckStatus("create index on vector field", status); +} + +void +CreatePartition(milvus::MilvusClientV2Ptr& client) { + auto status = client->CreatePartition( + milvus::CreatePartitionRequest().WithCollectionName(collection_name).WithPartitionName(partition_name)); + util::CheckStatus("create partition: " + partition_name, status); +} + +void +HasPartition(milvus::MilvusClientV2Ptr& client) { + milvus::HasPartitionResponse response; + auto status = client->HasPartition( + milvus::HasPartitionRequest().WithCollectionName(collection_name).WithPartitionName(partition_name), response); + util::CheckStatus("check partition existence", status); + std::cout << "Partition '" << partition_name << "' exists: " << (response.Has() ? "true" : "false") << std::endl; +} + +void +LoadPartitions(milvus::MilvusClientV2Ptr& client) { + auto status = client->LoadPartitions( + milvus::LoadPartitionsRequest().WithCollectionName(collection_name).AddPartitionName(partition_name)); + util::CheckStatus("load partition: " + partition_name, status); +} + +void +InsertIntoPartition(milvus::MilvusClientV2Ptr& client) { + milvus::EntityRows rows; + for (auto i = 0; i < 10; ++i) { + milvus::EntityRow row; + row[field_vector] = util::GenerateFloatVector(dimension); + rows.emplace_back(std::move(row)); + } + + milvus::InsertResponse response; + auto status = client->Insert(milvus::InsertRequest() + .WithCollectionName(collection_name) + .WithPartitionName(partition_name) + .WithRowsData(std::move(rows)), + response); + util::CheckStatus("insert rows into partition: " + partition_name, status); + + // flush to persist the data so the partition statistics are accurate + status = client->Flush(milvus::FlushRequest().AddCollectionName(collection_name)); + util::CheckStatus("flush collection", status); +} + +void +GetPartitionStats(milvus::MilvusClientV2Ptr& client) { + milvus::GetPartitionStatsResponse response; + auto status = client->GetPartitionStatistics( + milvus::GetPartitionStatsRequest().WithCollectionName(collection_name).WithPartitionName(partition_name), + response); + util::CheckStatus("get partition stats", status); + std::cout << "Partition '" << partition_name << "' stats: name=" << response.Stats().Name() + << ", row_count=" << response.Stats().RowCount() << std::endl; +} + +void +ReleasePartitions(milvus::MilvusClientV2Ptr& client) { + auto status = client->ReleasePartitions( + milvus::ReleasePartitionsRequest().WithCollectionName(collection_name).AddPartitionName(partition_name)); + util::CheckStatus("release partition: " + partition_name, status); +} + +void +DropPartition(milvus::MilvusClientV2Ptr& client) { + auto status = client->DropPartition( + milvus::DropPartitionRequest().WithCollectionName(collection_name).WithPartitionName(partition_name)); + util::CheckStatus("drop partition: " + partition_name, status); +} + +} // namespace + +int +main(int argc, char* argv[]) { + printf("Example start...\n"); + + auto client = milvus::MilvusClientV2::Create(); + + milvus::ConnectParam connect_param{"http://localhost:19530", "root:Milvus"}; + auto status = client->Connect(connect_param); + util::CheckStatus("connect milvus server", status); + + CreateCollection(client); + CreatePartition(client); + HasPartition(client); + LoadPartitions(client); + InsertIntoPartition(client); + GetPartitionStats(client); + ReleasePartitions(client); + DropPartition(client); + + client->DropCollection(milvus::DropCollectionRequest().WithCollectionName(collection_name)); + client->Disconnect(); + return 0; +} diff --git a/src/impl/MilvusClientV2Impl.cpp b/src/impl/MilvusClientV2Impl.cpp index b18b7f39..62b2a7a2 100644 --- a/src/impl/MilvusClientV2Impl.cpp +++ b/src/impl/MilvusClientV2Impl.cpp @@ -40,6 +40,29 @@ namespace milvus { +namespace { +// Shared conversion for GetCompactionPlans() and ListCompactionTasks(): map every +// CompactionMergeInfo field into a CompactionPlan. +void +ConvertCompactionPlans(const proto::milvus::GetCompactionPlansResponse& rpc_response, CompactionPlans& plans) { + plans.reserve(rpc_response.mergeinfos_size()); + for (int i = 0; i < rpc_response.mergeinfos_size(); ++i) { + const auto& info = rpc_response.mergeinfos(i); + CompactionPlan plan(std::vector(info.sources().begin(), info.sources().end()), info.target()); + plan.SetPlanId(info.plan_id()); + plan.SetTriggerId(info.trigger_id()); + plan.SetCollectionId(info.collection_id()); + plan.SetPartitionId(info.partition_id()); + plan.SetChannel(info.channel()); + plan.SetType(static_cast(info.type())); + plan.SetState(static_cast(info.state())); + plan.SetFailureReason(info.failure_reason()); + plan.SetTargets(std::vector(info.targets().begin(), info.targets().end())); + plans.emplace_back(std::move(plan)); + } +} +} // namespace + std::shared_ptr MilvusClientV2::Create() { return {new MilvusClientV2Impl(), [](MilvusClientV2Impl* client) noexcept { @@ -1582,9 +1605,8 @@ MilvusClientV2Impl::CreateIndex(const CreateIndexRequest& request) { return Status{StatusCode::INVALID_ARGUMENT, "IndexParams is empty, no index can be created"}; } for (const auto& index_param : index_params) { - auto status = - createIndex(request.DatabaseName(), request.CollectionName(), index_param, request.Sync(), - request.TimeoutMs()); + auto status = createIndex(request.DatabaseName(), request.CollectionName(), index_param, request.Sync(), + request.TimeoutMs()); if (!status.IsOk()) { return status; } @@ -2294,8 +2316,18 @@ MilvusClientV2Impl::hybridSearch(const HybridSearchRequest& request, HybridSearc auto validate = [&request]() { return request.Validate(); }; auto pre = [&endpoint, &database_name, &request, &cluster_id](proto::milvus::HybridSearchRequest& rpc_request) { - return ConvertHybridSearchRequest(request, database_name, rpc_request, cluster_id, - endpoint); + auto status = + ConvertHybridSearchRequest(request, database_name, rpc_request, cluster_id, endpoint); + if (!status.IsOk()) { + return status; + } + for (const auto& function_chain : request.FunctionChains()) { + status = ConvertFunctionChain(function_chain, *rpc_request.add_function_chains()); + if (!status.IsOk()) { + return status; + } + } + return Status::OK(); }; auto post = [this, &endpoint, &database_name, &request, @@ -3161,14 +3193,39 @@ MilvusClientV2Impl::GetCompactionPlans(const GetCompactionPlansRequest& request, break; } CompactionPlans plans; - plans.reserve(rpc_response.mergeinfos_size()); - for (int i = 0; i < rpc_response.mergeinfos_size(); ++i) { - auto& info = rpc_response.mergeinfos(i); - std::vector source_ids; - source_ids.reserve(info.sources_size()); - source_ids.insert(source_ids.end(), info.sources().begin(), info.sources().end()); - plans.emplace_back(source_ids, info.target()); + ConvertCompactionPlans(rpc_response, plans); + response.SetPlans(std::move(plans)); + return Status::OK(); + }; + + return connection_.Invoke( + pre, &MilvusConnection::GetCompactionPlans, post); +} + +Status +MilvusClientV2Impl::ListCompactionTasks(const ListCompactionTasksRequest& request, + GetCompactionPlansResponse& response) { + auto pre = [&request](proto::milvus::GetCompactionPlansRequest& rpc_request) { + rpc_request.set_db_name(request.DatabaseName()); + rpc_request.set_collection_name(request.CollectionName()); + return Status::OK(); + }; + + auto post = [&response, &request](const proto::milvus::GetCompactionPlansResponse& rpc_response) { + response.SetCollectionName(request.CollectionName()); + switch (rpc_response.state()) { + case proto::common::CompactionState::Completed: + response.SetState(CompactionStateCode::COMPLETED); + break; + case proto::common::CompactionState::Executing: + response.SetState(CompactionStateCode::EXECUTING); + break; + default: + response.SetState(CompactionStateCode::UNKNOWN); + break; } + CompactionPlans plans; + ConvertCompactionPlans(rpc_response, plans); response.SetPlans(std::move(plans)); return Status::OK(); }; @@ -4212,8 +4269,8 @@ MilvusClientV2Impl::createIndex(const std::string& db_name, const std::string& c if (timeout_ms > 0) { progress_monitor = ProgressMonitor{static_cast(timeout_ms + 999) / 1000}; } - auto wait_for_status = [&db_name, &collection_name, &field_name, &progress_monitor, this]( - const proto::common::Status&) { + auto wait_for_status = [&db_name, &collection_name, &field_name, &progress_monitor, + this](const proto::common::Status&) { return ConnectionHandler::WaitForStatus( [&db_name, &collection_name, &field_name, this](Progress& progress) -> Status { progress.total_ = 100; diff --git a/src/impl/MilvusClientV2Impl.h b/src/impl/MilvusClientV2Impl.h index 85661fad..fbc71881 100644 --- a/src/impl/MilvusClientV2Impl.h +++ b/src/impl/MilvusClientV2Impl.h @@ -297,6 +297,9 @@ class MilvusClientV2Impl : public MilvusClientV2, public std::enable_shared_from Status GetCompactionPlans(const GetCompactionPlansRequest& request, GetCompactionPlansResponse& response) final; + Status + ListCompactionTasks(const ListCompactionTasksRequest& request, GetCompactionPlansResponse& response) final; + Status CreateSnapshot(const CreateSnapshotRequest& request) final; diff --git a/src/impl/request/dql/HybridSearchRequest.cpp b/src/impl/request/dql/HybridSearchRequest.cpp index f2a41464..828bf537 100644 --- a/src/impl/request/dql/HybridSearchRequest.cpp +++ b/src/impl/request/dql/HybridSearchRequest.cpp @@ -18,6 +18,7 @@ #include +#include "../../utils/DqlUtils.h" #include "../../utils/ExtraParamUtils.h" namespace milvus { @@ -61,6 +62,28 @@ HybridSearchRequest::WithRerank(const FunctionPtr& rerank) { return *this; } +const std::vector& +HybridSearchRequest::FunctionChains() const { + return function_chains_; +} + +void +HybridSearchRequest::SetFunctionChains(std::vector&& function_chains) { + function_chains_ = std::move(function_chains); +} + +HybridSearchRequest& +HybridSearchRequest::WithFunctionChains(std::vector&& function_chains) { + SetFunctionChains(std::move(function_chains)); + return *this; +} + +HybridSearchRequest& +HybridSearchRequest::AddFunctionChain(const FunctionChain& function_chain) { + function_chains_.push_back(function_chain); + return *this; +} + int64_t HybridSearchRequest::Limit() const { return limit_; @@ -206,11 +229,20 @@ HybridSearchRequest::Validate() const { return status; } } - if (function_ == nullptr) { - return {StatusCode::INVALID_ARGUMENT, "Rerank function is undefined!"}; + if (function_ != nullptr && !function_chains_.empty()) { + return {StatusCode::INVALID_ARGUMENT, "Function chains and rerank cannot be used together"}; + } + if (function_ == nullptr && function_chains_.empty()) { + return {StatusCode::INVALID_ARGUMENT, "Rerank function or function chains is undefined!"}; } - if (function_->GetFunctionType() != FunctionType::RERANK) { - return {StatusCode::INVALID_ARGUMENT, "Hybrid search only accepts RERANK function!"}; + if (function_ != nullptr) { + if (function_->GetFunctionType() != FunctionType::RERANK) { + return {StatusCode::INVALID_ARGUMENT, "Hybrid search only accepts RERANK function!"}; + } + } + status = ValidateFunctionChains(function_chains_); + if (!status.IsOk()) { + return status; } return Status::OK(); diff --git a/src/impl/request/dql/SearchRequest.cpp b/src/impl/request/dql/SearchRequest.cpp index 79977d1c..0c56d306 100644 --- a/src/impl/request/dql/SearchRequest.cpp +++ b/src/impl/request/dql/SearchRequest.cpp @@ -19,6 +19,7 @@ #include #include "../../utils/Constants.h" +#include "../../utils/DqlUtils.h" #include "../../utils/ExtraParamUtils.h" namespace milvus { @@ -331,51 +332,9 @@ SearchRequest::Validate() const { if (ranker_ != nullptr && !function_chains_.empty()) { return {StatusCode::INVALID_ARGUMENT, "Function chains and rerank cannot be used together"}; } - for (const auto& chain : function_chains_) { - if (chain.Stage() == FunctionChainStage::UNSPECIFIED) { - return {StatusCode::INVALID_ARGUMENT, "UNSPECIFIED function chain stage is not supported for search"}; - } - for (const auto& op : chain.Ops()) { - if (op.Op().empty()) { - return {StatusCode::INVALID_ARGUMENT, "Function chain op name cannot be empty"}; - } - for (const auto& input : op.Inputs()) { - if (input.empty()) { - return {StatusCode::INVALID_ARGUMENT, "Function chain op input column name cannot be empty"}; - } - } - for (const auto& output : op.Outputs()) { - if (output.empty()) { - return {StatusCode::INVALID_ARGUMENT, "Function chain op output column name cannot be empty"}; - } - } - if (op.Op() == "limit") { - auto limit_param = op.Params().find("limit"); - if (limit_param != op.Params().end() && limit_param->second.is_number_integer()) { - if (limit_param->second.get() <= 0) { - return {StatusCode::INVALID_ARGUMENT, "Function chain limit must be greater than 0"}; - } - } - auto offset_param = op.Params().find("offset"); - if (offset_param != op.Params().end() && offset_param->second.is_number_integer()) { - if (offset_param->second.get() < 0) { - return {StatusCode::INVALID_ARGUMENT, - "Function chain offset must be greater than or equal to 0"}; - } - } - } - if (op.HasExpr()) { - const auto& expr = op.Expr(); - if (expr.Name().empty()) { - return {StatusCode::INVALID_ARGUMENT, "Function chain expression name cannot be empty"}; - } - for (const auto& arg : expr.Args()) { - if (arg.IsColumn() && arg.ColumnName().empty()) { - return {StatusCode::INVALID_ARGUMENT, "Function chain expression column name cannot be empty"}; - } - } - } - } + status = ValidateFunctionChains(function_chains_); + if (!status.IsOk()) { + return status; } if (search_aggregation_ == nullptr) { return Status::OK(); diff --git a/src/impl/request/utility/ListCompactionTasksRequest.cpp b/src/impl/request/utility/ListCompactionTasksRequest.cpp new file mode 100644 index 00000000..1acccbd1 --- /dev/null +++ b/src/impl/request/utility/ListCompactionTasksRequest.cpp @@ -0,0 +1,53 @@ +// Licensed to the LF AI & Data foundation under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "milvus/request/utility/ListCompactionTasksRequest.h" + +namespace milvus { + +const std::string& +ListCompactionTasksRequest::DatabaseName() const { + return db_name_; +} + +void +ListCompactionTasksRequest::SetDatabaseName(const std::string& db_name) { + db_name_ = db_name; +} + +ListCompactionTasksRequest& +ListCompactionTasksRequest::WithDatabaseName(const std::string& db_name) { + db_name_ = db_name; + return *this; +} + +const std::string& +ListCompactionTasksRequest::CollectionName() const { + return collection_name_; +} + +void +ListCompactionTasksRequest::SetCollectionName(const std::string& collection_name) { + collection_name_ = collection_name; +} + +ListCompactionTasksRequest& +ListCompactionTasksRequest::WithCollectionName(const std::string& collection_name) { + collection_name_ = collection_name; + return *this; +} + +} // namespace milvus diff --git a/src/impl/response/utility/GetCompactionPlansResponse.cpp b/src/impl/response/utility/GetCompactionPlansResponse.cpp index 441774f0..6f8f1c6d 100644 --- a/src/impl/response/utility/GetCompactionPlansResponse.cpp +++ b/src/impl/response/utility/GetCompactionPlansResponse.cpp @@ -48,4 +48,14 @@ GetCompactionPlansResponse::SetState(CompactionStateCode state) { state_ = state; } +const std::string& +GetCompactionPlansResponse::CollectionName() const { + return collection_name_; +} + +void +GetCompactionPlansResponse::SetCollectionName(const std::string& collection_name) { + collection_name_ = collection_name; +} + } // namespace milvus diff --git a/src/impl/types/CompactionPlan.cpp b/src/impl/types/CompactionPlan.cpp index 1cefe036..fbb27b36 100644 --- a/src/impl/types/CompactionPlan.cpp +++ b/src/impl/types/CompactionPlan.cpp @@ -53,4 +53,99 @@ CompactionPlan::SetDestinySegemnt(int64_t id) { dst_segment_ = id; } +int64_t +CompactionPlan::PlanId() const { + return plan_id_; +} + +void +CompactionPlan::SetPlanId(int64_t plan_id) { + plan_id_ = plan_id; +} + +int64_t +CompactionPlan::TriggerId() const { + return trigger_id_; +} + +void +CompactionPlan::SetTriggerId(int64_t trigger_id) { + trigger_id_ = trigger_id; +} + +int64_t +CompactionPlan::CollectionId() const { + return collection_id_; +} + +void +CompactionPlan::SetCollectionId(int64_t collection_id) { + collection_id_ = collection_id; +} + +int64_t +CompactionPlan::PartitionId() const { + return partition_id_; +} + +void +CompactionPlan::SetPartitionId(int64_t partition_id) { + partition_id_ = partition_id; +} + +const std::string& +CompactionPlan::Channel() const { + return channel_; +} + +void +CompactionPlan::SetChannel(const std::string& channel) { + channel_ = channel; +} + +CompactionType +CompactionPlan::Type() const { + return type_; +} + +void +CompactionPlan::SetType(CompactionType type) { + type_ = type; +} + +CompactionTaskState +CompactionPlan::State() const { + return state_; +} + +void +CompactionPlan::SetState(CompactionTaskState state) { + state_ = state; +} + +const std::string& +CompactionPlan::FailureReason() const { + return failure_reason_; +} + +void +CompactionPlan::SetFailureReason(const std::string& failure_reason) { + failure_reason_ = failure_reason; +} + +const std::vector& +CompactionPlan::Targets() const { + return targets_; +} + +void +CompactionPlan::SetTargets(const std::vector& targets) { + targets_ = targets; +} + +void +CompactionPlan::SetTargets(std::vector&& targets) { + targets_ = std::move(targets); +} + } // namespace milvus diff --git a/src/impl/utils/DqlUtils.cpp b/src/impl/utils/DqlUtils.cpp index 74c037dc..b5293272 100644 --- a/src/impl/utils/DqlUtils.cpp +++ b/src/impl/utils/DqlUtils.cpp @@ -32,6 +32,58 @@ #include "milvus/utils/FP16.h" namespace milvus { + +Status +ValidateFunctionChains(const std::vector& function_chains) { + for (const auto& chain : function_chains) { + if (chain.Stage() == FunctionChainStage::UNSPECIFIED) { + return {StatusCode::INVALID_ARGUMENT, "UNSPECIFIED function chain stage is not supported"}; + } + for (const auto& op : chain.Ops()) { + if (op.Op().empty()) { + return {StatusCode::INVALID_ARGUMENT, "Function chain op name cannot be empty"}; + } + for (const auto& input : op.Inputs()) { + if (input.empty()) { + return {StatusCode::INVALID_ARGUMENT, "Function chain op input column name cannot be empty"}; + } + } + for (const auto& output : op.Outputs()) { + if (output.empty()) { + return {StatusCode::INVALID_ARGUMENT, "Function chain op output column name cannot be empty"}; + } + } + if (op.Op() == "limit") { + auto limit_param = op.Params().find("limit"); + if (limit_param != op.Params().end() && limit_param->second.is_number_integer()) { + if (limit_param->second.get() <= 0) { + return {StatusCode::INVALID_ARGUMENT, "Function chain limit must be greater than 0"}; + } + } + auto offset_param = op.Params().find("offset"); + if (offset_param != op.Params().end() && offset_param->second.is_number_integer()) { + if (offset_param->second.get() < 0) { + return {StatusCode::INVALID_ARGUMENT, + "Function chain offset must be greater than or equal to 0"}; + } + } + } + if (op.HasExpr()) { + const auto& expr = op.Expr(); + if (expr.Name().empty()) { + return {StatusCode::INVALID_ARGUMENT, "Function chain expression name cannot be empty"}; + } + for (const auto& arg : expr.Args()) { + if (arg.IsColumn() && arg.ColumnName().empty()) { + return {StatusCode::INVALID_ARGUMENT, "Function chain expression column name cannot be empty"}; + } + } + } + } + } + return Status::OK(); +} + namespace { Status @@ -2129,11 +2181,13 @@ ConvertHybridSearchRequest(const T& request, const std::string& current_db, // set rerank auto reranker = request.Rerank(); - for (auto& pair : reranker->Params()) { - if (pair.first == CLUSTER_ID) { - continue; + if (reranker != nullptr) { + for (auto& pair : reranker->Params()) { + if (pair.first == CLUSTER_ID) { + continue; + } + setParamFunc(pair.first, pair.second); } - setParamFunc(pair.first, pair.second); } if (!cluster_id.empty()) { setParamFunc(CLUSTER_ID, cluster_id); diff --git a/src/impl/utils/DqlUtils.h b/src/impl/utils/DqlUtils.h index 721c36d9..785fc49d 100644 --- a/src/impl/utils/DqlUtils.h +++ b/src/impl/utils/DqlUtils.h @@ -133,6 +133,14 @@ AppendSearchResult(const SingleResult& from, SingleResult& to); Status IsAmbiguousParam(const std::string& key); +/** + * @brief Validate the stage, operations, and expressions of the given function chains. + * @param [in] function_chains the function chains. + * @return Status::OK when valid, otherwise an INVALID_ARGUMENT error. + */ +Status +ValidateFunctionChains(const std::vector& function_chains); + ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // explicitly instantiation of template methods to avoid link error // query diff --git a/src/include/milvus/MilvusClientV2.h b/src/include/milvus/MilvusClientV2.h index 9a6ff675..cd1a566d 100644 --- a/src/include/milvus/MilvusClientV2.h +++ b/src/include/milvus/MilvusClientV2.h @@ -127,6 +127,7 @@ #include "request/utility/GetFlushAllStateRequest.h" #include "request/utility/GetRefreshExternalCollectionProgressRequest.h" #include "request/utility/GetServerVersionRequest.h" +#include "request/utility/ListCompactionTasksRequest.h" #include "request/utility/ListRefreshExternalCollectionJobsRequest.h" #include "request/utility/ListSegmentsRequest.h" #include "request/utility/OptimizeRequest.h" @@ -1112,6 +1113,16 @@ class MILVUS_SDK_API MilvusClientV2 { virtual Status GetCompactionPlans(const GetCompactionPlansRequest& request, GetCompactionPlansResponse& response) = 0; + /** + * @brief List all compaction tasks still retained for a collection. + * + * @param [in] request input parameters + * @param [out] response output results + * @return Status operation successfully or not + */ + virtual Status + ListCompactionTasks(const ListCompactionTasksRequest& request, GetCompactionPlansResponse& response) = 0; + /** * @brief Create a snapshot for a collection. * diff --git a/src/include/milvus/request/collection/AddCollectionFunctionRequest.h b/src/include/milvus/request/collection/AddCollectionFunctionRequest.h index b079602c..200eff82 100644 --- a/src/include/milvus/request/collection/AddCollectionFunctionRequest.h +++ b/src/include/milvus/request/collection/AddCollectionFunctionRequest.h @@ -24,6 +24,9 @@ namespace milvus { /** * @brief Used by MilvusClientV2::AddCollectionFunction() + * @deprecated Milvus 3.0 and later do not support adding a function separately. Use + * AddFunctionField() instead, which adds the function together with its output + * field and bound index. */ class MILVUS_SDK_API AddCollectionFunctionRequest : public CollectionRequestBase { public: diff --git a/src/include/milvus/request/collection/DropCollectionFunctionRequest.h b/src/include/milvus/request/collection/DropCollectionFunctionRequest.h index 17e1729a..f176f96d 100644 --- a/src/include/milvus/request/collection/DropCollectionFunctionRequest.h +++ b/src/include/milvus/request/collection/DropCollectionFunctionRequest.h @@ -25,6 +25,9 @@ namespace milvus { /** * @brief Used by MilvusClientV2::DropCollectionFunction() + * @deprecated Milvus 3.0 and later do not support dropping a function separately. Use + * DropFunctionField() instead, which also removes the function's output field and + * its bound index. */ class MILVUS_SDK_API DropCollectionFunctionRequest : public CollectionRequestBase { public: diff --git a/src/include/milvus/request/dql/HybridSearchRequest.h b/src/include/milvus/request/dql/HybridSearchRequest.h index f79f3a57..d76da32c 100644 --- a/src/include/milvus/request/dql/HybridSearchRequest.h +++ b/src/include/milvus/request/dql/HybridSearchRequest.h @@ -17,6 +17,7 @@ #pragma once #include "../../types/Function.h" +#include "../../types/FunctionChain.h" #include "../../types/SubSearchRequest.h" #include "./DQLRequestBase.h" #include "milvus/Export.h" @@ -95,6 +96,34 @@ class MILVUS_SDK_API HybridSearchRequest : public DQLRequestBase& + FunctionChains() const; + + /** + * @brief Set function chains. Function chains and rerank cannot be used together. + * @param [in] function_chains the function chains. + */ + void + SetFunctionChains(std::vector&& function_chains); + + /** + * @brief Set function chains. Function chains and rerank cannot be used together. + * @param [in] function_chains the function chains. + */ + HybridSearchRequest& + WithFunctionChains(std::vector&& function_chains); + + /** + * @brief Add a function chain. Function chains and rerank cannot be used together. + * @param [in] function_chain the function chain. + */ + HybridSearchRequest& + AddFunctionChain(const FunctionChain& function_chain); + /** * @brief Get search limit(topk) * @return the limit. @@ -269,6 +298,7 @@ class MILVUS_SDK_API HybridSearchRequest : public DQLRequestBase sub_requests_; FunctionPtr function_; + std::vector function_chains_; int64_t limit_{10}; std::unordered_map extra_params_; diff --git a/src/include/milvus/request/utility/ListCompactionTasksRequest.h b/src/include/milvus/request/utility/ListCompactionTasksRequest.h new file mode 100644 index 00000000..822afcaf --- /dev/null +++ b/src/include/milvus/request/utility/ListCompactionTasksRequest.h @@ -0,0 +1,82 @@ +// Licensed to the LF AI & Data foundation under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include + +#include "milvus/Export.h" + +namespace milvus { + +/** + * @brief Used by MilvusClientV2::ListCompactionTasks() + */ +class MILVUS_SDK_API ListCompactionTasksRequest { + public: + /** + * @brief Constructor + */ + ListCompactionTasksRequest() = default; + + /** + * @brief Database name in which the collection is created. + * @return the database name. + */ + const std::string& + DatabaseName() const; + + /** + * @brief Set database name in which the collection is created. + * @param [in] db_name the DB name. + */ + void + SetDatabaseName(const std::string& db_name); + + /** + * @brief Set database name in which the collection is created. + * @param [in] db_name the DB name. + */ + ListCompactionTasksRequest& + WithDatabaseName(const std::string& db_name); + + /** + * @brief Get collection name. + * @return the collection name. + */ + const std::string& + CollectionName() const; + + /** + * @brief Set collection name. + * @param [in] collection_name the collection name. + */ + void + SetCollectionName(const std::string& collection_name); + + /** + * @brief Set collection name. + * @param [in] collection_name the collection name. + */ + ListCompactionTasksRequest& + WithCollectionName(const std::string& collection_name); + + private: + std::string db_name_; + std::string collection_name_; +}; + +} // namespace milvus diff --git a/src/include/milvus/response/utility/GetCompactionPlansResponse.h b/src/include/milvus/response/utility/GetCompactionPlansResponse.h index f5260cc6..694bf8d3 100644 --- a/src/include/milvus/response/utility/GetCompactionPlansResponse.h +++ b/src/include/milvus/response/utility/GetCompactionPlansResponse.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include "milvus/Export.h" #include "milvus/types/CompactionPlan.h" @@ -76,10 +77,25 @@ class MILVUS_SDK_API GetCompactionPlansResponse { void SetState(CompactionStateCode state); + /** + * @brief Get the collection name the compaction tasks belong to. + * @return the collection name. + */ + const std::string& + CollectionName() const; + + /** + * @brief Set the collection name the compaction tasks belong to. + * @param [in] collection_name the collection name. + */ + void + SetCollectionName(const std::string& collection_name); + private: CompactionPlans plans_; int64_t compaction_id_{0}; CompactionStateCode state_{CompactionStateCode::UNKNOWN}; + std::string collection_name_; }; } // namespace milvus diff --git a/src/include/milvus/types/CompactionPlan.h b/src/include/milvus/types/CompactionPlan.h index 0ab0aaf1..d7c07f9f 100644 --- a/src/include/milvus/types/CompactionPlan.h +++ b/src/include/milvus/types/CompactionPlan.h @@ -17,6 +17,7 @@ #pragma once #include +#include #include #include "milvus/Export.h" @@ -24,7 +25,43 @@ namespace milvus { /** - * @brief Compaction plan information. Used by MilvusClient::GetCompactionPlans(). + * @brief Type of one compaction task. Numeric values mirror common.proto CompactionType. + */ +enum class CompactionType { + UNDEFINED = 0, + MERGE = 2, + MIX = 3, + SINGLE = 4, + MINOR = 5, + MAJOR = 6, + LEVEL0_DELETE = 7, + CLUSTERING = 8, + SORT = 9, + PARTITION_KEY_SORT = 10, + CLUSTERING_PARTITION_KEY_SORT = 11, + BUMP_SCHEMA_VERSION = 12, +}; + +/** + * @brief State of one compaction task. Numeric values mirror common.proto CompactionTaskState. + */ +enum class CompactionTaskState { + UNKNOWN = 0, + EXECUTING = 1, + PIPELINING = 2, + COMPLETED = 3, + FAILED = 4, + TIMEOUT = 5, + ANALYZING = 6, + INDEXING = 7, + CLEANED = 8, + META_SAVED = 9, + STATISTIC = 10, +}; + +/** + * @brief Compaction plan information. Used by MilvusClient::GetCompactionPlans() and + * MilvusClientV2::ListCompactionTasks(). */ class MILVUS_SDK_API CompactionPlan { public: @@ -82,9 +119,151 @@ class MILVUS_SDK_API CompactionPlan { void SetDestinySegemnt(int64_t id); + /** + * @brief The server-side compaction task identifier. + * @return the plan id. + */ + int64_t + PlanId() const; + + /** + * @brief Set the server-side compaction task identifier. + * @param [in] plan_id the plan id. + */ + void + SetPlanId(int64_t plan_id); + + /** + * @brief The compaction trigger id. + * @return the trigger id. + */ + int64_t + TriggerId() const; + + /** + * @brief Set the compaction trigger id. + * @param [in] trigger_id the trigger id. + */ + void + SetTriggerId(int64_t trigger_id); + + /** + * @brief The collection id of this compaction task. + * @return the collection id. + */ + int64_t + CollectionId() const; + + /** + * @brief Set the collection id of this compaction task. + * @param [in] collection_id the collection id. + */ + void + SetCollectionId(int64_t collection_id); + + /** + * @brief The partition id of this compaction task. + * @return the partition id. + */ + int64_t + PartitionId() const; + + /** + * @brief Set the partition id of this compaction task. + * @param [in] partition_id the partition id. + */ + void + SetPartitionId(int64_t partition_id); + + /** + * @brief The channel of this compaction task. + * @return the channel. + */ + const std::string& + Channel() const; + + /** + * @brief Set the channel of this compaction task. + * @param [in] channel the channel. + */ + void + SetChannel(const std::string& channel); + + /** + * @brief The type of this compaction task. + * @return the compaction type. + */ + CompactionType + Type() const; + + /** + * @brief Set the type of this compaction task. + * @param [in] type the compaction type. + */ + void + SetType(CompactionType type); + + /** + * @brief The state of this compaction task. + * @return the compaction task state. + */ + CompactionTaskState + State() const; + + /** + * @brief Set the state of this compaction task. + * @param [in] state the compaction task state. + */ + void + SetState(CompactionTaskState state); + + /** + * @brief The failure reason of this compaction task, empty when it succeeded. + * @return the failure reason. + */ + const std::string& + FailureReason() const; + + /** + * @brief Set the failure reason of this compaction task. + * @param [in] failure_reason the failure reason. + */ + void + SetFailureReason(const std::string& failure_reason); + + /** + * @brief The complete output segment set. Prefer over DestinySegemnt(). + * @return the targets. + */ + const std::vector& + Targets() const; + + /** + * @brief Set the complete output segment set. + * @param [in] targets the targets. + */ + void + SetTargets(const std::vector& targets); + + /** + * @brief Set the complete output segment set. + * @param [in] targets the targets. + */ + void + SetTargets(std::vector&& targets); + private: std::vector src_segments_; int64_t dst_segment_ = 0; + int64_t plan_id_ = 0; + int64_t trigger_id_ = 0; + int64_t collection_id_ = 0; + int64_t partition_id_ = 0; + std::string channel_; + CompactionType type_{CompactionType::UNDEFINED}; + CompactionTaskState state_{CompactionTaskState::UNKNOWN}; + std::string failure_reason_; + std::vector targets_; }; /** diff --git a/test/it/v2/TestDQL.cpp b/test/it/v2/TestDQL.cpp index 8f684d98..5c140eaf 100644 --- a/test/it/v2/TestDQL.cpp +++ b/test/it/v2/TestDQL.cpp @@ -24,6 +24,7 @@ using ::milvus::StatusCode; using ::testing::_; +using ::testing::Property; namespace { @@ -283,3 +284,38 @@ TEST_F(UnconnectMilvusMockedTest, HybridSearchResponseExtraInfoMetadata) { EXPECT_FLOAT_EQ(response.CacheHitRatio(), 0.25f); EXPECT_EQ(response.Results().Results().size(), 1); } + +TEST_F(UnconnectMilvusMockedTest, HybridSearchFunctionChainsOnWire) { + auto client = CreateConnectedV2Client(service_, server_.ListenPort()); + + const ::milvus::proto::schema::FunctionChainStage stage = + ::milvus::proto::schema::FunctionChainStage::FunctionChainStageL2Rerank; + EXPECT_CALL(service_, + HybridSearch(_, Property(&::milvus::proto::milvus::HybridSearchRequest::function_chains_size, 1), _)) + .WillOnce([stage](::grpc::ServerContext*, const ::milvus::proto::milvus::HybridSearchRequest* request, + ::milvus::proto::milvus::SearchResults* response) { + EXPECT_EQ(request->function_chains(0).stage(), stage); + EXPECT_EQ(request->function_chains(0).ops_size(), 1); + EXPECT_EQ(request->function_chains(0).ops(0).op(), "limit"); + FillMinimalV2SearchResults(response); + return ::grpc::Status{}; + }); + + auto sub_request = std::make_shared(); + sub_request->WithAnnsField("anns_dummy").WithLimit(1); + sub_request->AddFloatVector(std::vector{0.1f, 0.2f, 0.3f, 0.4f}); + + milvus::FunctionChain chain(milvus::FunctionChainStage::L2_RERANK, "chain"); + chain.Limit(5); + + milvus::HybridSearchRequest request; + request.WithCollectionName("foo"); + request.AddSubRequest(sub_request); + request.WithLimit(1); + request.AddFunctionChain(chain); + + milvus::HybridSearchResponse response; + auto status = client->HybridSearch(request, response); + EXPECT_TRUE(status.IsOk()); + EXPECT_EQ(response.Results().Results().size(), 1); +} diff --git a/test/it/v2/TestUtility.cpp b/test/it/v2/TestUtility.cpp index d9c7fb75..14445e83 100644 --- a/test/it/v2/TestUtility.cpp +++ b/test/it/v2/TestUtility.cpp @@ -194,6 +194,15 @@ TEST_F(UnconnectMilvusMockedTest, GetCompactionPlansV2) { info->add_sources(i); } info->set_target(target); + info->set_plan_id(11); + info->set_trigger_id(12); + info->set_collection_id(13); + info->set_partition_id(14); + info->set_channel("ch-0"); + info->set_type(milvus::proto::common::CompactionType::CompactionTypeMajor); + info->set_state(milvus::proto::common::CompactionTaskState::CompactionTaskStateCompleted); + info->set_failure_reason("boom"); + info->add_targets(target + 1); return ::grpc::Status{}; }); @@ -206,6 +215,15 @@ TEST_F(UnconnectMilvusMockedTest, GetCompactionPlansV2) { ASSERT_EQ(response.Plans().size(), 1); EXPECT_THAT(response.Plans()[0].SourceSegments(), ElementsAreArray(sources)); EXPECT_EQ(response.Plans()[0].DestinySegemnt(), target); + EXPECT_EQ(response.Plans()[0].PlanId(), 11); + EXPECT_EQ(response.Plans()[0].TriggerId(), 12); + EXPECT_EQ(response.Plans()[0].CollectionId(), 13); + EXPECT_EQ(response.Plans()[0].PartitionId(), 14); + EXPECT_EQ(response.Plans()[0].Channel(), "ch-0"); + EXPECT_EQ(response.Plans()[0].Type(), milvus::CompactionType::MAJOR); + EXPECT_EQ(response.Plans()[0].State(), milvus::CompactionTaskState::COMPLETED); + EXPECT_EQ(response.Plans()[0].FailureReason(), "boom"); + EXPECT_THAT(response.Plans()[0].Targets(), ElementsAreArray(std::vector{target + 1})); } TEST_F(UnconnectMilvusMockedTest, GetCompactionPlansV2ExecutingState) { @@ -246,6 +264,55 @@ TEST_F(UnconnectMilvusMockedTest, GetCompactionPlansV2UnsetState) { EXPECT_EQ(response.CompactionID(), compaction_id); EXPECT_EQ(response.State(), milvus::CompactionStateCode::UNKNOWN); } + +TEST_F(UnconnectMilvusMockedTest, ListCompactionTasksV2) { + auto client = CreateConnectedV2Client(service_, server_.ListenPort()); + + const std::string db_name = "test_db"; + const std::string collection_name = "test_collection"; + const std::vector sources = {1, 2, 3}; + const int64_t target = 100; + + EXPECT_CALL(service_, GetCompactionStateWithPlans( + _, Property(&GetCompactionPlansRequest::collection_name, collection_name), _)) + .WillOnce([&](::grpc::ServerContext*, const GetCompactionPlansRequest*, GetCompactionPlansResponse* response) { + response->set_state(milvus::proto::common::CompactionState::Completed); + auto info = response->add_mergeinfos(); + for (auto i : sources) { + info->add_sources(i); + } + info->set_target(target); + info->set_plan_id(11); + info->set_trigger_id(12); + info->set_collection_id(13); + info->set_partition_id(14); + info->set_channel("ch-0"); + info->set_type(milvus::proto::common::CompactionType::CompactionTypeLevel0Delete); + info->set_state(milvus::proto::common::CompactionTaskState::CompactionTaskStateFailed); + info->set_failure_reason("boom"); + info->add_targets(target + 1); + return ::grpc::Status{}; + }); + + milvus::GetCompactionPlansResponse response; + auto status = client->ListCompactionTasks( + milvus::ListCompactionTasksRequest().WithDatabaseName(db_name).WithCollectionName(collection_name), response); + EXPECT_TRUE(status.IsOk()); + EXPECT_EQ(response.CollectionName(), collection_name); + EXPECT_EQ(response.State(), milvus::CompactionStateCode::COMPLETED); + ASSERT_EQ(response.Plans().size(), 1); + EXPECT_THAT(response.Plans()[0].SourceSegments(), ElementsAreArray(sources)); + EXPECT_EQ(response.Plans()[0].DestinySegemnt(), target); + EXPECT_EQ(response.Plans()[0].PlanId(), 11); + EXPECT_EQ(response.Plans()[0].TriggerId(), 12); + EXPECT_EQ(response.Plans()[0].CollectionId(), 13); + EXPECT_EQ(response.Plans()[0].PartitionId(), 14); + EXPECT_EQ(response.Plans()[0].Channel(), "ch-0"); + EXPECT_EQ(response.Plans()[0].Type(), milvus::CompactionType::LEVEL0_DELETE); + EXPECT_EQ(response.Plans()[0].State(), milvus::CompactionTaskState::FAILED); + EXPECT_EQ(response.Plans()[0].FailureReason(), "boom"); + EXPECT_THAT(response.Plans()[0].Targets(), ElementsAreArray(std::vector{target + 1})); +} TEST_F(UnconnectMilvusMockedTest, FlushAll) { auto client = CreateConnectedV2Client(service_, server_.ListenPort()); diff --git a/test/st/milvus_container.py b/test/st/milvus_container.py index 4c9be1b1..ce3820c0 100644 --- a/test/st/milvus_container.py +++ b/test/st/milvus_container.py @@ -45,7 +45,7 @@ DEFAULT_HEALTH_PORT = 19291 DEFAULT_TIMEOUT = 300 REAP_LABEL = "milvus-sdk-cpp-test" -MINIO_IMAGE = "quay.io/minio/minio:RELEASE.2025-09-07T16-13-09Z" +MINIO_IMAGE = "milvusdb/minio:RELEASE.2024-05-28T17-19-04Z" MINIO_PORT = 19200 MINIO_ACCESS_KEY = "minioadmin" MINIO_SECRET_KEY = "minioadmin" diff --git a/test/ut/request/TestDqlRequests.cpp b/test/ut/request/TestDqlRequests.cpp index b025c30e..8860bc75 100644 --- a/test/ut/request/TestDqlRequests.cpp +++ b/test/ut/request/TestDqlRequests.cpp @@ -780,6 +780,30 @@ TEST_F(HybridSearchRequestTest, GettersAndSetters) { EXPECT_FALSE(req.StrictGroupSize()); } +TEST_F(HybridSearchRequestTest, FunctionChainsGettersAndSetters) { + milvus::HybridSearchRequest req; + + milvus::FunctionChain chain(milvus::FunctionChainStage::L2_RERANK, "chain"); + chain.Limit(5); + req.AddFunctionChain(chain); + ASSERT_EQ(req.FunctionChains().size(), 1); + EXPECT_EQ(req.FunctionChains().at(0).Name(), "chain"); + EXPECT_EQ(req.FunctionChains().at(0).Stage(), milvus::FunctionChainStage::L2_RERANK); + + milvus::FunctionChain chain2(milvus::FunctionChainStage::L2_RERANK, "chain2"); + std::vector chains{chain2}; + auto& ref = req.WithFunctionChains(std::move(chains)); + EXPECT_EQ(&ref, &req); + ASSERT_EQ(req.FunctionChains().size(), 1); + EXPECT_EQ(req.FunctionChains().at(0).Name(), "chain2"); + + milvus::FunctionChain chain3(milvus::FunctionChainStage::L2_RERANK, "chain3"); + std::vector chains2{chain3}; + req.SetFunctionChains(std::move(chains2)); + ASSERT_EQ(req.FunctionChains().size(), 1); + EXPECT_EQ(req.FunctionChains().at(0).Name(), "chain3"); +} + TEST_F(HybridSearchRequestTest, WithRoundDecimal) { milvus::HybridSearchRequest req; auto& ref = req.WithRoundDecimal(4); @@ -869,6 +893,84 @@ TEST_F(HybridSearchRequestTest, ValidateAcceptsFullyValidRequest) { EXPECT_TRUE(status.IsOk()); } +TEST_F(HybridSearchRequestTest, ValidateAcceptsFunctionChains) { + milvus::HybridSearchRequest req; + auto sub = std::make_shared(); + sub->WithAnnsField("vec").WithLimit(10); + sub->AddFloatVector(std::vector{0.1f, 0.2f, 0.3f, 0.4f}); + req.WithLimit(10).AddSubRequest(sub); + + milvus::FunctionChain chain(milvus::FunctionChainStage::L2_RERANK, "chain"); + chain.Limit(5); + req.AddFunctionChain(chain); + + auto status = req.Validate(); + EXPECT_TRUE(status.IsOk()); +} + +TEST_F(HybridSearchRequestTest, ValidateRejectsFunctionChainsAndRerankTogether) { + milvus::HybridSearchRequest req; + auto sub = std::make_shared(); + sub->WithAnnsField("vec").WithLimit(10); + sub->AddFloatVector(std::vector{0.1f, 0.2f, 0.3f, 0.4f}); + req.WithLimit(10).AddSubRequest(sub); + + milvus::FunctionChain chain(milvus::FunctionChainStage::L2_RERANK, "chain"); + chain.Limit(5); + req.AddFunctionChain(chain); + req.WithRerank(std::make_shared(60)); + + auto status = req.Validate(); + EXPECT_FALSE(status.IsOk()); + EXPECT_EQ(status.Code(), milvus::StatusCode::INVALID_ARGUMENT); +} + +TEST_F(HybridSearchRequestTest, ValidateRejectsUndefinedRerankAndChains) { + milvus::HybridSearchRequest req; + auto sub = std::make_shared(); + sub->WithAnnsField("vec").WithLimit(10); + sub->AddFloatVector(std::vector{0.1f, 0.2f, 0.3f, 0.4f}); + req.WithLimit(10).AddSubRequest(sub); + + auto status = req.Validate(); + EXPECT_FALSE(status.IsOk()); + EXPECT_EQ(status.Code(), milvus::StatusCode::INVALID_ARGUMENT); +} + +TEST_F(HybridSearchRequestTest, ValidateRejectsFunctionChainNonPositiveLimit) { + milvus::FunctionChain chain(milvus::FunctionChainStage::L2_RERANK, "chain"); + chain.Limit(0, 0); + + milvus::HybridSearchRequest req; + auto sub = std::make_shared(); + sub->WithAnnsField("vec").WithLimit(10); + sub->AddFloatVector(std::vector{0.1f, 0.2f, 0.3f, 0.4f}); + req.WithLimit(10).AddSubRequest(sub); + req.AddFunctionChain(chain); + + auto status = req.Validate(); + EXPECT_FALSE(status.IsOk()); + EXPECT_EQ(status.Code(), milvus::StatusCode::INVALID_ARGUMENT); + EXPECT_EQ(status.Message(), "Function chain limit must be greater than 0"); +} + +TEST_F(HybridSearchRequestTest, ValidateRejectsFunctionChainNegativeOffset) { + milvus::FunctionChain chain(milvus::FunctionChainStage::L2_RERANK, "chain"); + chain.Limit(10, -1); + + milvus::HybridSearchRequest req; + auto sub = std::make_shared(); + sub->WithAnnsField("vec").WithLimit(10); + sub->AddFloatVector(std::vector{0.1f, 0.2f, 0.3f, 0.4f}); + req.WithLimit(10).AddSubRequest(sub); + req.AddFunctionChain(chain); + + auto status = req.Validate(); + EXPECT_FALSE(status.IsOk()); + EXPECT_EQ(status.Code(), milvus::StatusCode::INVALID_ARGUMENT); + EXPECT_EQ(status.Message(), "Function chain offset must be greater than or equal to 0"); +} + TEST_F(HybridSearchRequestTest, ValidateRejectsNullSubRequest) { milvus::HybridSearchRequest req; req.WithLimit(10); diff --git a/test/ut/request/TestUtilityRequests.cpp b/test/ut/request/TestUtilityRequests.cpp index ca474411..b23b665d 100644 --- a/test/ut/request/TestUtilityRequests.cpp +++ b/test/ut/request/TestUtilityRequests.cpp @@ -238,6 +238,24 @@ TEST_F(GetCompactionPlansRequestTest, SetMethod) { EXPECT_EQ(req.CompactionID(), 88888); } +class ListCompactionTasksRequestTest : public ::testing::Test {}; + +TEST_F(ListCompactionTasksRequestTest, GettersAndSetters) { + milvus::ListCompactionTasksRequest req; + + req.WithCollectionName("compact_coll"); + EXPECT_EQ(req.CollectionName(), "compact_coll"); + + req.WithDatabaseName("compact_db"); + EXPECT_EQ(req.DatabaseName(), "compact_db"); + + req.SetCollectionName("compact_coll_2"); + EXPECT_EQ(req.CollectionName(), "compact_coll_2"); + + req.SetDatabaseName("compact_db_2"); + EXPECT_EQ(req.DatabaseName(), "compact_db_2"); +} + class ListPersistentSegmentsRequestTest : public ::testing::Test {}; TEST_F(ListPersistentSegmentsRequestTest, GettersAndSetters) { diff --git a/test/ut/response/TestUtilityResponses.cpp b/test/ut/response/TestUtilityResponses.cpp index 5cba9230..bccdd302 100644 --- a/test/ut/response/TestUtilityResponses.cpp +++ b/test/ut/response/TestUtilityResponses.cpp @@ -118,6 +118,10 @@ TEST_F(GetCompactionPlansResponseTest, SetterAndGetter) { EXPECT_EQ(resp.State(), milvus::CompactionStateCode::UNKNOWN); resp.SetState(milvus::CompactionStateCode::COMPLETED); EXPECT_EQ(resp.State(), milvus::CompactionStateCode::COMPLETED); + + EXPECT_TRUE(resp.CollectionName().empty()); + resp.SetCollectionName("coll"); + EXPECT_EQ(resp.CollectionName(), "coll"); } class GetServerVersionResponseTest : public ::testing::Test {}; diff --git a/test/ut/types/TestCompactionPlan.cpp b/test/ut/types/TestCompactionPlan.cpp index af11ab3e..12c5c813 100644 --- a/test/ut/types/TestCompactionPlan.cpp +++ b/test/ut/types/TestCompactionPlan.cpp @@ -40,3 +40,32 @@ TEST_F(CompactionPlanTest, DefaultConstructor) { EXPECT_TRUE(plan.SourceSegments().empty()); EXPECT_EQ(0, plan.DestinySegemnt()); } + +TEST_F(CompactionPlanTest, FullFields) { + milvus::CompactionPlan plan; + plan.SetSourceSegments({1, 2, 3}); + plan.SetDestinySegemnt(100); + plan.SetPlanId(10); + plan.SetTriggerId(11); + plan.SetCollectionId(12); + plan.SetPartitionId(13); + plan.SetChannel("ch-0"); + plan.SetType(milvus::CompactionType::MAJOR); + plan.SetState(milvus::CompactionTaskState::COMPLETED); + plan.SetFailureReason("boom"); + std::vector targets{200, 201}; + plan.SetTargets(targets); + + EXPECT_EQ(plan.PlanId(), 10); + EXPECT_EQ(plan.TriggerId(), 11); + EXPECT_EQ(plan.CollectionId(), 12); + EXPECT_EQ(plan.PartitionId(), 13); + EXPECT_EQ(plan.Channel(), "ch-0"); + EXPECT_EQ(plan.Type(), milvus::CompactionType::MAJOR); + EXPECT_EQ(plan.State(), milvus::CompactionTaskState::COMPLETED); + EXPECT_EQ(plan.FailureReason(), "boom"); + EXPECT_EQ(plan.Targets(), targets); + + plan.SetTargets({300}); + EXPECT_EQ(plan.Targets(), (std::vector{300})); +}