#[cfg(any(test, feature = "test-helpers"))]
use std::any::Any;
use std::{fmt, sync::Arc, time::Duration};
use arrow_array::RecordBatch;
use arrow_schema::SchemaRef;
use datafusion::prelude::Expr;
use crate::{
Bm25SearchOptions, BoolMode, GcError, GcReport, InfinoError, MutationStats, OptimizeError,
OptimizeOptions, VectorFilter, superfile::VectorSearchOptions,
supertable::Supertable as SupertableHandle,
};
pub(crate) trait Table: Send + Sync {
fn schema(&self) -> SchemaRef;
fn append(&self, batch: &RecordBatch) -> Result<(), InfinoError>;
fn update(&self, predicate: Expr, batch: &RecordBatch) -> Result<MutationStats, InfinoError>;
fn delete(&self, predicate: Expr) -> Result<MutationStats, InfinoError>;
fn bm25_search(
&self,
column: &str,
query: &str,
k: usize,
opts: Bm25SearchOptions,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError>;
fn token_match(
&self,
column: &str,
query: &str,
mode: BoolMode,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError>;
fn exact_match(
&self,
column: &str,
value: &str,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError>;
fn count(&self, column: &str, query: &str, mode: BoolMode) -> Result<u64, InfinoError>;
fn vector_search(
&self,
column: &str,
query: &[f32],
k: usize,
opts: VectorSearchOptions,
filter: Option<VectorFilter<'_>>,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError>;
#[allow(clippy::too_many_arguments)]
fn hybrid_search(
&self,
text_column: &str,
text_query: &str,
mode: BoolMode,
vector_column: &str,
vector_query: &[f32],
opts: VectorSearchOptions,
k: usize,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError>;
fn optimize(&self, opts: &OptimizeOptions) -> Result<(), OptimizeError>;
fn gc(&self, safety_gap: Duration) -> Result<GcReport, GcError>;
#[cfg(any(test, feature = "test-helpers"))]
fn as_any(&self) -> &dyn Any;
}
impl Table for SupertableHandle {
fn schema(&self) -> SchemaRef {
SupertableHandle::schema(self)
}
fn append(&self, batch: &RecordBatch) -> Result<(), InfinoError> {
SupertableHandle::append(self, batch)
}
fn update(&self, predicate: Expr, batch: &RecordBatch) -> Result<MutationStats, InfinoError> {
SupertableHandle::update(self, predicate, batch)
}
fn delete(&self, predicate: Expr) -> Result<MutationStats, InfinoError> {
SupertableHandle::delete(self, predicate)
}
fn bm25_search(
&self,
column: &str,
query: &str,
k: usize,
opts: Bm25SearchOptions,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
SupertableHandle::bm25_search(self, column, query, k, opts.mode, opts.stats, projection)
}
fn token_match(
&self,
column: &str,
query: &str,
mode: BoolMode,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
SupertableHandle::token_match(self, column, query, mode, projection)
}
fn exact_match(
&self,
column: &str,
value: &str,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
SupertableHandle::exact_match(self, column, value, projection)
}
fn count(&self, column: &str, query: &str, mode: BoolMode) -> Result<u64, InfinoError> {
SupertableHandle::count(self, column, query, mode)
}
fn vector_search(
&self,
column: &str,
query: &[f32],
k: usize,
opts: VectorSearchOptions,
filter: Option<VectorFilter<'_>>,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
SupertableHandle::vector_search(self, column, query, k, opts, filter, projection)
}
fn hybrid_search(
&self,
text_column: &str,
text_query: &str,
mode: BoolMode,
vector_column: &str,
vector_query: &[f32],
opts: VectorSearchOptions,
k: usize,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
SupertableHandle::hybrid_search(
self,
text_column,
text_query,
mode,
vector_column,
vector_query,
opts,
k,
projection,
)
}
fn optimize(&self, opts: &OptimizeOptions) -> Result<(), OptimizeError> {
SupertableHandle::optimize(self, opts)
}
fn gc(&self, safety_gap: Duration) -> Result<GcReport, GcError> {
SupertableHandle::gc(self, safety_gap)
}
#[cfg(any(test, feature = "test-helpers"))]
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Clone)]
pub struct Supertable {
pub(crate) inner: Arc<dyn Table>,
}
impl Supertable {
pub(crate) fn from_local(handle: SupertableHandle) -> Self {
Self::from_table(Arc::new(handle))
}
pub(crate) fn from_table(inner: Arc<dyn Table>) -> Self {
Self { inner }
}
pub fn schema(&self) -> SchemaRef {
self.inner.schema()
}
pub fn append(&self, batch: &RecordBatch) -> Result<(), InfinoError> {
self.inner.append(batch)
}
pub fn update(
&self,
predicate: Expr,
batch: &RecordBatch,
) -> Result<MutationStats, InfinoError> {
self.inner.update(predicate, batch)
}
pub fn delete(&self, predicate: Expr) -> Result<MutationStats, InfinoError> {
self.inner.delete(predicate)
}
pub fn bm25_search(
&self,
column: &str,
query: &str,
k: usize,
opts: Bm25SearchOptions,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
self.inner.bm25_search(column, query, k, opts, projection)
}
pub fn token_match(
&self,
column: &str,
query: &str,
mode: BoolMode,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
self.inner.token_match(column, query, mode, projection)
}
pub fn exact_match(
&self,
column: &str,
value: &str,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
self.inner.exact_match(column, value, projection)
}
pub fn count(&self, column: &str, query: &str, mode: BoolMode) -> Result<u64, InfinoError> {
self.inner.count(column, query, mode)
}
pub fn vector_search(
&self,
column: &str,
query: &[f32],
k: usize,
filter: Option<VectorFilter<'_>>,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
self.inner.vector_search(
column,
query,
k,
VectorSearchOptions::default(),
filter,
projection,
)
}
test_visible! {
fn vector_search_with_options(
&self,
column: &str,
query: &[f32],
k: usize,
opts: VectorSearchOptions,
filter: Option<VectorFilter<'_>>,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
self.inner
.vector_search(column, query, k, opts, filter, projection)
}
}
pub fn hybrid_search(
&self,
text_column: &str,
text_query: &str,
mode: BoolMode,
vector_column: &str,
vector_query: &[f32],
k: usize,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
self.inner.hybrid_search(
text_column,
text_query,
mode,
vector_column,
vector_query,
VectorSearchOptions::default(),
k,
projection,
)
}
test_visible! {
#[allow(clippy::too_many_arguments)]
fn hybrid_search_with_options(
&self,
text_column: &str,
text_query: &str,
mode: BoolMode,
vector_column: &str,
vector_query: &[f32],
opts: VectorSearchOptions,
k: usize,
projection: Option<&[&str]>,
) -> Result<Vec<RecordBatch>, InfinoError> {
self.inner.hybrid_search(
text_column,
text_query,
mode,
vector_column,
vector_query,
opts,
k,
projection,
)
}
}
pub fn optimize(&self, opts: &OptimizeOptions) -> Result<(), OptimizeError> {
self.inner.optimize(opts)
}
pub fn gc(&self, safety_gap: Duration) -> Result<GcReport, GcError> {
self.inner.gc(safety_gap)
}
#[cfg(any(test, feature = "test-helpers"))]
pub fn local_handle(&self) -> &SupertableHandle {
self.inner
.as_any()
.downcast_ref::<SupertableHandle>()
.expect("local_handle called on a non-local table")
}
}
impl fmt::Debug for Supertable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Supertable").finish_non_exhaustive()
}
}