1use std::collections::HashSet;
16use std::num::NonZeroUsize;
17use std::sync::Arc;
18use std::sync::atomic::AtomicUsize;
19
20use api::v1::SemanticType;
21use common_telemetry::{debug, warn};
22use datatypes::arrow::record_batch::RecordBatch;
23use datatypes::vectors::Helper;
24use index::inverted_index::create::InvertedIndexCreator;
25use index::inverted_index::create::sort::external_sort::ExternalSorter;
26use index::inverted_index::create::sort_create::SortIndexCreator;
27use index::inverted_index::format::writer::InvertedIndexBlobWriter;
28use index::target::IndexTarget;
29use mito_codec::index::{IndexValueCodec, IndexValuesCodec};
30use mito_codec::row_converter::sparse::SparsePrimaryKeyView;
31use mito_codec::row_converter::{SortField, SparseOffsetsCache};
32use puffin::puffin_manager::{PuffinWriter, PutOptions};
33use smallvec::SmallVec;
34use snafu::{ResultExt, ensure};
35use store_api::codec::PrimaryKeyEncoding;
36use store_api::metadata::RegionMetadataRef;
37use store_api::storage::{ColumnId, FileId};
38use tokio::io::duplex;
39use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
40
41use crate::error::{
42 BiErrorsSnafu, DecodeSnafu, EncodeSnafu, IndexFinishSnafu, OperateAbortedIndexSnafu,
43 PuffinAddBlobSnafu, PushIndexValueSnafu, Result,
44};
45use crate::read::Batch;
46use crate::sst::index::TYPE_INVERTED_INDEX;
47use crate::sst::index::column::column_index_rows;
48use crate::sst::index::intermediate::{
49 IntermediateLocation, IntermediateManager, TempFileProvider,
50};
51use crate::sst::index::inverted_index::INDEX_BLOB_TYPE;
52use crate::sst::index::primary_key::PrimaryKeyRuns;
53use crate::sst::index::puffin_manager::SstPuffinWriter;
54use crate::sst::index::statistics::{ByteCount, RowCount, Statistics};
55
56const MIN_MEMORY_USAGE_THRESHOLD_PER_COLUMN: usize = 1024 * 1024; const PIPE_BUFFER_SIZE_FOR_SENDING_BLOB: usize = 8192;
61
62pub struct InvertedIndexer {
64 index_creator: Box<dyn InvertedIndexCreator>,
66 temp_file_provider: Arc<TempFileProvider>,
68
69 codec: IndexValuesCodec,
71 value_buf: Vec<u8>,
73 pk_offsets: SparseOffsetsCache,
75
76 stats: Statistics,
78 aborted: bool,
80
81 memory_usage: Arc<AtomicUsize>,
83
84 indexed_column_ids: Vec<(ColumnId, String)>,
86
87 metadata: RegionMetadataRef,
89}
90
91impl InvertedIndexer {
92 pub fn new(
95 sst_file_id: FileId,
96 metadata: &RegionMetadataRef,
97 intermediate_manager: IntermediateManager,
98 memory_usage_threshold: Option<usize>,
99 segment_row_count: NonZeroUsize,
100 indexed_column_ids: HashSet<ColumnId>,
101 ) -> Self {
102 let temp_file_provider = Arc::new(TempFileProvider::new(
103 IntermediateLocation::new(&metadata.region_id, &sst_file_id),
104 intermediate_manager,
105 ));
106
107 let memory_usage = Arc::new(AtomicUsize::new(0));
108
109 let sorter = ExternalSorter::factory(
110 temp_file_provider.clone() as _,
111 Some(MIN_MEMORY_USAGE_THRESHOLD_PER_COLUMN),
112 memory_usage.clone(),
113 memory_usage_threshold,
114 );
115 let index_creator = Box::new(SortIndexCreator::new(sorter, segment_row_count));
116
117 let codec = IndexValuesCodec::from_tag_columns(
118 metadata.primary_key_encoding,
119 metadata.primary_key_columns(),
120 );
121 let indexed_column_ids = indexed_column_ids
122 .into_iter()
123 .map(|col_id| {
124 let target_key = format!("{}", IndexTarget::ColumnId(col_id));
125 (col_id, target_key)
126 })
127 .collect();
128 Self {
129 codec,
130 index_creator,
131 temp_file_provider,
132 value_buf: vec![],
133 pk_offsets: SparseOffsetsCache::new(),
134 stats: Statistics::new(TYPE_INVERTED_INDEX),
135 aborted: false,
136 memory_usage,
137 indexed_column_ids,
138 metadata: metadata.clone(),
139 }
140 }
141
142 pub async fn update(&mut self, batch: &mut Batch) -> Result<()> {
145 ensure!(!self.aborted, OperateAbortedIndexSnafu);
146
147 if batch.is_empty() {
148 return Ok(());
149 }
150
151 if let Err(update_err) = self.do_update(batch).await {
152 if let Err(err) = self.do_cleanup().await {
154 if cfg!(any(test, feature = "test")) {
155 panic!("Failed to clean up index creator, err: {err}",);
156 } else {
157 warn!(err; "Failed to clean up index creator");
158 }
159 }
160 return Err(update_err);
161 }
162
163 Ok(())
164 }
165
166 pub async fn update_flat(&mut self, batch: &RecordBatch) -> Result<()> {
168 ensure!(!self.aborted, OperateAbortedIndexSnafu);
169
170 if batch.num_rows() == 0 {
171 return Ok(());
172 }
173
174 self.do_update_flat(batch).await
175 }
176
177 async fn do_update_flat(&mut self, batch: &RecordBatch) -> Result<()> {
178 let mut guard = self.stats.record_update();
179
180 guard.inc_row_count(batch.num_rows());
181
182 let is_sparse = self.metadata.primary_key_encoding == PrimaryKeyEncoding::Sparse;
183 let mut sparse_columns: SmallVec<[(ColumnId, &str); 8]> = SmallVec::new();
184
185 for (col_id, target_key) in &self.indexed_column_ids {
186 let Some(column_meta) = self.metadata.column_by_id(*col_id) else {
187 debug!(
188 "Column {} not found in the metadata during building inverted index",
189 col_id
190 );
191 continue;
192 };
193 let column_name = &column_meta.column_schema.name;
194 if let Some(column_array) = batch.column_by_name(column_name) {
195 let vector = Helper::try_into_vector(column_array.clone())
197 .context(crate::error::ConvertVectorSnafu)?;
198 let sort_field = SortField::new(vector.data_type());
199
200 for (row, count) in column_index_rows(batch, column_meta.semantic_type) {
201 let elem = IndexValueCodec::encode_value(
202 vector.get_ref(row),
203 &sort_field,
204 &mut self.value_buf,
205 )
206 .context(EncodeSnafu)?;
207 if self.index_creator.push_with_name_n(target_key, elem, count) {
208 self.index_creator
209 .spill()
210 .await
211 .context(PushIndexValueSnafu)?;
212 }
213 }
214 } else if is_sparse && column_meta.semantic_type == SemanticType::Tag {
215 if self.codec.pk_col_info(*col_id).is_some() {
216 sparse_columns.push((*col_id, target_key));
217 }
218 } else {
219 debug!(
220 "Column {} not found in the batch during building inverted index",
221 col_id
222 );
223 }
224 }
225
226 if !sparse_columns.is_empty() {
227 for (pk, count) in PrimaryKeyRuns::try_new(batch)? {
228 let mut view =
229 SparsePrimaryKeyView::new(pk, &mut self.pk_offsets).context(DecodeSnafu)?;
230 for &(col_id, target_key) in &sparse_columns {
232 let value = IndexValueCodec::encode_sparse_value(
233 &mut view,
234 col_id,
235 &mut self.value_buf,
236 )
237 .context(DecodeSnafu)?;
238 if self
239 .index_creator
240 .push_with_name_n(target_key, value, count)
241 {
242 self.index_creator
243 .spill()
244 .await
245 .context(PushIndexValueSnafu)?;
246 }
247 }
248 }
249 }
250
251 Ok(())
252 }
253
254 pub(crate) async fn finish(
257 &mut self,
258 puffin_writer: &mut SstPuffinWriter,
259 ) -> Result<(RowCount, ByteCount)> {
260 ensure!(!self.aborted, OperateAbortedIndexSnafu);
261
262 if self.stats.row_count() == 0 {
263 return Ok((0, 0));
265 }
266
267 let finish_res = self.do_finish(puffin_writer).await;
268 if let Err(err) = self.do_cleanup().await {
270 if cfg!(any(test, feature = "test")) {
271 panic!("Failed to clean up index creator, err: {err}",);
272 } else {
273 warn!(err; "Failed to clean up index creator");
274 }
275 }
276
277 finish_res.map(|_| (self.stats.row_count(), self.stats.byte_count()))
278 }
279
280 pub async fn abort(&mut self) -> Result<()> {
282 if self.aborted {
283 return Ok(());
284 }
285 self.aborted = true;
286
287 self.do_cleanup().await
288 }
289
290 async fn do_update(&mut self, batch: &mut Batch) -> Result<()> {
291 let mut guard = self.stats.record_update();
292
293 let n = batch.num_rows();
294 guard.inc_row_count(n);
295
296 for (col_id, target_key) in &self.indexed_column_ids {
297 match self.codec.pk_col_info(*col_id) {
298 Some(col_info) => {
300 let pk_idx = col_info.idx;
301 let field = &col_info.field;
302 let value = batch
303 .pk_col_value(self.codec.decoder(), pk_idx, *col_id)?
304 .filter(|v| !v.is_null())
305 .map(|v| {
306 self.value_buf.clear();
307 IndexValueCodec::encode_nonnull_value(
308 v.as_value_ref(),
309 field,
310 &mut self.value_buf,
311 )
312 .context(EncodeSnafu)?;
313 Ok(self.value_buf.as_slice())
314 })
315 .transpose()?;
316
317 if self.index_creator.push_with_name_n(target_key, value, n) {
318 self.index_creator
319 .spill()
320 .await
321 .context(PushIndexValueSnafu)?;
322 }
323 }
324 None => {
326 let Some(values) = batch.field_col_value(*col_id) else {
327 debug!(
328 "Column {} not found in the batch during building inverted index",
329 col_id
330 );
331 continue;
332 };
333 let sort_field = SortField::new(values.data.data_type());
334 for i in 0..n {
335 self.value_buf.clear();
336 let value = values.data.get_ref(i);
337 if value.is_null() {
338 if self.index_creator.push_with_name(target_key, None) {
339 self.index_creator
340 .spill()
341 .await
342 .context(PushIndexValueSnafu)?;
343 }
344 } else {
345 IndexValueCodec::encode_nonnull_value(
346 value,
347 &sort_field,
348 &mut self.value_buf,
349 )
350 .context(EncodeSnafu)?;
351 if self
352 .index_creator
353 .push_with_name(target_key, Some(&self.value_buf))
354 {
355 self.index_creator
356 .spill()
357 .await
358 .context(PushIndexValueSnafu)?;
359 }
360 }
361 }
362 }
363 }
364 }
365
366 Ok(())
367 }
368
369 async fn do_finish(&mut self, puffin_writer: &mut SstPuffinWriter) -> Result<()> {
388 let mut guard = self.stats.record_finish();
389
390 let (tx, rx) = duplex(PIPE_BUFFER_SIZE_FOR_SENDING_BLOB);
391 let mut index_writer = InvertedIndexBlobWriter::new(tx.compat_write());
392
393 let (index_finish, puffin_add_blob) = futures::join!(
394 self.index_creator
396 .finish(&mut index_writer, index::bitmap::BitmapType::Roaring),
397 puffin_writer.put_blob(
398 INDEX_BLOB_TYPE,
399 rx.compat(),
400 PutOptions::default(),
401 Default::default(),
402 )
403 );
404
405 match (
406 puffin_add_blob.context(PuffinAddBlobSnafu),
407 index_finish.context(IndexFinishSnafu),
408 ) {
409 (Err(e1), Err(e2)) => BiErrorsSnafu {
410 first: Box::new(e1),
411 second: Box::new(e2),
412 }
413 .fail()?,
414
415 (Ok(_), e @ Err(_)) => e?,
416 (e @ Err(_), Ok(_)) => e.map(|_| ())?,
417 (Ok(written_bytes), Ok(_)) => {
418 guard.inc_byte_count(written_bytes);
419 }
420 }
421
422 Ok(())
423 }
424
425 async fn do_cleanup(&mut self) -> Result<()> {
426 let _guard = self.stats.record_cleanup();
427
428 self.temp_file_provider.cleanup().await
429 }
430
431 pub fn column_ids(&self) -> impl Iterator<Item = ColumnId> + '_ {
432 self.indexed_column_ids.iter().map(|(col_id, _)| *col_id)
433 }
434
435 pub fn memory_usage(&self) -> usize {
436 self.memory_usage.load(std::sync::atomic::Ordering::Relaxed)
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use std::collections::BTreeSet;
443
444 use api::v1::SemanticType;
445 use datafusion_expr::{Expr as DfExpr, Operator, binary_expr, col, lit};
446 use datatypes::data_type::ConcreteDataType;
447 use datatypes::schema::ColumnSchema;
448 use datatypes::value::ValueRef;
449 use datatypes::vectors::{UInt8Vector, UInt64Vector};
450 use futures::future::BoxFuture;
451 use mito_codec::row_converter::{DensePrimaryKeyCodec, PrimaryKeyCodecExt};
452 use object_store::ObjectStore;
453 use object_store::services::Memory;
454 use puffin::puffin_manager::PuffinManager;
455 use puffin::puffin_manager::cache::PuffinMetadataCache;
456 use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder};
457 use store_api::region_request::PathType;
458 use store_api::storage::RegionId;
459
460 use super::*;
461 use crate::access_layer::RegionFilePathFactory;
462 use crate::cache::index::inverted_index::InvertedIndexCache;
463 use crate::metrics::CACHE_BYTES;
464 use crate::read::BatchColumn;
465 use crate::sst::file::{RegionFileId, RegionIndexId};
466 use crate::sst::index::inverted_index::applier::builder::InvertedIndexApplierBuilder;
467 use crate::sst::index::puffin_manager::PuffinManagerFactory;
468
469 fn mock_object_store() -> ObjectStore {
470 ObjectStore::new(Memory::default()).unwrap()
471 }
472
473 async fn new_intm_mgr(path: impl AsRef<str>) -> IntermediateManager {
474 IntermediateManager::init_fs(path).await.unwrap()
475 }
476
477 fn mock_region_metadata() -> RegionMetadataRef {
478 let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 2));
479 builder
480 .push_column_metadata(ColumnMetadata {
481 column_schema: ColumnSchema::new(
482 "tag_str",
483 ConcreteDataType::string_datatype(),
484 false,
485 ),
486 semantic_type: SemanticType::Tag,
487 column_id: 1,
488 })
489 .push_column_metadata(ColumnMetadata {
490 column_schema: ColumnSchema::new(
491 "tag_i32",
492 ConcreteDataType::int32_datatype(),
493 false,
494 ),
495 semantic_type: SemanticType::Tag,
496 column_id: 2,
497 })
498 .push_column_metadata(ColumnMetadata {
499 column_schema: ColumnSchema::new(
500 "ts",
501 ConcreteDataType::timestamp_millisecond_datatype(),
502 false,
503 ),
504 semantic_type: SemanticType::Timestamp,
505 column_id: 3,
506 })
507 .push_column_metadata(ColumnMetadata {
508 column_schema: ColumnSchema::new(
509 "field_u64",
510 ConcreteDataType::uint64_datatype(),
511 false,
512 ),
513 semantic_type: SemanticType::Field,
514 column_id: 4,
515 })
516 .primary_key(vec![1, 2]);
517
518 Arc::new(builder.build().unwrap())
519 }
520
521 fn new_batch(
522 str_tag: impl AsRef<str>,
523 i32_tag: impl Into<i32>,
524 u64_field: impl IntoIterator<Item = u64>,
525 ) -> Batch {
526 let fields = vec![
527 (0, SortField::new(ConcreteDataType::string_datatype())),
528 (1, SortField::new(ConcreteDataType::int32_datatype())),
529 ];
530 let codec = DensePrimaryKeyCodec::with_fields(fields);
531 let row: [ValueRef; 2] = [str_tag.as_ref().into(), i32_tag.into().into()];
532 let primary_key = codec.encode(row.into_iter()).unwrap();
533
534 let u64_field = BatchColumn {
535 column_id: 4,
536 data: Arc::new(UInt64Vector::from_iter_values(u64_field)),
537 };
538 let num_rows = u64_field.data.len();
539
540 Batch::new(
541 primary_key,
542 Arc::new(UInt64Vector::from_iter_values(std::iter::repeat_n(
543 0, num_rows,
544 ))),
545 Arc::new(UInt64Vector::from_iter_values(std::iter::repeat_n(
546 0, num_rows,
547 ))),
548 Arc::new(UInt8Vector::from_iter_values(std::iter::repeat_n(
549 1, num_rows,
550 ))),
551 vec![u64_field],
552 )
553 .unwrap()
554 }
555
556 async fn build_applier_factory(
557 prefix: &str,
558 rows: BTreeSet<(&'static str, i32, [u64; 2])>,
559 sst_indexed_column_ids: HashSet<ColumnId>,
560 ) -> impl Fn(DfExpr) -> BoxFuture<'static, Vec<usize>> {
561 let (d, factory) = PuffinManagerFactory::new_for_test_async(prefix).await;
562 let table_dir = "table0".to_string();
563 let sst_file_id = FileId::random();
564 let object_store = mock_object_store();
565 let region_metadata = mock_region_metadata();
566 let intm_mgr = new_intm_mgr(d.path().to_string_lossy()).await;
567 let memory_threshold = None;
568 let segment_row_count = 2;
569 let indexed_column_ids = HashSet::from_iter([1, 2, 4]);
570
571 let mut creator = InvertedIndexer::new(
572 sst_file_id,
573 ®ion_metadata,
574 intm_mgr,
575 memory_threshold,
576 NonZeroUsize::new(segment_row_count).unwrap(),
577 sst_indexed_column_ids,
578 );
579
580 for (str_tag, i32_tag, u64_field) in &rows {
581 let mut batch = new_batch(str_tag, *i32_tag, u64_field.iter().copied());
582 creator.update(&mut batch).await.unwrap();
583 }
584
585 let puffin_manager = factory.build(
586 object_store.clone(),
587 RegionFilePathFactory::new(table_dir.clone(), PathType::Bare),
588 );
589
590 let sst_file_id = RegionFileId::new(region_metadata.region_id, sst_file_id);
591 let index_id = RegionIndexId::new(sst_file_id, 0);
592 let mut writer = puffin_manager.writer(&index_id).await.unwrap();
593 let (row_count, _) = creator.finish(&mut writer).await.unwrap();
594 assert_eq!(row_count, rows.len() * segment_row_count);
595 writer.finish().await.unwrap();
596
597 move |expr| {
598 let _d = &d;
599 let cache = Arc::new(InvertedIndexCache::new(10, 10, 100));
600 let puffin_metadata_cache = Arc::new(PuffinMetadataCache::new(10, &CACHE_BYTES));
601 let applier = InvertedIndexApplierBuilder::new(
602 table_dir.clone(),
603 PathType::Bare,
604 object_store.clone(),
605 ®ion_metadata,
606 indexed_column_ids.clone(),
607 factory.clone(),
608 )
609 .with_inverted_index_cache(Some(cache))
610 .with_puffin_metadata_cache(Some(puffin_metadata_cache))
611 .build(&[expr])
612 .unwrap()
613 .unwrap();
614 let sst_metadata = Arc::new(region_metadata.clone());
615 let plan = applier.plan_for_sst(&sst_metadata).unwrap().unwrap();
616 Box::pin(async move {
617 applier
618 .apply(index_id, None, &plan.index_applier, None)
619 .await
620 .unwrap()
621 .matched_segment_ids
622 .iter_ones()
623 .collect()
624 })
625 }
626 }
627
628 #[tokio::test]
629 async fn test_create_and_query_missing_index() {
630 let rows = BTreeSet::from_iter([("aaa", 1, [1, 2]), ("bbb", 2, [3, 4])]);
631 let applier_factory = build_applier_factory(
633 "test_create_and_query_missing_index_",
634 rows,
635 HashSet::from([1]),
636 )
637 .await;
638
639 let cases = [
640 (col("tag_i32").eq(lit(1)), vec![0, 1]),
641 (
642 col("tag_i32")
643 .eq(lit(1))
644 .and(col("field_u64").eq(lit(1u64))),
645 vec![0, 1],
646 ),
647 (
648 col("tag_str").eq(lit("aaa")).and(col("tag_i32").eq(lit(1))),
649 vec![0],
650 ),
651 (
652 col("tag_str")
653 .eq(lit("missing"))
654 .and(col("tag_i32").eq(lit(1))),
655 vec![],
656 ),
657 ];
658 for (expr, expected) in cases {
659 let actual = applier_factory(expr.clone()).await;
660 assert_eq!(actual, expected, "{expr}");
661 }
662 }
663
664 #[tokio::test]
665 async fn test_create_and_query_get_key() {
666 let rows = BTreeSet::from_iter([
667 ("aaa", 1, [1, 2]),
668 ("aaa", 2, [2, 3]),
669 ("aaa", 3, [3, 4]),
670 ("aab", 1, [4, 5]),
671 ("aab", 2, [5, 6]),
672 ("aab", 3, [6, 7]),
673 ("abc", 1, [7, 8]),
674 ("abc", 2, [8, 9]),
675 ("abc", 3, [9, 10]),
676 ]);
677
678 let applier_factory = build_applier_factory(
679 "test_create_and_query_get_key_",
680 rows,
681 HashSet::from([1, 2, 4]),
682 )
683 .await;
684
685 let expr = col("tag_str").eq(lit("aaa"));
686 let res = applier_factory(expr).await;
687 assert_eq!(res, vec![0, 1, 2]);
688
689 let expr = col("tag_i32").eq(lit(2));
690 let res = applier_factory(expr).await;
691 assert_eq!(res, vec![1, 4, 7]);
692
693 let expr = col("tag_str").eq(lit("aaa")).and(col("tag_i32").eq(lit(2)));
694 let res = applier_factory(expr).await;
695 assert_eq!(res, vec![1]);
696
697 let expr = col("tag_str")
698 .eq(lit("aaa"))
699 .or(col("tag_str").eq(lit("abc")));
700 let res = applier_factory(expr).await;
701 assert_eq!(res, vec![0, 1, 2, 6, 7, 8]);
702
703 let expr = col("tag_str").in_list(vec![lit("aaa"), lit("abc")], false);
704 let res = applier_factory(expr).await;
705 assert_eq!(res, vec![0, 1, 2, 6, 7, 8]);
706
707 let expr = col("field_u64").eq(lit(2u64));
708 let res = applier_factory(expr).await;
709 assert_eq!(res, vec![0, 1]);
710 }
711
712 #[tokio::test]
713 async fn test_create_and_query_range() {
714 let rows = BTreeSet::from_iter([
715 ("aaa", 1, [1, 2]),
716 ("aaa", 2, [2, 3]),
717 ("aaa", 3, [3, 4]),
718 ("aab", 1, [4, 5]),
719 ("aab", 2, [5, 6]),
720 ("aab", 3, [6, 7]),
721 ("abc", 1, [7, 8]),
722 ("abc", 2, [8, 9]),
723 ("abc", 3, [9, 10]),
724 ]);
725
726 let applier_factory = build_applier_factory(
727 "test_create_and_query_range_",
728 rows,
729 HashSet::from([1, 2, 4]),
730 )
731 .await;
732
733 let expr = col("tag_str").between(lit("aaa"), lit("aab"));
734 let res = applier_factory(expr).await;
735 assert_eq!(res, vec![0, 1, 2, 3, 4, 5]);
736
737 let expr = col("tag_i32").between(lit(2), lit(3));
738 let res = applier_factory(expr).await;
739 assert_eq!(res, vec![1, 2, 4, 5, 7, 8]);
740
741 let expr = col("tag_str").between(lit("aaa"), lit("aaa"));
742 let res = applier_factory(expr).await;
743 assert_eq!(res, vec![0, 1, 2]);
744
745 let expr = col("tag_i32").between(lit(2), lit(2));
746 let res = applier_factory(expr).await;
747 assert_eq!(res, vec![1, 4, 7]);
748
749 let expr = col("field_u64").between(lit(2u64), lit(5u64));
750 let res = applier_factory(expr).await;
751 assert_eq!(res, vec![0, 1, 2, 3, 4]);
752 }
753
754 #[tokio::test]
755 async fn test_create_and_query_comparison() {
756 let rows = BTreeSet::from_iter([
757 ("aaa", 1, [1, 2]),
758 ("aaa", 2, [2, 3]),
759 ("aaa", 3, [3, 4]),
760 ("aab", 1, [4, 5]),
761 ("aab", 2, [5, 6]),
762 ("aab", 3, [6, 7]),
763 ("abc", 1, [7, 8]),
764 ("abc", 2, [8, 9]),
765 ("abc", 3, [9, 10]),
766 ]);
767
768 let applier_factory = build_applier_factory(
769 "test_create_and_query_comparison_",
770 rows,
771 HashSet::from([1, 2, 4]),
772 )
773 .await;
774
775 let expr = col("tag_str").lt(lit("aab"));
776 let res = applier_factory(expr).await;
777 assert_eq!(res, vec![0, 1, 2]);
778
779 let expr = col("tag_i32").lt(lit(2));
780 let res = applier_factory(expr).await;
781 assert_eq!(res, vec![0, 3, 6]);
782
783 let expr = col("field_u64").lt(lit(2u64));
784 let res = applier_factory(expr).await;
785 assert_eq!(res, vec![0]);
786
787 let expr = col("tag_str").gt(lit("aab"));
788 let res = applier_factory(expr).await;
789 assert_eq!(res, vec![6, 7, 8]);
790
791 let expr = col("tag_i32").gt(lit(2));
792 let res = applier_factory(expr).await;
793 assert_eq!(res, vec![2, 5, 8]);
794
795 let expr = col("field_u64").gt(lit(8u64));
796 let res = applier_factory(expr).await;
797 assert_eq!(res, vec![7, 8]);
798
799 let expr = col("tag_str").lt_eq(lit("aab"));
800 let res = applier_factory(expr).await;
801 assert_eq!(res, vec![0, 1, 2, 3, 4, 5]);
802
803 let expr = col("tag_i32").lt_eq(lit(2));
804 let res = applier_factory(expr).await;
805 assert_eq!(res, vec![0, 1, 3, 4, 6, 7]);
806
807 let expr = col("field_u64").lt_eq(lit(2u64));
808 let res = applier_factory(expr).await;
809 assert_eq!(res, vec![0, 1]);
810
811 let expr = col("tag_str").gt_eq(lit("aab"));
812 let res = applier_factory(expr).await;
813 assert_eq!(res, vec![3, 4, 5, 6, 7, 8]);
814
815 let expr = col("tag_i32").gt_eq(lit(2));
816 let res = applier_factory(expr).await;
817 assert_eq!(res, vec![1, 2, 4, 5, 7, 8]);
818
819 let expr = col("field_u64").gt_eq(lit(8u64));
820 let res = applier_factory(expr).await;
821 assert_eq!(res, vec![6, 7, 8]);
822
823 let expr = col("tag_str")
824 .gt(lit("aaa"))
825 .and(col("tag_str").lt(lit("abc")));
826 let res = applier_factory(expr).await;
827 assert_eq!(res, vec![3, 4, 5]);
828
829 let expr = col("tag_i32").gt(lit(1)).and(col("tag_i32").lt(lit(3)));
830 let res = applier_factory(expr).await;
831 assert_eq!(res, vec![1, 4, 7]);
832
833 let expr = col("field_u64")
834 .gt(lit(2u64))
835 .and(col("field_u64").lt(lit(9u64)));
836 let res = applier_factory(expr).await;
837 assert_eq!(res, vec![1, 2, 3, 4, 5, 6, 7]);
838 }
839
840 #[tokio::test]
841 async fn test_create_and_query_regex() {
842 let rows = BTreeSet::from_iter([
843 ("aaa", 1, [1, 2]),
844 ("aaa", 2, [2, 3]),
845 ("aaa", 3, [3, 4]),
846 ("aab", 1, [4, 5]),
847 ("aab", 2, [5, 6]),
848 ("aab", 3, [6, 7]),
849 ("abc", 1, [7, 8]),
850 ("abc", 2, [8, 9]),
851 ("abc", 3, [9, 10]),
852 ]);
853
854 let applier_factory = build_applier_factory(
855 "test_create_and_query_regex_",
856 rows,
857 HashSet::from([1, 2, 4]),
858 )
859 .await;
860
861 let expr = binary_expr(col("tag_str"), Operator::RegexMatch, lit(".*"));
862 let res = applier_factory(expr).await;
863 assert_eq!(res, vec![0, 1, 2, 3, 4, 5, 6, 7, 8]);
864
865 let expr = binary_expr(col("tag_str"), Operator::RegexMatch, lit("a.*c"));
866 let res = applier_factory(expr).await;
867 assert_eq!(res, vec![6, 7, 8]);
868
869 let expr = binary_expr(col("tag_str"), Operator::RegexMatch, lit("a.*b$"));
870 let res = applier_factory(expr).await;
871 assert_eq!(res, vec![3, 4, 5]);
872
873 let expr = binary_expr(col("tag_str"), Operator::RegexMatch, lit("\\w"));
874 let res = applier_factory(expr).await;
875 assert_eq!(res, vec![0, 1, 2, 3, 4, 5, 6, 7, 8]);
876
877 let expr = binary_expr(col("tag_str"), Operator::RegexMatch, lit("\\d"));
878 let res = applier_factory(expr).await;
879 assert!(res.is_empty());
880
881 let expr = binary_expr(col("tag_str"), Operator::RegexMatch, lit("^aaa$"));
882 let res = applier_factory(expr).await;
883 assert_eq!(res, vec![0, 1, 2]);
884 }
885}