nodedb_vector/collection/
lifecycle_insert_ops.rs1use nodedb_types::Surrogate;
6
7use super::lifecycle::VectorCollection;
8use crate::error::{VectorError, check_dim};
9
10impl VectorCollection {
11 pub fn insert(&mut self, vector: Vec<f32>) -> Result<u32, VectorError> {
18 check_dim(self.dim, vector.len())?;
19 let id = self.next_id;
20 match &mut self.ivf {
21 Some(ivf) => ivf.insert_with_id(id, vector)?,
22 None => {
23 self.growing.insert(vector)?;
24 }
25 }
26 self.next_id += 1;
27 Ok(id)
28 }
29
30 pub fn insert_with_surrogate(
49 &mut self,
50 vector: Vec<f32>,
51 surrogate: Surrogate,
52 ) -> Result<u32, VectorError> {
53 check_dim(self.dim, vector.len())?;
54 if surrogate != Surrogate::ZERO
55 && let Some(old) = self.surrogate_to_local.get(&surrogate).copied()
56 {
57 self.delete_inner(old);
58 self.surrogate_map.remove(&old);
59 }
60 let id = self.insert(vector)?;
61 if surrogate != Surrogate::ZERO {
62 self.surrogate_map.insert(id, surrogate);
63 self.surrogate_to_local.insert(surrogate, id);
64 }
65 Ok(id)
66 }
67
68 pub fn insert_batch_with_surrogates(
76 &mut self,
77 vectors: &[Vec<f32>],
78 surrogates: &[Surrogate],
79 ) -> Result<Vec<u32>, VectorError> {
80 for v in vectors {
81 check_dim(self.dim, v.len())?;
82 }
83 let mut ids = Vec::with_capacity(vectors.len());
84 for (i, v) in vectors.iter().enumerate() {
85 let surrogate = surrogates.get(i).copied().unwrap_or(Surrogate::ZERO);
86 ids.push(self.insert_with_surrogate(v.clone(), surrogate)?);
87 }
88 Ok(ids)
89 }
90
91 pub fn insert_multi_vector(
98 &mut self,
99 vectors: &[&[f32]],
100 document_surrogate: Surrogate,
101 ) -> Result<Vec<u32>, VectorError> {
102 for v in vectors {
103 check_dim(self.dim, v.len())?;
104 }
105 let mut ids = Vec::with_capacity(vectors.len());
106 for &v in vectors {
107 let id = self.insert(v.to_vec())?;
108 if document_surrogate != Surrogate::ZERO {
109 self.surrogate_map.insert(id, document_surrogate);
110 }
111 ids.push(id);
112 }
113 if document_surrogate != Surrogate::ZERO {
114 self.multi_doc_map.insert(document_surrogate, ids.clone());
115 }
116 Ok(ids)
117 }
118
119 pub fn delete_multi_vector(&mut self, document_surrogate: Surrogate) -> usize {
121 let Some(ids) = self.multi_doc_map.remove(&document_surrogate) else {
122 return 0;
123 };
124 let mut deleted = 0;
125 for id in &ids {
126 if self.delete(*id) {
127 deleted += 1;
128 }
129 self.surrogate_map.remove(id);
130 }
131 self.surrogate_to_local.remove(&document_surrogate);
132 deleted
133 }
134
135 pub fn get_surrogate(&self, vector_id: u32) -> Option<Surrogate> {
137 self.surrogate_map.get(&vector_id).copied()
138 }
139
140 pub fn local_for_surrogate(&self, surrogate: Surrogate) -> Option<u32> {
142 self.surrogate_to_local.get(&surrogate).copied()
143 }
144
145 pub fn delete(&mut self, id: u32) -> bool {
147 let ok = self.delete_inner(id);
148 if ok && let Some(s) = self.surrogate_map.remove(&id) {
149 self.surrogate_to_local.remove(&s);
150 }
151 ok
152 }
153
154 pub(super) fn delete_inner(&mut self, id: u32) -> bool {
155 if let Some(ivf) = &mut self.ivf
156 && ivf.contains(id)
157 {
158 return ivf.delete(id);
159 }
160 if id >= self.growing_base_id {
161 let local = id - self.growing_base_id;
162 if (local as usize) < self.growing.len() {
163 return self.growing.delete(local);
164 }
165 }
166 for seg in &mut self.sealed {
167 if id >= seg.base_id {
168 let local = id - seg.base_id;
169 if (local as usize) < seg.index.len() {
170 return seg.index.delete(local);
171 }
172 }
173 }
174 for seg in &mut self.building {
175 if id >= seg.base_id {
176 let local = id - seg.base_id;
177 if (local as usize) < seg.flat.len() {
178 return seg.flat.delete(local);
179 }
180 }
181 }
182 false
183 }
184
185 pub fn vector_for_id(&self, id: u32) -> Option<Vec<f32>> {
188 if let Some(ivf) = &self.ivf
189 && ivf.contains(id)
190 {
191 return ivf.get_vector(id).map(<[f32]>::to_vec);
192 }
193 if id >= self.growing_base_id {
194 let local = id - self.growing_base_id;
195 if (local as usize) < self.growing.len() {
196 return self.growing.get_vector(local).map(<[f32]>::to_vec);
197 }
198 }
199 for seg in &self.sealed {
200 if id >= seg.base_id {
201 let local = id - seg.base_id;
202 if (local as usize) < seg.index.len() {
203 if seg.index.is_deleted(local) {
204 return None;
205 }
206 return sealed_vector(seg, local);
207 }
208 }
209 }
210 for seg in &self.building {
211 if id >= seg.base_id {
212 let local = id - seg.base_id;
213 if (local as usize) < seg.flat.len() {
214 return seg.flat.get_vector(local).map(<[f32]>::to_vec);
215 }
216 }
217 }
218 None
219 }
220
221 pub fn vector_for_surrogate(&self, surrogate: Surrogate) -> Option<Vec<f32>> {
223 self.local_for_surrogate(surrogate)
224 .and_then(|id| self.vector_for_id(id))
225 }
226
227 pub fn delete_by_surrogate(&mut self, surrogate: Surrogate) -> bool {
229 let Some(global_id) = self.surrogate_to_local.get(&surrogate).copied() else {
230 return false;
231 };
232 self.delete(global_id)
233 }
234
235 pub fn undelete(&mut self, id: u32) -> bool {
242 if let Some(ivf) = &mut self.ivf
243 && ivf.contains(id)
244 {
245 return ivf.undelete(id);
246 }
247 if id >= self.growing_base_id {
248 let local = id - self.growing_base_id;
249 if (local as usize) < self.growing.len() {
250 return self.growing.undelete(local);
251 }
252 }
253 for seg in &mut self.sealed {
254 if id >= seg.base_id {
255 let local = id - seg.base_id;
256 if (local as usize) < seg.index.len() {
257 return seg.index.undelete(local);
258 }
259 }
260 }
261 for seg in &mut self.building {
262 if id >= seg.base_id {
263 let local = id - seg.base_id;
264 if (local as usize) < seg.flat.len() {
265 return seg.flat.undelete(local);
266 }
267 }
268 }
269 false
270 }
271
272 pub fn undelete_bound(&mut self, id: u32, surrogate: Surrogate) -> bool {
278 if !self.undelete(id) {
279 return false;
280 }
281 if surrogate != Surrogate::ZERO {
282 self.surrogate_map.insert(id, surrogate);
283 self.surrogate_to_local.insert(surrogate, id);
284 }
285 true
286 }
287}
288
289pub(super) fn sealed_vector(seg: &super::segment::SealedSegment, local: u32) -> Option<Vec<f32>> {
293 if let Some(mmap) = &seg.mmap_vectors {
294 return mmap.get_vector(local).map(<[f32]>::to_vec);
295 }
296 #[cfg(not(target_arch = "wasm32"))]
297 {
298 seg.index
299 .get_vector_or_backing(local)
300 .map(std::borrow::Cow::into_owned)
301 }
302 #[cfg(target_arch = "wasm32")]
303 {
304 seg.index.get_vector(local).map(<[f32]>::to_vec)
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311 use crate::hnsw::HnswParams;
312
313 fn collection() -> VectorCollection {
314 VectorCollection::new(2, HnswParams::default())
315 }
316
317 #[test]
318 fn re_insert_under_the_same_surrogate_leaves_one_live_node() {
319 let mut coll = collection();
320 let s = Surrogate::new(7);
321 let first = coll.insert_with_surrogate(vec![1.0, 0.0], s).unwrap();
322 let second = coll.insert_with_surrogate(vec![0.0, 1.0], s).unwrap();
323 assert_ne!(first, second);
324 assert_eq!(coll.live_count(), 1, "the old node must be tombstoned");
325 assert_eq!(coll.local_for_surrogate(s), Some(second));
326 assert_eq!(coll.get_surrogate(first), None);
327 assert_eq!(coll.get_surrogate(second), Some(s));
328 }
329
330 #[test]
331 fn delete_then_insert_under_the_same_surrogate_leaves_one_live_node() {
332 let mut coll = collection();
333 let s = Surrogate::new(9);
334 let first = coll.insert_with_surrogate(vec![1.0, 0.0], s).unwrap();
335 assert!(coll.delete_by_surrogate(s));
336 assert_eq!(coll.local_for_surrogate(s), None);
337 let second = coll.insert_with_surrogate(vec![0.0, 1.0], s).unwrap();
338 assert_ne!(first, second);
339 assert_eq!(coll.live_count(), 1);
340 assert_eq!(coll.local_for_surrogate(s), Some(second));
341 assert!(!coll.delete(first), "the first node is already gone");
342 }
343
344 #[test]
345 fn undelete_bound_restores_the_node_and_its_binding() {
346 let mut coll = collection();
347 let s = Surrogate::new(5);
348 let id = coll.insert_with_surrogate(vec![1.0, 0.0], s).unwrap();
349 assert!(coll.delete(id));
350 assert_eq!(coll.local_for_surrogate(s), None);
351
352 assert!(coll.undelete_bound(id, s));
353 assert_eq!(coll.live_count(), 1);
354 assert_eq!(coll.local_for_surrogate(s), Some(id));
355 assert_eq!(coll.get_surrogate(id), Some(s));
356 assert!(
357 !coll.undelete_bound(id, s),
358 "a live node has no tombstone to clear"
359 );
360 }
361
362 #[test]
363 fn delete_by_surrogate_is_idempotent() {
364 let mut coll = collection();
365 let s = Surrogate::new(3);
366 coll.insert_with_surrogate(vec![1.0, 0.0], s).unwrap();
367 assert!(coll.delete_by_surrogate(s));
368 assert!(!coll.delete_by_surrogate(s));
369 assert_eq!(coll.live_count(), 0);
370 }
371
372 #[test]
373 fn vector_for_surrogate_reads_the_growing_segment_and_hides_deletes() {
374 let mut coll = collection();
375 let s = Surrogate::new(11);
376 coll.insert_with_surrogate(vec![0.5, 0.25], s).unwrap();
377 assert_eq!(coll.vector_for_surrogate(s), Some(vec![0.5, 0.25]));
378 assert!(coll.delete_by_surrogate(s));
379 assert_eq!(coll.vector_for_surrogate(s), None);
380 assert_eq!(coll.vector_for_id(999), None);
381 }
382
383 #[test]
384 fn headless_vectors_bind_no_surrogate() {
385 let mut coll = collection();
386 let first = coll
387 .insert_with_surrogate(vec![1.0, 0.0], Surrogate::ZERO)
388 .unwrap();
389 let second = coll
390 .insert_with_surrogate(vec![0.0, 1.0], Surrogate::ZERO)
391 .unwrap();
392 coll.insert_multi_vector(&[&[0.5, 0.5]], Surrogate::ZERO)
393 .unwrap();
394 assert_ne!(first, second);
395 assert_eq!(coll.live_count(), 3, "every headless insert stays live");
396 assert_eq!(coll.local_for_surrogate(Surrogate::ZERO), None);
397 assert_eq!(coll.get_surrogate(first), None);
398 assert_eq!(coll.get_surrogate(second), None);
399 assert!(coll.surrogate_to_local.is_empty());
400 assert!(coll.multi_doc_map.is_empty());
401
402 assert!(
403 !coll.delete_by_surrogate(Surrogate::ZERO),
404 "a delete by ZERO reaches no vector"
405 );
406 assert_eq!(coll.live_count(), 3);
407 }
408}