Brute-force scan over fresh vectors (excluding tombstones), returning the top-`k` results sorted ascending by distance.
(&self, query: &[f32], k: usize, metric: DistanceMetric)
| 62 | /// Brute-force scan over fresh vectors (excluding tombstones), returning |
| 63 | /// the top-`k` results sorted ascending by distance. |
| 64 | pub fn search(&self, query: &[f32], k: usize, metric: DistanceMetric) -> Vec<(u32, f32)> { |
| 65 | if k == 0 { |
| 66 | return Vec::new(); |
| 67 | } |
| 68 | |
| 69 | let mut scored: Vec<(u32, f32)> = self |
| 70 | .fresh |
| 71 | .iter() |
| 72 | .filter(|(id, _)| !self.tombstones.contains(id)) |
| 73 | .map(|(id, vec)| (*id, distance(query, vec, metric))) |
| 74 | .collect(); |
| 75 | |
| 76 | // Partial sort: cheapest path to top-k. |
| 77 | if k < scored.len() { |
| 78 | scored.select_nth_unstable_by(k, |a, b| { |
| 79 | a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal) |
| 80 | }); |
| 81 | scored.truncate(k); |
| 82 | } |
| 83 | |
| 84 | scored.sort_unstable_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal)); |
| 85 | |
| 86 | scored |
| 87 | } |
| 88 | |
| 89 | /// Drain all staged fresh vectors for patching into the main HNSW. |
| 90 | /// After this call `fresh_len()` returns 0. |