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, ProjectionInput, 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 LastRow,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
90pub enum TimeSeriesDistribution {
91 TimeWindowed,
94 PerSeries,
97}
98
99#[derive(Default, Clone, Debug, PartialEq)]
100pub struct ScanRequest {
101 pub projection_input: Option<ProjectionInput>,
104 pub filters: Vec<Expr>,
106 pub output_ordering: Option<Vec<OrderOption>>,
108 pub limit: Option<usize>,
113 pub series_row_selector: Option<TimeSeriesRowSelector>,
115 pub memtable_max_sequence: Option<SequenceNumber>,
121 pub memtable_min_sequence: Option<SequenceNumber>,
124 pub sst_min_sequence: Option<SequenceNumber>,
127 pub skip_sst_files: bool,
130 pub snapshot_on_scan: bool,
132 pub distribution: Option<TimeSeriesDistribution>,
134 pub vector_search: Option<VectorSearchRequest>,
137 pub json_type_hint: HashMap<String, JsonNativeType>,
139 pub preserve_pk_dictionary_encoding: bool,
141}
142
143impl ScanRequest {
144 pub fn projection_indices(&self) -> Option<&[usize]> {
146 self.projection_input
147 .as_ref()
148 .map(|projection_input| projection_input.projection.as_slice())
149 }
150}
151
152impl Display for ScanRequest {
153 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
154 enum Delimiter {
155 None,
156 Init,
157 }
158
159 impl Delimiter {
160 fn as_str(&mut self) -> &str {
161 match self {
162 Delimiter::None => {
163 *self = Delimiter::Init;
164 ""
165 }
166 Delimiter::Init => ", ",
167 }
168 }
169 }
170
171 let mut delimiter = Delimiter::None;
172
173 write!(f, "ScanRequest {{ ")?;
174 if let Some(projection) = &self.projection_input {
175 write!(f, "{}projection: {:?}", delimiter.as_str(), projection)?;
176 }
177 if !self.filters.is_empty() {
178 write!(
179 f,
180 "{}filters: [{}]",
181 delimiter.as_str(),
182 self.filters
183 .iter()
184 .map(|f| f.to_string())
185 .collect::<Vec<_>>()
186 .join(", ")
187 )?;
188 }
189 if let Some(output_ordering) = &self.output_ordering {
190 write!(
191 f,
192 "{}output_ordering: {:?}",
193 delimiter.as_str(),
194 output_ordering
195 )?;
196 }
197 if let Some(limit) = &self.limit {
198 write!(f, "{}limit: {}", delimiter.as_str(), limit)?;
199 }
200 if let Some(series_row_selector) = &self.series_row_selector {
201 write!(
202 f,
203 "{}series_row_selector: {}",
204 delimiter.as_str(),
205 series_row_selector
206 )?;
207 }
208 if let Some(sequence) = &self.memtable_max_sequence {
209 write!(f, "{}sequence: {}", delimiter.as_str(), sequence)?;
210 }
211 if let Some(sst_min_sequence) = &self.sst_min_sequence {
212 write!(
213 f,
214 "{}sst_min_sequence: {}",
215 delimiter.as_str(),
216 sst_min_sequence
217 )?;
218 }
219 if self.skip_sst_files {
220 write!(
221 f,
222 "{}skip_sst_files: {}",
223 delimiter.as_str(),
224 self.skip_sst_files
225 )?;
226 }
227 if self.snapshot_on_scan {
228 write!(
229 f,
230 "{}snapshot_on_scan: {}",
231 delimiter.as_str(),
232 self.snapshot_on_scan
233 )?;
234 }
235 if self.preserve_pk_dictionary_encoding {
236 write!(
237 f,
238 "{}preserve_pk_dictionary_encoding: true",
239 delimiter.as_str()
240 )?;
241 }
242 if let Some(distribution) = &self.distribution {
243 write!(f, "{}distribution: {}", delimiter.as_str(), distribution)?;
244 }
245 if let Some(vector_search) = &self.vector_search {
246 write!(
247 f,
248 "{}vector_search: column_id={}, k={}, metric={}",
249 delimiter.as_str(),
250 vector_search.column_id,
251 vector_search.k,
252 vector_search.metric
253 )?;
254 }
255 if !self.json_type_hint.is_empty() {
256 write!(
257 f,
258 "{}json_type_hint: {}",
259 delimiter.as_str(),
260 self.json_type_hint
261 .iter()
262 .map(|(column, json_type)| format!("({column}: {json_type})"))
263 .join(", ")
264 )?;
265 }
266 write!(f, " }}")
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use datafusion_expr::{Operator, binary_expr, col, lit};
273
274 use super::*;
275
276 #[test]
277 fn test_display_scan_request() {
278 let request = ScanRequest {
279 ..Default::default()
280 };
281 assert_eq!(request.to_string(), "ScanRequest { }");
282
283 let projection_input = Some(vec![1, 2].into());
284 let request = ScanRequest {
285 projection_input,
286 filters: vec![
287 binary_expr(col("i"), Operator::Gt, lit(1)),
288 binary_expr(col("s"), Operator::Eq, lit("x")),
289 ],
290 limit: Some(10),
291 ..Default::default()
292 };
293 assert_eq!(
294 request.to_string(),
295 r#"ScanRequest { projection: ProjectionInput { projection: [1, 2], nested_paths: [] }, filters: [i > Int32(1), s = Utf8("x")], limit: 10 }"#
296 );
297
298 let request = ScanRequest {
299 filters: vec![
300 binary_expr(col("i"), Operator::Gt, lit(1)),
301 binary_expr(col("s"), Operator::Eq, lit("x")),
302 ],
303 limit: Some(10),
304 ..Default::default()
305 };
306 assert_eq!(
307 request.to_string(),
308 r#"ScanRequest { filters: [i > Int32(1), s = Utf8("x")], limit: 10 }"#
309 );
310
311 let projection_input = Some(vec![1, 2].into());
312 let request = ScanRequest {
313 projection_input,
314 limit: Some(10),
315 ..Default::default()
316 };
317 assert_eq!(
318 request.to_string(),
319 "ScanRequest { projection: ProjectionInput { projection: [1, 2], nested_paths: [] }, limit: 10 }"
320 );
321
322 let projection_input = Some(ProjectionInput::new(vec![1, 2]).with_nested_paths(vec![
323 vec!["j".to_string(), "a".to_string(), "b".to_string()],
324 vec!["s".to_string(), "x".to_string()],
325 ]));
326 let request = ScanRequest {
327 projection_input,
328 limit: Some(10),
329 ..Default::default()
330 };
331 assert_eq!(
332 request.to_string(),
333 r#"ScanRequest { projection: ProjectionInput { projection: [1, 2], nested_paths: [["j", "a", "b"], ["s", "x"]] }, limit: 10 }"#
334 );
335
336 let request = ScanRequest {
337 snapshot_on_scan: true,
338 ..Default::default()
339 };
340 assert_eq!(
341 request.to_string(),
342 "ScanRequest { snapshot_on_scan: true }"
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}