use std::os::raw::c_void;
use crate::error::{check_error, to_cstring, Error, ErrorCode, Result};
use crate::query::{FlatQueryParams, FtsQueryParams, HnswQueryParams, IvfQueryParams};
use crate::query::Fts;
pub struct MultiQuery {
pub(crate) handle: *mut zvec_rust_sys::zvec_multi_query_t,
}
impl MultiQuery {
pub fn new() -> Result<Self> {
let handle = unsafe { zvec_rust_sys::zvec_multi_query_create() };
if handle.is_null() {
return Err(Error {
code: ErrorCode::InternalError,
message: "failed to create multi-query".into(),
});
}
Ok(MultiQuery { handle })
}
pub unsafe fn as_raw(&self) -> *mut zvec_rust_sys::zvec_multi_query_t {
self.handle
}
pub fn add_sub_query(&mut self, sub: &SubQuery) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_multi_query_add_sub_query(self.handle, sub.handle)
})
}
pub fn sub_query_count(&self) -> usize {
unsafe { zvec_rust_sys::zvec_multi_query_get_sub_query_count(self.handle) }
}
pub fn set_topk(&mut self, topk: i32) -> Result<()> {
check_error(unsafe { zvec_rust_sys::zvec_multi_query_set_topk(self.handle, topk) })
}
pub fn topk(&self) -> i32 {
unsafe { zvec_rust_sys::zvec_multi_query_get_topk(self.handle) }
}
pub fn set_filter(&mut self, filter: &str) -> Result<()> {
let c_filter = to_cstring(filter)?;
check_error(unsafe {
zvec_rust_sys::zvec_multi_query_set_filter(self.handle, c_filter.as_ptr())
})
}
pub fn set_include_vector(&mut self, include: bool) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_multi_query_set_include_vector(self.handle, include)
})
}
pub fn include_vector(&self) -> bool {
unsafe { zvec_rust_sys::zvec_multi_query_get_include_vector(self.handle) }
}
pub fn set_output_fields(&mut self, fields: &[&str]) -> Result<()> {
if fields.is_empty() {
return Ok(());
}
let c_fields: Vec<_> = fields
.iter()
.map(|f| to_cstring(f))
.collect::<Result<Vec<_>>>()?;
let c_ptrs: Vec<_> = c_fields.iter().map(|s| s.as_ptr()).collect();
check_error(unsafe {
zvec_rust_sys::zvec_multi_query_set_output_fields(
self.handle,
c_ptrs.as_ptr(),
c_ptrs.len(),
)
})
}
pub fn set_rerank_rrf(&mut self, rank_constant: i32) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_multi_query_set_rerank_rrf(self.handle, rank_constant)
})
}
pub fn set_rerank_weighted(&mut self, weights: &[f64]) -> Result<()> {
if weights.is_empty() {
return Err(Error {
code: ErrorCode::InvalidArgument,
message: "weights cannot be empty".into(),
});
}
check_error(unsafe {
zvec_rust_sys::zvec_multi_query_set_rerank_weighted(
self.handle,
weights.as_ptr(),
weights.len(),
)
})
}
}
impl Drop for MultiQuery {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe { zvec_rust_sys::zvec_multi_query_destroy(self.handle) };
}
}
}
unsafe impl Send for MultiQuery {}
pub struct SubQuery {
pub(crate) handle: *mut zvec_rust_sys::zvec_sub_query_t,
}
impl SubQuery {
pub fn new() -> Result<Self> {
let handle = unsafe { zvec_rust_sys::zvec_sub_query_create() };
if handle.is_null() {
return Err(Error {
code: ErrorCode::InternalError,
message: "failed to create sub-query".into(),
});
}
Ok(SubQuery { handle })
}
pub unsafe fn as_raw(&self) -> *mut zvec_rust_sys::zvec_sub_query_t {
self.handle
}
pub fn set_num_candidates(&mut self, n: i32) -> Result<()> {
check_error(unsafe { zvec_rust_sys::zvec_sub_query_set_num_candidates(self.handle, n) })
}
pub fn num_candidates(&self) -> i32 {
unsafe { zvec_rust_sys::zvec_sub_query_get_num_candidates(self.handle) }
}
pub fn set_field_name(&mut self, name: &str) -> Result<()> {
let c_name = to_cstring(name)?;
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_field_name(self.handle, c_name.as_ptr())
})
}
pub fn set_query_vector(&mut self, data: &[f32]) -> Result<()> {
if data.is_empty() {
return Err(Error {
code: ErrorCode::InvalidArgument,
message: "query vector cannot be empty".into(),
});
}
let bytes = std::mem::size_of_val(data);
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_query_vector(
self.handle,
data.as_ptr() as *const c_void,
bytes,
)
})
}
pub fn set_sparse_vector(&mut self, indices: &[u32], values: &[f32]) -> Result<()> {
if indices.len() != values.len() {
return Err(Error {
code: ErrorCode::InvalidArgument,
message: "indices and values must have the same length".into(),
});
}
if indices.is_empty() {
return Err(Error {
code: ErrorCode::InvalidArgument,
message: "sparse vector cannot be empty".into(),
});
}
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_sparse_vector(
self.handle,
indices.as_ptr(),
values.as_ptr(),
indices.len(),
)
})
}
pub fn set_sparse_indices(&mut self, indices: &[u32]) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_sparse_indices(
self.handle,
indices.as_ptr(),
indices.len(),
)
})
}
pub fn set_sparse_values(&mut self, values: &[f32]) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_sparse_values(
self.handle,
values.as_ptr(),
values.len(),
)
})
}
pub fn set_hnsw_params(&mut self, mut params: HnswQueryParams) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_hnsw_params(self.handle, params.handle)
})?;
params.handle = std::ptr::null_mut();
Ok(())
}
pub fn set_ivf_params(&mut self, mut params: IvfQueryParams) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_ivf_params(self.handle, params.handle)
})?;
params.handle = std::ptr::null_mut();
Ok(())
}
pub fn set_flat_params(&mut self, mut params: FlatQueryParams) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_flat_params(self.handle, params.handle)
})?;
params.handle = std::ptr::null_mut();
Ok(())
}
pub fn set_fts_params(&mut self, mut params: FtsQueryParams) -> Result<()> {
check_error(unsafe {
zvec_rust_sys::zvec_sub_query_set_fts_params(self.handle, params.handle)
})?;
params.handle = std::ptr::null_mut();
Ok(())
}
pub fn set_fts(&mut self, fts: &Fts) -> Result<()> {
check_error(unsafe { zvec_rust_sys::zvec_sub_query_set_fts(self.handle, fts.handle) })
}
}
impl Drop for SubQuery {
fn drop(&mut self) {
if !self.handle.is_null() {
unsafe { zvec_rust_sys::zvec_sub_query_destroy(self.handle) };
}
}
}
unsafe impl Send for SubQuery {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_and_drop_multi_query() {
let mq = MultiQuery::new().expect("create multi-query");
assert_eq!(mq.sub_query_count(), 0);
}
#[test]
fn create_and_drop_sub_query() {
let _sq = SubQuery::new().expect("create sub-query");
}
#[test]
fn multi_query_basic_setters() {
let mut mq = MultiQuery::new().expect("create multi-query");
mq.set_topk(20).expect("set topk");
assert_eq!(mq.topk(), 20);
mq.set_include_vector(true).expect("set include_vector");
assert!(mq.include_vector());
}
#[test]
fn add_sub_query_increments_count() {
let mut mq = MultiQuery::new().expect("create multi-query");
let mut sq = SubQuery::new().expect("create sub-query");
sq.set_field_name("vec").expect("set field name");
sq.set_query_vector(&[0.1, 0.2, 0.3, 0.4])
.expect("set query vector");
sq.set_num_candidates(50).expect("set num candidates");
mq.add_sub_query(&sq).expect("add sub-query");
assert_eq!(mq.sub_query_count(), 1);
}
#[test]
fn rerank_weighted_rejects_empty() {
let mut mq = MultiQuery::new().expect("create multi-query");
let err = mq.set_rerank_weighted(&[]).unwrap_err();
assert_eq!(err.code, ErrorCode::InvalidArgument);
}
#[test]
fn sub_query_set_fts() {
let mut sub = SubQuery::new().expect("create sub-query");
sub.set_field_name("content").expect("set field name");
let mut fts = Fts::new().expect("create fts payload");
fts.set_match_string("hello world").expect("set match string");
sub.set_fts(&fts).expect("set fts payload");
}
#[test]
fn sub_query_set_fts_params() {
let mut sub = SubQuery::new().expect("create sub-query");
sub.set_field_name("content").expect("set field name");
let params = FtsQueryParams::new(Some("AND")).expect("create fts params");
sub.set_fts_params(params).expect("set fts params");
}
}