pub struct VecDex<K, D, S: Scalar = f32>{ /* private fields */ }Expand description
A persistent, disk-backed approximate nearest-neighbor index using the HNSW (Hierarchical Navigable Small World) algorithm.
Type parameters:
K: user-facing key type.D: distance metric (L2,Cosine,InnerProduct).S: scalar type for vector components (f32orf64, defaultf32).
Each mutation or bulk-insert chunk uses one atomic engine batch for rows and graph state. This preserves their consistency across recovery under the ownership contract below; it does not imply power-loss durability for every successful call.
§Handle ownership
Serde deserialization and from_meta restore a handle
to the same storage, with independent in-memory caches. Recovery replaces
the active handle: retire the previous handle before using the restored one
for mutations. Alternating writes through restored aliases can corrupt
counters or index state, even when those writes are serialized.
While mutations occur, route every read and write through one live instance
(for example, share it behind a Mutex or RwLock). Multiple independent
handles may read the same index only while no handle mutates it.
Implementations§
Source§impl<K, D, S> VecDex<K, D, S>
impl<K, D, S> VecDex<K, D, S>
Sourcepub fn new_in(ns: &Namespace, config: HnswConfig) -> Result<Self>
pub fn new_in(ns: &Namespace, config: HnswConfig) -> Result<Self>
new placed in ns — every internal component
lands in the same namespace (a composite never spans namespaces).
Sourcepub fn new(config: HnswConfig) -> Result<Self>
pub fn new(config: HnswConfig) -> Result<Self>
Creates a new, empty VecDex with the given configuration.
§Errors
VsdbError::InvalidConfig unless dim > 0, m >= 2,
m_max0 >= m (else base-layer nodes have no edges and become
unreachable) and ef_construction > 0 (else a layer search returns
no candidates). VsdbError::ReadOnly in read-only mode.
Sourcepub fn instance_id(&self) -> InstanceId
pub fn instance_id(&self) -> InstanceId
Returns the unique instance ID.
Sourcepub fn save_meta(&self) -> Result<InstanceId>
pub fn save_meta(&self) -> Result<InstanceId>
Persists this instance’s metadata to disk so that it can be
recovered later via from_meta.
The metadata is create-time constant (single handle + creation config), so calling this once after creation is sufficient for the lifetime of the instance.
Sourcepub fn from_meta(instance_id: impl Into<InstanceId>) -> Result<Self>
pub fn from_meta(instance_id: impl Into<InstanceId>) -> Result<Self>
Recovers a VecDex from previously saved metadata.
Hydrates runtime caches from persisted rows and graph state. Each mutation or bulk-insert chunk commits those rows together; recovery does not rebuild the graph.
This replaces the active handle; it does not share caches with existing aliases. The handle ownership contract also applies to direct serde deserialization.
Sourcepub fn set_ef_search(&mut self, ef: usize) -> Result<()>
pub fn set_ef_search(&mut self, ef: usize) -> Result<()>
Updates the default search beam width (persisted).
§Errors
VsdbError::ReadOnly in read-only mode, or the commit error;
nothing changes in either case.
Sourcepub fn get(&self, key: &K) -> Option<Vec<S>>
pub fn get(&self, key: &K) -> Option<Vec<S>>
Returns the vector associated with the given key, if it exists.
Sourcepub fn contains_key(&self, key: &K) -> bool
pub fn contains_key(&self, key: &K) -> bool
Returns true if the index contains the given key.
Sourcepub fn iter(&self) -> impl Iterator<Item = (K, Vec<S>)> + '_
pub fn iter(&self) -> impl Iterator<Item = (K, Vec<S>)> + '_
Returns an iterator over all (key, vector) pairs.
Sourcepub fn clear(&mut self) -> Result<()>
pub fn clear(&mut self) -> Result<()>
Clears all indexed data.
The wipe (one engine-level range tombstone) and the reset graph
state row — which preserves the live ef_search, also across a
later restore — commit in one atomic engine write batch: a
crash can never expose a partially-cleared index.
§Errors
VsdbError::ReadOnly in read-only mode, or the commit error;
nothing changes in either case.
Sourcepub fn insert(&mut self, key: &K, vector: &[S]) -> Result<()>
pub fn insert(&mut self, key: &K, vector: &[S]) -> Result<()>
Inserts a vector associated with a user key.
If the key already exists, removal of the old node and insertion of the replacement are staged in the same atomic batch.
All rows of the insert proper — the vector, both key mappings, the node info, every adjacency update, and the graph state — are committed through one atomic engine batch.
§Errors
If the vector dimension mismatches the index configuration, or if the batch commit fails (in which case neither the on-disk state nor the in-memory state is modified).
Sourcepub fn insert_batch(&mut self, items: &[(K, Vec<S>)]) -> Result<()>
pub fn insert_batch(&mut self, items: &[(K, Vec<S>)]) -> Result<()>
Inserts a batch of (key, vector) pairs.
Semantically equivalent to calling insert in a
loop, but the inserts are staged in chunks that share one
transaction (and thus one atomic engine write batch) each —
amortizing the per-commit cost, which dominates bulk loads.
Pre-existing keys are removed and reinserted inside their chunk’s
atomic transaction, and duplicate keys inside items collapse to
the last occurrence.
Sourcepub fn search(&self, query: &[S], k: usize) -> Result<Vec<(K, S)>>
pub fn search(&self, query: &[S], k: usize) -> Result<Vec<(K, S)>>
Searches for the k nearest neighbors of the query vector.
Sourcepub fn search_ef(&self, query: &[S], k: usize, ef: usize) -> Result<Vec<(K, S)>>
pub fn search_ef(&self, query: &[S], k: usize, ef: usize) -> Result<Vec<(K, S)>>
Searches with a custom ef (beam width) for recall/speed tradeoff.
Sourcepub fn search_with_filter(
&self,
query: &[S],
k: usize,
predicate: impl Fn(&K) -> bool,
) -> Result<Vec<(K, S)>>
pub fn search_with_filter( &self, query: &[S], k: usize, predicate: impl Fn(&K) -> bool, ) -> Result<Vec<(K, S)>>
Searches with a key predicate evaluated during beam search.
Non-matching nodes still participate in graph traversal to maintain
connectivity, but are excluded from the result set. The search keeps
expanding until it holds max(ef, k) matching candidates and the
frontier is farther than the worst of them, so a selective predicate
costs more visited nodes (roughly ∝ 1 / selectivity) rather than
fewer results; a predicate that almost nothing satisfies stops at a
visit cap of max(64 × max(ef, k), 4096) evaluated nodes.
Use search_ef_with_filter to trade
recall against work explicitly.
Sourcepub fn search_ef_with_filter(
&self,
query: &[S],
k: usize,
ef: usize,
predicate: impl Fn(&K) -> bool,
) -> Result<Vec<(K, S)>>
pub fn search_ef_with_filter( &self, query: &[S], k: usize, ef: usize, predicate: impl Fn(&K) -> bool, ) -> Result<Vec<(K, S)>>
Filtered search with a custom ef (beam width).
Sourcepub fn remove(&mut self, key: &K) -> Result<bool>
pub fn remove(&mut self, key: &K) -> Result<bool>
Removes a vector by user key. Returns true if the key existed.
Former neighbors of the removed node are reconnected to each other (best-effort) to preserve graph connectivity. Every row update — edge rewires, row removals, and the graph state — is committed through one atomic engine batch.
§Errors
If the batch commit fails, neither the on-disk state nor the in-memory state is modified.
Sourcepub fn compact(&mut self) -> Result<()>
pub fn compact(&mut self) -> Result<()>
Rebuilds the HNSW graph from the existing vectors.
Useful after many deletions to restore graph quality and recall. Vectors are re-inserted in random order for better graph quality.
The whole rebuild is staged through one wiped transaction: the range tombstone and every row of the new graph commit in a single atomic engine write batch, so a crash (or an error return) leaves either the old graph or the new one — never anything in between. This is a cold maintenance API: the new graph is staged in memory before the commit, so expect transient memory proportional to the index size.
Trait Implementations§
Source§impl<'de, K, D, S> Deserialize<'de> for VecDex<K, D, S>
impl<'de, K, D, S> Deserialize<'de> for VecDex<K, D, S>
Source§fn deserialize<De>(deserializer: De) -> Result<Self, De::Error>where
De: Deserializer<'de>,
fn deserialize<De>(deserializer: De) -> Result<Self, De::Error>where
De: Deserializer<'de>,
Auto Trait Implementations§
impl<K, D, S = f32> !Freeze for VecDex<K, D, S>
impl<K, D, S = f32> !RefUnwindSafe for VecDex<K, D, S>
impl<K, D, S = f32> !UnwindSafe for VecDex<K, D, S>
impl<K, D, S> Send for VecDex<K, D, S>
impl<K, D, S> Sync for VecDex<K, D, S>
impl<K, D, S> Unpin for VecDex<K, D, S>
impl<K, D, S> UnsafeUnpin for VecDex<K, D, S>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> KeyDe for Twhere
T: DeserializeOwned,
impl<T> KeyDe for Twhere
T: DeserializeOwned,
Source§impl<T> KeyEn for Twhere
T: Serialize,
impl<T> KeyEn for Twhere
T: Serialize,
Source§fn try_encode_key(&self) -> Result<Vec<u8>, VsdbError>
fn try_encode_key(&self) -> Result<Vec<u8>, VsdbError>
Err only if the
Serialize implementation is broken — see module-level trust
model for details.