Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
653 changes: 653 additions & 0 deletions .github/workflows/09-release-build.yml

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
cmake_minimum_required(VERSION 3.13)
# Zvec bundles Apache Arrow 21.0.0, which requires CMake >= 3.26 to build its
# bundled Apache Thrift dependency. Set this here so users get a clear error
# upfront instead of failing after a long build (Issue #468).
cmake_minimum_required(VERSION 3.26)
cmake_policy(SET CMP0077 NEW)
project(zvec)
set(CC_CXX_STANDARD 17)
Expand Down
39 changes: 39 additions & 0 deletions cmake/zvecConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# zvecConfig.cmake.in - CMake package configuration template
# This file is processed by configure_package_config_file() to generate
# the final zvecConfig.cmake installed with the library.

@PACKAGE_INIT@

# Require CMake 3.13 or newer
cmake_minimum_required(VERSION 3.13)

# Set zvec root directory
set(ZVEC_ROOT_DIR "@PACKAGE_CMAKE_INSTALL_PREFIX@")

# Include imported targets (shared libraries)
include("@PACKAGE_CMAKE_INSTALL_LIBDIR@/cmake/zvec/zvec_c_apiTargets.cmake" OPTIONAL RESULT_VARIABLE _zvec_c_api_targets)
if(_zvec_c_api_targets)
message(STATUS "Loaded zvec_c_api targets from ${_zvec_c_api_targets}")
endif()

# Alias for convenience (zvec::zvec -> zvec::zvec_c_api)
if(TARGET zvec::zvec_c_api AND NOT TARGET zvec::zvec)
add_library(zvec::zvec ALIAS zvec::zvec_c_api)
message(STATUS "Created alias zvec::zvec -> zvec::zvec_c_api")
endif()

# Helper function to print zvec info
function(zvec_info)
message(STATUS "zvec version: @ZVEC_VERSION_MAJOR@.@ZVEC_VERSION_MINOR@.@ZVEC_VERSION_PATCH@")
message(STATUS "zvec include dir: ${ZVEC_ROOT_DIR}/@CMAKE_INSTALL_INCLUDEDIR@")
message(STATUS "zvec library dir: ${ZVEC_ROOT_DIR}/@CMAKE_INSTALL_LIBDIR@")
endfunction()

# Mark package as found
set(zvec_FOUND TRUE)
set(ZVEC_FOUND TRUE)

# Provide legacy variables for backwards compatibility
set(ZVEC_INCLUDE_DIRS "${ZVEC_ROOT_DIR}/@CMAKE_INSTALL_INCLUDEDIR@")
set(ZVEC_LIBRARY_DIRS "${ZVEC_ROOT_DIR}/@CMAKE_INSTALL_LIBDIR@")
set(ZVEC_LIBRARIES zvec::zvec_c_api)
56 changes: 56 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ function(zvec_add_all_in_one_shared TARGET_NAME OUTPUT_NAME)
endif()

install(TARGETS ${TARGET_NAME}
EXPORT ${TARGET_NAME}Targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
Expand All @@ -167,3 +168,58 @@ if(BUILD_ZVEC_SHARED)
zvec_turbo
)
endif()

# =============================================================================
# CMake Package Configuration (for find_package(zvec))
# =============================================================================

# Only install package config if shared libraries are built
if(BUILD_ZVEC_CORE_SHARED OR BUILD_ZVEC_SHARED)
include(CMakePackageConfigHelpers)

# Generate package version file
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfigVersion.cmake"
VERSION "${ZVEC_VERSION_MAJOR}.${ZVEC_VERSION_MINOR}.${ZVEC_VERSION_PATCH}"
COMPATIBILITY SameMajorVersion
)

# Generate package config from template
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/zvecConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)

# Install package config files
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)
endif()

# Export targets from build tree (for find_package(zvec) without install)
if(BUILD_ZVEC_CORE_SHARED)
export(EXPORT zvec_core_sharedTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/zvec_core_sharedTargets.cmake"
NAMESPACE zvec::
)
install(EXPORT zvec_core_sharedTargets
FILE zvec_core_sharedTargets.cmake
NAMESPACE zvec::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)
endif()

if(BUILD_ZVEC_SHARED)
export(EXPORT zvec_sharedTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/zvec_sharedTargets.cmake"
NAMESPACE zvec::
)
install(EXPORT zvec_sharedTargets
FILE zvec_sharedTargets.cmake
NAMESPACE zvec::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)
endif()
40 changes: 40 additions & 0 deletions src/binding/c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ endif()

# Install shared library
install(TARGETS zvec_c_api
EXPORT zvec_c_apiTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
Expand All @@ -254,3 +255,42 @@ install(TARGETS zvec_c_api
install(FILES ${PROJECT_SOURCE_DIR}/src/include/zvec/c_api.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/zvec
)

# =============================================================================
# CMake Package Configuration (for find_package(zvec))
# =============================================================================

# Export targets for use from build tree
export(EXPORT zvec_c_apiTargets
FILE "${CMAKE_CURRENT_BINARY_DIR}/zvec_c_apiTargets.cmake"
NAMESPACE zvec::
)

# Install targets for use from install tree
install(EXPORT zvec_c_apiTargets
FILE zvec_c_apiTargets.cmake
NAMESPACE zvec::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)

# Generate & install package version file
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfigVersion.cmake"
VERSION "${ZVEC_VERSION_MAJOR}.${ZVEC_VERSION_MINOR}.${ZVEC_VERSION_PATCH}"
COMPATIBILITY SameMajorVersion
)

# Generate package config from template
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/cmake/zvecConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfig.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)

# Install package config files
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/zvecConfigVersion.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/zvec
)
94 changes: 94 additions & 0 deletions src/binding/c/c_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3497,6 +3497,45 @@ zvec_error_code_t zvec_doc_add_field_by_struct(zvec_doc_t *doc,
return ZVEC_OK;)
}

// ---------------------------------------------------------------------------
// Sparse Vector Document Field Helper (Issue #443)
// ---------------------------------------------------------------------------

zvec_error_code_t zvec_doc_set_sparse_vector(
zvec_doc_t *doc,
const char *field_name,
const uint32_t *indices,
const float *values,
size_t count) {
if (!doc || !field_name) {
set_last_error("Invalid arguments: null pointer");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
if ((!indices || !values) && count > 0) {
set_last_error("Indices or values pointer is null while count > 0");
return ZVEC_ERROR_INVALID_ARGUMENT;
}

ZVEC_TRY_RETURN_ERROR(
"Failed to set sparse vector field",
auto *doc_ptr = reinterpret_cast<zvec::Doc *>(doc);
std::string name(field_name);

if (count == 0) {
// Set empty sparse vector (pair of empty vectors)
doc_ptr->set(name,
std::pair<std::vector<uint32_t>, std::vector<float>>{});
return ZVEC_OK;
}

std::vector<uint32_t> idx_vec(indices, indices + count);
std::vector<float> val_vec(values, values + count);
doc_ptr->set(name,
std::pair<std::vector<uint32_t>, std::vector<float>>{
std::move(idx_vec), std::move(val_vec)});
return ZVEC_OK;)
}

const char *zvec_doc_get_pk_pointer(const zvec_doc_t *doc) {
if (!doc) return nullptr;
auto doc_ptr = reinterpret_cast<const zvec::Doc *>(doc);
Expand Down Expand Up @@ -5797,6 +5836,61 @@ zvec_error_code_t zvec_multi_query_set_rerank_weighted(
return ZVEC_OK;
}

// ---------------------------------------------------------------------------
// Standalone Reranker API (Issue #443)
// ---------------------------------------------------------------------------
// Internal wrapper: holds a copy of RerankParams.
struct RerankerWrapper {
zvec::reranker::RerankParams params;
};

zvec_reranker_t *zvec_reranker_create_rrf(int rank_constant) {
ZVEC_TRY_RETURN_NULL("Failed to create RRF reranker",
auto *wrapper = new RerankerWrapper();
wrapper->params = zvec::reranker::RrfParams{rank_constant};
return reinterpret_cast<zvec_reranker_t *>(wrapper);)
return nullptr;
}

zvec_reranker_t *zvec_reranker_create_weighted(const double *weights,
size_t weight_count) {
if (!weights && weight_count > 0) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT,
"Weights pointer is null");
return nullptr;
}
ZVEC_TRY_RETURN_NULL(
"Failed to create Weighted reranker",
auto *wrapper = new RerankerWrapper();
wrapper->params = zvec::reranker::WeightedParams{
std::vector<double>(weights, weights + weight_count)};
return reinterpret_cast<zvec_reranker_t *>(wrapper);)
return nullptr;
}

void zvec_reranker_destroy(zvec_reranker_t *reranker) {
if (reranker) {
delete reinterpret_cast<RerankerWrapper *>(reranker);
}
}

zvec_error_code_t zvec_multi_query_set_reranker(
zvec_multi_query_t *query, const zvec_reranker_t *reranker) {
if (!query) {
SET_LAST_ERROR(ZVEC_ERROR_INVALID_ARGUMENT, "Query pointer is null");
return ZVEC_ERROR_INVALID_ARGUMENT;
}
auto *mq = reinterpret_cast<zvec::MultiQuery *>(query);
if (!reranker) {
// Clear reranking by setting default RRF with default constant
mq->rerank = zvec::reranker::RrfParams{60};
return ZVEC_OK;
}
auto *wrapper = reinterpret_cast<const RerankerWrapper *>(reranker);
mq->rerank = wrapper->params;
return ZVEC_OK;
}

// =============================================================================
// MultiVectorQuery Implementation
// =============================================================================
Expand Down
23 changes: 21 additions & 2 deletions src/db/index/segment/segment.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1992,6 +1992,9 @@ Status SegmentImpl::create_scalar_index(const std::vector<std::string> &columns,
auto new_segment_meta = std::make_shared<SegmentMeta>(*segment_meta_);
if (fields.empty()) {
*segment_meta = new_segment_meta;
// Fix: return current indexer pointer so the caller does not
// pass nullptr to reload_scalar_index (Issue #427).
*scalar_indexer = invert_indexers_;
return Status::OK();
}

Expand Down Expand Up @@ -2121,7 +2124,9 @@ Status SegmentImpl::drop_scalar_index(const std::vector<std::string> &columns,

if (fields.empty()) {
*segment_meta = new_segment_meta;
*scalar_indexer = nullptr;
// Fix: return current indexer pointer so the caller does not
// pass nullptr to reload_scalar_index (Issue #427).
*scalar_indexer = invert_indexers_;
return Status::OK();
}

Expand Down Expand Up @@ -2167,7 +2172,21 @@ Status SegmentImpl::reload_scalar_index(
segment_meta_ = segment_meta;

if (!scalar_indexer) {
// no need to reload inverted indexer
// Fix: clean up the old inverted indexer directory so that a subsequent
// create_index() can create a fresh one without "Column family already
// exists" errors (Issue #427).
if (invert_indexers_) {
auto old_dir = invert_indexers_->working_dir();
invert_indexers_ = nullptr;
FileHelper::RemoveDirectory(old_dir);
}
return Status::OK();
}

// Identity-pointer guard: avoid accidentally deleting an indexer's own
// directory during reload (e.g. if scalar_indexer == invert_indexers_).
if (invert_indexers_ && invert_indexers_ == scalar_indexer) {
LOG_WARN("reload_scalar_index: identity-pointer guard triggered, skipping");
return Status::OK();
}

Expand Down
Loading