1use arrow::buffer::{OffsetBuffer, ScalarBuffer};
7use arrow_array::{BooleanArray, ListArray, RecordBatch, UInt64Array};
8use arrow_schema::{Field, Schema};
9use async_trait::async_trait;
10use bytes::Bytes;
11use datafusion::functions::string::contains::ContainsFunc;
12use datafusion::functions_nested::array_has;
13use datafusion::physical_plan::SendableRecordBatchStream;
14use datafusion_common::{Column, scalar::ScalarValue};
15use std::collections::{HashMap, HashSet};
16use std::fmt::Debug;
17use std::pin::Pin;
18use std::{any::Any, ops::Bound, sync::Arc};
19
20use datafusion_expr::{Expr, expr::ScalarFunction};
21use deepsize::DeepSizeOf;
22use inverted::query::{FtsQuery, FtsQueryNode, FtsSearchParams, MatchQuery, fill_fts_query_column};
23use lance_core::{Error, Result};
24use lance_io::stream::{RecordBatchStream, RecordBatchStreamAdapter};
25use lance_select::{NullableRowAddrSet, RowAddrTreeMap, RowSetOps};
26use roaring::RoaringBitmap;
27use serde::Serialize;
28
29use crate::metrics::MetricsCollector;
30use crate::scalar::registry::TrainingCriteria;
31use crate::{Index, IndexParams, IndexType};
32pub use lance_table::format::IndexFile;
33
34pub mod bitmap;
35pub mod bloomfilter;
36pub mod btree;
37pub mod expression;
38pub mod inverted;
39pub mod json;
40pub mod label_list;
41pub mod lance_format;
42pub mod ngram;
43pub mod registry;
44#[cfg(feature = "geo")]
45pub mod rtree;
46pub mod zoned;
47pub mod zonemap;
48
49use crate::frag_reuse::FragReuseIndex;
50pub use inverted::tokenizer::InvertedIndexParams;
51use lance_datafusion::udf::CONTAINS_TOKENS_UDF;
52
53pub const LANCE_SCALAR_INDEX: &str = "__lance_scalar_index";
54
55#[derive(Debug, Clone, PartialEq, Eq, DeepSizeOf)]
61pub enum BuiltinIndexType {
62 BTree,
63 Bitmap,
64 LabelList,
65 NGram,
66 ZoneMap,
67 BloomFilter,
68 RTree,
69 Inverted,
70}
71
72impl BuiltinIndexType {
73 pub fn as_str(&self) -> &str {
74 match self {
75 Self::BTree => "btree",
76 Self::Bitmap => "bitmap",
77 Self::LabelList => "labellist",
78 Self::NGram => "ngram",
79 Self::ZoneMap => "zonemap",
80 Self::Inverted => "inverted",
81 Self::BloomFilter => "bloomfilter",
82 Self::RTree => "rtree",
83 }
84 }
85}
86
87impl TryFrom<IndexType> for BuiltinIndexType {
88 type Error = Error;
89
90 fn try_from(value: IndexType) -> Result<Self> {
91 match value {
92 IndexType::BTree => Ok(Self::BTree),
93 IndexType::Bitmap => Ok(Self::Bitmap),
94 IndexType::LabelList => Ok(Self::LabelList),
95 IndexType::NGram => Ok(Self::NGram),
96 IndexType::ZoneMap => Ok(Self::ZoneMap),
97 IndexType::Inverted => Ok(Self::Inverted),
98 IndexType::BloomFilter => Ok(Self::BloomFilter),
99 IndexType::RTree => Ok(Self::RTree),
100 _ => Err(Error::index("Invalid index type".to_string())),
101 }
102 }
103}
104
105#[derive(Debug, Clone, PartialEq)]
106pub struct ScalarIndexParams {
107 pub index_type: String,
111 pub params: Option<String>,
116}
117
118impl Default for ScalarIndexParams {
119 fn default() -> Self {
120 Self {
121 index_type: BuiltinIndexType::BTree.as_str().to_string(),
122 params: None,
123 }
124 }
125}
126
127impl ScalarIndexParams {
128 pub fn for_builtin(index_type: BuiltinIndexType) -> Self {
130 Self {
131 index_type: index_type.as_str().to_string(),
132 params: None,
133 }
134 }
135
136 pub fn new(index_type: String) -> Self {
138 Self {
139 index_type,
140 params: None,
141 }
142 }
143
144 pub fn with_params<ParamsType: Serialize>(mut self, params: &ParamsType) -> Self {
146 self.params = Some(serde_json::to_string(params).unwrap());
147 self
148 }
149}
150
151impl IndexParams for ScalarIndexParams {
152 fn as_any(&self) -> &dyn std::any::Any {
153 self
154 }
155
156 fn index_name(&self) -> &str {
157 LANCE_SCALAR_INDEX
158 }
159}
160
161impl IndexParams for InvertedIndexParams {
162 fn as_any(&self) -> &dyn std::any::Any {
163 self
164 }
165
166 fn index_name(&self) -> &str {
167 "INVERTED"
168 }
169}
170
171#[async_trait]
173pub trait IndexWriter: Send {
174 async fn write_record_batch(&mut self, batch: RecordBatch) -> Result<u64>;
178 async fn add_global_buffer(&mut self, _data: Bytes) -> Result<u32> {
180 Err(Error::not_supported(
181 "global buffers are not supported by this index writer",
182 ))
183 }
184 async fn finish(&mut self) -> Result<()>;
186 async fn finish_with_metadata(&mut self, metadata: HashMap<String, String>) -> Result<()>;
188}
189
190#[async_trait]
192pub trait IndexReader: Send + Sync {
193 async fn read_record_batch(&self, n: u64, batch_size: u64) -> Result<RecordBatch>;
195 async fn read_global_buffer(&self, _index: u32) -> Result<Bytes> {
197 Err(Error::not_supported(
198 "global buffers are not supported by this index reader",
199 ))
200 }
201 async fn read_range(
206 &self,
207 range: std::ops::Range<usize>,
208 projection: Option<&[&str]>,
209 ) -> Result<RecordBatch>;
210 async fn read_range_stream(
218 &self,
219 range: std::ops::Range<usize>,
220 projection: Option<&[&str]>,
221 ) -> Result<Pin<Box<dyn RecordBatchStream>>> {
222 let batch = self.read_range(range, projection).await?;
223 let schema = batch.schema();
224 Ok(Box::pin(RecordBatchStreamAdapter::new(
225 schema,
226 futures::stream::once(async move { Ok(batch) }),
227 )))
228 }
229 async fn num_batches(&self, batch_size: u64) -> u32;
231 fn num_rows(&self) -> usize;
233 fn schema(&self) -> &lance_core::datatypes::Schema;
235}
236
237#[async_trait]
243pub trait IndexStore: std::fmt::Debug + Send + Sync + DeepSizeOf {
244 fn as_any(&self) -> &dyn Any;
245 fn clone_arc(&self) -> Arc<dyn IndexStore>;
246
247 fn io_parallelism(&self) -> usize;
249
250 async fn new_index_file(&self, name: &str, schema: Arc<Schema>)
252 -> Result<Box<dyn IndexWriter>>;
253
254 async fn open_index_file(&self, name: &str) -> Result<Arc<dyn IndexReader>>;
256
257 async fn copy_index_file(&self, name: &str, dest_store: &dyn IndexStore) -> Result<()>;
261
262 async fn rename_index_file(&self, name: &str, new_name: &str) -> Result<()>;
264
265 async fn delete_index_file(&self, name: &str) -> Result<()>;
267
268 async fn list_files_with_sizes(&self) -> Result<Vec<IndexFile>>;
273}
274
275pub trait AnyQuery: std::fmt::Debug + Any + Send + Sync {
286 fn as_any(&self) -> &dyn Any;
288 fn format(&self, col: &str) -> String;
290 fn to_expr(&self, col: String) -> Expr;
292 fn dyn_eq(&self, other: &dyn AnyQuery) -> bool;
294}
295
296impl PartialEq for dyn AnyQuery {
297 fn eq(&self, other: &Self) -> bool {
298 self.dyn_eq(other)
299 }
300}
301#[derive(Debug, Clone, PartialEq)]
303pub struct FullTextSearchQuery {
304 pub query: FtsQuery,
305
306 pub limit: Option<i64>,
308
309 pub wand_factor: Option<f32>,
314}
315
316impl FullTextSearchQuery {
317 pub fn new(query: String) -> Self {
319 let query = MatchQuery::new(query).into();
320 Self {
321 query,
322 limit: None,
323 wand_factor: None,
324 }
325 }
326
327 pub fn new_fuzzy(term: String, max_distance: Option<u32>) -> Self {
329 let query = MatchQuery::new(term).with_fuzziness(max_distance).into();
330 Self {
331 query,
332 limit: None,
333 wand_factor: None,
334 }
335 }
336
337 pub fn new_query(query: FtsQuery) -> Self {
339 Self {
340 query,
341 limit: None,
342 wand_factor: None,
343 }
344 }
345
346 pub fn with_column(mut self, column: String) -> Result<Self> {
349 self.query = fill_fts_query_column(&self.query, &[column], true)?;
350 Ok(self)
351 }
352
353 pub fn with_columns(mut self, columns: &[String]) -> Result<Self> {
356 self.query = fill_fts_query_column(&self.query, columns, true)?;
357 Ok(self)
358 }
359
360 pub fn limit(mut self, limit: Option<i64>) -> Self {
363 self.limit = limit;
364 self
365 }
366
367 pub fn wand_factor(mut self, wand_factor: Option<f32>) -> Self {
368 self.wand_factor = wand_factor;
369 self
370 }
371
372 pub fn columns(&self) -> HashSet<String> {
373 self.query.columns()
374 }
375
376 pub fn params(&self) -> FtsSearchParams {
377 FtsSearchParams::new()
378 .with_limit(self.limit.map(|limit| limit as usize))
379 .with_wand_factor(self.wand_factor.unwrap_or(1.0))
380 }
381}
382
383#[derive(Debug, Clone, PartialEq)]
393pub enum SargableQuery {
394 Range(Bound<ScalarValue>, Bound<ScalarValue>),
396 IsIn(Vec<ScalarValue>),
398 Equals(ScalarValue),
400 FullTextSearch(FullTextSearchQuery),
402 IsNull(),
404 LikePrefix(ScalarValue),
407}
408
409impl AnyQuery for SargableQuery {
410 fn as_any(&self) -> &dyn Any {
411 self
412 }
413
414 fn format(&self, col: &str) -> String {
415 match self {
416 Self::Range(lower, upper) => match (lower, upper) {
417 (Bound::Unbounded, Bound::Unbounded) => "true".to_string(),
418 (Bound::Unbounded, Bound::Included(rhs)) => format!("{} <= {}", col, rhs),
419 (Bound::Unbounded, Bound::Excluded(rhs)) => format!("{} < {}", col, rhs),
420 (Bound::Included(lhs), Bound::Unbounded) => format!("{} >= {}", col, lhs),
421 (Bound::Included(lhs), Bound::Included(rhs)) => {
422 format!("{} >= {} && {} <= {}", col, lhs, col, rhs)
423 }
424 (Bound::Included(lhs), Bound::Excluded(rhs)) => {
425 format!("{} >= {} && {} < {}", col, lhs, col, rhs)
426 }
427 (Bound::Excluded(lhs), Bound::Unbounded) => format!("{} > {}", col, lhs),
428 (Bound::Excluded(lhs), Bound::Included(rhs)) => {
429 format!("{} > {} && {} <= {}", col, lhs, col, rhs)
430 }
431 (Bound::Excluded(lhs), Bound::Excluded(rhs)) => {
432 format!("{} > {} && {} < {}", col, lhs, col, rhs)
433 }
434 },
435 Self::IsIn(values) => {
436 format!(
437 "{} IN [{}]",
438 col,
439 values
440 .iter()
441 .map(|val| val.to_string())
442 .collect::<Vec<_>>()
443 .join(",")
444 )
445 }
446 Self::FullTextSearch(query) => {
447 format!("fts({})", query.query)
448 }
449 Self::IsNull() => {
450 format!("{} IS NULL", col)
451 }
452 Self::Equals(val) => {
453 format!("{} = {}", col, val)
454 }
455 Self::LikePrefix(prefix) => {
456 format!("{} LIKE '{}%'", col, prefix)
457 }
458 }
459 }
460
461 fn to_expr(&self, col: String) -> Expr {
462 let col_expr = Expr::Column(Column::new_unqualified(col));
463 match self {
464 Self::Range(lower, upper) => match (lower, upper) {
465 (Bound::Unbounded, Bound::Unbounded) => {
466 Expr::Literal(ScalarValue::Boolean(Some(true)), None)
467 }
468 (Bound::Unbounded, Bound::Included(rhs)) => {
469 col_expr.lt_eq(Expr::Literal(rhs.clone(), None))
470 }
471 (Bound::Unbounded, Bound::Excluded(rhs)) => {
472 col_expr.lt(Expr::Literal(rhs.clone(), None))
473 }
474 (Bound::Included(lhs), Bound::Unbounded) => {
475 col_expr.gt_eq(Expr::Literal(lhs.clone(), None))
476 }
477 (Bound::Included(lhs), Bound::Included(rhs)) => col_expr.between(
478 Expr::Literal(lhs.clone(), None),
479 Expr::Literal(rhs.clone(), None),
480 ),
481 (Bound::Included(lhs), Bound::Excluded(rhs)) => col_expr
482 .clone()
483 .gt_eq(Expr::Literal(lhs.clone(), None))
484 .and(col_expr.lt(Expr::Literal(rhs.clone(), None))),
485 (Bound::Excluded(lhs), Bound::Unbounded) => {
486 col_expr.gt(Expr::Literal(lhs.clone(), None))
487 }
488 (Bound::Excluded(lhs), Bound::Included(rhs)) => col_expr
489 .clone()
490 .gt(Expr::Literal(lhs.clone(), None))
491 .and(col_expr.lt_eq(Expr::Literal(rhs.clone(), None))),
492 (Bound::Excluded(lhs), Bound::Excluded(rhs)) => col_expr
493 .clone()
494 .gt(Expr::Literal(lhs.clone(), None))
495 .and(col_expr.lt(Expr::Literal(rhs.clone(), None))),
496 },
497 Self::IsIn(values) => col_expr.in_list(
498 values
499 .iter()
500 .map(|val| Expr::Literal(val.clone(), None))
501 .collect::<Vec<_>>(),
502 false,
503 ),
504 Self::FullTextSearch(query) => col_expr.like(Expr::Literal(
505 ScalarValue::Utf8(Some(query.query.to_string())),
506 None,
507 )),
508 Self::IsNull() => col_expr.is_null(),
509 Self::Equals(value) => col_expr.eq(Expr::Literal(value.clone(), None)),
510 Self::LikePrefix(prefix) => {
511 let pattern = match prefix {
512 ScalarValue::Utf8(Some(s)) => ScalarValue::Utf8(Some(format!("{}%", s))),
513 ScalarValue::LargeUtf8(Some(s)) => {
514 ScalarValue::LargeUtf8(Some(format!("{}%", s)))
515 }
516 other => other.clone(),
517 };
518 col_expr.like(Expr::Literal(pattern, None))
519 }
520 }
521 }
522
523 fn dyn_eq(&self, other: &dyn AnyQuery) -> bool {
524 match other.as_any().downcast_ref::<Self>() {
525 Some(o) => self == o,
526 None => false,
527 }
528 }
529}
530
531#[derive(Debug, Clone, PartialEq)]
533pub enum LabelListQuery {
534 HasAllLabels(Vec<ScalarValue>),
536 HasAnyLabel(Vec<ScalarValue>),
538}
539
540impl AnyQuery for LabelListQuery {
541 fn as_any(&self) -> &dyn Any {
542 self
543 }
544
545 fn format(&self, col: &str) -> String {
546 format!("{}", self.to_expr(col.to_string()))
547 }
548
549 fn to_expr(&self, col: String) -> Expr {
550 match self {
551 Self::HasAllLabels(labels) => {
552 let labels_arr = ScalarValue::iter_to_array(labels.iter().cloned()).unwrap();
553 let offsets_buffer =
554 OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, labels_arr.len() as i32]));
555 let labels_list = ListArray::try_new(
556 Arc::new(Field::new("item", labels_arr.data_type().clone(), true)),
557 offsets_buffer,
558 labels_arr,
559 None,
560 )
561 .unwrap();
562 let labels_arr = Arc::new(labels_list);
563 Expr::ScalarFunction(ScalarFunction {
564 func: Arc::new(array_has::ArrayHasAll::new().into()),
565 args: vec![
566 Expr::Column(Column::new_unqualified(col)),
567 Expr::Literal(ScalarValue::List(labels_arr), None),
568 ],
569 })
570 }
571 Self::HasAnyLabel(labels) => {
572 let labels_arr = ScalarValue::iter_to_array(labels.iter().cloned()).unwrap();
573 let offsets_buffer =
574 OffsetBuffer::new(ScalarBuffer::<i32>::from(vec![0, labels_arr.len() as i32]));
575 let labels_list = ListArray::try_new(
576 Arc::new(Field::new("item", labels_arr.data_type().clone(), true)),
577 offsets_buffer,
578 labels_arr,
579 None,
580 )
581 .unwrap();
582 let labels_arr = Arc::new(labels_list);
583 Expr::ScalarFunction(ScalarFunction {
584 func: Arc::new(array_has::ArrayHasAny::new().into()),
585 args: vec![
586 Expr::Column(Column::new_unqualified(col)),
587 Expr::Literal(ScalarValue::List(labels_arr), None),
588 ],
589 })
590 }
591 }
592 }
593
594 fn dyn_eq(&self, other: &dyn AnyQuery) -> bool {
595 match other.as_any().downcast_ref::<Self>() {
596 Some(o) => self == o,
597 None => false,
598 }
599 }
600}
601
602#[derive(Debug, Clone, PartialEq)]
604pub enum TextQuery {
605 StringContains(String),
607 }
611
612impl AnyQuery for TextQuery {
613 fn as_any(&self) -> &dyn Any {
614 self
615 }
616
617 fn format(&self, col: &str) -> String {
618 format!("{}", self.to_expr(col.to_string()))
619 }
620
621 fn to_expr(&self, col: String) -> Expr {
622 match self {
623 Self::StringContains(substr) => Expr::ScalarFunction(ScalarFunction {
624 func: Arc::new(ContainsFunc::new().into()),
625 args: vec![
626 Expr::Column(Column::new_unqualified(col)),
627 Expr::Literal(ScalarValue::Utf8(Some(substr.clone())), None),
628 ],
629 }),
630 }
631 }
632
633 fn dyn_eq(&self, other: &dyn AnyQuery) -> bool {
634 match other.as_any().downcast_ref::<Self>() {
635 Some(o) => self == o,
636 None => false,
637 }
638 }
639}
640
641#[derive(Debug, Clone, PartialEq)]
643pub enum TokenQuery {
644 TokensContains(String),
647}
648
649#[derive(Debug, Clone, PartialEq)]
654pub enum BloomFilterQuery {
655 Equals(ScalarValue),
657 IsNull(),
659 IsIn(Vec<ScalarValue>),
661}
662
663impl AnyQuery for BloomFilterQuery {
664 fn as_any(&self) -> &dyn Any {
665 self
666 }
667
668 fn format(&self, col: &str) -> String {
669 match self {
670 Self::Equals(val) => {
671 format!("{} = {}", col, val)
672 }
673 Self::IsNull() => {
674 format!("{} IS NULL", col)
675 }
676 Self::IsIn(values) => {
677 format!(
678 "{} IN [{}]",
679 col,
680 values
681 .iter()
682 .map(|val| val.to_string())
683 .collect::<Vec<_>>()
684 .join(",")
685 )
686 }
687 }
688 }
689
690 fn to_expr(&self, col: String) -> Expr {
691 let col_expr = Expr::Column(Column::new_unqualified(col));
692 match self {
693 Self::Equals(value) => col_expr.eq(Expr::Literal(value.clone(), None)),
694 Self::IsNull() => col_expr.is_null(),
695 Self::IsIn(values) => col_expr.in_list(
696 values
697 .iter()
698 .map(|val| Expr::Literal(val.clone(), None))
699 .collect::<Vec<_>>(),
700 false,
701 ),
702 }
703 }
704
705 fn dyn_eq(&self, other: &dyn AnyQuery) -> bool {
706 match other.as_any().downcast_ref::<Self>() {
707 Some(o) => self == o,
708 None => false,
709 }
710 }
711}
712
713impl AnyQuery for TokenQuery {
714 fn as_any(&self) -> &dyn Any {
715 self
716 }
717
718 fn format(&self, col: &str) -> String {
719 format!("{}", self.to_expr(col.to_string()))
720 }
721
722 fn to_expr(&self, col: String) -> Expr {
723 match self {
724 Self::TokensContains(substr) => Expr::ScalarFunction(ScalarFunction {
725 func: Arc::new(CONTAINS_TOKENS_UDF.clone()),
726 args: vec![
727 Expr::Column(Column::new_unqualified(col)),
728 Expr::Literal(ScalarValue::Utf8(Some(substr.clone())), None),
729 ],
730 }),
731 }
732 }
733
734 fn dyn_eq(&self, other: &dyn AnyQuery) -> bool {
735 match other.as_any().downcast_ref::<Self>() {
736 Some(o) => self == o,
737 None => false,
738 }
739 }
740}
741
742#[cfg(feature = "geo")]
743#[derive(Debug, Clone, PartialEq)]
744pub struct RelationQuery {
745 pub value: ScalarValue,
746 pub field: Field,
747}
748
749#[cfg(feature = "geo")]
751#[derive(Debug, Clone, PartialEq)]
752pub enum GeoQuery {
753 IntersectQuery(RelationQuery),
754 IsNull,
755}
756
757#[cfg(feature = "geo")]
758impl AnyQuery for GeoQuery {
759 fn as_any(&self) -> &dyn Any {
760 self
761 }
762
763 fn format(&self, col: &str) -> String {
764 match self {
765 Self::IntersectQuery(query) => {
766 format!("Intersect({} {})", col, query.value)
767 }
768 Self::IsNull => {
769 format!("{} IS NULL", col)
770 }
771 }
772 }
773
774 fn to_expr(&self, _col: String) -> Expr {
775 todo!()
776 }
777
778 fn dyn_eq(&self, other: &dyn AnyQuery) -> bool {
779 match other.as_any().downcast_ref::<Self>() {
780 Some(o) => self == o,
781 None => false,
782 }
783 }
784}
785
786#[derive(Debug, PartialEq)]
788pub enum SearchResult {
789 Exact(NullableRowAddrSet),
791 AtMost(NullableRowAddrSet),
795 AtLeast(NullableRowAddrSet),
800}
801
802impl SearchResult {
803 pub fn exact(row_ids: impl Into<RowAddrTreeMap>) -> Self {
804 Self::Exact(NullableRowAddrSet::new(row_ids.into(), Default::default()))
805 }
806
807 pub fn at_most(row_ids: impl Into<RowAddrTreeMap>) -> Self {
808 Self::AtMost(NullableRowAddrSet::new(row_ids.into(), Default::default()))
809 }
810
811 pub fn at_least(row_ids: impl Into<RowAddrTreeMap>) -> Self {
812 Self::AtLeast(NullableRowAddrSet::new(row_ids.into(), Default::default()))
813 }
814
815 pub fn with_nulls(self, nulls: impl Into<RowAddrTreeMap>) -> Self {
816 match self {
817 Self::Exact(row_ids) => Self::Exact(row_ids.with_nulls(nulls.into())),
818 Self::AtMost(row_ids) => Self::AtMost(row_ids.with_nulls(nulls.into())),
819 Self::AtLeast(row_ids) => Self::AtLeast(row_ids.with_nulls(nulls.into())),
820 }
821 }
822
823 pub fn row_addrs(&self) -> &NullableRowAddrSet {
824 match self {
825 Self::Exact(row_addrs) => row_addrs,
826 Self::AtMost(row_addrs) => row_addrs,
827 Self::AtLeast(row_addrs) => row_addrs,
828 }
829 }
830
831 pub fn is_exact(&self) -> bool {
832 matches!(self, Self::Exact(_))
833 }
834}
835
836pub struct CreatedIndex {
838 pub index_details: prost_types::Any,
843 pub index_version: u32,
847 pub files: Option<Vec<IndexFile>>,
852}
853
854pub struct UpdateCriteria {
856 pub requires_old_data: bool,
860 pub data_criteria: TrainingCriteria,
862}
863
864#[derive(Debug, Clone)]
871pub enum OldIndexDataFilter {
872 Fragments {
876 to_keep: RoaringBitmap,
877 to_remove: RoaringBitmap,
878 },
879 RowIds(RowAddrTreeMap),
884}
885
886impl OldIndexDataFilter {
887 pub fn filter_row_ids(&self, row_ids: &UInt64Array) -> BooleanArray {
889 match self {
890 Self::Fragments { to_keep, .. } => row_ids
891 .iter()
892 .map(|id| id.map(|id| to_keep.contains((id >> 32) as u32)))
893 .collect(),
894 Self::RowIds(valid_row_ids) => row_ids
895 .iter()
896 .map(|id| id.map(|id| valid_row_ids.contains(id)))
897 .collect(),
898 }
899 }
900}
901
902impl UpdateCriteria {
903 pub fn requires_old_data(data_criteria: TrainingCriteria) -> Self {
904 Self {
905 requires_old_data: true,
906 data_criteria,
907 }
908 }
909
910 pub fn only_new_data(data_criteria: TrainingCriteria) -> Self {
911 Self {
912 requires_old_data: false,
913 data_criteria,
914 }
915 }
916}
917
918pub fn compute_next_prefix(prefix: &str) -> Option<String> {
938 if prefix.is_empty() {
939 return None;
940 }
941
942 let chars: Vec<char> = prefix.chars().collect();
943
944 for i in (0..chars.len()).rev() {
946 if let Some(next_char) = next_unicode_char(chars[i]) {
947 let mut result: String = chars[..i].iter().collect();
948 result.push(next_char);
949 return Some(result);
950 }
951 }
953
954 None
956}
957
958fn next_unicode_char(c: char) -> Option<char> {
961 let cp = c as u32;
962 let next_cp = cp.checked_add(1)?;
963
964 let next_cp = if (0xD800..=0xDFFF).contains(&next_cp) {
966 0xE000
967 } else {
968 next_cp
969 };
970
971 char::from_u32(next_cp)
972}
973
974#[async_trait]
976pub trait ScalarIndex: Send + Sync + std::fmt::Debug + Index + DeepSizeOf {
977 async fn search(
981 &self,
982 query: &dyn AnyQuery,
983 metrics: &dyn MetricsCollector,
984 ) -> Result<SearchResult>;
985
986 fn can_remap(&self) -> bool;
988
989 async fn remap(
991 &self,
992 mapping: &HashMap<u64, Option<u64>>,
993 dest_store: &dyn IndexStore,
994 ) -> Result<CreatedIndex>;
995
996 async fn update(
1001 &self,
1002 new_data: SendableRecordBatchStream,
1003 dest_store: &dyn IndexStore,
1004 old_data_filter: Option<OldIndexDataFilter>,
1005 ) -> Result<CreatedIndex>;
1006
1007 fn update_criteria(&self) -> UpdateCriteria;
1009
1010 fn derive_index_params(&self) -> Result<ScalarIndexParams>;
1015}