Skip to main content

weavatrix_search_vector/mutable/
mutations.rs

1use super::support::{
2    current_vector, key_exists, prepare_records, prepare_vector, replace_metadata,
3};
4use super::{MutableVectorIndex, MutationOutcome, VectorRecord};
5use crate::error::SearchError;
6use crate::metadata::Metadata;
7
8impl MutableVectorIndex {
9    /// Inserts a new key.
10    ///
11    /// # Errors
12    ///
13    /// Returns [`SearchError::DuplicateKey`] when the live key already exists,
14    /// or a typed vector validation error.
15    pub fn insert(&self, key: u64, vector: &[f32], metadata: Metadata) -> Result<(), SearchError> {
16        let normalized = prepare_vector(self.config.dimensions, self.config.metric, vector)?;
17        let mut state = self
18            .state
19            .write()
20            .unwrap_or_else(std::sync::PoisonError::into_inner);
21        if key_exists(&state, key) {
22            return Err(SearchError::DuplicateKey(key));
23        }
24        state.deleted.remove(&key);
25        state.pending.insert(key, normalized);
26        replace_metadata(&mut state.metadata, key, metadata);
27        state.generation = state.generation.wrapping_add(1);
28        Ok(())
29    }
30
31    /// Inserts or replaces one key without rebuilding the immutable base.
32    ///
33    /// # Errors
34    ///
35    /// Returns a typed vector validation error.
36    pub fn upsert(
37        &self,
38        key: u64,
39        vector: &[f32],
40        metadata: Metadata,
41    ) -> Result<MutationOutcome, SearchError> {
42        let normalized = prepare_vector(self.config.dimensions, self.config.metric, vector)?;
43        let mut state = self
44            .state
45            .write()
46            .unwrap_or_else(std::sync::PoisonError::into_inner);
47        let outcome = if key_exists(&state, key) {
48            MutationOutcome::Updated
49        } else {
50            MutationOutcome::Inserted
51        };
52        state.deleted.remove(&key);
53        state.pending.insert(key, normalized);
54        replace_metadata(&mut state.metadata, key, metadata);
55        state.generation = state.generation.wrapping_add(1);
56        Ok(outcome)
57    }
58
59    /// Atomically inserts a batch after validating every record.
60    ///
61    /// # Errors
62    ///
63    /// Returns without applying any record when the batch contains duplicate
64    /// keys, an existing live key, or an invalid vector.
65    pub fn insert_batch(&self, records: &[VectorRecord]) -> Result<(), SearchError> {
66        let prepared = prepare_records(&self.config, records)?;
67        let mut state = self
68            .state
69            .write()
70            .unwrap_or_else(std::sync::PoisonError::into_inner);
71        for (key, _, _) in &prepared {
72            if key_exists(&state, *key) {
73                return Err(SearchError::DuplicateKey(*key));
74            }
75        }
76        for (key, vector, metadata) in prepared {
77            state.deleted.remove(&key);
78            state.pending.insert(key, vector);
79            replace_metadata(&mut state.metadata, key, metadata);
80        }
81        state.generation = state.generation.wrapping_add(1);
82        Ok(())
83    }
84
85    /// Atomically inserts or replaces a batch after validating every record.
86    ///
87    /// Results follow input order.
88    ///
89    /// # Errors
90    ///
91    /// Returns without applying any record when the batch contains duplicate
92    /// keys or an invalid vector.
93    pub fn upsert_batch(
94        &self,
95        records: &[VectorRecord],
96    ) -> Result<Vec<MutationOutcome>, SearchError> {
97        let prepared = prepare_records(&self.config, records)?;
98        let mut state = self
99            .state
100            .write()
101            .unwrap_or_else(std::sync::PoisonError::into_inner);
102        let outcomes = prepared
103            .iter()
104            .map(|(key, _, _)| {
105                if key_exists(&state, *key) {
106                    MutationOutcome::Updated
107                } else {
108                    MutationOutcome::Inserted
109                }
110            })
111            .collect::<Vec<_>>();
112        for (key, vector, metadata) in prepared {
113            state.deleted.remove(&key);
114            state.pending.insert(key, vector);
115            replace_metadata(&mut state.metadata, key, metadata);
116        }
117        state.generation = state.generation.wrapping_add(1);
118        Ok(outcomes)
119    }
120
121    /// Tombstones a live key. Returns whether a vector was removed.
122    pub fn delete(&self, key: u64) -> bool {
123        let mut state = self
124            .state
125            .write()
126            .unwrap_or_else(std::sync::PoisonError::into_inner);
127        if !key_exists(&state, key) {
128            return false;
129        }
130        state.pending.remove(&key);
131        if state.base.vector(key).is_some()
132            || state
133                .sealed
134                .as_ref()
135                .is_some_and(|index| index.vector(key).is_some())
136        {
137            state.deleted.insert(key);
138        }
139        state.metadata.remove(key);
140        state.generation = state.generation.wrapping_add(1);
141        true
142    }
143
144    /// Tombstones every live key in `keys` and returns the number removed.
145    pub fn delete_batch(&self, keys: &[u64]) -> usize {
146        let mut state = self
147            .state
148            .write()
149            .unwrap_or_else(std::sync::PoisonError::into_inner);
150        let mut removed = 0;
151        for key in keys {
152            if !key_exists(&state, *key) {
153                continue;
154            }
155            state.pending.remove(key);
156            if state.base.vector(*key).is_some()
157                || state
158                    .sealed
159                    .as_ref()
160                    .is_some_and(|index| index.vector(*key).is_some())
161            {
162                state.deleted.insert(*key);
163            }
164            state.metadata.remove(*key);
165            removed += 1;
166        }
167        if removed != 0 {
168            state.generation = state.generation.wrapping_add(1);
169        }
170        removed
171    }
172
173    /// Renames one live vector key without rebuilding either graph.
174    ///
175    /// # Errors
176    ///
177    /// Returns [`SearchError::DuplicateKey`] when `to` is already live.
178    pub fn rename(&self, from: u64, to: u64) -> Result<bool, SearchError> {
179        if from == to {
180            let state = self
181                .state
182                .read()
183                .unwrap_or_else(std::sync::PoisonError::into_inner);
184            return Ok(key_exists(&state, from));
185        }
186        let mut state = self
187            .state
188            .write()
189            .unwrap_or_else(std::sync::PoisonError::into_inner);
190        if !key_exists(&state, from) {
191            return Ok(false);
192        }
193        if key_exists(&state, to) {
194            return Err(SearchError::DuplicateKey(to));
195        }
196        let vector = current_vector(&state, from)
197            .ok_or(SearchError::MissingKey(from))?
198            .to_vec();
199        let metadata = state.metadata.remove(from);
200        state.pending.remove(&from);
201        if state.base.vector(from).is_some()
202            || state
203                .sealed
204                .as_ref()
205                .is_some_and(|index| index.vector(from).is_some())
206        {
207            state.deleted.insert(from);
208        }
209        state.deleted.remove(&to);
210        state.pending.insert(to, vector);
211        if let Some(metadata) = metadata {
212            state.metadata.insert(to, metadata);
213        }
214        state.generation = state.generation.wrapping_add(1);
215        Ok(true)
216    }
217
218    /// Replaces metadata without changing the vector.
219    ///
220    /// # Errors
221    ///
222    /// Returns [`SearchError::MissingKey`] when `key` is not live.
223    pub fn set_metadata(&self, key: u64, metadata: Metadata) -> Result<(), SearchError> {
224        let mut state = self
225            .state
226            .write()
227            .unwrap_or_else(std::sync::PoisonError::into_inner);
228        if !key_exists(&state, key) {
229            return Err(SearchError::MissingKey(key));
230        }
231        replace_metadata(&mut state.metadata, key, metadata);
232        state.generation = state.generation.wrapping_add(1);
233        Ok(())
234    }
235
236    #[must_use]
237    pub fn metadata(&self, key: u64) -> Option<Metadata> {
238        let state = self
239            .state
240            .read()
241            .unwrap_or_else(std::sync::PoisonError::into_inner);
242        state.metadata.get(key).cloned()
243    }
244}