use std::borrow::Cow;
use std::sync::Arc;
use arrow_schema::{DataType, Field};
use async_trait::async_trait;
use datafusion::execution::SendableRecordBatchStream;
use futures::future::BoxFuture;
use lance_core::{
Result,
cache::{CacheKey, CacheKeySchema, KeyBuilder, LanceCache, UnsizedCacheKey},
deepsize::DeepSizeOf,
};
use crate::progress::IndexBuildProgress;
use crate::registry::IndexPluginRegistry;
use crate::scalar::RowIdRemapper;
use crate::scalar::{CreatedIndex, IndexStore, ScalarIndex, expression::ScalarQueryParser};
pub use crate::scalar::{TrainingCriteria, TrainingOrdering};
pub const VALUE_COLUMN_NAME: &str = "value";
pub trait TrainingRequest: std::any::Any + Send + Sync {
fn as_any(&self) -> &dyn std::any::Any;
fn criteria(&self) -> &TrainingCriteria;
}
pub(crate) struct DefaultTrainingRequest {
criteria: TrainingCriteria,
}
impl DefaultTrainingRequest {
pub fn new(criteria: TrainingCriteria) -> Self {
Self { criteria }
}
}
impl TrainingRequest for DefaultTrainingRequest {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn criteria(&self) -> &TrainingCriteria {
&self.criteria
}
}
#[async_trait]
pub trait BasicTrainer: Send + Sync {
fn new_training_request(&self, params: &str, field: &Field)
-> Result<Box<dyn TrainingRequest>>;
async fn train_index(
&self,
data: SendableRecordBatchStream,
index_store: &dyn IndexStore,
request: Box<dyn TrainingRequest>,
fragment_ids: Option<Vec<u32>>,
progress: Arc<dyn IndexBuildProgress>,
) -> Result<CreatedIndex>;
}
#[async_trait]
pub trait ScalarIndexPlugin: Send + Sync + std::fmt::Debug {
fn basic_trainer(&self) -> Option<&dyn BasicTrainer> {
None
}
fn name(&self) -> &str;
fn provides_exact_answer(&self) -> bool;
fn version(&self) -> u32;
fn new_query_parser(
&self,
index_name: String,
index_details: &prost_types::Any,
) -> Option<Box<dyn ScalarQueryParser>>;
async fn load_index(
&self,
index_store: Arc<dyn IndexStore>,
index_details: &prost_types::Any,
frag_reuse_index: Option<Arc<dyn RowIdRemapper>>,
cache: &LanceCache,
) -> Result<Arc<dyn ScalarIndex>>;
async fn get_from_cache(
&self,
_index_store: Arc<dyn IndexStore>,
_frag_reuse_index: Option<Arc<dyn RowIdRemapper>>,
cache: &LanceCache,
) -> Result<Option<Arc<dyn ScalarIndex>>> {
Ok(cache.get_unsized_with_key(&ScalarIndexCacheKey).await)
}
async fn put_in_cache(&self, cache: &LanceCache, index: Arc<dyn ScalarIndex>) -> Result<()> {
cache
.insert_unsized_with_key(&ScalarIndexCacheKey, index)
.await;
Ok(())
}
async fn get_or_insert_in_cache(
&self,
index_store: Arc<dyn IndexStore>,
frag_reuse_index: Option<Arc<dyn RowIdRemapper>>,
cache: &LanceCache,
load: ScalarIndexLoad<'_>,
) -> Result<Arc<dyn ScalarIndex>> {
if let Some(index) = self
.get_from_cache(index_store, frag_reuse_index, cache)
.await?
{
return Ok(index);
}
let index = load.await?;
self.put_in_cache(cache, index.clone()).await?;
Ok(index)
}
async fn load_statistics(
&self,
_index_store: Arc<dyn IndexStore>,
_index_details: &prost_types::Any,
) -> Result<Option<serde_json::Value>> {
Ok(None)
}
fn attach_registry(&self, _registry: Arc<IndexPluginRegistry>) {}
fn details_as_json(&self, _details: &prost_types::Any) -> Result<serde_json::Value> {
Ok(serde_json::json!({}))
}
async fn create_seed_writer(
&self,
_field_path: &str,
_data_type: &DataType,
_index_details: &prost_types::Any,
) -> Result<Option<Box<dyn super::seed::IndexSeedWriter>>> {
Ok(None)
}
fn might_use_seeds(&self, _index_details: &prost_types::Any) -> bool {
false
}
async fn update_from_seeds(
&self,
_seeds: Vec<super::seed::FragmentSeed>,
_reference_index: Arc<dyn ScalarIndex>,
_index_details: &prost_types::Any,
_dest_store: &dyn IndexStore,
) -> Result<Option<CreatedIndex>> {
Ok(None)
}
}
pub type ScalarIndexLoad<'a> = BoxFuture<'a, Result<Arc<dyn ScalarIndex>>>;
pub async fn single_flight_open<K, ToState, FromState>(
cache: &LanceCache,
state_key: K,
load: ScalarIndexLoad<'_>,
to_state: ToState,
from_state: FromState,
) -> Result<Arc<dyn ScalarIndex>>
where
K: CacheKey + Send,
K::ValueType: DeepSizeOf + Send + Sync + 'static,
ToState: FnOnce(&dyn ScalarIndex) -> Result<K::ValueType> + Send,
FromState: FnOnce(Arc<K::ValueType>) -> Result<Arc<dyn ScalarIndex>> + Send,
{
let state = cache
.get_or_insert_with_key(state_key, move || async move {
let index = load.await?;
to_state(index.as_ref())
})
.await?;
from_state(state)
}
pub struct ScalarIndexCacheKey;
impl UnsizedCacheKey for ScalarIndexCacheKey {
type ValueType = dyn ScalarIndex;
fn key(&self) -> Cow<'_, str> {
Cow::Borrowed("scalar_index")
}
fn type_name() -> &'static str {
"ScalarIndex"
}
fn schema() -> CacheKeySchema {
CacheKeySchema::new("lance.scalar.registry.scalar-index-key", 1)
}
fn write_key(&self, builder: &mut KeyBuilder) {
builder.write_variant(0);
}
}