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

539 lines
18 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 <math.h>
#include <stdbool.h>
#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 Create a test document with all data types
* @param doc_index Document index for generating unique data
* @return zvec_doc_t* Created document pointer
*/
static zvec_doc_t *create_full_type_test_doc(int doc_index) {
zvec_doc_t *doc = zvec_doc_create();
if (!doc) {
fprintf(stderr, "Failed to create document\n");
return NULL;
}
// Set primary key
char pk_buffer[32];
snprintf(pk_buffer, sizeof(pk_buffer), "doc_%d", doc_index);
zvec_doc_set_pk(doc, pk_buffer);
// Add Id field with inverted index
char id_buffer[32];
snprintf(id_buffer, sizeof(id_buffer), "id_%d", doc_index);
zvec_doc_add_field_by_value(doc, "id", ZVEC_DATA_TYPE_STRING, id_buffer,
strlen(id_buffer));
// Add scalar fields with different data types
// String field
char string_value[64];
snprintf(string_value, sizeof(string_value), "test_string_%d", doc_index);
zvec_doc_add_field_by_value(doc, "string_field", ZVEC_DATA_TYPE_STRING,
string_value, strlen(string_value));
// Boolean field
bool bool_value = (doc_index % 2 == 0);
zvec_doc_add_field_by_value(doc, "bool_field", ZVEC_DATA_TYPE_BOOL,
&bool_value, sizeof(bool_value));
// Integer fields
int32_t int32_value = doc_index * 1000;
zvec_doc_add_field_by_value(doc, "int32_field", ZVEC_DATA_TYPE_INT32,
&int32_value, sizeof(int32_value));
int64_t int64_value = (int64_t)doc_index * 1000000LL;
zvec_doc_add_field_by_value(doc, "int64_field", ZVEC_DATA_TYPE_INT64,
&int64_value, sizeof(int64_value));
// Floating point fields
float float_value = (float)doc_index * 1.5f;
zvec_doc_add_field_by_value(doc, "float_field", ZVEC_DATA_TYPE_FLOAT,
&float_value, sizeof(float_value));
double double_value = (double)doc_index * 2.718281828;
zvec_doc_add_field_by_value(doc, "double_field", ZVEC_DATA_TYPE_DOUBLE,
&double_value, sizeof(double_value));
// Vector fields with different dimensions
// FP32 vector (3D)
float fp32_vector[3] = {(float)doc_index, (float)doc_index * 2.0f,
(float)doc_index * 3.0f};
zvec_doc_add_field_by_value(doc, "vector_fp32", ZVEC_DATA_TYPE_VECTOR_FP32,
fp32_vector, 3 * sizeof(float));
// Larger FP32 vector (16D)
float large_vector[16];
for (int i = 0; i < 16; i++) {
large_vector[i] = (float)(doc_index * 16 + i) / 256.0f;
}
zvec_doc_add_field_by_value(doc, "large_vector", ZVEC_DATA_TYPE_VECTOR_FP32,
large_vector, 16 * sizeof(float));
return doc;
}
/**
* @brief Compare two documents for equality
*/
static bool compare_documents(const zvec_doc_t *doc1, const zvec_doc_t *doc2) {
if (!doc1 || !doc2) return false;
// Compare primary keys
const char *pk1 = zvec_doc_get_pk_pointer(doc1);
const char *pk2 = zvec_doc_get_pk_pointer(doc2);
if (!pk1 || !pk2 || strcmp(pk1, pk2) != 0) {
return false;
}
// TODO: Compare other fields and values
return true;
}
/**
* @brief Print document fields and their values
* @param doc The document to print
* @param doc_index Document index for identification
*/
static void print_doc(const zvec_doc_t *doc, int doc_index) {
if (!doc) {
printf("Document %d: NULL document\n", doc_index);
return;
}
printf("\n=== Document %d ===\n", doc_index);
// Print primary key
const char *pk = zvec_doc_get_pk_pointer(doc);
printf("Primary Key: %s\n", pk ? pk : "NULL");
// Print document ID
uint64_t doc_id = zvec_doc_get_doc_id(doc);
printf("Document ID: %llu\n", (unsigned long long)doc_id);
// Print score
float score = zvec_doc_get_score(doc);
printf("Score: %.6f\n", score);
// Print scalar fields
printf("\nScalar Fields:\n");
// ID field (using pointer function for strings)
const void *id_value = NULL;
size_t id_size = 0;
zvec_error_code_t error = zvec_doc_get_field_value_pointer(
doc, "id", ZVEC_DATA_TYPE_STRING, &id_value, &id_size);
if (error == ZVEC_OK && id_value) {
printf(" id: %.*s\n", (int)id_size, (const char *)id_value);
}
// String field (using pointer function for strings)
const void *string_value = NULL;
size_t string_size = 0;
error = zvec_doc_get_field_value_pointer(
doc, "string_field", ZVEC_DATA_TYPE_STRING, &string_value, &string_size);
if (error == ZVEC_OK && string_value) {
printf(" string_field: %.*s\n", (int)string_size,
(const char *)string_value);
}
// Boolean field
bool bool_value;
error = zvec_doc_get_field_value_basic(doc, "bool_field", ZVEC_DATA_TYPE_BOOL,
&bool_value, sizeof(bool_value));
if (error == ZVEC_OK) {
printf(" bool_field: %s\n", bool_value ? "true" : "false");
}
// Int32 field
int32_t int32_value;
error =
zvec_doc_get_field_value_basic(doc, "int32_field", ZVEC_DATA_TYPE_INT32,
&int32_value, sizeof(int32_value));
if (error == ZVEC_OK) {
printf(" int32_field: %d\n", int32_value);
}
// Int64 field
int64_t int64_value;
error =
zvec_doc_get_field_value_basic(doc, "int64_field", ZVEC_DATA_TYPE_INT64,
&int64_value, sizeof(int64_value));
if (error == ZVEC_OK) {
printf(" int64_field: %lld\n", (long long)int64_value);
}
// Float field
float float_value;
error =
zvec_doc_get_field_value_basic(doc, "float_field", ZVEC_DATA_TYPE_FLOAT,
&float_value, sizeof(float_value));
if (error == ZVEC_OK) {
printf(" float_field: %.6f\n", float_value);
}
// Double field
double double_value;
error =
zvec_doc_get_field_value_basic(doc, "double_field", ZVEC_DATA_TYPE_DOUBLE,
&double_value, sizeof(double_value));
if (error == ZVEC_OK) {
printf(" double_field: %.6f\n", double_value);
}
// Print vector fields (using copy function for complex types)
printf("\nVector Fields:\n");
// FP32 vector (3D)
void *fp32_vector = NULL;
size_t fp32_size = 0;
error = zvec_doc_get_field_value_copy(
doc, "vector_fp32", ZVEC_DATA_TYPE_VECTOR_FP32, &fp32_vector, &fp32_size);
if (error == ZVEC_OK && fp32_vector) {
const float *vec = (const float *)fp32_vector;
size_t dim = fp32_size / sizeof(float);
printf(" vector_fp32 (%zuD): [", dim);
for (size_t i = 0; i < dim && i < 10; i++) { // Limit to first 10 elements
printf("%.3f", vec[i]);
if (i < dim - 1 && i < 9) printf(", ");
}
if (dim > 10) printf(", ...");
printf("]\n");
zvec_free(fp32_vector); // Free the allocated memory
}
// Large vector (16D)
void *large_vector = NULL;
size_t large_size = 0;
error = zvec_doc_get_field_value_copy(doc, "large_vector",
ZVEC_DATA_TYPE_VECTOR_FP32,
&large_vector, &large_size);
if (error == ZVEC_OK && large_vector) {
const float *vec = (const float *)large_vector;
size_t dim = large_size / sizeof(float);
printf(" large_vector (%zuD): [", dim);
for (size_t i = 0; i < dim && i < 10; i++) { // Limit to first 10 elements
printf("%.3f", vec[i]);
if (i < dim - 1 && i < 9) printf(", ");
}
if (dim > 10) printf(", ...");
printf("]\n");
zvec_free(large_vector); // Free the allocated memory
}
printf("==================\n\n");
}
/**
* @brief Document creation, manipulation, and query example
*/
int main() {
printf("=== ZVec Document Example ===\n\n");
zvec_error_code_t error;
// 1. Create collection schema for document testing
zvec_collection_schema_t *schema =
zvec_collection_schema_create("doc_example_collection");
if (!schema) {
fprintf(stderr, "Failed to create collection schema\n");
return -1;
}
printf("✓ Collection schema created\n");
// 2. Create index parameters
zvec_index_params_t *invert_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT);
if (!invert_params) {
fprintf(stderr, "Failed to create invert index parameters\n");
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_invert_params(invert_params, true, false);
zvec_index_params_t *hnsw_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
if (!hnsw_params) {
fprintf(stderr, "Failed to create HNSW index parameters\n");
zvec_index_params_destroy(invert_params);
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_index_params_set_metric_type(hnsw_params, ZVEC_METRIC_TYPE_L2);
zvec_index_params_set_hnsw_params(hnsw_params, 16, 200);
// 3. Create fields for all data types
printf("Creating fields for all data types...\n");
// Id field with inverted index
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);
error = zvec_collection_schema_add_field(schema, id_field);
if (handle_error(error, "adding ID field") == ZVEC_OK) {
printf("✓ ID field with inverted index added\n");
}
}
// Scalar fields
zvec_field_schema_t *string_field =
zvec_field_schema_create("string_field", ZVEC_DATA_TYPE_STRING, true, 0);
zvec_field_schema_t *bool_field =
zvec_field_schema_create("bool_field", ZVEC_DATA_TYPE_BOOL, true, 0);
zvec_field_schema_t *int32_field =
zvec_field_schema_create("int32_field", ZVEC_DATA_TYPE_INT32, true, 0);
zvec_field_schema_t *int64_field =
zvec_field_schema_create("int64_field", ZVEC_DATA_TYPE_INT64, true, 0);
zvec_field_schema_t *float_field =
zvec_field_schema_create("float_field", ZVEC_DATA_TYPE_FLOAT, true, 0);
zvec_field_schema_t *double_field =
zvec_field_schema_create("double_field", ZVEC_DATA_TYPE_DOUBLE, true, 0);
if (string_field) zvec_collection_schema_add_field(schema, string_field);
if (bool_field) zvec_collection_schema_add_field(schema, bool_field);
if (int32_field) zvec_collection_schema_add_field(schema, int32_field);
if (int64_field) zvec_collection_schema_add_field(schema, int64_field);
if (float_field) zvec_collection_schema_add_field(schema, float_field);
if (double_field) zvec_collection_schema_add_field(schema, double_field);
// Vector fields
zvec_field_schema_t *vector_fp32_field = zvec_field_schema_create(
"vector_fp32", ZVEC_DATA_TYPE_VECTOR_FP32, false, 3);
zvec_field_schema_t *large_vector_field = zvec_field_schema_create(
"large_vector", ZVEC_DATA_TYPE_VECTOR_FP32, false, 16);
if (vector_fp32_field) {
zvec_field_schema_set_index_params(vector_fp32_field, hnsw_params);
error = zvec_collection_schema_add_field(schema, vector_fp32_field);
if (handle_error(error, "adding vector FP32 field") == ZVEC_OK) {
printf("✓ Vector FP32 field with HNSW index added\n");
}
}
if (large_vector_field) {
zvec_field_schema_set_index_params(large_vector_field, hnsw_params);
error = zvec_collection_schema_add_field(schema, large_vector_field);
if (handle_error(error, "adding large vector field") == ZVEC_OK) {
printf("✓ Large vector field with HNSW 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("./doc_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 and insert multiple test documents
printf("Creating and inserting test documents...\n");
#define DOC_COUNT 5
// Use dynamic allocation for MSVC compatibility (no VLA support)
zvec_doc_t **test_docs =
(zvec_doc_t **)malloc(DOC_COUNT * sizeof(zvec_doc_t *));
if (!test_docs) {
fprintf(stderr, "Failed to allocate test documents\n");
goto cleanup;
}
for (int i = 0; i < DOC_COUNT; i++) {
test_docs[i] = create_full_type_test_doc(i);
if (!test_docs[i]) {
fprintf(stderr, "Failed to create document %d\n", i);
// Cleanup
for (int j = 0; j < i; j++) {
zvec_doc_destroy(test_docs[j]);
}
goto cleanup;
}
printf("✓ Created document %d with PK: %s\n", i,
zvec_doc_get_pk_pointer(test_docs[i]));
}
// Print all documents before insertion
printf("\nDocuments before insertion:\n");
for (int i = 0; i < DOC_COUNT; i++) {
print_doc(test_docs[i], i);
}
// Insert documents
size_t success_count = 0, error_count = 0;
error = zvec_collection_insert(collection, (const zvec_doc_t **)test_docs,
DOC_COUNT, &success_count, &error_count);
if (handle_error(error, "inserting documents") == ZVEC_OK) {
printf("✓ Documents inserted - Success: %zu, Failed: %zu\n", success_count,
error_count);
}
// 6. Flush collection
error = zvec_collection_flush(collection);
if (handle_error(error, "flushing collection") != ZVEC_OK) {
printf("Warning: Collection flush failed\n");
} else {
printf("✓ Collection flushed successfully\n");
}
// Use the first document's vector for querying
float query_vector[] = {0.0f, 0.0f, 0.0f};
zvec_vector_query_t *query = zvec_vector_query_create();
if (!query) {
fprintf(stderr, "Failed to create vector query\n");
zvec_collection_destroy(collection);
zvec_collection_schema_destroy(schema);
return -1;
}
zvec_vector_query_set_field_name(query, "vector_fp32");
zvec_vector_query_set_query_vector(query, query_vector, 3 * sizeof(float));
zvec_vector_query_set_topk(query, 5);
zvec_vector_query_set_filter(query, "");
zvec_vector_query_set_include_vector(query, true);
zvec_vector_query_set_include_doc_id(query, true);
zvec_doc_t **query_results = NULL;
size_t result_count = 0;
error = zvec_collection_query(collection, (const zvec_vector_query_t *)query,
&query_results, &result_count);
if (handle_error(error, "querying documents") != ZVEC_OK) {
query_results = NULL;
result_count = 0;
}
printf("Query returned %zu results\n", result_count);
// Print query results
printf("\nQuery Results:\n");
for (size_t i = 0; i < result_count; i++) {
print_doc(query_results[i], i);
}
// Compare query results
for (size_t i = 0; i < result_count && i < DOC_COUNT; i++) {
const char *result_pk = zvec_doc_get_pk_pointer(query_results[i]);
printf("Comparing query result[%zu]: %s\n", i, result_pk);
// Find matching original document
bool found = false;
for (int j = 0; j < DOC_COUNT; j++) {
const char *original_pk = zvec_doc_get_pk_pointer(test_docs[j]);
if (strcmp(result_pk, original_pk) == 0) {
if (compare_documents(test_docs[j], query_results[i])) {
printf("✓ Query result %s matches original document\n", result_pk);
} else {
printf("✗ Query result %s does not match original document\n",
result_pk);
}
found = true;
break;
}
}
if (!found) {
printf("⚠ Original document not found for: %s\n", result_pk);
}
}
// 7. Filter query test
printf("\n=== Filter Query Test ===\n");
// Create filtered query
zvec_vector_query_set_filter(query, "string_field = 'string_field_0'");
zvec_doc_t **filtered_results = NULL;
size_t filtered_count = 0;
error = zvec_collection_query(collection, (const zvec_vector_query_t *)query,
&filtered_results, &filtered_count);
if (handle_error(error, "filtered querying") == ZVEC_OK) {
printf("Filtered query returned %zu results\n", filtered_count);
// Verify filter results
bool filter_correct = true;
for (size_t i = 0; i < filtered_count; i++) {
// Note: Field value access may require different API
// For now, we'll just check that we got results
const char *pk = zvec_doc_get_pk_pointer(filtered_results[i]);
if (strstr(pk, "doc_") == NULL) {
filter_correct = false;
break;
}
}
if (filter_correct) {
printf("✓ Filter query results are correct\n");
} else {
printf("✗ Filter query results are incorrect\n");
}
if (filtered_results) {
zvec_docs_free(filtered_results, filtered_count);
}
}
// 8. Cleanup query results
if (query_results) {
zvec_docs_free(query_results, result_count);
}
// 9. Cleanup documents
for (int i = 0; i < DOC_COUNT; i++) {
zvec_doc_destroy(test_docs[i]);
}
free(test_docs); // Free the array itself
// 10. Final cleanup
cleanup:
zvec_collection_destroy(collection);
zvec_collection_schema_destroy(schema);
printf("✓ Document example completed\n");
return 0;
}