1use std::collections::HashMap;
16use std::fmt::{Display, Formatter};
17
18use common_error::ext::BoxedError;
19use common_recordbatch::OrderOption;
20use datafusion_expr::expr::Expr;
21pub use datatypes::schema::{VectorDistanceMetric, VectorIndexEngineType};
23use datatypes::types::json_type::JsonNativeType;
24use itertools::Itertools;
25use strum::Display;
26
27use crate::storage::{ColumnId, SequenceNumber};
28
29#[derive(Debug, Clone, PartialEq)]
31pub struct VectorSearchRequest {
32 pub column_id: ColumnId,
34 pub query_vector: Vec<f32>,
36 pub k: usize,
38 pub metric: VectorDistanceMetric,
40}
41
42#[derive(Debug, Clone, PartialEq)]
44pub struct VectorSearchMatches {
45 pub keys: Vec<u64>,
47 pub distances: Vec<f32>,
49}
50
51pub trait VectorIndexEngine: Send + Sync {
56 fn add(&mut self, key: u64, vector: &[f32]) -> Result<(), BoxedError>;
58
59 fn search(&self, query: &[f32], k: usize) -> Result<VectorSearchMatches, BoxedError>;
61
62 fn serialized_length(&self) -> usize;
64
65 fn save_to_buffer(&self, buffer: &mut [u8]) -> Result<(), BoxedError>;
67
68 fn reserve(&mut self, capacity: usize) -> Result<(), BoxedError>;
70
71 fn size(&self) -> usize;
73
74 fn capacity(&self) -> usize;
76
77 fn memory_usage(&self) -> usize;
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
83pub enum TimeSeriesRowSelector {
84 #[strum(to_string = "LastRow {{ after_merge: {after_merge} }}")]
86 LastRow {
87 after_merge: bool,
89 },
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
94pub enum TimeSeriesDistribution {
95 TimeWindowed,
98 PerSeries,
101}
102
103#[derive(Default, Clone, Debug, PartialEq)]
104pub struct ScanRequest {
105 pub projection: Option<Vec<usize>>,
108 pub filters: Vec<Expr>,
110 pub output_ordering: Option<Vec<OrderOption>>,
112 pub limit: Option<usize>,
117 pub series_row_selector: Option<TimeSeriesRowSelector>,
119 pub memtable_max_sequence: Option<SequenceNumber>,
125 pub memtable_min_sequence: Option<SequenceNumber>,
128 pub sst_min_sequence: Option<SequenceNumber>,
131 pub skip_sst_files: bool,
134 pub snapshot_on_scan: bool,
136 pub exact_sequence_range: bool,
145 pub distribution: Option<TimeSeriesDistribution>,
147 pub vector_search: Option<VectorSearchRequest>,
150 pub json_type_hint: HashMap<String, JsonNativeType>,
152 pub preserve_pk_dictionary_encoding: bool,
154}
155
156impl Display for ScanRequest {
157 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
158 enum Delimiter {
159 None,
160 Init,
161 }
162
163 impl Delimiter {
164 fn as_str(&mut self) -> &str {
165 match self {
166 Delimiter::None => {
167 *self = Delimiter::Init;
168 ""
169 }
170 Delimiter::Init => ", ",
171 }
172 }
173 }
174
175 let mut delimiter = Delimiter::None;
176
177 write!(f, "ScanRequest {{ ")?;
178 if let Some(projection) = &self.projection {
179 write!(f, "{}projection: {:?}", delimiter.as_str(), projection)?;
180 }
181 if !self.filters.is_empty() {
182 write!(
183 f,
184 "{}filters: [{}]",
185 delimiter.as_str(),
186 self.filters
187 .iter()
188 .map(|f| f.to_string())
189 .collect::<Vec<_>>()
190 .join(", ")
191 )?;
192 }
193 if let Some(output_ordering) = &self.output_ordering {
194 write!(
195 f,
196 "{}output_ordering: {:?}",
197 delimiter.as_str(),
198 output_ordering
199 )?;
200 }
201 if let Some(limit) = &self.limit {
202 write!(f, "{}limit: {}", delimiter.as_str(), limit)?;
203 }
204 if let Some(series_row_selector) = &self.series_row_selector {
205 write!(
206 f,
207 "{}series_row_selector: {}",
208 delimiter.as_str(),
209 series_row_selector
210 )?;
211 }
212 if let Some(sequence) = &self.memtable_max_sequence {
213 write!(f, "{}sequence: {}", delimiter.as_str(), sequence)?;
214 }
215 if let Some(sst_min_sequence) = &self.sst_min_sequence {
216 write!(
217 f,
218 "{}sst_min_sequence: {}",
219 delimiter.as_str(),
220 sst_min_sequence
221 )?;
222 }
223 if self.skip_sst_files {
224 write!(
225 f,
226 "{}skip_sst_files: {}",
227 delimiter.as_str(),
228 self.skip_sst_files
229 )?;
230 }
231 if self.snapshot_on_scan {
232 write!(
233 f,
234 "{}snapshot_on_scan: {}",
235 delimiter.as_str(),
236 self.snapshot_on_scan
237 )?;
238 }
239 if self.exact_sequence_range {
240 write!(f, "{}exact_sequence_range: true", delimiter.as_str())?;
241 }
242 if self.preserve_pk_dictionary_encoding {
243 write!(
244 f,
245 "{}preserve_pk_dictionary_encoding: true",
246 delimiter.as_str()
247 )?;
248 }
249 if let Some(distribution) = &self.distribution {
250 write!(f, "{}distribution: {}", delimiter.as_str(), distribution)?;
251 }
252 if let Some(vector_search) = &self.vector_search {
253 write!(
254 f,
255 "{}vector_search: column_id={}, k={}, metric={}",
256 delimiter.as_str(),
257 vector_search.column_id,
258 vector_search.k,
259 vector_search.metric
260 )?;
261 }
262 if !self.json_type_hint.is_empty() {
263 write!(
264 f,
265 "{}json_type_hint: {}",
266 delimiter.as_str(),
267 self.json_type_hint
268 .iter()
269 .map(|(column, json_type)| format!("({column}: {json_type})"))
270 .join(", ")
271 )?;
272 }
273 write!(f, " }}")
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use datafusion_expr::{Operator, binary_expr, col, lit};
280
281 use super::*;
282
283 #[test]
284 fn test_display_scan_request() {
285 let request = ScanRequest {
286 ..Default::default()
287 };
288 assert_eq!(request.to_string(), "ScanRequest { }");
289
290 let projection = Some(vec![1, 2]);
291 let request = ScanRequest {
292 projection,
293 filters: vec![
294 binary_expr(col("i"), Operator::Gt, lit(1)),
295 binary_expr(col("s"), Operator::Eq, lit("x")),
296 ],
297 limit: Some(10),
298 ..Default::default()
299 };
300 assert_eq!(
301 request.to_string(),
302 r#"ScanRequest { projection: [1, 2], filters: [i > Int32(1), s = Utf8("x")], limit: 10 }"#
303 );
304
305 let request = ScanRequest {
306 filters: vec![
307 binary_expr(col("i"), Operator::Gt, lit(1)),
308 binary_expr(col("s"), Operator::Eq, lit("x")),
309 ],
310 limit: Some(10),
311 ..Default::default()
312 };
313 assert_eq!(
314 request.to_string(),
315 r#"ScanRequest { filters: [i > Int32(1), s = Utf8("x")], limit: 10 }"#
316 );
317
318 let projection = Some(vec![1, 2]);
319 let request = ScanRequest {
320 projection,
321 limit: Some(10),
322 ..Default::default()
323 };
324 assert_eq!(
325 request.to_string(),
326 "ScanRequest { projection: [1, 2], limit: 10 }"
327 );
328
329 let request = ScanRequest {
330 series_row_selector: Some(TimeSeriesRowSelector::LastRow { after_merge: true }),
331 snapshot_on_scan: true,
332 exact_sequence_range: true,
333 ..Default::default()
334 };
335 assert_eq!(
336 request.to_string(),
337 "ScanRequest { series_row_selector: LastRow { after_merge: true }, snapshot_on_scan: true, exact_sequence_range: true }"
338 );
339
340 assert_eq!(
341 TimeSeriesRowSelector::LastRow { after_merge: false }.to_string(),
342 "LastRow { after_merge: false }"
343 );
344
345 let request = ScanRequest {
346 skip_sst_files: true,
347 ..Default::default()
348 };
349 assert_eq!(request.to_string(), "ScanRequest { skip_sst_files: true }");
350 }
351}