Files
alibaba--zvec/examples/c/index_example.c
T
2026-07-13 12:47:42 +08:00

398 lines
15 KiB
C

// 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.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zvec/c_api.h"
/**
* @brief Print error message and return error code
*/
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;
}
/**
* @brief Index creation and management example
*/
int main() {
printf("=== ZVec Index Example ===\n\n");
zvec_error_code_t error;
// 1. Create collection schema
zvec_collection_schema_t *schema =
zvec_collection_schema_create("index_example_collection");
if (!schema) {
fprintf(stderr, "Failed to create collection schema\n");
return -1;
}
printf("✓ Collection schema created successfully\n");
// 2. Create different index parameter configurations
printf("Creating index parameters...\n");
// Inverted index parameters
zvec_index_params_t *invert_params_standard =
zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT);
if (!invert_params_standard) {
fprintf(stderr, "Failed to create invert index parameters (standard)\n");
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_invert_params(invert_params_standard, true, false);
zvec_index_params_t *invert_params_extended =
zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT);
if (!invert_params_extended) {
fprintf(stderr, "Failed to create invert index parameters (extended)\n");
zvec_index_params_destroy(invert_params_standard);
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_invert_params(invert_params_extended, true, true);
// HNSW index parameters with different configurations
zvec_index_params_t *hnsw_params_fast =
zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
if (!hnsw_params_fast) {
fprintf(stderr, "Failed to create HNSW index parameters (fast)\n");
zvec_index_params_destroy(invert_params_standard);
zvec_index_params_destroy(invert_params_extended);
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_metric_type(hnsw_params_fast, ZVEC_METRIC_TYPE_L2);
zvec_index_params_set_hnsw_params(hnsw_params_fast, 16, 100);
// Demonstrate INT8 quantization with random rotation preprocessing
// (enable_rotate rotates vectors before INT8 quantization to reduce error)
zvec_index_params_set_quantize_type(hnsw_params_fast,
ZVEC_QUANTIZE_TYPE_INT8);
zvec_index_params_set_quantizer_enable_rotate(hnsw_params_fast, true);
zvec_index_params_t *hnsw_params_balanced =
zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
if (!hnsw_params_balanced) {
fprintf(stderr, "Failed to create HNSW index parameters (balanced)\n");
zvec_index_params_destroy(invert_params_standard);
zvec_index_params_destroy(invert_params_extended);
zvec_index_params_destroy(hnsw_params_fast);
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_metric_type(hnsw_params_balanced,
ZVEC_METRIC_TYPE_COSINE);
zvec_index_params_set_hnsw_params(hnsw_params_balanced, 32, 200);
zvec_index_params_t *hnsw_params_accurate =
zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
if (!hnsw_params_accurate) {
fprintf(stderr, "Failed to create HNSW index parameters (accurate)\n");
zvec_index_params_destroy(invert_params_standard);
zvec_index_params_destroy(invert_params_extended);
zvec_index_params_destroy(hnsw_params_fast);
zvec_index_params_destroy(hnsw_params_balanced);
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_metric_type(hnsw_params_accurate, ZVEC_METRIC_TYPE_IP);
zvec_index_params_set_hnsw_params(hnsw_params_accurate, 64, 400);
// Flat index parameters
zvec_index_params_t *flat_params_l2 =
zvec_index_params_create(ZVEC_INDEX_TYPE_FLAT);
if (!flat_params_l2) {
fprintf(stderr, "Failed to create Flat index parameters (L2)\n");
zvec_index_params_destroy(invert_params_standard);
zvec_index_params_destroy(invert_params_extended);
zvec_index_params_destroy(hnsw_params_fast);
zvec_index_params_destroy(hnsw_params_balanced);
zvec_index_params_destroy(hnsw_params_accurate);
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_metric_type(flat_params_l2, ZVEC_METRIC_TYPE_L2);
zvec_index_params_t *flat_params_cosine =
zvec_index_params_create(ZVEC_INDEX_TYPE_FLAT);
if (!flat_params_cosine) {
fprintf(stderr, "Failed to create Flat index parameters (cosine)\n");
zvec_index_params_destroy(invert_params_standard);
zvec_index_params_destroy(invert_params_extended);
zvec_index_params_destroy(hnsw_params_fast);
zvec_index_params_destroy(hnsw_params_balanced);
zvec_index_params_destroy(hnsw_params_accurate);
zvec_index_params_destroy(flat_params_l2);
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_metric_type(flat_params_cosine,
ZVEC_METRIC_TYPE_COSINE);
// 3. Create fields with different index types
printf("Creating fields with various index types...\n");
// Fields with inverted indexes
zvec_field_schema_t *id_field =
zvec_field_schema_create("id", ZVEC_DATA_TYPE_STRING, false, 0);
if (id_field) {
zvec_field_schema_set_index_params(id_field, invert_params_standard);
error = zvec_collection_schema_add_field(schema, id_field);
if (handle_error(error, "adding ID field") == ZVEC_OK) {
printf("✓ ID field with standard inverted index added\n");
}
}
zvec_field_schema_t *category_field =
zvec_field_schema_create("category", ZVEC_DATA_TYPE_STRING, true, 0);
if (category_field) {
zvec_field_schema_set_index_params(category_field, invert_params_extended);
error = zvec_collection_schema_add_field(schema, category_field);
if (handle_error(error, "adding category field") == ZVEC_OK) {
printf("✓ Category field with extended inverted index added\n");
}
}
// Vector fields with HNSW indexes (different configurations)
zvec_field_schema_t *fast_search_field = zvec_field_schema_create(
"fast_vector", ZVEC_DATA_TYPE_VECTOR_FP32, false, 64);
if (fast_search_field) {
zvec_field_schema_set_index_params(fast_search_field, hnsw_params_fast);
error = zvec_collection_schema_add_field(schema, fast_search_field);
if (handle_error(error, "adding fast search field") == ZVEC_OK) {
printf("✓ Fast search vector field (64D) with HNSW index added\n");
}
}
zvec_field_schema_t *balanced_field = zvec_field_schema_create(
"balanced_vector", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128);
if (balanced_field) {
zvec_field_schema_set_index_params(balanced_field, hnsw_params_balanced);
error = zvec_collection_schema_add_field(schema, balanced_field);
if (handle_error(error, "adding balanced field") == ZVEC_OK) {
printf("✓ Balanced vector field (128D) with HNSW index added\n");
}
}
zvec_field_schema_t *accurate_field = zvec_field_schema_create(
"accurate_vector", ZVEC_DATA_TYPE_VECTOR_FP32, false, 256);
if (accurate_field) {
zvec_field_schema_set_index_params(accurate_field, hnsw_params_accurate);
error = zvec_collection_schema_add_field(schema, accurate_field);
if (handle_error(error, "adding accurate field") == ZVEC_OK) {
printf("✓ Accurate vector field (256D) with HNSW index added\n");
}
}
// Vector field with Flat index
zvec_field_schema_t *exact_field = zvec_field_schema_create(
"exact_vector", ZVEC_DATA_TYPE_VECTOR_FP32, false, 32);
if (exact_field) {
zvec_field_schema_set_index_params(exact_field, flat_params_l2);
error = zvec_collection_schema_add_field(schema, exact_field);
if (handle_error(error, "adding exact field") == ZVEC_OK) {
printf("✓ Exact search vector field (32D) with Flat index added\n");
}
}
// 4. Create collection
zvec_collection_options_t *options = zvec_collection_options_create();
if (!options) {
fprintf(stderr, "Failed to create collection options\n");
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_collection_t *collection = NULL;
error = zvec_collection_create_and_open("./index_example_collection", schema,
options, &collection);
zvec_collection_options_destroy(options);
if (handle_error(error, "creating collection") != ZVEC_OK) {
zvec_collection_schema_destroy(schema);
return -1;
}
printf("✓ Collection created successfully\n");
// 5. Create test data
printf("Creating test documents...\n");
zvec_doc_t *docs[3];
for (int i = 0; i < 3; i++) {
docs[i] = zvec_doc_create();
if (!docs[i]) {
fprintf(stderr, "Failed to create document %d\n", i);
// Cleanup
for (int j = 0; j < i; j++) {
zvec_doc_destroy(docs[j]);
}
goto cleanup;
}
}
// Prepare vector data
float fast_vec[3][64];
float balanced_vec[3][128];
float accurate_vec[3][256];
float exact_vec[3][32];
// Generate different vector patterns for testing
for (int doc_idx = 0; doc_idx < 3; doc_idx++) {
for (int i = 0; i < 64; i++) {
fast_vec[doc_idx][i] = (float)(doc_idx * 64 + i) / (64.0f * 3.0f);
}
for (int i = 0; i < 128; i++) {
balanced_vec[doc_idx][i] = (float)(doc_idx * 128 + i) / (128.0f * 3.0f);
}
for (int i = 0; i < 256; i++) {
accurate_vec[doc_idx][i] = (float)(doc_idx * 256 + i) / (256.0f * 3.0f);
}
for (int i = 0; i < 32; i++) {
exact_vec[doc_idx][i] = (float)(doc_idx * 32 + i) / (32.0f * 3.0f);
}
}
// Populate documents
for (int i = 0; i < 3; i++) {
char pk[16];
snprintf(pk, sizeof(pk), "doc%d", i + 1);
zvec_doc_set_pk(docs[i], pk);
char id_val[16];
snprintf(id_val, sizeof(id_val), "ID_%d", i + 1);
zvec_doc_add_field_by_value(docs[i], "id", ZVEC_DATA_TYPE_STRING, id_val,
strlen(id_val));
char category_val[16];
snprintf(category_val, sizeof(category_val), "cat_%d", (i % 2) + 1);
zvec_doc_add_field_by_value(docs[i], "category", ZVEC_DATA_TYPE_STRING,
category_val, strlen(category_val));
zvec_doc_add_field_by_value(docs[i], "fast_vector",
ZVEC_DATA_TYPE_VECTOR_FP32, fast_vec[i],
64 * sizeof(float));
zvec_doc_add_field_by_value(docs[i], "balanced_vector",
ZVEC_DATA_TYPE_VECTOR_FP32, balanced_vec[i],
128 * sizeof(float));
zvec_doc_add_field_by_value(docs[i], "accurate_vector",
ZVEC_DATA_TYPE_VECTOR_FP32, accurate_vec[i],
256 * sizeof(float));
zvec_doc_add_field_by_value(docs[i], "exact_vector",
ZVEC_DATA_TYPE_VECTOR_FP32, exact_vec[i],
32 * sizeof(float));
}
// 6. Insert documents
size_t success_count = 0, error_count = 0;
error = zvec_collection_insert(collection, (const zvec_doc_t **)docs, 3,
&success_count, &error_count);
if (handle_error(error, "inserting documents") == ZVEC_OK) {
printf("✓ Documents inserted - Success: %zu, Failed: %zu\n", success_count,
error_count);
}
// Cleanup documents
for (int i = 0; i < 3; i++) {
zvec_doc_destroy(docs[i]);
}
// 7. Flush collection to build indexes
error = zvec_collection_flush(collection);
if (handle_error(error, "flushing collection") == ZVEC_OK) {
printf("✓ Collection flushed - indexes built\n");
}
// 8. Test different query types
printf("Testing various index queries...\n");
// Test HNSW query (balanced)
zvec_vector_query_t *hnsw_query = zvec_vector_query_create();
if (!hnsw_query) {
fprintf(stderr, "Failed to create HNSW query\n");
goto cleanup;
}
zvec_vector_query_set_field_name(hnsw_query, "balanced_vector");
zvec_vector_query_set_query_vector(hnsw_query, balanced_vec[0],
128 * sizeof(float));
zvec_vector_query_set_topk(hnsw_query, 2);
zvec_vector_query_set_filter(hnsw_query, "");
zvec_vector_query_set_include_vector(hnsw_query, false);
zvec_vector_query_set_include_doc_id(hnsw_query, true);
zvec_doc_t **hnsw_results = NULL;
size_t hnsw_result_count = 0;
error =
zvec_collection_query(collection, (const zvec_vector_query_t *)hnsw_query,
&hnsw_results, &hnsw_result_count);
if (error == ZVEC_OK) {
printf("✓ HNSW query successful - Found %zu results\n", hnsw_result_count);
zvec_docs_free(hnsw_results, hnsw_result_count);
}
zvec_vector_query_destroy(hnsw_query);
// Test Flat query (exact)
zvec_vector_query_t *flat_query = zvec_vector_query_create();
if (!flat_query) {
fprintf(stderr, "Failed to create Flat query\n");
goto cleanup;
}
zvec_vector_query_set_field_name(flat_query, "exact_vector");
zvec_vector_query_set_query_vector(flat_query, exact_vec[0],
32 * sizeof(float));
zvec_vector_query_set_topk(flat_query, 2);
zvec_vector_query_set_filter(flat_query, "");
zvec_vector_query_set_include_vector(flat_query, false);
zvec_vector_query_set_include_doc_id(flat_query, true);
zvec_doc_t **flat_results = NULL;
size_t flat_result_count = 0;
error =
zvec_collection_query(collection, (const zvec_vector_query_t *)flat_query,
&flat_results, &flat_result_count);
if (error == ZVEC_OK) {
printf("✓ Flat (exact) query successful - Found %zu results\n",
flat_result_count);
zvec_docs_free(flat_results, flat_result_count);
}
zvec_vector_query_destroy(flat_query);
// 9. Performance comparison information
printf("\nIndex Performance Characteristics:\n");
printf("- Inverted Index: Fast text search, supports filtering\n");
printf(
"- HNSW Index: Approximate nearest neighbor search, good balance of "
"speed/accuracy\n");
printf("- Flat Index: Exact search, slower but 100%% accurate\n");
printf(
"- Trade-off: Speed vs Accuracy - choose based on your requirements\n");
// 10. Cleanup
cleanup:
zvec_collection_destroy(collection);
zvec_collection_schema_destroy(schema);
// Cleanup index parameters
printf("✓ Index example completed\n");
return 0;
}