Skip to main content

uqa_storage/hnsw_index/
canonical.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Canonical reads use active raw vectors, independently of graph navigation and normalization.
8
9use super::HNSWIndex;
10use crate::{
11    read_control::StorageReadControl,
12    vector_index::{copy_vector, ordinal_count, VectorRead},
13    StorageBackendResult,
14};
15use std::ops::Bound::{Excluded, Unbounded};
16use uqa_core::{memory::BudgetedVec, DocId};
17
18impl VectorRead for HNSWIndex {
19    fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()> {
20        control.check()
21    }
22    fn dimensions(&self) -> u32 {
23        self.dimensions
24    }
25    fn next_document_after(
26        &self,
27        after: Option<DocId>,
28        control: &StorageReadControl,
29    ) -> StorageBackendResult<Option<DocId>> {
30        control.check()?;
31        Ok(self
32            .active
33            .range((
34                after.map_or(Unbounded, |document| Excluded((document, u32::MAX))),
35                Unbounded,
36            ))
37            .next()
38            .map(|(key, _)| key.0))
39    }
40    fn document_vector_count(
41        &self,
42        document: DocId,
43        control: &StorageReadControl,
44    ) -> StorageBackendResult<u64> {
45        ordinal_count(
46            self.active
47                .range((document, 0)..=(document, u32::MAX))
48                .map(|(key, _)| key.1),
49            control,
50        )
51    }
52    fn read_vector(
53        &self,
54        document: DocId,
55        ordinal: u32,
56        control: &StorageReadControl,
57    ) -> StorageBackendResult<Option<BudgetedVec<f32>>> {
58        control.check()?;
59        let Some(node) = self.active.get(&(document, ordinal)) else {
60            return Ok(None);
61        };
62        let node = self
63            .nodes
64            .get(node)
65            .filter(|node| {
66                !node.deleted && node.doc_id == document && node.vector_ordinal == ordinal
67            })
68            .ok_or_else(|| {
69                crate::mvcc::VersionError::InvalidEncoding(
70                    "HNSW canonical identity has no active raw vector",
71                )
72                .into_storage_error()
73            })?;
74        copy_vector(&node.raw_vector, control).map(Some)
75    }
76}