mod immutable_numeric_index; mod mmap_numeric_index; mod mutable_numeric_index; #[cfg(test)] mod tests; use std::cmp::{max, min}; use std::marker::PhantomData; use std::ops::Bound; use std::ops::Bound::{Excluded, Included, Unbounded}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::sync::Arc; use chrono::DateTime; use common::types::PointOffsetType; use delegate::delegate; use mmap_numeric_index::MmapNumericIndex; use mutable_numeric_index::{InMemoryNumericIndex, MutableNumericIndex}; use parking_lot::RwLock; use rocksdb::DB; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json::Value; use uuid::Uuid; use self::immutable_numeric_index::ImmutableNumericIndex; use super::histogram::Point; use super::mmap_point_to_values::MmapValue; use super::utils::check_boundaries; use super::FieldIndexBuilderTrait; use crate::common::operation_error::{OperationError, OperationResult}; use crate::common::Flusher; use crate::index::field_index::histogram::{Histogram, Numericable}; use crate::index::field_index::stat_tools::estimate_multi_value_selection_cardinality; use crate::index::field_index::{ CardinalityEstimation, PayloadBlockCondition, PayloadFieldIndex, PrimaryCondition, ValueIndexer, }; use crate::index::key_encoding::{ decode_f64_key_ascending, decode_i64_key_ascending, decode_u128_key_ascending, encode_f64_key_ascending, encode_i64_key_ascending, encode_u128_key_ascending, }; use crate::telemetry::PayloadIndexTelemetry; use crate::types::{ DateTimePayloadType, FieldCondition, FloatPayloadType, IntPayloadType, Match, MatchValue, PayloadKeyType, Range, RangeInterface, UuidIntType, UuidPayloadType, ValueVariants, }; const HISTOGRAM_MAX_BUCKET_SIZE: usize = 10_000; const HISTOGRAM_PRECISION: f64 = 0.01; pub trait StreamRange { fn stream_range( &self, range: &RangeInterface, ) -> Box + '_>; } pub trait Encodable: Copy + Serialize + DeserializeOwned + 'static { fn encode_key(&self, id: PointOffsetType) -> Vec; fn decode_key(key: &[u8]) -> (PointOffsetType, Self); fn cmp_encoded(&self, other: &Self) -> std::cmp::Ordering; } impl Encodable for IntPayloadType { fn encode_key(&self, id: PointOffsetType) -> Vec { encode_i64_key_ascending(*self, id) } fn decode_key(key: &[u8]) -> (PointOffsetType, Self) { decode_i64_key_ascending(key) } fn cmp_encoded(&self, other: &Self) -> std::cmp::Ordering { self.cmp(other) } } impl Encodable for u128 { fn encode_key(&self, id: PointOffsetType) -> Vec { encode_u128_key_ascending(*self, id) } fn decode_key(key: &[u8]) -> (PointOffsetType, Self) { decode_u128_key_ascending(key) } fn cmp_encoded(&self, other: &Self) -> std::cmp::Ordering { self.cmp(other) } } impl Encodable for FloatPayloadType { fn encode_key(&self, id: PointOffsetType) -> Vec { encode_f64_key_ascending(*self, id) } fn decode_key(key: &[u8]) -> (PointOffsetType, Self) { decode_f64_key_ascending(key) } fn cmp_encoded(&self, other: &Self) -> std::cmp::Ordering { if self.is_nan() && other.is_nan() { return std::cmp::Ordering::Equal; } if self.is_nan() { return std::cmp::Ordering::Less; } if other.is_nan() { return std::cmp::Ordering::Greater; } self.partial_cmp(other).unwrap() } } /// Encodes timestamps as i64 in microseconds impl Encodable for DateTimePayloadType { fn encode_key(&self, id: PointOffsetType) -> Vec { encode_i64_key_ascending(self.timestamp(), id) } fn decode_key(key: &[u8]) -> (PointOffsetType, Self) { let (id, timestamp) = decode_i64_key_ascending(key); let datetime = DateTime::from_timestamp(timestamp / 1000, (timestamp % 1000) as u32 * 1_000_000) .unwrap_or_else(|| { log::warn!("Failed to decode timestamp {timestamp}, fallback to UNIX_EPOCH"); DateTime::UNIX_EPOCH }); (id, datetime.into()) } fn cmp_encoded(&self, other: &Self) -> std::cmp::Ordering { self.timestamp().cmp(&other.timestamp()) } } impl Range { pub(in crate::index::field_index::numeric_index) fn as_index_key_bounds( &self, ) -> (Bound>, Bound>) { let start_bound = match self { Range { gt: Some(gt), .. } => Excluded(Point::new(*gt, PointOffsetType::MAX)), Range { gte: Some(gte), .. } => Included(Point::new(*gte, PointOffsetType::MIN)), _ => Unbounded, }; let end_bound = match self { Range { lt: Some(lt), .. } => Excluded(Point::new(*lt, PointOffsetType::MIN)), Range { lte: Some(lte), .. } => Included(Point::new(*lte, PointOffsetType::MAX)), _ => Unbounded, }; (start_bound, end_bound) } } pub enum NumericIndexInner { Mutable(MutableNumericIndex), Immutable(ImmutableNumericIndex), Mmap(MmapNumericIndex), } impl NumericIndexInner { pub fn new_memory(db: Arc>, field: &str, is_appendable: bool) -> Self { if is_appendable { NumericIndexInner::Mutable(MutableNumericIndex::new(db, field)) } else { NumericIndexInner::Immutable(ImmutableNumericIndex::new(db, field)) } } pub fn new_mmap(path: &Path) -> OperationResult { Ok(NumericIndexInner::Mmap(MmapNumericIndex::load(path)?)) } fn get_histogram(&self) -> &Histogram { match self { NumericIndexInner::Mutable(index) => index.get_histogram(), NumericIndexInner::Immutable(index) => index.get_histogram(), NumericIndexInner::Mmap(index) => index.get_histogram(), } } fn get_points_count(&self) -> usize { match self { NumericIndexInner::Mutable(index) => index.get_points_count(), NumericIndexInner::Immutable(index) => index.get_points_count(), NumericIndexInner::Mmap(index) => index.get_points_count(), } } fn total_unique_values_count(&self) -> usize { match self { NumericIndexInner::Mutable(index) => index.total_unique_values_count(), NumericIndexInner::Immutable(index) => index.total_unique_values_count(), NumericIndexInner::Mmap(index) => index.total_unique_values_count(), } } pub fn load(&mut self) -> OperationResult { match self { NumericIndexInner::Mutable(index) => index.load(), NumericIndexInner::Immutable(index) => index.load(), NumericIndexInner::Mmap(_) => { // Mmap index is always loaded Ok(true) } } } pub fn flusher(&self) -> Flusher { match self { NumericIndexInner::Mutable(index) => index.get_db_wrapper().flusher(), NumericIndexInner::Immutable(index) => index.get_db_wrapper().flusher(), NumericIndexInner::Mmap(index) => index.flusher(), } } pub fn files(&self) -> Vec { match self { NumericIndexInner::Mutable(_) => vec![], NumericIndexInner::Immutable(_) => vec![], NumericIndexInner::Mmap(index) => index.files(), } } pub fn remove_point(&mut self, idx: PointOffsetType) -> OperationResult<()> { match self { NumericIndexInner::Mutable(index) => index.remove_point(idx), NumericIndexInner::Immutable(index) => index.remove_point(idx), NumericIndexInner::Mmap(index) => { index.remove_point(idx); Ok(()) } } } pub fn check_values_any(&self, idx: PointOffsetType, check_fn: impl Fn(&T) -> bool) -> bool { match self { NumericIndexInner::Mutable(index) => index.check_values_any(idx, check_fn), NumericIndexInner::Immutable(index) => index.check_values_any(idx, check_fn), NumericIndexInner::Mmap(index) => index.check_values_any(idx, check_fn), } } pub fn get_values(&self, idx: PointOffsetType) -> Option + '_>> { match self { NumericIndexInner::Mutable(index) => index.get_values(idx), NumericIndexInner::Immutable(index) => index.get_values(idx), NumericIndexInner::Mmap(index) => index.get_values(idx), } } pub fn values_count(&self, idx: PointOffsetType) -> usize { match self { NumericIndexInner::Mutable(index) => index.values_count(idx).unwrap_or_default(), NumericIndexInner::Immutable(index) => index.values_count(idx).unwrap_or_default(), NumericIndexInner::Mmap(index) => index.values_count(idx).unwrap_or_default(), } } /// Maximum number of values per point /// /// # Warning /// /// Zero if the index is empty. pub fn max_values_per_point(&self) -> usize { match self { NumericIndexInner::Mutable(index) => index.get_max_values_per_point(), NumericIndexInner::Immutable(index) => index.get_max_values_per_point(), NumericIndexInner::Mmap(index) => index.get_max_values_per_point(), } } fn range_cardinality(&self, range: &RangeInterface) -> CardinalityEstimation { let max_values_per_point = self.max_values_per_point(); if max_values_per_point == 0 { return CardinalityEstimation::exact(0); } let range = match range { RangeInterface::Float(float_range) => float_range.map(T::from_f64), RangeInterface::DateTime(datetime_range) => { datetime_range.map(|dt| T::from_u128(dt.timestamp() as u128)) } }; let lbound = if let Some(lte) = range.lte { Included(lte) } else if let Some(lt) = range.lt { Excluded(lt) } else { Unbounded }; let gbound = if let Some(gte) = range.gte { Included(gte) } else if let Some(gt) = range.gt { Excluded(gt) } else { Unbounded }; let histogram_estimation = self.get_histogram().estimate(gbound, lbound); let min_estimation = histogram_estimation.0; let max_estimation = histogram_estimation.2; let total_values = self.total_unique_values_count(); // Example: points_count = 1000, total values = 2000, values_count = 500 // min = max(1, 500 - (2000 - 1000)) = 1 // exp = 500 / (2000 / 1000) = 250 // max = min(1000, 500) = 500 // Example: points_count = 1000, total values = 1200, values_count = 500 // min = max(1, 500 - (1200 - 1000)) = 300 // exp = 500 / (1200 / 1000) = 416 // max = min(1000, 500) = 500 // Note: max_values_per_point is never zero here because we check it above let expected_min = max( min_estimation / max_values_per_point, max( min(1, min_estimation), min_estimation.saturating_sub(total_values - self.get_points_count()), ), ); let expected_max = min(self.get_points_count(), max_estimation); let estimation = estimate_multi_value_selection_cardinality( self.get_points_count(), total_values, histogram_estimation.1, ) .round() as usize; CardinalityEstimation { primary_clauses: vec![], min: expected_min, exp: min(expected_max, max(estimation, expected_min)), max: expected_max, } } pub fn get_telemetry_data(&self) -> PayloadIndexTelemetry { PayloadIndexTelemetry { field_name: None, points_count: self.get_points_count(), points_values_count: self.get_histogram().get_total_count(), histogram_bucket_size: Some(self.get_histogram().current_bucket_size()), } } pub fn values_is_empty(&self, idx: PointOffsetType) -> bool { self.values_count(idx) == 0 } pub fn point_ids_by_value(&self, value: &T) -> Box + '_> { let start = Bound::Included(Point::new(*value, PointOffsetType::MIN)); let end = Bound::Included(Point::new(*value, PointOffsetType::MAX)); match &self { NumericIndexInner::Mutable(mutable) => Box::new(mutable.values_range(start, end)), NumericIndexInner::Immutable(immutable) => Box::new(immutable.values_range(start, end)), NumericIndexInner::Mmap(mmap) => Box::new(mmap.values_range(start, end)), } } /// Tries to estimate the amount of points for a given key. pub fn estimate_points(&self, value: &T) -> usize { let start = Bound::Included(Point::new(*value, PointOffsetType::MIN)); let end = Bound::Included(Point::new(*value, PointOffsetType::MAX)); match &self { NumericIndexInner::Mutable(mutable) => { let mut iter = mutable.map().range((start, end)); let first = iter.next(); let last = iter.next_back(); match (first, last) { (Some(_), None) => 1, (Some(start), Some(end)) => (start.idx..end.idx).len(), (None, _) => 0, } } NumericIndexInner::Immutable(immutable) => { let range_size = immutable.values_range_size(start, end); if range_size == 0 { return 0; } let avg_values_per_point = self.total_unique_values_count() as f32 / self.get_points_count() as f32; (range_size as f32 / avg_values_per_point).max(1.0).round() as usize } NumericIndexInner::Mmap(mmap) => { let range_size = mmap.values_range_size(start, end); if range_size == 0 { return 0; } let avg_values_per_point = self.total_unique_values_count() as f32 / self.get_points_count() as f32; (range_size as f32 / avg_values_per_point).max(1.0).round() as usize } } } } pub struct NumericIndex { inner: NumericIndexInner, _phantom: PhantomData

, } pub trait NumericIndexIntoInnerValue { fn into_inner_value(value: P) -> T; } impl NumericIndex { pub fn new(db: Arc>, field: &str, is_appendable: bool) -> Self { Self { inner: NumericIndexInner::new_memory(db, field, is_appendable), _phantom: PhantomData, } } pub fn new_mmap(path: &Path) -> OperationResult { Ok(Self { inner: NumericIndexInner::new_mmap(path)?, _phantom: PhantomData, }) } pub fn builder(db: Arc>, field: &str) -> NumericIndexBuilder where Self: ValueIndexer, { NumericIndexBuilder(Self::new(db, field, true)) } #[cfg(test)] pub fn builder_immutable(db: Arc>, field: &str) -> NumericIndexImmutableBuilder where Self: ValueIndexer, { NumericIndexImmutableBuilder { index: Self::new(db.clone(), field, true), field: field.to_owned(), db, } } pub fn builder_mmap(path: &Path) -> NumericIndexMmapBuilder where Self: ValueIndexer + NumericIndexIntoInnerValue, { NumericIndexMmapBuilder { path: path.to_owned(), in_memory_index: InMemoryNumericIndex::default(), _phantom: PhantomData, } } pub fn inner(&self) -> &NumericIndexInner { &self.inner } pub fn mut_inner(&mut self) -> &mut NumericIndexInner { &mut self.inner } delegate! { to self.inner { pub fn check_values_any(&self, idx: PointOffsetType, check_fn: impl Fn(&T) -> bool) -> bool; pub fn clear(self) -> OperationResult<()>; pub fn get_telemetry_data(&self) -> PayloadIndexTelemetry; pub fn load(&mut self) -> OperationResult; pub fn values_count(&self, idx: PointOffsetType) -> usize; pub fn get_values(&self, idx: PointOffsetType) -> Option + '_>>; pub fn values_is_empty(&self, idx: PointOffsetType) -> bool; } } } pub struct NumericIndexBuilder( NumericIndex, ) where NumericIndex: ValueIndexer; impl FieldIndexBuilderTrait for NumericIndexBuilder where NumericIndex: ValueIndexer, { type FieldIndexType = NumericIndex; fn init(&mut self) -> OperationResult<()> { match &mut self.0.inner { NumericIndexInner::Mutable(index) => index.get_db_wrapper().recreate_column_family(), NumericIndexInner::Immutable(_) => unreachable!(), NumericIndexInner::Mmap(_) => unreachable!(), } } fn add_point(&mut self, id: PointOffsetType, payload: &[&Value]) -> OperationResult<()> { self.0.add_point(id, payload) } fn finalize(self) -> OperationResult { self.0.inner.flusher()()?; Ok(self.0) } } #[cfg(test)] pub struct NumericIndexImmutableBuilder where NumericIndex: ValueIndexer, { index: NumericIndex, field: String, db: Arc>, } #[cfg(test)] impl FieldIndexBuilderTrait for NumericIndexImmutableBuilder where NumericIndex: ValueIndexer, { type FieldIndexType = NumericIndex; fn init(&mut self) -> OperationResult<()> { match &mut self.index.inner { NumericIndexInner::Mutable(index) => index.get_db_wrapper().recreate_column_family(), NumericIndexInner::Immutable(_) => unreachable!(), NumericIndexInner::Mmap(_) => unreachable!(), } } fn add_point(&mut self, id: PointOffsetType, payload: &[&Value]) -> OperationResult<()> { self.index.add_point(id, payload) } fn finalize(self) -> OperationResult { self.index.inner.flusher()()?; drop(self.index); let mut inner: NumericIndexInner = NumericIndexInner::new_memory(self.db, &self.field, false); inner.load()?; Ok(NumericIndex { inner, _phantom: PhantomData, }) } } pub struct NumericIndexMmapBuilder where T: Encodable + Numericable + MmapValue + Default, NumericIndex: ValueIndexer + NumericIndexIntoInnerValue, { path: PathBuf, in_memory_index: InMemoryNumericIndex, _phantom: PhantomData

, } impl FieldIndexBuilderTrait for NumericIndexMmapBuilder where NumericIndex: ValueIndexer + NumericIndexIntoInnerValue, { type FieldIndexType = NumericIndex; fn init(&mut self) -> OperationResult<()> { Ok(()) } fn add_point(&mut self, id: PointOffsetType, payload: &[&Value]) -> OperationResult<()> { self.in_memory_index.remove_point(id); let mut flatten_values: Vec<_> = vec![]; for value in payload.iter() { let payload_values = as ValueIndexer>::get_values(value); flatten_values.extend(payload_values); } let flatten_values = flatten_values .into_iter() .map(NumericIndex::into_inner_value) .collect(); self.in_memory_index.add_many_to_list(id, flatten_values); Ok(()) } fn finalize(self) -> OperationResult { let inner = MmapNumericIndex::build(self.in_memory_index, &self.path)?; Ok(NumericIndex { inner: NumericIndexInner::Mmap(inner), _phantom: PhantomData, }) } } impl PayloadFieldIndex for NumericIndexInner { fn count_indexed_points(&self) -> usize { self.get_points_count() } fn load(&mut self) -> OperationResult { NumericIndexInner::load(self) } fn clear(self) -> OperationResult<()> { match self { NumericIndexInner::Mutable(index) => index.get_db_wrapper().recreate_column_family(), NumericIndexInner::Immutable(index) => index.get_db_wrapper().recreate_column_family(), NumericIndexInner::Mmap(index) => index.clear(), } } fn flusher(&self) -> Flusher { NumericIndexInner::flusher(self) } fn files(&self) -> Vec { NumericIndexInner::files(self) } fn filter( &self, condition: &FieldCondition, ) -> Option + '_>> { if let Some(Match::Value(MatchValue { value: ValueVariants::String(keyword), })) = &condition.r#match { let keyword = keyword.as_str(); if let Ok(uuid) = Uuid::from_str(keyword) { let value = T::from_u128(uuid.as_u128()); return Some(self.point_ids_by_value(&value)); } } let range_cond = condition.range.as_ref()?; let (start_bound, end_bound) = match range_cond { RangeInterface::Float(float_range) => float_range.map(T::from_f64), RangeInterface::DateTime(datetime_range) => { datetime_range.map(|dt| T::from_u128(dt.timestamp() as u128)) } } .as_index_key_bounds(); // map.range // Panics if range start > end. Panics if range start == end and both bounds are Excluded. if !check_boundaries(&start_bound, &end_bound) { return Some(Box::new(std::iter::empty())); } Some(match self { NumericIndexInner::Mutable(index) => { Box::new(index.values_range(start_bound, end_bound)) } NumericIndexInner::Immutable(index) => { Box::new(index.values_range(start_bound, end_bound)) } NumericIndexInner::Mmap(index) => Box::new(index.values_range(start_bound, end_bound)), }) } fn estimate_cardinality(&self, condition: &FieldCondition) -> Option { if let Some(Match::Value(MatchValue { value: ValueVariants::String(keyword), })) = &condition.r#match { let keyword = keyword.as_str(); if let Ok(uuid) = Uuid::from_str(keyword) { let key = T::from_u128(uuid.as_u128()); let estimated_count = self.estimate_points(&key); return Some( CardinalityEstimation::exact(estimated_count) .with_primary_clause(PrimaryCondition::Condition(condition.clone())), ); } } condition.range.as_ref().map(|range| { let mut cardinality = self.range_cardinality(range); cardinality .primary_clauses .push(PrimaryCondition::Condition(condition.clone())); cardinality }) } fn payload_blocks( &self, threshold: usize, key: PayloadKeyType, ) -> Box + '_> { let mut lower_bound = Unbounded; let mut pre_lower_bound: Option> = None; let mut payload_conditions = Vec::new(); let value_per_point = self.total_unique_values_count() as f64 / self.get_points_count() as f64; let effective_threshold = (threshold as f64 * value_per_point) as usize; loop { let upper_bound = self .get_histogram() .get_range_by_size(lower_bound, effective_threshold / 2); if let Some(pre_lower_bound) = pre_lower_bound { let range = Range { lt: match upper_bound { Excluded(val) => Some(val.to_f64()), _ => None, }, gt: match pre_lower_bound { Excluded(val) => Some(val.to_f64()), _ => None, }, gte: match pre_lower_bound { Included(val) => Some(val.to_f64()), _ => None, }, lte: match upper_bound { Included(val) => Some(val.to_f64()), _ => None, }, }; let cardinality = self.range_cardinality(&RangeInterface::Float(range.clone())); let condition = PayloadBlockCondition { condition: FieldCondition::new_range(key.clone(), range), cardinality: cardinality.exp, }; payload_conditions.push(condition); } else if upper_bound == Unbounded { // One block covers all points payload_conditions.push(PayloadBlockCondition { condition: FieldCondition::new_range( key.clone(), Range { gte: None, lte: None, lt: None, gt: None, }, ), cardinality: self.get_points_count(), }); } pre_lower_bound = Some(lower_bound); lower_bound = match upper_bound { Included(val) => Excluded(val), Excluded(val) => Excluded(val), Unbounded => break, }; } Box::new(payload_conditions.into_iter()) } } impl ValueIndexer for NumericIndex { type ValueType = IntPayloadType; fn add_many( &mut self, id: PointOffsetType, values: Vec, ) -> OperationResult<()> { match &mut self.inner { NumericIndexInner::Mutable(index) => index.add_many_to_list(id, values), NumericIndexInner::Immutable(_) => Err(OperationError::service_error( "Can't add values to immutable numeric index", )), NumericIndexInner::Mmap(_) => Err(OperationError::service_error( "Can't add values to mmap numeric index", )), } } fn get_value(value: &Value) -> Option { value.as_i64() } fn remove_point(&mut self, id: PointOffsetType) -> OperationResult<()> { self.inner.remove_point(id) } } impl NumericIndexIntoInnerValue for NumericIndex { fn into_inner_value(value: IntPayloadType) -> IntPayloadType { value } } impl ValueIndexer for NumericIndex { type ValueType = DateTimePayloadType; fn add_many( &mut self, id: PointOffsetType, values: Vec, ) -> OperationResult<()> { match &mut self.inner { NumericIndexInner::Mutable(index) => { index.add_many_to_list(id, values.into_iter().map(Self::into_inner_value).collect()) } NumericIndexInner::Immutable(_) => Err(OperationError::service_error( "Can't add values to immutable numeric index", )), NumericIndexInner::Mmap(_) => Err(OperationError::service_error( "Can't add values to mmap numeric index", )), } } fn get_value(value: &Value) -> Option { DateTimePayloadType::from_str(value.as_str()?).ok() } fn remove_point(&mut self, id: PointOffsetType) -> OperationResult<()> { self.inner.remove_point(id) } } impl NumericIndexIntoInnerValue for NumericIndex { fn into_inner_value(value: DateTimePayloadType) -> IntPayloadType { value.timestamp() } } impl ValueIndexer for NumericIndex { type ValueType = FloatPayloadType; fn add_many( &mut self, id: PointOffsetType, values: Vec, ) -> OperationResult<()> { match &mut self.inner { NumericIndexInner::Mutable(index) => index.add_many_to_list(id, values), NumericIndexInner::Immutable(_) => Err(OperationError::service_error( "Can't add values to immutable numeric index", )), NumericIndexInner::Mmap(_) => Err(OperationError::service_error( "Can't add values to mmap numeric index", )), } } fn get_value(value: &Value) -> Option { value.as_f64() } fn remove_point(&mut self, id: PointOffsetType) -> OperationResult<()> { self.inner.remove_point(id) } } impl NumericIndexIntoInnerValue for NumericIndex { fn into_inner_value(value: FloatPayloadType) -> FloatPayloadType { value } } impl ValueIndexer for NumericIndex { type ValueType = UuidPayloadType; fn add_many( &mut self, id: PointOffsetType, values: Vec, ) -> OperationResult<()> { match &mut self.inner { NumericIndexInner::Mutable(index) => { let values: Vec = values.iter().map(|i| i.as_u128()).collect(); index.add_many_to_list(id, values) } NumericIndexInner::Immutable(_) => Err(OperationError::service_error( "Can't add values to immutable numeric index", )), NumericIndexInner::Mmap(_) => Err(OperationError::service_error( "Can't add values to mmap numeric index", )), } } fn get_value(value: &Value) -> Option { Uuid::parse_str(value.as_str()?).ok() } fn remove_point(&mut self, id: PointOffsetType) -> OperationResult<()> { self.inner.remove_point(id) } } impl NumericIndexIntoInnerValue for NumericIndex { fn into_inner_value(value: UuidPayloadType) -> UuidIntType { value.as_u128() } } impl StreamRange for NumericIndexInner where T: Encodable + Numericable + MmapValue + Default, { fn stream_range( &self, range: &RangeInterface, ) -> Box + '_> { let range = match range { RangeInterface::Float(float_range) => float_range.map(T::from_f64), RangeInterface::DateTime(datetime_range) => { datetime_range.map(|dt| T::from_u128(dt.timestamp() as u128)) } }; let (start_bound, end_bound) = range.as_index_key_bounds(); // map.range // Panics if range start > end. Panics if range start == end and both bounds are Excluded. if !check_boundaries(&start_bound, &end_bound) { return Box::new(std::iter::empty()); } match self { NumericIndexInner::Mutable(index) => { Box::new(index.orderable_values_range(start_bound, end_bound)) } NumericIndexInner::Immutable(index) => { Box::new(index.orderable_values_range(start_bound, end_bound)) } NumericIndexInner::Mmap(index) => { Box::new(index.orderable_values_range(start_bound, end_bound)) } } } } fn numeric_index_storage_cf_name(field: &str) -> String { format!("{field}_numeric") }