weavatrix-search-vector 0.3.1

Persistent, mutable, bounded vector candidate search for Rust and Weavatrix
Documentation
use crate::error::SearchError;
use crate::vector::{MAX_ROUTING_PROBES as VECTOR_MAX_ROUTING_PROBES, ROUTING_PROBES};

/// Completeness policy for filtered approximate search.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum FilterSearchPolicy {
    /// Apply the predicate while traversing HNSW and return the candidates
    /// reached without a full vector scan.
    Traversal,
    /// Use traversal filtering first, then use the exact oracle only when the
    /// graph cannot supply the requested number of accepted results.
    #[default]
    ExactFallback,
}

/// Per-query approximation policy.
///
/// Increasing `expansion` explores more graph nodes. Increasing
/// `routing_probes` recovers more candidates from nearby deterministic routing
/// buckets, which is useful when graph expansion alone no longer improves
/// recall.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SearchPolicy {
    /// Minimum number of graph candidates retained per replica.
    pub expansion: usize,
    /// Number of deterministic routing buckets probed after graph traversal.
    ///
    /// Values from `1` through [`Self::MAX_ROUTING_PROBES`] are supported.
    pub routing_probes: usize,
}

impl SearchPolicy {
    /// Largest supported routing recovery width.
    pub const MAX_ROUTING_PROBES: usize = VECTOR_MAX_ROUTING_PROBES;

    /// Creates the low-overhead default policy used by
    /// [`crate::VectorIndex::search`].
    #[must_use]
    pub const fn new(expansion: usize) -> Self {
        Self {
            expansion,
            routing_probes: ROUTING_PROBES,
        }
    }

    /// Creates a stronger deterministic recovery policy for recall-sensitive
    /// queries.
    #[must_use]
    pub const fn high_recall(expansion: usize) -> Self {
        Self {
            expansion,
            routing_probes: 12,
        }
    }

    /// Changes only the routing recovery width.
    #[must_use]
    pub const fn with_routing_probes(mut self, routing_probes: usize) -> Self {
        self.routing_probes = routing_probes;
        self
    }

    pub(crate) fn validate(self) -> Result<Self, SearchError> {
        if self.expansion == 0 {
            return Err(SearchError::InvalidConfig(
                "search policy expansion must be greater than zero",
            ));
        }
        if self.routing_probes == 0 || self.routing_probes > Self::MAX_ROUTING_PROBES {
            return Err(SearchError::InvalidConfig(
                "search policy routing_probes must be between 1 and 470",
            ));
        }
        Ok(self)
    }
}