use std::sync::Arc;
use arrow::array::AsArray;
use arrow::datatypes::{UInt32Type, UInt64Type};
use arrow_array::{UInt32Array, UInt64Array};
use lance_core::ROW_ID;
use lance_core::Result;
use tokio::sync::OnceCell;
use crate::frag_reuse::FragReuseIndex;
use crate::scalar::inverted::index::{DocSet, NUM_TOKEN_COL};
use crate::scalar::{IndexReader, IndexStore};
use lance_select::mask::RowAddrMask;
pub enum LazyDocSet {
Loaded(LoadedDocSet),
Deferred(Box<DeferredDocSet>),
}
pub struct LoadedDocSet {
docs: Arc<DocSet>,
num_rows: usize,
total_tokens: u64,
}
pub struct DeferredDocSet {
store: Arc<dyn IndexStore>,
docs_path: String,
is_legacy: bool,
frag_reuse_index: Option<Arc<FragReuseIndex>>,
num_rows: usize,
total_tokens: OnceCell<u64>,
num_tokens_col: OnceCell<Arc<UInt32Array>>,
row_ids_col: OnceCell<Arc<UInt64Array>>,
full: OnceCell<Arc<DocSet>>,
}
impl std::fmt::Debug for LazyDocSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Loaded(l) => f
.debug_struct("LazyDocSet::Loaded")
.field("num_rows", &l.num_rows)
.field("total_tokens", &l.total_tokens)
.finish(),
Self::Deferred(d) => f
.debug_struct("LazyDocSet::Deferred")
.field("num_rows", &d.num_rows)
.field("total_tokens_loaded", &d.total_tokens.initialized())
.field("full_loaded", &d.full.initialized())
.finish(),
}
}
}
impl lance_core::deepsize::DeepSizeOf for LazyDocSet {
fn deep_size_of_children(&self, ctx: &mut lance_core::deepsize::Context) -> usize {
match self {
Self::Loaded(l) => l.docs.deep_size_of_children(ctx),
Self::Deferred(d) => {
d.full
.get()
.map(|d| d.deep_size_of_children(ctx))
.unwrap_or(0)
+ d.num_tokens_col
.get()
.map(|arr| arr.len() * std::mem::size_of::<u32>())
.unwrap_or(0)
+ d.row_ids_col
.get()
.map(|arr| arr.len() * std::mem::size_of::<u64>())
.unwrap_or(0)
}
}
}
}
impl LazyDocSet {
pub fn new(
store: Arc<dyn IndexStore>,
docs_path: String,
num_rows: usize,
is_legacy: bool,
frag_reuse_index: Option<Arc<FragReuseIndex>>,
) -> Self {
Self::Deferred(Box::new(DeferredDocSet {
store,
docs_path,
is_legacy,
frag_reuse_index,
num_rows,
total_tokens: OnceCell::new(),
num_tokens_col: OnceCell::new(),
row_ids_col: OnceCell::new(),
full: OnceCell::new(),
}))
}
pub fn from_loaded(docs: DocSet) -> Self {
let num_rows = docs.len();
let total_tokens = docs.total_tokens_num();
Self::Loaded(LoadedDocSet {
docs: Arc::new(docs),
num_rows,
total_tokens,
})
}
pub fn len(&self) -> usize {
match self {
Self::Loaded(l) => l.num_rows,
Self::Deferred(d) => d.num_rows,
}
}
pub fn total_tokens_cached(&self) -> Option<u64> {
match self {
Self::Loaded(l) => Some(l.total_tokens),
Self::Deferred(d) => d.total_tokens.get().copied(),
}
}
pub fn has_frag_reuse_remap(&self) -> bool {
match self {
Self::Loaded(_) => false,
Self::Deferred(d) => d.frag_reuse_index.is_some(),
}
}
pub async fn total_tokens_num(&self) -> Result<u64> {
match self {
Self::Loaded(l) => Ok(l.total_tokens),
Self::Deferred(d) => d.total_tokens_num().await,
}
}
pub async fn ensure_loaded(&self) -> Result<Arc<DocSet>> {
match self {
Self::Loaded(l) => Ok(l.docs.clone()),
Self::Deferred(d) => d.ensure_loaded().await,
}
}
pub async fn ensure_num_tokens_loaded(&self) -> Result<Arc<DocSet>> {
match self {
Self::Loaded(l) => Ok(l.docs.clone()),
Self::Deferred(d) => d.ensure_num_tokens_loaded().await,
}
}
pub async fn docs_for_wand(&self, mask: &RowAddrMask) -> Result<Arc<DocSet>> {
if mask.is_select_all() && !self.has_frag_reuse_remap() {
self.ensure_num_tokens_loaded().await
} else {
self.ensure_loaded().await
}
}
pub async fn resolve_row_ids(&self, doc_ids: &[u32]) -> Result<Vec<u64>> {
match self {
Self::Loaded(l) => Ok(doc_ids.iter().map(|&d| l.docs.row_id(d)).collect()),
Self::Deferred(d) => d.resolve_row_ids(doc_ids).await,
}
}
}
impl DeferredDocSet {
async fn reader(&self) -> Result<Arc<dyn IndexReader>> {
self.store.open_index_file(&self.docs_path).await
}
async fn total_tokens_num(&self) -> Result<u64> {
if let Some(v) = self.total_tokens.get() {
return Ok(*v);
}
if let Some(full) = self.full.get() {
let v = full.total_tokens_num();
let _ = self.total_tokens.set(v);
return Ok(v);
}
let col = self.num_tokens_column().await?;
let sum: u64 = col.values().iter().map(|&n| n as u64).sum();
let _ = self.total_tokens.set(sum);
Ok(sum)
}
async fn num_tokens_column(&self) -> Result<Arc<UInt32Array>> {
self.num_tokens_col
.get_or_try_init(|| async {
let reader = self.reader().await?;
let batch = reader
.read_range(0..self.num_rows, Some(&[NUM_TOKEN_COL]))
.await?;
Result::Ok(Arc::new(
batch[NUM_TOKEN_COL].as_primitive::<UInt32Type>().clone(),
))
})
.await
.cloned()
}
async fn row_ids_column(&self) -> Result<Arc<UInt64Array>> {
self.row_ids_col
.get_or_try_init(|| async {
let reader = self.reader().await?;
let batch = reader.read_range(0..self.num_rows, Some(&[ROW_ID])).await?;
Result::Ok(Arc::new(batch[ROW_ID].as_primitive::<UInt64Type>().clone()))
})
.await
.cloned()
}
async fn ensure_loaded(&self) -> Result<Arc<DocSet>> {
let docs = self
.full
.get_or_try_init(|| async {
let docs = if self.num_tokens_col.get().is_some() {
let num_tokens = self.num_tokens_column().await?;
let row_ids = self.row_ids_column().await?;
DocSet::from_columns(
row_ids.as_ref(),
num_tokens.as_ref(),
self.is_legacy,
self.frag_reuse_index.clone(),
)?
} else {
DocSet::load(
self.reader().await?,
self.is_legacy,
self.frag_reuse_index.clone(),
)
.await?
};
Result::Ok(Arc::new(docs))
})
.await?
.clone();
let _ = self.total_tokens.set(docs.total_tokens_num());
Ok(docs)
}
async fn ensure_num_tokens_loaded(&self) -> Result<Arc<DocSet>> {
if let Some(full) = self.full.get() {
return Ok(full.clone());
}
let num_tokens = self.num_tokens_column().await?;
let docs = Arc::new(DocSet::from_num_tokens_only(num_tokens.as_ref()));
let _ = self.total_tokens.set(docs.total_tokens_num());
Ok(docs)
}
async fn resolve_row_ids(&self, doc_ids: &[u32]) -> Result<Vec<u64>> {
if let Some(full) = self.full.get()
&& full.has_row_ids()
{
return Ok(doc_ids.iter().map(|&d| full.row_id(d)).collect());
}
if let Some(arr) = self.row_ids_col.get() {
return Ok(doc_ids.iter().map(|&d| arr.value(d as usize)).collect());
}
let ranges: Vec<std::ops::Range<usize>> = doc_ids
.iter()
.map(|&d| d as usize..d as usize + 1)
.collect();
let reader = self.reader().await?;
let batch = reader.read_ranges(&ranges, Some(&[ROW_ID])).await?;
let arr = batch[ROW_ID].as_primitive::<UInt64Type>();
Ok((0..arr.len()).map(|i| arr.value(i)).collect())
}
}