use std::path::Path;
use std::sync::Arc;
use arrow::array::{ArrayRef, PrimitiveArray};
use arrow::datatypes::{ArrowPrimitiveType, Field, Schema};
use arrow::record_batch::RecordBatch;
use log::{debug, info};
use crate::metadata::FileInfo;
use crate::traits::backend::StorageBackend;
use crate::{StorageError, StorageResult};
fn uri_to_path(uri: &str) -> StorageResult<std::path::PathBuf> {
let url = url::Url::parse(uri)
.map_err(|e| StorageError::Invalid(format!("bad dataset URI `{uri}`: {e}")))?;
url.to_file_path().map_err(|_| {
StorageError::Invalid(format!("dataset URI is not a local file path: `{uri}`"))
})
}
pub trait LanceStorage {
async fn write_lance_batch_async(&self, uri: String, batch: RecordBatch) -> StorageResult<()> {
info!("Writing Lance dataset (in-house v2.1) to {}", uri);
let path = uri_to_path(&uri)?;
tokio::task::spawn_blocking(move || crate::lancefmt::write_dataset(&batch, &path))
.await
.map_err(|e| StorageError::Io(format!("lancefmt writer task failed: {e}")))?
}
async fn read_lance_all_batches_async(&self, uri: String) -> StorageResult<RecordBatch> {
info!("Reading Lance dataset (in-house v2.1) from {}", uri);
let path = uri_to_path(&uri)?;
let combined = tokio::task::spawn_blocking(move || crate::lancefmt::scan_all(&path))
.await
.map_err(|e| StorageError::Io(format!("lancefmt reader task failed: {e}")))??;
debug!(
"Combined Lance batch for {:?} has {} rows",
uri,
combined.num_rows()
);
Ok(combined)
}
async fn save_primitive_column<T: ArrowPrimitiveType>(
&self,
key: &str,
field_name: &str,
values: Vec<T::Native>,
md_path: &Path,
) -> StorageResult<()>
where
Self: StorageBackend,
{
self.validate_initialized(md_path)?;
let path = self.file_path(key);
let len = values.len();
info!("Saving {} values for {} (field {})", len, key, field_name);
let schema = Schema::new(vec![Field::new(field_name, T::DATA_TYPE, false)]);
let batch = RecordBatch::try_new(
Arc::new(schema),
vec![Arc::new(PrimitiveArray::<T>::from_iter_values(values)) as ArrayRef],
)
.map_err(|e| StorageError::Lance(e.to_string()))?;
let mut metadata = self.load_metadata().await?;
metadata = metadata.add_file(
key,
FileInfo::new(
format!("{}_{}.lance", self.get_name(), key),
"vector",
(len, 1),
None,
None,
)?,
);
self.save_metadata(&metadata).await?;
let uri = Self::path_to_uri(&path)?;
self.write_lance_batch_async(uri, batch).await?;
info!("Vector {} saved successfully", key);
Ok(())
}
}