Files
2026-07-13 12:47:42 +08:00

994 lines
36 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 "utils.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#endif
// =============================================================================
// Internal Helper Functions
// =============================================================================
static char *strdup_safe(const char *str) {
if (!str) return NULL;
size_t len = strlen(str) + 1;
char *copy = (char *)malloc(len);
if (copy) {
memcpy(copy, str, len);
}
return copy;
}
// =============================================================================
// Schema Creation Helper Functions Implementation
// =============================================================================
zvec_collection_schema_t *zvec_test_create_temp_schema(void) {
// Create collection schema using C API
zvec_collection_schema_t *schema = zvec_collection_schema_create("demo");
zvec_collection_schema_set_max_doc_count_per_segment(schema, 1000);
// Create index parameters using new opaque pointer API
zvec_index_params_t *invert_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT);
zvec_index_params_set_invert_params(invert_params, true, true);
zvec_index_params_t *dense_hnsw_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
zvec_index_params_set_metric_type(dense_hnsw_params, ZVEC_METRIC_TYPE_L2);
zvec_index_params_set_hnsw_params(dense_hnsw_params, 16, 100);
zvec_index_params_t *sparse_hnsw_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
zvec_index_params_set_metric_type(sparse_hnsw_params, ZVEC_METRIC_TYPE_IP);
zvec_index_params_set_hnsw_params(sparse_hnsw_params, 16, 100);
zvec_index_params_t *name_invert_params =
zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT);
zvec_index_params_set_invert_params(name_invert_params, false, false);
// Create and add fields
zvec_field_schema_t *id_field =
zvec_field_schema_create("id", ZVEC_DATA_TYPE_INT64, false, 0);
zvec_field_schema_set_index_params(id_field, invert_params);
zvec_collection_schema_add_field(schema, id_field);
// Create name field (inverted index without optimization)
zvec_field_schema_t *name_field =
zvec_field_schema_create("name", ZVEC_DATA_TYPE_STRING, false, 0);
zvec_field_schema_set_index_params(name_field, name_invert_params);
zvec_collection_schema_add_field(schema, name_field);
// Create weight field (no index)
zvec_field_schema_t *weight_field =
zvec_field_schema_create("weight", ZVEC_DATA_TYPE_FLOAT, true, 0);
zvec_collection_schema_add_field(schema, weight_field);
// Create dense field (HNSW index)
zvec_field_schema_t *dense_field =
zvec_field_schema_create("dense", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128);
zvec_field_schema_set_index_params(dense_field, dense_hnsw_params);
zvec_collection_schema_add_field(schema, dense_field);
// Create sparse field (HNSW index)
zvec_field_schema_t *sparse_field = zvec_field_schema_create(
"sparse", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32, false, 0);
zvec_field_schema_set_index_params(sparse_field, sparse_hnsw_params);
zvec_collection_schema_add_field(schema, sparse_field);
// Cleanup index parameters
zvec_index_params_destroy(invert_params);
zvec_index_params_destroy(dense_hnsw_params);
zvec_index_params_destroy(sparse_hnsw_params);
zvec_index_params_destroy(name_invert_params);
return schema;
}
zvec_collection_schema_t *zvec_test_create_scalar_schema(void) {
// Create collection schema using C API
zvec_collection_schema_t *schema = zvec_collection_schema_create("demo");
// Create fields
zvec_field_schema_t *int32_field =
zvec_field_schema_create("int32", ZVEC_DATA_TYPE_INT32, false, 0);
zvec_collection_schema_add_field(schema, int32_field);
zvec_field_schema_t *string_field =
zvec_field_schema_create("string", ZVEC_DATA_TYPE_STRING, false, 0);
zvec_collection_schema_add_field(schema, string_field);
return schema;
}
zvec_collection_schema_t *zvec_test_create_normal_schema(
bool nullable, const char *name,
const zvec_index_params_t *scalar_index_params,
const zvec_index_params_t *vector_index_params, uint64_t max_doc_count) {
// Create collection schema using C API
zvec_collection_schema_t *schema =
zvec_collection_schema_create(name ? name : "demo");
zvec_collection_schema_set_max_doc_count_per_segment(schema, max_doc_count);
// Create scalar fields (8)
const char *scalar_names[] = {"int32", "string", "uint32", "bool",
"float", "double", "int64", "uint64"};
zvec_data_type_t scalar_types[] = {
ZVEC_DATA_TYPE_INT32, ZVEC_DATA_TYPE_STRING, ZVEC_DATA_TYPE_UINT32,
ZVEC_DATA_TYPE_BOOL, ZVEC_DATA_TYPE_FLOAT, ZVEC_DATA_TYPE_DOUBLE,
ZVEC_DATA_TYPE_INT64, ZVEC_DATA_TYPE_UINT64};
for (int i = 0; i < 8; i++) {
zvec_field_schema_t *field =
zvec_field_schema_create(scalar_names[i], scalar_types[i], nullable, 0);
if (scalar_index_params) {
zvec_field_schema_set_index_params(field, scalar_index_params);
}
zvec_collection_schema_add_field(schema, field);
}
// Create array fields (8)
const char *array_names[] = {"array_int32", "array_string", "array_uint32",
"array_bool", "array_float", "array_double",
"array_int64", "array_uint64"};
zvec_data_type_t array_types[] = {
ZVEC_DATA_TYPE_ARRAY_INT32, ZVEC_DATA_TYPE_ARRAY_STRING,
ZVEC_DATA_TYPE_ARRAY_UINT32, ZVEC_DATA_TYPE_ARRAY_BOOL,
ZVEC_DATA_TYPE_ARRAY_FLOAT, ZVEC_DATA_TYPE_ARRAY_DOUBLE,
ZVEC_DATA_TYPE_ARRAY_INT64, ZVEC_DATA_TYPE_ARRAY_UINT64};
for (int i = 0; i < 8; i++) {
zvec_field_schema_t *field =
zvec_field_schema_create(array_names[i], array_types[i], nullable, 0);
if (scalar_index_params) {
zvec_field_schema_set_index_params(field, scalar_index_params);
}
zvec_collection_schema_add_field(schema, field);
}
// Create vector fields (5)
// dense vectors
zvec_field_schema_t *dense_fp32 = zvec_field_schema_create(
"dense_fp32", ZVEC_DATA_TYPE_VECTOR_FP32, false, 128);
if (vector_index_params) {
zvec_field_schema_set_index_params(dense_fp32, vector_index_params);
}
zvec_collection_schema_add_field(schema, dense_fp32);
zvec_field_schema_t *dense_fp16 = zvec_field_schema_create(
"dense_fp16", ZVEC_DATA_TYPE_VECTOR_FP16, false, 128);
zvec_index_params_t *flat_params1 = zvec_test_create_default_flat_params();
zvec_field_schema_set_index_params(dense_fp16, flat_params1);
zvec_index_params_destroy(flat_params1);
zvec_collection_schema_add_field(schema, dense_fp16);
zvec_field_schema_t *dense_int8 = zvec_field_schema_create(
"dense_int8", ZVEC_DATA_TYPE_VECTOR_INT8, false, 128);
zvec_index_params_t *flat_params2 = zvec_test_create_default_flat_params();
zvec_field_schema_set_index_params(dense_int8, flat_params2);
zvec_index_params_destroy(flat_params2);
zvec_collection_schema_add_field(schema, dense_int8);
// sparse vectors
zvec_field_schema_t *sparse_fp32 = zvec_field_schema_create(
"sparse_fp32", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32, false, 0);
if (vector_index_params) {
zvec_field_schema_set_index_params(sparse_fp32, vector_index_params);
}
zvec_collection_schema_add_field(schema, sparse_fp32);
zvec_field_schema_t *sparse_fp16 = zvec_field_schema_create(
"sparse_fp16", ZVEC_DATA_TYPE_SPARSE_VECTOR_FP16, false, 0);
zvec_index_params_t *flat_params3 = zvec_test_create_default_flat_params();
zvec_field_schema_set_index_params(sparse_fp16, flat_params3);
zvec_index_params_destroy(flat_params3);
zvec_collection_schema_add_field(schema, sparse_fp16);
return schema;
}
zvec_collection_schema_t *zvec_test_create_schema_with_scalar_index(
bool nullable, bool enable_optimize, const char *name) {
zvec_index_params_t *invert_params =
zvec_test_create_default_invert_params(enable_optimize);
zvec_collection_schema_t *schema =
zvec_test_create_normal_schema(nullable, name, invert_params, NULL, 1000);
free(invert_params);
return schema;
}
zvec_collection_schema_t *zvec_test_create_schema_with_vector_index(
bool nullable, const char *name,
const zvec_index_params_t *vector_index_params) {
zvec_index_params_t *default_params = NULL;
if (!vector_index_params) {
default_params = zvec_test_create_default_hnsw_params();
}
zvec_collection_schema_t *schema = zvec_test_create_normal_schema(
nullable, name, NULL,
vector_index_params ? vector_index_params : default_params, 1000);
if (default_params) {
free(default_params);
}
return schema;
}
zvec_collection_schema_t *zvec_test_create_schema_with_max_doc_count(
uint64_t doc_count) {
return zvec_test_create_normal_schema(false, "demo", NULL, NULL, doc_count);
}
// =============================================================================
// Document Creation Helper Functions Implementation
// =============================================================================
char *zvec_test_make_pk(uint64_t doc_id) {
char *pk = (char *)malloc(32); // Sufficiently large buffer
if (pk) {
snprintf(pk, 32, "pk_%llu", (unsigned long long)doc_id);
}
return pk;
}
uint64_t zvec_test_extract_doc_id(const char *pk) {
if (!pk || strlen(pk) < 4) return 0;
return strtoull(pk + 3, NULL, 10);
}
zvec_doc_t *zvec_test_create_doc(uint64_t doc_id,
const zvec_collection_schema_t *schema,
const char *pk) {
if (!schema) return NULL;
zvec_doc_t *doc = zvec_doc_create();
if (!doc) return NULL;
// Set primary key
char *primary_key = pk ? strdup_safe(pk) : zvec_test_make_pk(doc_id);
if (primary_key) {
zvec_doc_set_pk(doc, primary_key);
free(primary_key);
}
// Create test data for each field
const char **field_names = NULL;
size_t field_count = 0;
zvec_error_code_t ret = zvec_collection_schema_get_all_field_names(
schema, &field_names, &field_count);
if (ret != ZVEC_OK || !field_names) {
zvec_doc_destroy(doc);
return NULL;
}
for (size_t i = 0; i < field_count; i++) {
const char *field_name = field_names[i];
const zvec_field_schema_t *field =
zvec_collection_schema_get_field(schema, field_name);
if (!field) continue;
zvec_data_type_t field_type = zvec_field_schema_get_data_type(field);
uint32_t field_dimension = zvec_field_schema_get_dimension(field);
switch (field_type) {
case ZVEC_DATA_TYPE_BINARY: {
char binary_str[32];
snprintf(binary_str, sizeof(binary_str), "binary_%llu",
(unsigned long long)doc_id);
zvec_doc_add_field_by_value(doc, field_name, field_type, binary_str,
strlen(binary_str));
break;
}
case ZVEC_DATA_TYPE_BOOL: {
zvec_doc_add_field_by_value(doc, field_name, field_type,
&(bool){doc_id % 10 == 0}, sizeof(bool));
break;
}
case ZVEC_DATA_TYPE_INT32: {
zvec_doc_add_field_by_value(doc, field_name, field_type,
&(int32_t){(int32_t)doc_id},
sizeof(int32_t));
break;
}
case ZVEC_DATA_TYPE_INT64: {
zvec_doc_add_field_by_value(doc, field_name, field_type,
&(int64_t){(int64_t)doc_id},
sizeof(int64_t));
break;
}
case ZVEC_DATA_TYPE_UINT32: {
zvec_doc_add_field_by_value(doc, field_name, field_type,
&(uint32_t){(uint32_t)doc_id},
sizeof(uint32_t));
break;
}
case ZVEC_DATA_TYPE_UINT64: {
zvec_doc_add_field_by_value(doc, field_name, field_type,
&(uint64_t){(uint64_t)doc_id},
sizeof(uint64_t));
break;
}
case ZVEC_DATA_TYPE_FLOAT: {
zvec_doc_add_field_by_value(doc, field_name, field_type,
&(float){(float)doc_id}, sizeof(float));
break;
}
case ZVEC_DATA_TYPE_DOUBLE: {
zvec_doc_add_field_by_value(doc, field_name, field_type,
&(double){(double)doc_id}, sizeof(double));
break;
}
case ZVEC_DATA_TYPE_STRING: {
char string_val[64];
snprintf(string_val, sizeof(string_val), "value_%llu",
(unsigned long long)doc_id);
zvec_doc_add_field_by_value(doc, field_name, field_type, string_val,
strlen(string_val));
break;
}
case ZVEC_DATA_TYPE_ARRAY_BOOL: {
bool bool_array[10];
for (int j = 0; j < 10; j++) {
bool_array[j] = (doc_id + j) % 2 == 0;
}
zvec_doc_add_field_by_value(doc, field_name, field_type, bool_array,
sizeof(bool_array));
break;
}
case ZVEC_DATA_TYPE_ARRAY_INT32: {
int32_t int32_array[10];
for (int j = 0; j < 10; j++) {
int32_array[j] = (int32_t)doc_id;
}
zvec_doc_add_field_by_value(doc, field_name, field_type, int32_array,
sizeof(int32_array));
break;
}
case ZVEC_DATA_TYPE_ARRAY_INT64: {
int64_t int64_array[10];
for (int j = 0; j < 10; j++) {
int64_array[j] = (int64_t)doc_id;
}
zvec_doc_add_field_by_value(doc, field_name, field_type, int64_array,
sizeof(int64_array));
break;
}
case ZVEC_DATA_TYPE_ARRAY_UINT32: {
uint32_t uint32_array[10];
for (int j = 0; j < 10; j++) {
uint32_array[j] = (uint32_t)doc_id;
}
zvec_doc_add_field_by_value(doc, field_name, field_type, uint32_array,
sizeof(uint32_array));
break;
}
case ZVEC_DATA_TYPE_ARRAY_UINT64: {
uint64_t uint64_array[10];
for (int j = 0; j < 10; j++) {
uint64_array[j] = (uint64_t)doc_id;
}
zvec_doc_add_field_by_value(doc, field_name, field_type, uint64_array,
sizeof(uint64_array));
break;
}
case ZVEC_DATA_TYPE_ARRAY_FLOAT: {
float float_array[10];
for (int j = 0; j < 10; j++) {
float_array[j] = (float)doc_id;
}
zvec_doc_add_field_by_value(doc, field_name, field_type, float_array,
sizeof(float_array));
break;
}
case ZVEC_DATA_TYPE_ARRAY_DOUBLE: {
double double_array[10];
for (int j = 0; j < 10; j++) {
double_array[j] = (double)doc_id;
}
zvec_doc_add_field_by_value(doc, field_name, field_type, double_array,
sizeof(double_array));
break;
}
case ZVEC_DATA_TYPE_ARRAY_STRING: {
// String arrays need special handling
char string_data[256];
size_t offset = 0;
for (int j = 0; j < 10; j++) {
char temp_str[32];
snprintf(temp_str, sizeof(temp_str), "value_%llu_%d",
(unsigned long long)doc_id, j);
size_t len = strlen(temp_str);
if (offset + len + 1 < sizeof(string_data)) {
strcpy(string_data + offset, temp_str);
offset += len + 1;
}
}
zvec_doc_add_field_by_value(doc, field_name, field_type, string_data,
offset);
break;
}
case ZVEC_DATA_TYPE_VECTOR_BINARY32: {
uint32_t *vector_data =
(uint32_t *)malloc(field_dimension * sizeof(uint32_t));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (uint32_t)(doc_id + j);
}
zvec_doc_add_field_by_value(doc, field_name, field_type, vector_data,
field_dimension * sizeof(uint32_t));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_BINARY64: {
uint64_t *vector_data =
(uint64_t *)malloc(field_dimension * sizeof(uint64_t));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (uint64_t)(doc_id + j);
}
zvec_doc_add_field_by_value(doc, field_name, field_type, vector_data,
field_dimension * sizeof(uint64_t));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_FP32: {
float *vector_data = (float *)malloc(field_dimension * sizeof(float));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (float)(doc_id + j * 0.1);
}
zvec_doc_add_field_by_value(doc, field_name, field_type, vector_data,
field_dimension * sizeof(float));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_FP64: {
double *vector_data =
(double *)malloc(field_dimension * sizeof(double));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (double)(doc_id + j * 0.1);
}
zvec_doc_add_field_by_value(doc, field_name, field_type, vector_data,
field_dimension * sizeof(double));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_FP16: {
// FP16 needs special handling, simplified to FP32 here
float *vector_data = (float *)malloc(field_dimension * sizeof(float));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (float)(doc_id + j * 0.1);
}
zvec_doc_add_field_by_value(doc, field_name, field_type, vector_data,
field_dimension * sizeof(float));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_INT8: {
int8_t *vector_data =
(int8_t *)malloc(field_dimension * sizeof(int8_t));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (int8_t)((doc_id + j) % 256);
}
zvec_doc_add_field_by_value(doc, field_name, field_type, vector_data,
field_dimension * sizeof(int8_t));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_INT16: {
int16_t *vector_data =
(int16_t *)malloc(field_dimension * sizeof(int16_t));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (int16_t)((doc_id + j) % 65536);
}
zvec_doc_add_field_by_value(doc, field_name, field_type, vector_data,
field_dimension * sizeof(int16_t));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32: {
// Sparse vectors need special handling
uint32_t nnz = field_dimension > 0 ? field_dimension / 10
: 10; // Number of non-zero elements
size_t sparse_size =
sizeof(uint32_t) + nnz * (sizeof(uint32_t) + sizeof(float));
void *sparse_data = malloc(sparse_size);
if (sparse_data) {
uint32_t *data_ptr = (uint32_t *)sparse_data;
*data_ptr = nnz; // Set number of non-zero elements
uint32_t *indices = data_ptr + 1;
float *values = (float *)(indices + nnz);
for (uint32_t j = 0; j < nnz; j++) {
indices[j] = j * 10; // Index
values[j] = (float)(doc_id + j * 0.1); // Value
}
zvec_doc_add_field_by_value(doc, field_name, field_type, sparse_data,
sparse_size);
free(sparse_data);
}
break;
}
case ZVEC_DATA_TYPE_SPARSE_VECTOR_FP16: {
// Sparse FP16 vectors, simplified handling
uint32_t nnz = field_dimension > 0 ? field_dimension / 10 : 10;
size_t sparse_size =
sizeof(uint32_t) +
nnz * (sizeof(uint32_t) +
sizeof(float)); // Still use float for storage
void *sparse_data = malloc(sparse_size);
if (sparse_data) {
uint32_t *data_ptr = (uint32_t *)sparse_data;
*data_ptr = nnz;
uint32_t *indices = data_ptr + 1;
float *values = (float *)(indices + nnz);
for (uint32_t j = 0; j < nnz; j++) {
indices[j] = j * 10;
values[j] = (float)(doc_id + j * 0.1);
}
zvec_doc_add_field_by_value(doc, field_name, field_type, sparse_data,
sparse_size);
free(sparse_data);
}
break;
}
default:
// Unsupported data type
break;
}
// Remove reference to removed variable err
/*
if (err != ZVEC_OK) {
// Error handling: continue processing other fields
}
*/
}
// Free field names array (individual strings are freed by the API)
if (field_names) {
for (size_t i = 0; i < field_count; i++) {
free((char *)field_names[i]);
}
free(field_names);
}
return doc;
}
zvec_doc_t *zvec_test_create_doc_null(uint64_t doc_id,
const zvec_collection_schema_t *schema,
const char *pk) {
// Reuse create_doc function, but only process vector fields
zvec_doc_t *doc = zvec_doc_create();
if (!doc) return NULL;
// Set primary key
char *primary_key = pk ? strdup_safe(pk) : zvec_test_make_pk(doc_id);
if (primary_key) {
zvec_doc_set_pk(doc, primary_key);
free(primary_key);
}
// Only create data for vector fields
const char **field_names = NULL;
size_t field_count = 0;
zvec_error_code_t ret = zvec_collection_schema_get_all_field_names(
schema, &field_names, &field_count);
if (ret != ZVEC_OK || !field_names) {
zvec_doc_destroy(doc);
return NULL;
}
for (size_t i = 0; i < field_count; i++) {
const char *field_name = field_names[i];
const zvec_field_schema_t *field =
zvec_collection_schema_get_field(schema, field_name);
if (!field) continue;
zvec_data_type_t field_type = zvec_field_schema_get_data_type(field);
uint32_t field_dimension = zvec_field_schema_get_dimension(field);
// Only process specific vector type fields
if (field_type != ZVEC_DATA_TYPE_VECTOR_FP32 &&
field_type != ZVEC_DATA_TYPE_VECTOR_FP16 &&
field_type != ZVEC_DATA_TYPE_VECTOR_INT8 &&
field_type != ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32 &&
field_type != ZVEC_DATA_TYPE_SPARSE_VECTOR_FP16) {
continue;
}
zvec_error_code_t err = ZVEC_OK;
switch (field_type) {
case ZVEC_DATA_TYPE_VECTOR_FP32: {
float *vector_data = (float *)malloc(field_dimension * sizeof(float));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (float)(doc_id + j * 0.1);
}
err = zvec_doc_add_field_by_value(doc, field_name, field_type,
vector_data,
field_dimension * sizeof(float));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_FP64: {
double *vector_data =
(double *)malloc(field_dimension * sizeof(double));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (double)(doc_id + j * 0.1);
}
err = zvec_doc_add_field_by_value(doc, field_name, field_type,
vector_data,
field_dimension * sizeof(double));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_FP16: {
float *vector_data = (float *)malloc(field_dimension * sizeof(float));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (float)(doc_id + j * 0.1);
}
err = zvec_doc_add_field_by_value(doc, field_name, field_type,
vector_data,
field_dimension * sizeof(float));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_INT8: {
int8_t *vector_data =
(int8_t *)malloc(field_dimension * sizeof(int8_t));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (int8_t)(doc_id % 128);
}
err = zvec_doc_add_field_by_value(doc, field_name, field_type,
vector_data,
field_dimension * sizeof(int8_t));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_VECTOR_INT16: {
int16_t *vector_data =
(int16_t *)malloc(field_dimension * sizeof(int16_t));
if (vector_data) {
for (uint32_t j = 0; j < field_dimension; j++) {
vector_data[j] = (int16_t)(doc_id % 32768);
}
err = zvec_doc_add_field_by_value(doc, field_name, field_type,
vector_data,
field_dimension * sizeof(int16_t));
free(vector_data);
}
break;
}
case ZVEC_DATA_TYPE_SPARSE_VECTOR_FP16:
case ZVEC_DATA_TYPE_SPARSE_VECTOR_FP32: {
const size_t nnz = 100;
size_t sparse_size =
sizeof(size_t) + nnz * (sizeof(uint32_t) + sizeof(float));
char *sparse_data = (char *)malloc(sparse_size);
if (sparse_data) {
char *ptr = sparse_data;
*((size_t *)ptr) = nnz;
ptr += sizeof(size_t);
for (size_t j = 0; j < nnz; j++) {
*((uint32_t *)ptr) = (uint32_t)j;
ptr += sizeof(uint32_t);
*((float *)ptr) = (float)(doc_id + j * 0.1);
ptr += sizeof(float);
}
err = zvec_doc_add_field_by_value(doc, field_name, field_type,
sparse_data, sparse_size);
free(sparse_data);
}
break;
}
default:
break;
}
if (err != ZVEC_OK) {
// Free field names array before returning
if (field_names) {
for (size_t i = 0; i < field_count; i++) {
free((char *)field_names[i]);
}
free(field_names);
}
zvec_doc_destroy(doc);
return NULL;
}
}
// Free field names array (individual strings are freed by the API)
if (field_names) {
for (size_t i = 0; i < field_count; i++) {
free((char *)field_names[i]);
}
free(field_names);
}
return doc;
}
zvec_doc_t *zvec_test_create_doc_with_fields(
uint64_t doc_id, const char **field_names,
const zvec_data_type_t *field_types, size_t field_count, const char *pk) {
zvec_doc_t *doc = zvec_doc_create();
if (!doc) return NULL;
// Set primary key
char *primary_key = pk ? strdup_safe(pk) : zvec_test_make_pk(doc_id);
if (primary_key) {
zvec_doc_set_pk(doc, primary_key);
free(primary_key);
}
// Create data for specified fields
for (size_t i = 0; i < field_count; i++) {
zvec_error_code_t err = ZVEC_OK;
switch (field_types[i]) {
case ZVEC_DATA_TYPE_INT32:
err = zvec_doc_add_field_by_value(doc, field_names[i], field_types[i],
&(int32_t){(int32_t)doc_id},
sizeof(int32_t));
break;
case ZVEC_DATA_TYPE_STRING: {
char string_val[64];
snprintf(string_val, sizeof(string_val), "value_%llu",
(unsigned long long)doc_id);
err = zvec_doc_add_field_by_value(doc, field_names[i], field_types[i],
string_val, strlen(string_val));
break;
}
case ZVEC_DATA_TYPE_FLOAT:
err =
zvec_doc_add_field_by_value(doc, field_names[i], field_types[i],
&(float){(float)doc_id}, sizeof(float));
break;
case ZVEC_DATA_TYPE_VECTOR_FP32: {
float vector_data[128];
for (int j = 0; j < 128; j++) {
vector_data[j] = (float)(doc_id + j * 0.1);
}
err = zvec_doc_add_field_by_value(doc, field_names[i], field_types[i],
vector_data, sizeof(vector_data));
break;
}
default:
// Other types can be added here
break;
}
if (err != ZVEC_OK) {
zvec_doc_destroy(doc);
return NULL;
}
}
return doc;
}
// =============================================================================
// Index Parameter Creation Helper Functions Implementation
// =============================================================================
zvec_index_params_t *zvec_test_create_default_hnsw_params(void) {
zvec_index_params_t *params = zvec_index_params_create(ZVEC_INDEX_TYPE_HNSW);
if (!params) return NULL;
zvec_index_params_set_metric_type(params, ZVEC_METRIC_TYPE_IP);
zvec_index_params_set_hnsw_params(params, 16, 100);
return params;
}
zvec_index_params_t *zvec_test_create_default_flat_params(void) {
zvec_index_params_t *params = zvec_index_params_create(ZVEC_INDEX_TYPE_FLAT);
if (!params) return NULL;
zvec_index_params_set_metric_type(params, ZVEC_METRIC_TYPE_IP);
return params;
}
zvec_index_params_t *zvec_test_create_default_invert_params(
bool enable_optimize) {
zvec_index_params_t *params =
zvec_index_params_create(ZVEC_INDEX_TYPE_INVERT);
if (!params) return NULL;
zvec_index_params_set_invert_params(params, enable_optimize, enable_optimize);
return params;
}
// =============================================================================
// Field Schema Creation Helper Functions Implementation
// =============================================================================
zvec_field_schema_t *zvec_test_create_scalar_field(
const char *name, zvec_data_type_t data_type, bool nullable,
const zvec_index_params_t *invert_params) {
// Use the public API to create the field
zvec_field_schema_t *field =
zvec_field_schema_create(name, data_type, nullable, 0);
if (!field) return NULL;
if (invert_params) {
// Clone the index params using setter API
zvec_index_type_t type = zvec_index_params_get_type(invert_params);
zvec_index_params_t *cloned_params = zvec_index_params_create(type);
if (cloned_params) {
bool enable_range_opt, enable_wildcard;
zvec_index_params_get_invert_params(invert_params, &enable_range_opt,
&enable_wildcard);
zvec_index_params_set_invert_params(cloned_params, enable_range_opt,
enable_wildcard);
zvec_field_schema_set_index_params(field, cloned_params);
zvec_index_params_destroy(cloned_params);
}
}
return field;
}
zvec_field_schema_t *zvec_test_create_vector_field(
const char *name, zvec_data_type_t data_type, uint32_t dimension,
bool nullable, const zvec_index_params_t *vector_index_params) {
// Use the public API to create the field
zvec_field_schema_t *field =
zvec_field_schema_create(name, data_type, nullable, dimension);
if (!field) return NULL;
if (vector_index_params) {
// Clone the index params using setter API
zvec_index_type_t type = zvec_index_params_get_type(vector_index_params);
zvec_index_params_t *cloned_params = zvec_index_params_create(type);
if (cloned_params) {
int m, ef_construction;
m = zvec_index_params_get_hnsw_m(vector_index_params);
ef_construction =
zvec_index_params_get_hnsw_ef_construction(vector_index_params);
zvec_index_params_set_hnsw_params(cloned_params, m, ef_construction);
zvec_field_schema_set_index_params(field, cloned_params);
zvec_index_params_destroy(cloned_params);
}
}
return field;
}
zvec_field_schema_t *zvec_test_create_sparse_vector_field(
const char *name, zvec_data_type_t data_type, bool nullable,
const zvec_index_params_t *vector_index_params) {
// Use the public API to create the field
zvec_field_schema_t *field =
zvec_field_schema_create(name, data_type, nullable, 0);
if (!field) return NULL;
if (vector_index_params) {
// Clone the index params using setter API
zvec_index_type_t type = zvec_index_params_get_type(vector_index_params);
zvec_index_params_t *cloned_params = zvec_index_params_create(type);
if (cloned_params) {
int m, ef_construction;
m = zvec_index_params_get_hnsw_m(vector_index_params);
ef_construction =
zvec_index_params_get_hnsw_ef_construction(vector_index_params);
zvec_index_params_set_hnsw_params(cloned_params, m, ef_construction);
zvec_field_schema_set_index_params(field, cloned_params);
zvec_index_params_destroy(cloned_params);
}
}
return field;
}
// =============================================================================
// Memory Management Helper Functions Implementation
// =============================================================================
// Note: zvec_test_free_field_schemas is deprecated.
// Use zvec_field_schema_destroy() to free individual field schemas.
void zvec_test_free_strings(char **strings, size_t count) {
if (!strings) return;
for (size_t i = 0; i < count; i++) {
if (strings[i]) {
free(strings[i]);
}
}
free(strings);
}
// =============================================================================
// File System Helper Functions Implementation
// =============================================================================
/**
* @brief Delete directory and all its contents (wrapper function)
*
* @param dir_path Directory path
* @return int 0 for success, -1 for failure
*/
int zvec_test_delete_dir(const char *dir_path) {
if (!dir_path) {
return -1;
}
#ifdef _WIN32
// Windows platform implementation
char cmd[1024];
snprintf(cmd, sizeof(cmd), "rd /s /q \"%s\" >nul 2>&1", dir_path);
int result = system(cmd);
return (result == 0) ? 0 : -1;
#else
// Unix/Linux/macOS/iOS: pure C recursive removal (no system() call)
struct stat st;
if (stat(dir_path, &st) != 0) {
return -1; // path does not exist
}
if (!S_ISDIR(st.st_mode)) {
return unlink(dir_path); // regular file
}
DIR *dir = opendir(dir_path);
if (!dir) {
return -1;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char child[1024];
snprintf(child, sizeof(child), "%s/%s", dir_path, entry->d_name);
zvec_test_delete_dir(child); // recurse
}
closedir(dir);
return rmdir(dir_path);
#endif
}