manifold_vectors/
dense.rs

1//! Dense fixed-dimension vector storage with efficient access.
2
3use manifold::{
4    AccessGuard, ReadOnlyTable, ReadTransaction, ReadableTable, ReadableTableMetadata,
5    StorageError, Table, TableDefinition, TableError, WriteTransaction,
6};
7use std::ops::Deref;
8use uuid::Uuid;
9
10/// A table storing fixed-dimension dense vectors.
11pub struct VectorTable<'txn, const DIM: usize> {
12    table: Table<'txn, Uuid, [f32; DIM]>,
13}
14
15impl<'txn, const DIM: usize> VectorTable<'txn, DIM> {
16    /// Opens a vector table for writing.
17    pub fn open(txn: &'txn WriteTransaction, name: &str) -> Result<Self, TableError> {
18        let def: TableDefinition<Uuid, [f32; DIM]> = TableDefinition::new(name);
19        let table = txn.open_table(def)?;
20        Ok(Self { table })
21    }
22
23    /// Inserts a vector with the given key.
24    pub fn insert(&mut self, key: &Uuid, vector: &[f32; DIM]) -> Result<(), TableError> {
25        self.table.insert(key, vector)?;
26        Ok(())
27    }
28
29    /// Inserts multiple vectors in a single batch operation.
30    pub fn insert_batch(
31        &mut self,
32        items: &[(Uuid, [f32; DIM])],
33        sorted: bool,
34    ) -> Result<(), StorageError> {
35        self.table.insert_bulk(items.to_vec(), sorted)?;
36        Ok(())
37    }
38
39    /// Removes a vector by key.
40    ///
41    /// Returns the removed vector if it existed, or None if the key was not found.
42    pub fn remove(&mut self, key: &Uuid) -> Result<Option<VectorGuard<'_, DIM>>, StorageError> {
43        match self.table.remove(key) {
44            Ok(Some(guard)) => {
45                let value_cached = guard.value();
46                Ok(Some(VectorGuard {
47                    value_cached,
48                    _guard: guard,
49                }))
50            }
51            Ok(None) => Ok(None),
52            Err(e) => Err(e),
53        }
54    }
55
56    /// Removes multiple vectors in a single batch operation.
57    ///
58    /// Returns the number of vectors actually removed.
59    pub fn remove_bulk(&mut self, keys: &[Uuid]) -> Result<usize, StorageError> {
60        self.table.remove_bulk(keys.iter().copied())
61    }
62
63    /// Returns the number of vectors stored in this table.
64    pub fn len(&self) -> Result<u64, StorageError> {
65        self.table.len()
66    }
67
68    /// Returns `true` if the table contains no vectors.
69    pub fn is_empty(&self) -> Result<bool, StorageError> {
70        Ok(self.len()? == 0)
71    }
72}
73
74/// Read-only vector table providing efficient access.
75///
76/// This table leverages Manifold's fixed-width Value trait for arrays,
77/// which deserializes directly from memory-mapped pages.
78pub struct VectorTableRead<const DIM: usize> {
79    table: ReadOnlyTable<Uuid, [f32; DIM]>,
80}
81
82impl<const DIM: usize> VectorTableRead<DIM> {
83    /// Opens a vector table for reading.
84    pub fn open(txn: &ReadTransaction, name: &str) -> Result<Self, StorageError> {
85        let def: TableDefinition<Uuid, [f32; DIM]> = TableDefinition::new(name);
86        let table = txn.open_table(def).map_err(|e| match e {
87            TableError::Storage(s) => s,
88            _ => StorageError::Io(std::io::Error::other(e)),
89        })?;
90        Ok(Self { table })
91    }
92
93    /// Retrieves a vector by key.
94    ///
95    /// Returns a guard that holds the vector data cached from deserialization.
96    /// The vector is deserialized once when the guard is created.
97    pub fn get(&self, key: &Uuid) -> Result<Option<VectorGuard<'_, DIM>>, StorageError> {
98        Ok(self.table.get(key)?.map(VectorGuard::new))
99    }
100
101    /// Returns the number of vectors stored in this table.
102    pub fn len(&self) -> Result<u64, StorageError> {
103        self.table.len()
104    }
105
106    /// Returns `true` if the table contains no vectors.
107    pub fn is_empty(&self) -> Result<bool, StorageError> {
108        Ok(self.len()? == 0)
109    }
110
111    /// Iterates over all vectors in the table.
112    pub fn all_vectors(&self) -> Result<VectorIter<'_, DIM>, StorageError> {
113        Ok(VectorIter {
114            inner: self.table.iter()?,
115        })
116    }
117}
118
119/// A guard providing access to a stored vector.
120///
121/// The vector data is deserialized once when the guard is created,
122/// then cached for subsequent accesses.
123pub struct VectorGuard<'a, const DIM: usize> {
124    value_cached: [f32; DIM],
125    _guard: AccessGuard<'a, [f32; DIM]>,
126}
127
128impl<'a, const DIM: usize> VectorGuard<'a, DIM> {
129    fn new(guard: AccessGuard<'a, [f32; DIM]>) -> Self {
130        let value_cached = guard.value();
131        Self {
132            value_cached,
133            _guard: guard,
134        }
135    }
136
137    /// Returns a reference to the vector data.
138    pub fn value(&self) -> &[f32; DIM] {
139        &self.value_cached
140    }
141
142    /// Returns the vector data as a slice.
143    pub fn as_slice(&self) -> &[f32] {
144        &self.value_cached
145    }
146}
147
148impl<const DIM: usize> Deref for VectorGuard<'_, DIM> {
149    type Target = [f32; DIM];
150
151    fn deref(&self) -> &Self::Target {
152        &self.value_cached
153    }
154}
155
156/// Iterator over vectors in a `VectorTableRead`.
157pub struct VectorIter<'a, const DIM: usize> {
158    inner: manifold::Range<'a, Uuid, [f32; DIM]>,
159}
160
161impl<'a, const DIM: usize> Iterator for VectorIter<'a, DIM> {
162    type Item = Result<(Uuid, VectorGuard<'a, DIM>), StorageError>;
163
164    fn next(&mut self) -> Option<Self::Item> {
165        self.inner.next().map(|result| {
166            result
167                .map(|(key_guard, value_guard)| (key_guard.value(), VectorGuard::new(value_guard)))
168        })
169    }
170}