Search across all segments, merging results by distance.
(&self, query: &[f32], top_k: usize, ef: usize)
| 121 | impl VectorCollection { |
| 122 | /// Search across all segments, merging results by distance. |
| 123 | pub fn search(&self, query: &[f32], top_k: usize, ef: usize) -> Vec<SearchResult> { |
| 124 | // Codec-dispatch fast path: if a collection-level HnswCodecIndex has |
| 125 | // been built (RaBitQ or BBQ), use it exclusively for sealed-segment |
| 126 | // results and fall back to the growing/building flat segments only. |
| 127 | if let Some(ref dispatch) = self.codec_dispatch { |
| 128 | let mut all: Vec<SearchResult> = Vec::new(); |
| 129 | |
| 130 | let codec_results = dispatch.search(query, top_k, ef); |
| 131 | for r in codec_results { |
| 132 | all.push(SearchResult { |
| 133 | id: r.id, |
| 134 | distance: r.distance, |
| 135 | }); |
| 136 | } |
| 137 | |
| 138 | // Growing segment (brute-force, not yet in codec index). |
| 139 | let growing_results = self.growing.search(query, top_k); |
| 140 | for mut r in growing_results { |
| 141 | r.id += self.growing_base_id; |
| 142 | all.push(r); |
| 143 | } |
| 144 | |
| 145 | // Building segments (brute-force while codec index rebuilds). |
| 146 | for seg in &self.building { |
| 147 | let results = seg.flat.search(query, top_k); |
| 148 | for mut r in results { |
| 149 | r.id += seg.base_id; |
| 150 | all.push(r); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | all.sort_by(|a, b| { |
| 155 | a.distance |
| 156 | .partial_cmp(&b.distance) |
| 157 | .unwrap_or(std::cmp::Ordering::Equal) |
| 158 | }); |
| 159 | all.truncate(top_k); |
| 160 | return all; |
| 161 | } |
| 162 | |
| 163 | let mut all: Vec<SearchResult> = Vec::new(); |
| 164 | |
| 165 | // Search growing segment (brute-force). |
| 166 | let growing_results = self.growing.search(query, top_k); |
| 167 | for mut r in growing_results { |
| 168 | r.id += self.growing_base_id; |
| 169 | all.push(r); |
| 170 | } |
| 171 | |
| 172 | // Search sealed segments. |
| 173 | for seg in &self.sealed { |
| 174 | let results = if seg.pq.is_some() || seg.sq8.is_some() { |
| 175 | match quantized_search(seg, query, top_k, ef, self.params.metric) { |
| 176 | Ok(r) => r, |
| 177 | Err(e) => { |
| 178 | tracing::warn!(error = %e, "quantized_search budget exhausted; skipping segment"); |
| 179 | seg.index.search(query, top_k, ef) |
| 180 | } |