use crate::expression::Expression;
use radixdb_core::I64Map;
use radixdb_core::{DataType, IndexEntry, IndexType, Operator, Result, RowIdVec, Value};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IndexKeyRange {
pub start: Value,
pub end: Value,
}
pub trait Index: Send + Sync {
fn name(&self) -> &str;
fn table_name(&self) -> &str;
fn build(&mut self) -> Result<()>;
fn add(&self, values: &[Value], row_id: i64, ref_id: i64) -> Result<()>;
fn add_batch(&self, entries: &I64Map<Vec<Value>>) -> Result<()>;
fn remove(&self, values: &[Value], row_id: i64, ref_id: i64) -> Result<()>;
fn remove_batch(&self, entries: &I64Map<Vec<Value>>) -> Result<()>;
fn add_batch_slice(&self, entries: &[(i64, &[Value])]) -> Result<()>;
fn remove_batch_slice(&self, entries: &[(i64, &[Value])]) -> Result<()>;
fn column_ids(&self) -> &[i32];
fn column_names(&self) -> &[String];
fn data_types(&self) -> &[DataType];
fn index_type(&self) -> IndexType;
fn is_unique(&self) -> bool;
#[doc(hidden)]
fn metadata_inner(&self) -> Option<std::sync::Arc<dyn Index>> {
None
}
fn partial_predicate(&self) -> Option<&crate::index::PartialIndexPredicate> {
None
}
#[doc(hidden)]
fn prepared_key_encoder(&self) -> Option<crate::index::PreparedIndexKeyEncoder> {
None
}
#[doc(hidden)]
fn all_entries(&self) -> Result<Vec<IndexEntry>> {
self.find_range(&[], &[], false, false)
}
fn find(&self, values: &[Value]) -> Result<Vec<IndexEntry>>;
fn find_range(
&self,
min: &[Value],
max: &[Value],
min_inclusive: bool,
max_inclusive: bool,
) -> Result<Vec<IndexEntry>>;
#[doc(hidden)]
fn find_physical_range(
&self,
min: &[Value],
max: &[Value],
min_inclusive: bool,
max_inclusive: bool,
) -> Result<Vec<IndexEntry>> {
self.find_range(min, max, min_inclusive, max_inclusive)
}
fn find_range_ordered_limited(
&self,
min: &[Value],
max: &[Value],
min_inclusive: bool,
max_inclusive: bool,
ascending: bool,
limit: usize,
) -> Result<Vec<IndexEntry>> {
let mut entries = self.find_range(min, max, min_inclusive, max_inclusive)?;
if !ascending {
entries.reverse();
}
entries.truncate(limit);
Ok(entries)
}
fn find_with_operator(&self, op: Operator, values: &[Value]) -> Result<Vec<IndexEntry>>;
fn get_row_ids_equal(&self, values: &[Value]) -> Result<RowIdVec> {
#[cfg(feature = "bench-harness")]
let started = std::time::Instant::now();
let mut row_ids = RowIdVec::new();
let result = self.get_row_ids_equal_into(values, &mut row_ids);
#[cfg(feature = "bench-harness")]
crate::instrumentation::record_index_lookup(1, row_ids.len(), started.elapsed());
result?;
Ok(row_ids)
}
fn get_row_ids_equal_into(&self, values: &[Value], buffer: &mut Vec<i64>) -> Result<()> {
let entries = self.find(values)?;
buffer.reserve(entries.len());
for entry in entries {
buffer.push(entry.row_id);
}
Ok(())
}
fn get_row_ids_in_range(
&self,
min_value: &[Value],
max_value: &[Value],
include_min: bool,
include_max: bool,
) -> Result<RowIdVec> {
#[cfg(feature = "bench-harness")]
let started = std::time::Instant::now();
let mut row_ids = RowIdVec::new();
let result = self.get_row_ids_in_range_into(
min_value,
max_value,
include_min,
include_max,
&mut row_ids,
);
#[cfg(feature = "bench-harness")]
crate::instrumentation::record_index_lookup(1, row_ids.len(), started.elapsed());
result?;
Ok(row_ids)
}
fn get_row_ids_in_range_into(
&self,
min_value: &[Value],
max_value: &[Value],
include_min: bool,
include_max: bool,
buffer: &mut Vec<i64>,
) -> Result<()> {
let entries = self.find_range(min_value, max_value, include_min, include_max)?;
buffer.reserve(entries.len());
for entry in entries {
buffer.push(entry.row_id);
}
Ok(())
}
fn get_row_ids_in(&self, value_list: &[Value]) -> Result<RowIdVec> {
#[cfg(feature = "bench-harness")]
let started = std::time::Instant::now();
let mut results = RowIdVec::new();
let result = self.get_row_ids_in_into(value_list, &mut results);
#[cfg(feature = "bench-harness")]
crate::instrumentation::record_index_lookup(
value_list.len(),
results.len(),
started.elapsed(),
);
result?;
Ok(results)
}
fn get_row_ids_in_into(&self, value_list: &[Value], buffer: &mut Vec<i64>) -> Result<()> {
for value in value_list {
self.get_row_ids_equal_into(std::slice::from_ref(value), buffer)?;
}
Ok(())
}
fn get_filtered_row_ids(&self, expr: &dyn Expression) -> Result<RowIdVec>;
fn get_min_value(&self) -> Option<Value> {
None }
fn get_max_value(&self) -> Option<Value> {
None }
fn get_all_values(&self) -> Vec<Value> {
Vec::new() }
fn get_distinct_count_excluding_null(&self) -> Option<usize> {
None }
fn get_row_ids_ordered(
&self,
_ascending: bool,
_limit: usize,
_offset: usize,
) -> Option<Vec<i64>> {
None }
fn get_grouped_row_ids(&self) -> Option<Vec<(Value, Vec<i64>)>> {
None }
fn for_each_group(
&self,
_callback: &mut dyn FnMut(&Value, &[i64]) -> Result<bool>,
) -> Option<Result<()>> {
None }
fn search_nearest(
&self,
_query: &Value,
_k: usize,
_ef_search: usize,
) -> Option<Vec<(i64, f64)>> {
None
}
fn hnsw_distance_metric(&self) -> Option<u8> {
None
}
fn hnsw_m(&self) -> Option<u16> {
None
}
fn hnsw_ef_construction(&self) -> Option<u16> {
None
}
fn default_ef_search(&self) -> Option<usize> {
None
}
fn hnsw_graph_bytes(&self) -> Result<Option<Vec<u8>>> {
Ok(None)
}
fn hnsw_indexed_row_ids(&self) -> Option<Vec<i64>> {
None
}
fn clear(&self) {}
fn cleanup(&self) -> Result<()> {
Ok(())
}
fn as_any(&self) -> &dyn std::any::Any;
fn close(&mut self) -> Result<()>;
}
#[cfg(test)]
mod tests {
use super::*;
fn _assert_object_safe(_: &dyn Index) {}
}