use super::index::HnswIndex;
use super::upsert::{self, UpsertResult};
use crate::validation::validate_dimension_match;
#[allow(dead_code)] pub(crate) struct DirectVectorWriter<'a> {
hnsw_index: &'a HnswIndex,
}
#[allow(dead_code)] impl<'a> DirectVectorWriter<'a> {
#[must_use]
pub(crate) fn new(hnsw_index: &'a HnswIndex) -> Self {
Self { hnsw_index }
}
pub(crate) fn write_batch_direct(
&self,
vectors: &[(u64, &[f32])],
) -> crate::error::Result<Vec<UpsertResult>> {
if vectors.is_empty() {
return Ok(Vec::new());
}
for (_, vector) in vectors {
validate_dimension_match(self.hnsw_index.dimension, vector.len())?;
}
let ids: Vec<u64> = vectors.iter().map(|(id, _)| *id).collect();
let results = upsert::upsert_mapping_batch(
&self.hnsw_index.mappings,
&self.hnsw_index.vectors,
self.hnsw_index.enable_vector_storage,
&ids,
);
if self.hnsw_index.enable_vector_storage {
self.write_to_contiguous(vectors, &results)?;
}
Ok(results)
}
fn write_to_contiguous(
&self,
vectors: &[(u64, &[f32])],
results: &[UpsertResult],
) -> crate::error::Result<()> {
let inner = self.hnsw_index.inner.read();
inner.with_contiguous_vectors_mut(|storage| {
let max_idx = results.iter().map(|r| r.idx).max().unwrap_or(0);
storage.ensure_capacity(max_idx + 1)?;
for ((_, vector), result) in vectors.iter().zip(results.iter()) {
storage.insert_at(result.idx, vector)?;
}
Ok(())
})
}
#[allow(clippy::unnecessary_wraps)] pub(crate) fn sync_to_sharded(&self, results: &[UpsertResult]) -> crate::error::Result<()> {
if !self.hnsw_index.enable_vector_storage || results.is_empty() {
return Ok(());
}
let inner = self.hnsw_index.inner.read();
let pairs: Vec<(usize, Vec<f32>)> = inner.with_contiguous_vectors_read(|storage| {
let mut out = Vec::with_capacity(results.len());
for result in results {
if let Some(vec) = storage.get(result.idx) {
out.push((result.idx, vec.to_vec()));
}
}
out
});
self.hnsw_index.vectors.insert_batch(pairs);
Ok(())
}
}