feat(c_api): add DiskANN index type support to C API (#495)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
lichen2015 2026-07-10 16:11:09 +08:00 committed by GitHub
parent a90ec5b1d6
commit 78ef197aaa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 1054 additions and 0 deletions

View File

@ -93,6 +93,12 @@ target_link_libraries(c_api_optimized_example PRIVATE
zvec-c-api
)
# DiskANN example
add_executable(c_api_diskann_example diskann_example.c)
target_link_libraries(c_api_diskann_example PRIVATE
zvec-c-api
)
# Strip symbols to reduce executable size
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND (ANDROID OR (CMAKE_SYSTEM_NAME STREQUAL "Linux")))
add_custom_command(TARGET c_api_basic_example POST_BUILD
@ -113,14 +119,19 @@ if(CMAKE_BUILD_TYPE STREQUAL "Release" AND (ANDROID OR (CMAKE_SYSTEM_NAME STREQU
add_custom_command(TARGET c_api_optimized_example POST_BUILD
COMMAND ${CMAKE_STRIP} "$<TARGET_FILE:c_api_optimized_example>"
COMMENT "Stripping symbols from c_api_optimized_example")
add_custom_command(TARGET c_api_diskann_example POST_BUILD
COMMAND ${CMAKE_STRIP} "$<TARGET_FILE:c_api_diskann_example>"
COMMENT "Stripping symbols from c_api_diskann_example")
endif()
# Optimize for size
if(CMAKE_BUILD_TYPE STREQUAL "Release" AND ANDROID)
set_property(TARGET c_api_basic_example c_api_collection_schema_example c_api_doc_example
c_api_index_example c_api_field_schema_example c_api_optimized_example
c_api_diskann_example
PROPERTY COMPILE_FLAGS "-Os")
set_property(TARGET c_api_basic_example c_api_collection_schema_example c_api_doc_example
c_api_index_example c_api_field_schema_example c_api_optimized_example
c_api_diskann_example
PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()

View File

@ -0,0 +1,346 @@
// Copyright 2025-present the zvec project
//
// Licensed 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.
/**
* @file diskann_example.c
* @brief End-to-end example demonstrating DiskANN index usage via the C API.
*
* DiskANN is a disk-based approximate nearest neighbor search algorithm
* optimized for large-scale datasets that exceed available memory. It uses
* a Vamana graph structure combined with product quantization (PQ) to
* achieve high recall with efficient disk I/O.
*
* NOTE: DiskANN requires Linux x86_64 with libaio. On other platforms the
* example will compile but the runtime plugin will fail to load.
*
* Workflow demonstrated:
* 1. Create collection schema with DiskANN-indexed vector field
* 2. Insert documents with high-dimensional vectors
* 3. Flush collection (triggers PQ training + graph build)
* 4. Search using DiskANN query parameters (list_size controls recall)
* 5. Clean up all resources
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zvec/c_api.h"
/* --------------------------------------------------------------------------
* Helpers
* -------------------------------------------------------------------------- */
static zvec_error_code_t handle_error(zvec_error_code_t error,
const char *context) {
if (error != ZVEC_OK) {
char *error_msg = NULL;
zvec_get_last_error(&error_msg);
fprintf(stderr, "Error in %s: %d - %s\n", context, error,
error_msg ? error_msg : "Unknown error");
zvec_free(error_msg);
}
return error;
}
#define VECTOR_DIM 64
#define NUM_DOCS 100
#define COLLECTION_DIR "./diskann_example_collection"
/* --------------------------------------------------------------------------
* Main
* -------------------------------------------------------------------------- */
int main(void) {
printf("=== ZVec DiskANN Index Example ===\n\n");
zvec_error_code_t error;
int i;
/* ------------------------------------------------------------------
* Step 1: Create collection schema
* ------------------------------------------------------------------ */
printf("[Step 1] Creating collection schema...\n");
zvec_collection_schema_t *schema =
zvec_collection_schema_create("diskann_example");
if (!schema) {
fprintf(stderr, "Failed to create schema\n");
return 1;
}
/* Index params — declared up-front and NULL-initialized so the
* cleanup_schema path never touches an uninitialized pointer even if an
* early field addition fails. */
zvec_index_params_t *invert_params = NULL;
zvec_index_params_t *diskann_params = NULL;
/* Scalar field with inverted index (for primary key / filtering) */
invert_params = zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT);
zvec_index_params_set_invert_params(invert_params, true, false);
zvec_field_schema_t *id_field =
zvec_field_schema_create("id", ZVEC_DATA_TYPE_STRING, false, 0);
zvec_field_schema_set_index_params(id_field, invert_params);
error = zvec_collection_schema_add_field(schema, id_field);
if (handle_error(error, "adding id field") != ZVEC_OK) {
goto cleanup_schema;
}
printf(" + id field (STRING, inverted index)\n");
/* Vector field with DiskANN index */
diskann_params = zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN);
if (!diskann_params) {
fprintf(stderr, "Failed to create DiskANN index parameters\n");
goto cleanup_schema;
}
zvec_index_params_set_metric_type(diskann_params, ZVEC_METRIC_TYPE_L2);
zvec_index_params_set_diskann_params(
diskann_params, 64, /* max_degree: graph connectivity */
100, /* list_size: build-time candidates */
8); /* pq_chunk_num: PQ chunks (0=auto) */
printf(
" DiskANN index params: max_degree=%d, list_size=%d, pq_chunk_num=%d\n",
zvec_index_params_get_diskann_max_degree(diskann_params),
zvec_index_params_get_diskann_list_size(diskann_params),
zvec_index_params_get_diskann_pq_chunk_num(diskann_params));
zvec_field_schema_t *embedding_field = zvec_field_schema_create(
"embedding", ZVEC_DATA_TYPE_VECTOR_FP32, false, VECTOR_DIM);
zvec_field_schema_set_index_params(embedding_field, diskann_params);
error = zvec_collection_schema_add_field(schema, embedding_field);
if (handle_error(error, "adding embedding field") != ZVEC_OK) {
goto cleanup_schema;
}
printf(" + embedding field (VECTOR_FP32, %dD, DiskANN index)\n", VECTOR_DIM);
/* Index params are copied into field schemas; safe to destroy now */
zvec_index_params_destroy(invert_params);
zvec_index_params_destroy(diskann_params);
invert_params = NULL;
diskann_params = NULL;
/* ------------------------------------------------------------------
* Step 2: Create and open collection
* ------------------------------------------------------------------ */
printf("\n[Step 2] Creating collection...\n");
zvec_collection_options_t *options = zvec_collection_options_create();
zvec_collection_t *collection = NULL;
error = zvec_collection_create_and_open(COLLECTION_DIR, schema, options,
&collection);
zvec_collection_options_destroy(options);
if (handle_error(error, "creating collection") != ZVEC_OK) {
goto cleanup_schema;
}
printf(" Collection created at %s\n", COLLECTION_DIR);
/* ------------------------------------------------------------------
* Step 3: Generate and insert documents
* ------------------------------------------------------------------ */
printf("\n[Step 3] Inserting %d documents with %dD vectors...\n", NUM_DOCS,
VECTOR_DIM);
/* Allocate vector storage */
float(*vectors)[VECTOR_DIM] =
(float(*)[VECTOR_DIM])malloc(NUM_DOCS * VECTOR_DIM * sizeof(float));
if (!vectors) {
fprintf(stderr, "Failed to allocate vector storage\n");
goto cleanup_collection;
}
/* Generate deterministic vector data */
for (i = 0; i < NUM_DOCS; i++) {
for (int d = 0; d < VECTOR_DIM; d++) {
vectors[i][d] = (float)((i * VECTOR_DIM + d) % 1000) / 1000.0f;
}
}
/* Insert in batches */
int batch_size = 20;
size_t total_success = 0, total_error = 0;
for (int batch_start = 0; batch_start < NUM_DOCS; batch_start += batch_size) {
int count = batch_start + batch_size > NUM_DOCS ? NUM_DOCS - batch_start
: batch_size;
zvec_doc_t **docs =
(zvec_doc_t **)malloc((size_t)count * sizeof(zvec_doc_t *));
for (i = 0; i < count; i++) {
int idx = batch_start + i;
docs[i] = zvec_doc_create();
char pk[32];
snprintf(pk, sizeof(pk), "doc_%04d", idx);
zvec_doc_set_pk(docs[i], pk);
zvec_doc_add_field_by_value(docs[i], "id", ZVEC_DATA_TYPE_STRING, pk,
strlen(pk));
zvec_doc_add_field_by_value(docs[i], "embedding",
ZVEC_DATA_TYPE_VECTOR_FP32, vectors[idx],
VECTOR_DIM * sizeof(float));
}
size_t success_count = 0, error_count = 0;
error = zvec_collection_insert(collection, (const zvec_doc_t **)docs,
(size_t)count, &success_count, &error_count);
if (error != ZVEC_OK) {
handle_error(error, "inserting batch");
}
total_success += success_count;
total_error += error_count;
for (i = 0; i < count; i++) {
zvec_doc_destroy(docs[i]);
}
free(docs);
}
printf(" Inserted: %zu succeeded, %zu failed\n", total_success, total_error);
/* ------------------------------------------------------------------
* Step 4: Flush to trigger index build (PQ training + graph construction)
* ------------------------------------------------------------------ */
printf("\n[Step 4] Flushing collection (triggers DiskANN index build)...\n");
error = zvec_collection_flush(collection);
if (handle_error(error, "flushing collection") != ZVEC_OK) {
goto cleanup_vectors;
}
zvec_collection_stats_t *stats = NULL;
error = zvec_collection_get_stats(collection, &stats);
if (error == ZVEC_OK && stats) {
printf(" Document count after flush: %llu\n",
(unsigned long long)zvec_collection_stats_get_doc_count(stats));
zvec_collection_stats_destroy(stats);
}
/* ------------------------------------------------------------------
* Step 5: Search with DiskANN query parameters
* ------------------------------------------------------------------ */
printf("\n[Step 5] Searching with DiskANN query parameters...\n");
/* Create DiskANN query params — list_size controls the search frontier
* (beam width). Larger values improve recall at the cost of latency. */
zvec_diskann_query_params_t *da_qp = zvec_query_params_diskann_create(200);
if (!da_qp) {
fprintf(stderr, "Failed to create DiskANN query params\n");
goto cleanup_vectors;
}
printf(" DiskANN query params: list_size=%d\n",
zvec_query_params_diskann_get_list_size(da_qp));
/* Build the vector query */
zvec_vector_query_t *query = zvec_vector_query_create();
zvec_vector_query_set_field_name(query, "embedding");
zvec_vector_query_set_query_vector(query, vectors[0],
VECTOR_DIM * sizeof(float));
zvec_vector_query_set_topk(query, 10);
zvec_vector_query_set_include_vector(query, false);
zvec_vector_query_set_include_doc_id(query, true);
/* Attach DiskANN query params (ownership transfers to query) */
error = zvec_vector_query_set_diskann_params(query, da_qp);
if (handle_error(error, "setting DiskANN query params") != ZVEC_OK) {
zvec_vector_query_destroy(query);
goto cleanup_vectors;
}
/* da_qp is now owned by query — do NOT call diskann_destroy on it */
/* Execute the query */
zvec_doc_t **results = NULL;
size_t result_count = 0;
error = zvec_collection_query(collection, (const zvec_vector_query_t *)query,
&results, &result_count);
if (error != ZVEC_OK) {
handle_error(error, "executing DiskANN query");
printf(
" (This is expected on non-Linux platforms — DiskANN requires "
"libaio)\n");
} else {
printf(" Query returned %zu results:\n", result_count);
for (size_t r = 0; r < result_count && r < 5; r++) {
const char *pk = zvec_doc_get_pk_copy(results[r]);
printf(" [%zu] pk=%s doc_id=%llu score=%.6f\n", r + 1,
pk ? pk : "NULL",
(unsigned long long)zvec_doc_get_doc_id(results[r]),
zvec_doc_get_score(results[r]));
if (pk) {
zvec_free((void *)pk);
}
}
if (result_count > 5) {
printf(" ... and %zu more\n", result_count - 5);
}
zvec_docs_free(results, result_count);
}
zvec_vector_query_destroy(query);
/* ------------------------------------------------------------------
* Step 6: Demonstrate list_size tuning (higher recall vs. lower latency)
* ------------------------------------------------------------------ */
printf("\n[Step 6] Tuning list_size for recall/latency trade-off...\n");
int list_sizes[] = {50, 100, 300};
for (int li = 0; li < 3; li++) {
zvec_diskann_query_params_t *tune_qp =
zvec_query_params_diskann_create(list_sizes[li]);
zvec_vector_query_t *tune_query = zvec_vector_query_create();
zvec_vector_query_set_field_name(tune_query, "embedding");
zvec_vector_query_set_query_vector(tune_query, vectors[0],
VECTOR_DIM * sizeof(float));
zvec_vector_query_set_topk(tune_query, 10);
zvec_vector_query_set_include_doc_id(tune_query, true);
zvec_vector_query_set_diskann_params(tune_query, tune_qp);
zvec_doc_t **tune_results = NULL;
size_t tune_count = 0;
error = zvec_collection_query(collection,
(const zvec_vector_query_t *)tune_query,
&tune_results, &tune_count);
if (error == ZVEC_OK) {
printf(" list_size=%3d -> %zu results returned\n", list_sizes[li],
tune_count);
zvec_docs_free(tune_results, tune_count);
} else {
printf(" list_size=%3d -> query failed (expected on non-Linux)\n",
list_sizes[li]);
}
zvec_vector_query_destroy(tune_query);
}
/* ------------------------------------------------------------------
* Cleanup
* ------------------------------------------------------------------ */
cleanup_vectors:
free(vectors);
cleanup_collection:
zvec_collection_destroy(collection);
cleanup_schema:
zvec_collection_schema_destroy(schema);
if (invert_params) {
zvec_index_params_destroy(invert_params);
}
if (diskann_params) {
zvec_index_params_destroy(diskann_params);
}
printf("\n DiskANN index type string: %s\n",
zvec_index_type_to_string(ZVEC_INDEX_TYPE_DISKANN));
printf("=== Example completed ===\n");
return 0;
}

View File

@ -1361,6 +1361,15 @@ zvec_index_params_t *zvec_index_params_create(zvec_index_type_t index_type) {
new zvec::FlatIndexParams(zvec::MetricType::L2, // metric_type
zvec::QuantizeType::UNDEFINED);
break;
case ZVEC_INDEX_TYPE_DISKANN:
cpp_params =
new zvec::DiskAnnIndexParams(
zvec::MetricType::L2, // metric_type
100, // max_degree (default)
50, // list_size (default)
0, // pq_chunk_num (default)
zvec::QuantizeType::UNDEFINED);
break;
}
// Return as opaque pointer (raw pointer)
@ -1661,6 +1670,104 @@ zvec_error_code_t zvec_index_params_get_vamana_params(
return ZVEC_OK;
}
/**
* @brief Set DiskANN-specific parameters
* @param params Index parameters (must be DiskANN type)
* @param max_degree Graph connectivity (max degree of Vamana graph)
* @param list_size Build-time list size
* @param pq_chunk_num PQ chunk count
* @return ZVEC_OK on success, error code on failure
*/
zvec_error_code_t zvec_index_params_set_diskann_params(
zvec_index_params_t *params, int max_degree, int list_size,
int pq_chunk_num) {
if (!params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Invalid params or not DiskANN index type");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *cpp_params = reinterpret_cast<zvec::IndexParams *>(params);
auto *diskann_params = dynamic_cast<zvec::DiskAnnIndexParams *>(cpp_params);
if (!diskann_params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Invalid params or not DiskANN index type");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
diskann_params->set_max_degree(max_degree);
diskann_params->set_list_size(list_size);
diskann_params->set_pq_chunk_num(pq_chunk_num);
return ZVEC_OK;
}
/**
* @brief Get DiskANN max_degree parameter
* @param params Index parameters (must be DiskANN type)
* @return max_degree parameter value, or 0 on error
*/
int zvec_index_params_get_diskann_max_degree(
const zvec_index_params_t *params) {
if (!params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Invalid params or not DiskANN index type");
return 0;
}
auto *cpp_params = reinterpret_cast<const zvec::IndexParams *>(params);
auto *diskann_params =
dynamic_cast<const zvec::DiskAnnIndexParams *>(cpp_params);
if (!diskann_params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Invalid params or not DiskANN index type");
return 0;
}
return diskann_params->max_degree();
}
/**
* @brief Get DiskANN list_size parameter
* @param params Index parameters (must be DiskANN type)
* @return list_size parameter value, or 0 on error
*/
int zvec_index_params_get_diskann_list_size(
const zvec_index_params_t *params) {
if (!params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Invalid params or not DiskANN index type");
return 0;
}
auto *cpp_params = reinterpret_cast<const zvec::IndexParams *>(params);
auto *diskann_params =
dynamic_cast<const zvec::DiskAnnIndexParams *>(cpp_params);
if (!diskann_params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Invalid params or not DiskANN index type");
return 0;
}
return diskann_params->list_size();
}
/**
* @brief Get DiskANN pq_chunk_num parameter
* @param params Index parameters (must be DiskANN type)
* @return pq_chunk_num parameter value, or 0 on error
*/
int zvec_index_params_get_diskann_pq_chunk_num(
const zvec_index_params_t *params) {
if (!params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Invalid params or not DiskANN index type");
return 0;
}
auto *cpp_params = reinterpret_cast<const zvec::IndexParams *>(params);
auto *diskann_params =
dynamic_cast<const zvec::DiskAnnIndexParams *>(cpp_params);
if (!diskann_params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Invalid params or not DiskANN index type");
return 0;
}
return diskann_params->pq_chunk_num();
}
/**
* @brief Set IVF-specific parameters
* @param params Index parameters (must be IVF type)
@ -2697,6 +2804,8 @@ const char *zvec_index_type_to_string(zvec_index_type_t index_type) {
return "INVERT";
case ZVEC_INDEX_TYPE_FTS:
return "FTS";
case ZVEC_INDEX_TYPE_DISKANN:
return "DiskANN";
default:
return "UNKNOWN_INDEX_TYPE";
}
@ -4848,6 +4957,100 @@ bool zvec_query_params_hnsw_get_is_using_refiner(
return ptr->is_using_refiner();
}
// =============================================================================
// DiskAnnQueryParams implementation - wrapper around zvec::DiskAnnQueryParams
// =============================================================================
zvec_diskann_query_params_t *zvec_query_params_diskann_create(int list_size) {
ZVEC_TRY_RETURN_NULL(
"Failed to create DiskAnnQueryParams",
auto *params = new zvec::DiskAnnQueryParams(list_size);
return reinterpret_cast<zvec_diskann_query_params_t *>(params);)
return nullptr;
}
void zvec_query_params_diskann_destroy(zvec_diskann_query_params_t *params) {
if (params) {
delete reinterpret_cast<zvec::DiskAnnQueryParams *>(params);
}
}
zvec_error_code_t zvec_query_params_diskann_set_list_size(
zvec_diskann_query_params_t *params, int list_size) {
if (!params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"DiskANN query params pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *ptr = reinterpret_cast<zvec::DiskAnnQueryParams *>(params);
ptr->set_list_size(list_size);
return ZVEC_OK;
}
int zvec_query_params_diskann_get_list_size(
const zvec_diskann_query_params_t *params) {
if (!params) return 300; // DiskAnnQueryParams default
auto *ptr = reinterpret_cast<const zvec::DiskAnnQueryParams *>(params);
return ptr->list_size();
}
zvec_error_code_t zvec_query_params_diskann_set_radius(
zvec_diskann_query_params_t *params, float radius) {
if (!params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"DiskANN query params pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *ptr = reinterpret_cast<zvec::DiskAnnQueryParams *>(params);
ptr->set_radius(radius);
return ZVEC_OK;
}
float zvec_query_params_diskann_get_radius(
const zvec_diskann_query_params_t *params) {
if (!params) return 0.0f;
auto *ptr = reinterpret_cast<const zvec::DiskAnnQueryParams *>(params);
return ptr->radius();
}
zvec_error_code_t zvec_query_params_diskann_set_is_linear(
zvec_diskann_query_params_t *params, bool is_linear) {
if (!params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"DiskANN query params pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *ptr = reinterpret_cast<zvec::DiskAnnQueryParams *>(params);
ptr->set_is_linear(is_linear);
return ZVEC_OK;
}
bool zvec_query_params_diskann_get_is_linear(
const zvec_diskann_query_params_t *params) {
if (!params) return false;
auto *ptr = reinterpret_cast<const zvec::DiskAnnQueryParams *>(params);
return ptr->is_linear();
}
zvec_error_code_t zvec_query_params_diskann_set_is_using_refiner(
zvec_diskann_query_params_t *params, bool is_using_refiner) {
if (!params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"DiskANN query params pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *ptr = reinterpret_cast<zvec::DiskAnnQueryParams *>(params);
ptr->set_is_using_refiner(is_using_refiner);
return ZVEC_OK;
}
bool zvec_query_params_diskann_get_is_using_refiner(
const zvec_diskann_query_params_t *params) {
if (!params) return false;
auto *ptr = reinterpret_cast<const zvec::DiskAnnQueryParams *>(params);
return ptr->is_using_refiner();
}
// =============================================================================
// IVFQueryParams implementation - wrapper around zvec::IVFQueryParams
// =============================================================================
@ -5467,6 +5670,23 @@ zvec_error_code_t zvec_vector_query_set_vamana_params(
return ZVEC_OK;
}
zvec_error_code_t zvec_vector_query_set_diskann_params(
zvec_vector_query_t *query, zvec_diskann_query_params_t *diskann_params) {
if (!query || !diskann_params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Query or DiskANN params pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *query_ptr = reinterpret_cast<zvec::SearchQuery *>(query);
auto *params_ptr =
reinterpret_cast<zvec::DiskAnnQueryParams *>(diskann_params);
query_ptr->target_.query_params_.reset(params_ptr);
return ZVEC_OK;
}
// =============================================================================
// Fts payload implementation - wrapper around zvec::FtsClause (value type)
// =============================================================================
@ -5823,6 +6043,24 @@ zvec_error_code_t zvec_group_by_vector_query_set_vamana_params(
return ZVEC_OK;
}
zvec_error_code_t zvec_group_by_vector_query_set_diskann_params(
zvec_group_by_vector_query_t *query,
zvec_diskann_query_params_t *diskann_params) {
if (!query || !diskann_params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Query or DiskANN params pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *query_ptr = reinterpret_cast<zvec::GroupByVectorQuery *>(query);
auto *params_ptr =
reinterpret_cast<zvec::DiskAnnQueryParams *>(diskann_params);
query_ptr->target_.query_params_.reset(params_ptr);
return ZVEC_OK;
}
// =============================================================================
// Reranker Implementation
// =============================================================================
@ -6193,6 +6431,20 @@ zvec_error_code_t zvec_sub_query_set_fts_params(
return ZVEC_OK;
}
zvec_error_code_t zvec_sub_query_set_diskann_params(
zvec_sub_query_t *query, zvec_diskann_query_params_t *diskann_params) {
if (!query || !diskann_params) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Sub-vector query or DiskANN params pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *ptr = reinterpret_cast<zvec::SubQuery *>(query);
auto *params_ptr =
reinterpret_cast<zvec::DiskAnnQueryParams *>(diskann_params);
ptr->target_.query_params_.reset(params_ptr);
return ZVEC_OK;
}
zvec_error_code_t zvec_sub_query_set_fts(zvec_sub_query_t *query,
const zvec_fts_t *fts) {
if (!query) {

View File

@ -826,6 +826,7 @@ typedef uint32_t zvec_index_type_t;
#define ZVEC_INDEX_TYPE_HNSW 1
#define ZVEC_INDEX_TYPE_IVF 2
#define ZVEC_INDEX_TYPE_FLAT 3
#define ZVEC_INDEX_TYPE_DISKANN 5
#define ZVEC_INDEX_TYPE_VAMANA 6
#define ZVEC_INDEX_TYPE_INVERT 10
#define ZVEC_INDEX_TYPE_FTS 11
@ -1042,6 +1043,42 @@ ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_index_params_get_vamana_params(
int *out_search_list_size, float *out_alpha, bool *out_saturate_graph,
bool *out_use_contiguous_memory);
/**
* @brief Set DiskANN specific parameters
* @param params Index parameters (must be DiskANN type)
* @param max_degree Graph connectivity (max degree of Vamana graph)
* @param list_size Build-time list size (candidate list during construction)
* @param pq_chunk_num PQ chunk count (0 disables PQ)
* @return ZVEC_OK on success, error code on failure
*/
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_index_params_set_diskann_params(
zvec_index_params_t *params, int max_degree, int list_size,
int pq_chunk_num);
/**
* @brief Get DiskANN max_degree parameter
* @param params Index parameters (must not be NULL)
* @return max_degree parameter
*/
ZVEC_EXPORT int ZVEC_CALL
zvec_index_params_get_diskann_max_degree(const zvec_index_params_t *params);
/**
* @brief Get DiskANN list_size parameter
* @param params Index parameters (must not be NULL)
* @return list_size parameter
*/
ZVEC_EXPORT int ZVEC_CALL
zvec_index_params_get_diskann_list_size(const zvec_index_params_t *params);
/**
* @brief Get DiskANN pq_chunk_num parameter
* @param params Index parameters (must not be NULL)
* @return pq_chunk_num parameter
*/
ZVEC_EXPORT int ZVEC_CALL
zvec_index_params_get_diskann_pq_chunk_num(const zvec_index_params_t *params);
/**
* @brief Set IVF specific parameters
* @param params Index parameters (must be IVF type)
@ -1189,6 +1226,16 @@ typedef struct zvec_fts_query_params_t zvec_fts_query_params_t;
*/
typedef struct zvec_vamana_query_params_t zvec_vamana_query_params_t;
/**
* @brief DiskANN query parameters handle (opaque pointer)
*
* Internally maps to zvec::DiskAnnQueryParams* (raw pointer).
* Created by zvec_query_params_diskann_create() and destroyed by
* zvec_query_params_diskann_destroy(). Caller owns the pointer and must
* explicitly destroy it.
*/
typedef struct zvec_diskann_query_params_t zvec_diskann_query_params_t;
// =============================================================================
// Query Structures (Opaque Pointer Pattern)
@ -1670,6 +1717,95 @@ zvec_query_params_vamana_set_is_using_refiner(
ZVEC_EXPORT bool ZVEC_CALL zvec_query_params_vamana_get_is_using_refiner(
const zvec_vamana_query_params_t *params);
// -----------------------------------------------------------------------------
// zvec_diskann_query_params_t (DiskANN Query Parameters)
// -----------------------------------------------------------------------------
/**
* @brief Create DiskANN query parameters
* @param list_size Search frontier size (default: 300)
* @return zvec_diskann_query_params_t* Pointer to the newly created DiskANN
* query parameters
*/
ZVEC_EXPORT zvec_diskann_query_params_t *ZVEC_CALL
zvec_query_params_diskann_create(int list_size);
/**
* @brief Destroy DiskANN query parameters
* @param params DiskANN query parameters pointer
*/
ZVEC_EXPORT void ZVEC_CALL
zvec_query_params_diskann_destroy(zvec_diskann_query_params_t *params);
/**
* @brief Set search frontier size
* @param params DiskANN query parameters pointer
* @param list_size Search frontier size
* @return zvec_error_code_t Error code
*/
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_query_params_diskann_set_list_size(
zvec_diskann_query_params_t *params, int list_size);
/**
* @brief Get search frontier size
* @param params DiskANN query parameters pointer
* @return int Search frontier size
*/
ZVEC_EXPORT int ZVEC_CALL zvec_query_params_diskann_get_list_size(
const zvec_diskann_query_params_t *params);
/**
* @brief Set search radius (common parameter from QueryParams base)
* @param params DiskANN query parameters pointer
* @param radius Search radius
* @return zvec_error_code_t Error code
*/
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_query_params_diskann_set_radius(
zvec_diskann_query_params_t *params, float radius);
/**
* @brief Get search radius (common parameter from QueryParams base)
* @param params DiskANN query parameters pointer
* @return float Search radius
*/
ZVEC_EXPORT float ZVEC_CALL
zvec_query_params_diskann_get_radius(const zvec_diskann_query_params_t *params);
/**
* @brief Set linear search mode (common parameter from QueryParams base)
* @param params DiskANN query parameters pointer
* @param is_linear Whether linear search
* @return zvec_error_code_t Error code
*/
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_query_params_diskann_set_is_linear(
zvec_diskann_query_params_t *params, bool is_linear);
/**
* @brief Get linear search mode (common parameter from QueryParams base)
* @param params DiskANN query parameters pointer
* @return bool Whether linear search
*/
ZVEC_EXPORT bool ZVEC_CALL zvec_query_params_diskann_get_is_linear(
const zvec_diskann_query_params_t *params);
/**
* @brief Set whether to use refiner (common parameter from QueryParams base)
* @param params DiskANN query parameters pointer
* @param is_using_refiner Whether to use refiner
* @return zvec_error_code_t Error code
*/
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL
zvec_query_params_diskann_set_is_using_refiner(
zvec_diskann_query_params_t *params, bool is_using_refiner);
/**
* @brief Get whether to use refiner (common parameter from QueryParams base)
* @param params DiskANN query parameters pointer
* @return bool Whether to use refiner
*/
ZVEC_EXPORT bool ZVEC_CALL zvec_query_params_diskann_get_is_using_refiner(
const zvec_diskann_query_params_t *params);
// -----------------------------------------------------------------------------
// zvec_vector_query_t (Vector Query)
// -----------------------------------------------------------------------------
@ -1861,6 +1997,15 @@ ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_vector_query_set_fts_params(
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_vector_query_set_vamana_params(
zvec_vector_query_t *query, zvec_vamana_query_params_t *vamana_params);
/**
* @brief Set DiskANN query parameters (takes ownership)
* @param query Vector query pointer
* @param diskann_params DiskANN query parameters pointer
* @return zvec_error_code_t Error code
*/
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_vector_query_set_diskann_params(
zvec_vector_query_t *query, zvec_diskann_query_params_t *diskann_params);
// -----------------------------------------------------------------------------
// zvec_fts_t (FTS query payload)
// -----------------------------------------------------------------------------
@ -2144,6 +2289,17 @@ zvec_group_by_vector_query_set_vamana_params(
zvec_group_by_vector_query_t *query,
zvec_vamana_query_params_t *vamana_params);
/**
* @brief Set DiskANN query parameters (takes ownership)
* @param query Group by vector query pointer
* @param diskann_params DiskANN query parameters pointer
* @return zvec_error_code_t Error code
*/
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL
zvec_group_by_vector_query_set_diskann_params(
zvec_group_by_vector_query_t *query,
zvec_diskann_query_params_t *diskann_params);
// -----------------------------------------------------------------------------
// Rerank Strategy (set on MultiQuery)
// -----------------------------------------------------------------------------
@ -2422,6 +2578,15 @@ ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_sub_query_set_fts_params(
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL
zvec_sub_query_set_fts(zvec_sub_query_t *query, const zvec_fts_t *fts);
/**
* @brief Set DiskANN query parameters (takes ownership)
* @param query Sub-query pointer
* @param diskann_params DiskANN query parameters pointer
* @return zvec_error_code_t Error code
*/
ZVEC_EXPORT zvec_error_code_t ZVEC_CALL zvec_sub_query_set_diskann_params(
zvec_sub_query_t *query, zvec_diskann_query_params_t *diskann_params);
// =============================================================================
// Collection Options and Statistics (Opaque Pointer Pattern)
// =============================================================================
@ -3881,6 +4046,31 @@ zvec_metric_type_to_string(zvec_metric_type_t metric_type);
.ivf.n_probe = (_nprobe) })
// clang-format on
/**
* @brief Simplified DiskANN index parameters initialization macro
* @param _metric Distance metric type
* @param _max_degree Graph connectivity (max degree)
* @param _list_size Build-time list size
* @param _pq_chunk_num PQ chunk count
* @param _quant Quantization type
*
* Usage example:
* @code
* zvec_index_params_t params = ZVEC_DISKANN_PARAMS(
* ZVEC_METRIC_TYPE_L2, 100, 50, 16, ZVEC_QUANTIZE_TYPE_UNDEFINED);
* @endcode
*/
// clang-format off
#define ZVEC_DISKANN_PARAMS(_metric, _max_degree, _list_size, _pq_chunk_num, _quant) \
((zvec_index_params_t){ \
.index_type = ZVEC_INDEX_TYPE_DISKANN, \
.metric_type = (_metric), \
.quantize_type = (_quant), \
.diskann.max_degree = (_max_degree), \
.diskann.list_size = (_list_size), \
.diskann.pq_chunk_num = (_pq_chunk_num) })
// clang-format on
/**
* @brief Simplified string initialization macro
* @param str String content

View File

@ -3482,11 +3482,22 @@ void test_index_params_functions(void) {
TEST_ASSERT(n_iters == 10);
TEST_ASSERT(use_soar == false); // Default is false
// Test DiskANN index params
zvec_index_params_t *diskann_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN);
TEST_ASSERT(diskann_params != NULL);
TEST_ASSERT(zvec_index_params_get_type(diskann_params) ==
ZVEC_INDEX_TYPE_DISKANN);
TEST_ASSERT(zvec_index_params_get_diskann_max_degree(diskann_params) == 100);
TEST_ASSERT(zvec_index_params_get_diskann_list_size(diskann_params) == 50);
TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(diskann_params) == 0);
// Cleanup
zvec_index_params_destroy(hnsw_params);
zvec_index_params_destroy(invert_params);
zvec_index_params_destroy(flat_params);
zvec_index_params_destroy(ivf_params);
zvec_index_params_destroy(diskann_params);
TEST_END();
}
@ -3729,11 +3740,27 @@ void test_index_params_api_functions(void) {
TEST_ASSERT(zvec_index_params_get_metric_type(flat_params) ==
ZVEC_METRIC_TYPE_IP);
// Test zvec_index_params_create for DiskANN
zvec_index_params_t *diskann_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN);
TEST_ASSERT(diskann_params != NULL);
TEST_ASSERT(zvec_index_params_get_type(diskann_params) ==
ZVEC_INDEX_TYPE_DISKANN);
TEST_ASSERT(zvec_index_params_get_metric_type(diskann_params) ==
ZVEC_METRIC_TYPE_L2);
// Test zvec_index_params_set_diskann_params
zvec_index_params_set_diskann_params(diskann_params, 200, 100, 8);
TEST_ASSERT(zvec_index_params_get_diskann_max_degree(diskann_params) == 200);
TEST_ASSERT(zvec_index_params_get_diskann_list_size(diskann_params) == 100);
TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(diskann_params) == 8);
// Cleanup
zvec_index_params_destroy(hnsw_params);
zvec_index_params_destroy(ivf_params);
zvec_index_params_destroy(invert_params);
zvec_index_params_destroy(flat_params);
zvec_index_params_destroy(diskann_params);
TEST_END();
}
@ -3801,6 +3828,11 @@ void test_query_params_functions(void) {
zvec_query_params_flat_create(false, 2.0f);
TEST_ASSERT(flat_params != NULL);
// Test DiskANN query parameters
zvec_diskann_query_params_t *diskann_params =
zvec_query_params_diskann_create(500);
TEST_ASSERT(diskann_params != NULL);
zvec_error_code_t err;
// Test HNSW-specific parameters
@ -3897,17 +3929,43 @@ void test_query_params_functions(void) {
TEST_ASSERT(zvec_query_params_vamana_get_is_using_refiner(vamana_params) ==
false);
// Test DiskANN-specific parameters
TEST_ASSERT(zvec_query_params_diskann_get_list_size(diskann_params) == 500);
err = zvec_query_params_diskann_set_list_size(diskann_params, 800);
TEST_ASSERT(err == ZVEC_OK);
TEST_ASSERT(zvec_query_params_diskann_get_list_size(diskann_params) == 800);
// Test DiskANN common parameters (radius, is_linear, is_using_refiner)
err = zvec_query_params_diskann_set_radius(diskann_params, 1.2f);
TEST_ASSERT(err == ZVEC_OK);
radius = zvec_query_params_diskann_get_radius(diskann_params);
TEST_ASSERT(radius == 1.2f);
err = zvec_query_params_diskann_set_is_linear(diskann_params, true);
TEST_ASSERT(err == ZVEC_OK);
is_linear = zvec_query_params_diskann_get_is_linear(diskann_params);
TEST_ASSERT(is_linear == true);
err = zvec_query_params_diskann_set_is_using_refiner(diskann_params, true);
TEST_ASSERT(err == ZVEC_OK);
is_using_refiner =
zvec_query_params_diskann_get_is_using_refiner(diskann_params);
TEST_ASSERT(is_using_refiner == true);
// Test destruction of valid parameters
zvec_query_params_hnsw_destroy(hnsw_params);
zvec_query_params_ivf_destroy(ivf_params);
zvec_query_params_flat_destroy(flat_params);
zvec_query_params_vamana_destroy(vamana_params);
zvec_query_params_diskann_destroy(diskann_params);
// Test boundary cases - null pointer handling
zvec_query_params_hnsw_destroy(NULL);
zvec_query_params_ivf_destroy(NULL);
zvec_query_params_flat_destroy(NULL);
zvec_query_params_vamana_destroy(NULL);
zvec_query_params_diskann_destroy(NULL);
// Test null pointer handling for setters
err = zvec_query_params_hnsw_set_radius(NULL, 0.5f);
@ -3918,14 +3976,20 @@ void test_query_params_functions(void) {
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
err = zvec_query_params_vamana_set_ef_search(NULL, 100);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
err = zvec_query_params_diskann_set_radius(NULL, 0.5f);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
err = zvec_query_params_diskann_set_list_size(NULL, 100);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
// Test default values for getters with NULL
TEST_ASSERT(zvec_query_params_hnsw_get_radius(NULL) == 0.0f);
TEST_ASSERT(zvec_query_params_ivf_get_radius(NULL) == 0.0f);
TEST_ASSERT(zvec_query_params_flat_get_radius(NULL) == 0.0f);
TEST_ASSERT(zvec_query_params_diskann_get_radius(NULL) == 0.0f);
TEST_ASSERT(zvec_query_params_hnsw_get_is_linear(NULL) == false);
TEST_ASSERT(zvec_query_params_ivf_get_is_linear(NULL) == false);
TEST_ASSERT(zvec_query_params_flat_get_is_linear(NULL) == false);
TEST_ASSERT(zvec_query_params_diskann_get_is_linear(NULL) == false);
TEST_ASSERT(zvec_query_params_hnsw_get_is_using_refiner(NULL) == false);
TEST_ASSERT(zvec_query_params_ivf_get_is_using_refiner(NULL) == false);
TEST_ASSERT(zvec_query_params_flat_get_is_using_refiner(NULL) == false);
@ -3933,6 +3997,8 @@ void test_query_params_functions(void) {
TEST_ASSERT(zvec_query_params_vamana_get_radius(NULL) == 0.0f);
TEST_ASSERT(zvec_query_params_vamana_get_is_linear(NULL) == false);
TEST_ASSERT(zvec_query_params_vamana_get_is_using_refiner(NULL) == false);
TEST_ASSERT(zvec_query_params_diskann_get_is_using_refiner(NULL) == false);
TEST_ASSERT(zvec_query_params_diskann_get_list_size(NULL) == 300);
TEST_END();
}
@ -5258,12 +5324,25 @@ void test_index_params_creation_functions(void) {
false);
TEST_ASSERT(verr == ZVEC_ERROR_INVALID_ARGUMENT);
// Test DiskANN parameters using new API
zvec_index_params_t *diskann_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN);
TEST_ASSERT(diskann_params != NULL);
TEST_ASSERT(zvec_index_params_get_type(diskann_params) ==
ZVEC_INDEX_TYPE_DISKANN);
zvec_index_params_set_metric_type(diskann_params, ZVEC_METRIC_TYPE_COSINE);
zvec_index_params_set_diskann_params(diskann_params, 64, 25, 4);
TEST_ASSERT(zvec_index_params_get_diskann_max_degree(diskann_params) == 64);
TEST_ASSERT(zvec_index_params_get_diskann_list_size(diskann_params) == 25);
TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(diskann_params) == 4);
// Cleanup
zvec_index_params_destroy(hnsw_params);
zvec_index_params_destroy(ivf_params);
zvec_index_params_destroy(flat_params);
zvec_index_params_destroy(invert_params);
zvec_index_params_destroy(vamana_params);
zvec_index_params_destroy(diskann_params);
TEST_END();
}
@ -6092,6 +6171,177 @@ void test_collection_schema_getters(void) {
TEST_END();
}
// =============================================================================
// DiskANN Tests
// =============================================================================
void test_diskann_index_params_functions(void) {
TEST_START();
// Create DiskANN index params with defaults
zvec_index_params_t *params =
zvec_index_params_create(ZVEC_INDEX_TYPE_DISKANN);
TEST_ASSERT(params != NULL);
TEST_ASSERT(zvec_index_params_get_type(params) == ZVEC_INDEX_TYPE_DISKANN);
// Check defaults: max_degree=100, list_size=50, pq_chunk_num=0
// (aligned with DiskAnnIndexParams constructor defaults)
TEST_ASSERT(zvec_index_params_get_diskann_max_degree(params) == 100);
TEST_ASSERT(zvec_index_params_get_diskann_list_size(params) == 50);
TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(params) == 0);
// Default metric type is L2
TEST_ASSERT(zvec_index_params_get_metric_type(params) == ZVEC_METRIC_TYPE_L2);
// Set and verify custom values
zvec_index_params_set_metric_type(params, ZVEC_METRIC_TYPE_COSINE);
TEST_ASSERT(zvec_index_params_get_metric_type(params) ==
ZVEC_METRIC_TYPE_COSINE);
zvec_error_code_t err =
zvec_index_params_set_diskann_params(params, 200, 100, 8);
TEST_ASSERT(err == ZVEC_OK);
TEST_ASSERT(zvec_index_params_get_diskann_max_degree(params) == 200);
TEST_ASSERT(zvec_index_params_get_diskann_list_size(params) == 100);
TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(params) == 8);
// Type-mismatch error path: HNSW params must not accept DiskANN setter
zvec_index_params_t *hnsw = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
TEST_ASSERT(hnsw != NULL);
err = zvec_index_params_set_diskann_params(hnsw, 100, 50, 0);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
zvec_index_params_destroy(hnsw);
// NULL pointer handling
err = zvec_index_params_set_diskann_params(NULL, 100, 50, 0);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
TEST_ASSERT(zvec_index_params_get_diskann_max_degree(NULL) == 0);
TEST_ASSERT(zvec_index_params_get_diskann_list_size(NULL) == 0);
TEST_ASSERT(zvec_index_params_get_diskann_pq_chunk_num(NULL) == 0);
// to_string should report DiskANN
const char *type_str = zvec_index_type_to_string(ZVEC_INDEX_TYPE_DISKANN);
TEST_ASSERT(type_str != NULL && strcmp(type_str, "DiskANN") == 0);
zvec_index_params_destroy(params);
TEST_END();
}
void test_diskann_query_params_functions(void) {
TEST_START();
// Create with default list_size
zvec_diskann_query_params_t *p_default =
zvec_query_params_diskann_create(300);
TEST_ASSERT(p_default != NULL);
TEST_ASSERT(zvec_query_params_diskann_get_list_size(p_default) == 300);
zvec_query_params_diskann_destroy(p_default);
// Create with custom list_size
zvec_diskann_query_params_t *p = zvec_query_params_diskann_create(500);
TEST_ASSERT(p != NULL);
TEST_ASSERT(zvec_query_params_diskann_get_list_size(p) == 500);
// Set/get list_size
zvec_error_code_t err = zvec_query_params_diskann_set_list_size(p, 1000);
TEST_ASSERT(err == ZVEC_OK);
TEST_ASSERT(zvec_query_params_diskann_get_list_size(p) == 1000);
// Common params: radius
err = zvec_query_params_diskann_set_radius(p, 1.5f);
TEST_ASSERT(err == ZVEC_OK);
TEST_ASSERT(zvec_query_params_diskann_get_radius(p) == 1.5f);
// Common params: is_linear
err = zvec_query_params_diskann_set_is_linear(p, true);
TEST_ASSERT(err == ZVEC_OK);
TEST_ASSERT(zvec_query_params_diskann_get_is_linear(p) == true);
// Common params: is_using_refiner
err = zvec_query_params_diskann_set_is_using_refiner(p, true);
TEST_ASSERT(err == ZVEC_OK);
TEST_ASSERT(zvec_query_params_diskann_get_is_using_refiner(p) == true);
zvec_query_params_diskann_destroy(p);
// NULL pointer handling: destroy
zvec_query_params_diskann_destroy(NULL);
// NULL pointer handling: setters return error
err = zvec_query_params_diskann_set_list_size(NULL, 100);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
err = zvec_query_params_diskann_set_radius(NULL, 0.5f);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
err = zvec_query_params_diskann_set_is_linear(NULL, false);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
err = zvec_query_params_diskann_set_is_using_refiner(NULL, false);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
// NULL pointer handling: getters return safe defaults
TEST_ASSERT(zvec_query_params_diskann_get_list_size(NULL) == 300);
TEST_ASSERT(zvec_query_params_diskann_get_radius(NULL) == 0.0f);
TEST_ASSERT(zvec_query_params_diskann_get_is_linear(NULL) == false);
TEST_ASSERT(zvec_query_params_diskann_get_is_using_refiner(NULL) == false);
TEST_END();
}
void test_diskann_wiring_on_vector_query(void) {
TEST_START();
zvec_error_code_t err;
// Test wiring on zvec_vector_query_t
zvec_vector_query_t *vq = zvec_vector_query_create();
TEST_ASSERT(vq != NULL);
zvec_diskann_query_params_t *dp1 = zvec_query_params_diskann_create(400);
TEST_ASSERT(dp1 != NULL);
err = zvec_vector_query_set_diskann_params(vq, dp1);
TEST_ASSERT(err == ZVEC_OK);
// NULL handling
zvec_diskann_query_params_t *dp_null = zvec_query_params_diskann_create(100);
err = zvec_vector_query_set_diskann_params(NULL, dp_null);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
zvec_query_params_diskann_destroy(dp_null);
err = zvec_vector_query_set_diskann_params(vq, NULL);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
zvec_vector_query_destroy(vq);
// Test wiring on zvec_group_by_vector_query_t
zvec_group_by_vector_query_t *gbq = zvec_group_by_vector_query_create();
TEST_ASSERT(gbq != NULL);
zvec_diskann_query_params_t *dp2 = zvec_query_params_diskann_create(200);
TEST_ASSERT(dp2 != NULL);
err = zvec_group_by_vector_query_set_diskann_params(gbq, dp2);
TEST_ASSERT(err == ZVEC_OK);
err = zvec_group_by_vector_query_set_diskann_params(NULL, dp2);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
zvec_group_by_vector_query_destroy(gbq);
// Test wiring on zvec_sub_query_t
zvec_sub_query_t *sq = zvec_sub_query_create();
TEST_ASSERT(sq != NULL);
zvec_diskann_query_params_t *dp3 = zvec_query_params_diskann_create(150);
TEST_ASSERT(dp3 != NULL);
err = zvec_sub_query_set_diskann_params(sq, dp3);
TEST_ASSERT(err == ZVEC_OK);
err = zvec_sub_query_set_diskann_params(NULL, dp3);
TEST_ASSERT(err == ZVEC_ERROR_INVALID_ARGUMENT);
zvec_sub_query_destroy(sq);
TEST_END();
}
// =============================================================================
// Main function
// =============================================================================
@ -6181,6 +6431,11 @@ int main(void) {
test_fts_wiring_on_sub_query();
test_fts_end_to_end();
// DiskANN tests
test_diskann_index_params_functions();
test_diskann_query_params_functions();
test_diskann_wiring_on_vector_query();
test_multi_vector_query_with_rrf_reranker();
test_multi_vector_query_with_weighted_reranker();
// Performance tests