feox-vector 0.1.1

Embedded vector store on FeOxDB - exact and ANN cosine search with metadata filtering
Documentation
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

use arc_swap::ArcSwapOption;
use feox_ann::AnnIndex;
use scc::HashIndex;

use crate::filter::MetadataFilterIndex;

pub(crate) struct AnnSnapshot {
    pub(crate) index: AnnIndex,
    pub(crate) filters: MetadataFilterIndex,
}

#[derive(Default)]
pub(crate) struct AnnRegistry {
    scopes: HashIndex<String, Arc<AnnScope>>,
}

impl AnnRegistry {
    pub(crate) fn scope(&self, key: &str) -> Arc<AnnScope> {
        if let Some(scope) = self.scopes.peek_with(key, |_, scope| scope.clone()) {
            return scope;
        }
        self.scopes
            .entry(key.to_string())
            .or_insert_with(|| Arc::new(AnnScope::default()))
            .get()
            .clone()
    }

    pub(crate) fn mark_dirty_if_present(&self, key: &str) {
        if let Some(scope) = self.scopes.peek_with(key, |_, scope| scope.clone()) {
            scope.mark_dirty();
        }
    }
}

pub(crate) struct AnnScope {
    snapshot: ArcSwapOption<AnnSnapshot>,
    dirty: AtomicBool,
    rebuilding: AtomicBool,
}

impl Default for AnnScope {
    fn default() -> Self {
        Self {
            snapshot: ArcSwapOption::from(None),
            dirty: AtomicBool::new(false),
            rebuilding: AtomicBool::new(false),
        }
    }
}

impl AnnScope {
    pub(crate) fn snapshot(&self) -> Option<Arc<AnnSnapshot>> {
        self.snapshot.load_full()
    }

    pub(crate) fn query_snapshot(&self) -> Option<Arc<AnnSnapshot>> {
        if self.requires_exact_query() {
            return None;
        }
        let snapshot = self.snapshot.load_full();
        if self.requires_exact_query() {
            return None;
        }
        snapshot
    }

    pub(crate) fn mark_dirty(&self) {
        self.dirty.store(true, Ordering::Release);
    }

    fn requires_exact_query(&self) -> bool {
        self.dirty.load(Ordering::Acquire) || self.rebuilding.load(Ordering::Acquire)
    }

    pub(crate) fn begin_rebuild(&self) -> bool {
        if self
            .rebuilding
            .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
            .is_ok()
        {
            self.dirty.store(false, Ordering::Release);
            return true;
        }
        false
    }

    pub(crate) fn publish(&self, snapshot: AnnSnapshot) {
        self.snapshot.store(Some(Arc::new(snapshot)));
        self.rebuilding.store(false, Ordering::Release);
    }

    pub(crate) fn finish_failed_rebuild(&self) {
        self.dirty.store(true, Ordering::Release);
        self.rebuilding.store(false, Ordering::Release);
    }
}