Skip to content

Commit e55b697

Browse files
committed
fix: read_all_batches schema discovery and test store root
- Auto-detect parquet schema from first file for proper column projection when using LakeSoulReader in read_all_batches - Use LocalFileSystem::new() (root at /) in tests so absolute index_prefix paths work correctly (matching Python behavior) - Mark test_read_parquet_schema and test_build_and_list_files as #[ignore] (require prepare_data.py) Signed-off-by: chenxu <chenxu@dmetasoul.com>
1 parent d2dc268 commit e55b697

9 files changed

Lines changed: 115 additions & 188 deletions

File tree

python/uv.lock

Lines changed: 59 additions & 154 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/lakesoul-io/src/vector/builder.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,35 @@ impl VectorShardIndexBuilder {
195195

196196
let prefix = self.table_prefix();
197197

198+
// Read the schema from the first parquet file so the reader knows
199+
// which columns to project.
200+
let schema = self
201+
.file_paths
202+
.first()
203+
.and_then(|p| {
204+
let path = p
205+
.trim_start_matches("file://")
206+
.trim_start_matches("s3://")
207+
.trim_start_matches("s3a://");
208+
std::fs::File::open(path).ok()
209+
})
210+
.and_then(|f| {
211+
parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder::try_new(f)
212+
.ok()
213+
})
214+
.map(|b| b.schema().clone());
215+
198216
let mut config_builder = LakeSoulIOConfigBuilder::new()
199217
.with_files(self.file_paths.clone())
200218
.with_prefix(prefix)
201219
.with_primary_keys(vec![pk_col.clone()])
202220
.with_batch_size(8192)
203221
.with_thread_num(1);
204222

223+
if let Some(s) = schema {
224+
config_builder = config_builder.with_schema(s);
225+
}
226+
205227
// Apply object store options
206228
for (k, v) in &self.object_store_options {
207229
config_builder =

rust/lakesoul-io/src/vector/reader.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@ pub fn extract_vector_batch(
196196
#[cfg(test)]
197197
mod tests {
198198
use super::*;
199-
use arrow_array::types::Float32Type;
200199
use std::sync::Arc;
201200

202201
fn make_fixed_size_list_batch(
@@ -205,7 +204,6 @@ mod tests {
205204
dim: usize,
206205
) -> RecordBatch {
207206
let id_array = Arc::new(UInt64Array::from(ids)) as arrow_array::ArrayRef;
208-
let n = vectors.len();
209207

210208
// Build FixedSizeList: flatten all values
211209
let flat: Vec<f32> = vectors.iter().flatten().copied().collect();

rust/lakesoul-io/tests/vector_e2e_test.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ const DIM: usize = 200;
2828

2929
/// Helper: read .fvecs file into Vec<Vec<f32>>
3030
fn read_fvecs(path: &str, n: Option<usize>) -> Vec<Vec<f32>> {
31-
use std::io::Seek;
3231
let mut f = std::fs::File::open(path).unwrap();
33-
let f_len = f.metadata().unwrap().len() as usize;
3432
let mut read_one = || -> Option<Vec<f32>> {
3533
let mut dim_buf = [0u8; 4];
3634
f.read_exact(&mut dim_buf).ok()?;
@@ -97,7 +95,7 @@ fn compute_recall(predicted: &[u64], ground_truth: &[i32], k: usize) -> f64 {
9795
#[ignore = "requires prepare_data.py to be run first"]
9896
async fn test_glove_e2e_build_and_search() {
9997
let tmp = TempDir::new().unwrap();
100-
let store = Arc::new(LocalFileSystem::new_with_prefix(tmp.path()).unwrap());
98+
let store = Arc::new(LocalFileSystem::new());
10199
let index_prefix = "_vector_index/vec/-5/0/";
102100

103101
let config = VectorIndexConfig {
@@ -156,8 +154,8 @@ async fn test_glove_e2e_build_and_search() {
156154

157155
/// Quick test: verify LakeSoulReader can read our parquet schema
158156
#[tokio::test]
157+
#[ignore = "requires prepare_data.py to be run first"]
159158
async fn test_read_parquet_schema() {
160-
use futures::StreamExt;
161159
use lakesoul_io::config::LakeSoulIOConfigBuilder;
162160
use lakesoul_io::reader::LakeSoulReader;
163161

@@ -208,9 +206,10 @@ async fn test_read_parquet_schema() {
208206

209207
/// Quick test: build and verify files on disk
210208
#[tokio::test]
209+
#[ignore = "requires prepare_data.py to be run first"]
211210
async fn test_build_and_list_files() {
212211
let tmp = TempDir::new().unwrap();
213-
let store = Arc::new(LocalFileSystem::new_with_prefix(tmp.path()).unwrap());
212+
let store = Arc::new(LocalFileSystem::new());
214213
let index_prefix = "_vector_index/vec/-5/0/";
215214

216215
let config = VectorIndexConfig {
@@ -267,12 +266,10 @@ async fn test_reader_with_vector_search() {
267266
OPTION_KEY_VECTOR_SEARCH_TOP_K,
268267
};
269268
use lakesoul_io::reader::LakeSoulReader;
270-
use std::io::Read;
271269

272270
let tmp = TempDir::new().unwrap();
273271
let tmp_path = tmp.path().to_str().unwrap().to_string();
274272
let parquet_path = format!("{}/part-abc_0000.parquet", tmp_path);
275-
let index_prefix = "_vector_index/vec/-5/0/";
276273
let pk_col = "id";
277274
let vec_col = "vec";
278275

@@ -325,7 +322,7 @@ async fn test_reader_with_vector_search() {
325322
}
326323

327324
// 2. Build vector index
328-
let store = Arc::new(LocalFileSystem::new_with_prefix(tmp.path()).unwrap());
325+
let store = Arc::new(LocalFileSystem::new());
329326
let config = VectorIndexConfig {
330327
column_name: vec_col.to_string(),
331328
dim,

rust/lakesoul-vector/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,6 @@ tempfile = { workspace = true }
4747

4848
[lints]
4949
workspace = true
50+
51+
[features]
52+
huge_pages = []

rust/lakesoul-vector/src/rabitq/ivf/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ impl IvfRabitqBuilder {
128128
_ => crate::rabitq::manifest::load_manifest(mstore).await?,
129129
};
130130
let mut clusters = Vec::with_capacity(cluster_map.len());
131-
for (_cid, entry) in cluster_map.iter() {
131+
for entry in cluster_map.values() {
132132
// Read centroid from the base segment (version 0).
133133
let base = entry.base_segment().ok_or_else(|| {
134134
RabitqError::InvalidPersistence("cluster has no base segment")

rust/lakesoul-vector/src/rabitq/ivf/mod.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -554,10 +554,7 @@ impl IvfRabitqIndex {
554554
);
555555
}
556556

557-
(
558-
indices.to_vec(),
559-
quantized_vectors,
560-
)
557+
(indices.to_vec(), quantized_vectors)
561558
})
562559
.collect();
563560
println!("Quantization complete");
@@ -1431,9 +1428,10 @@ impl IvfRabitqIndex {
14311428

14321429
// Apply filter if provided
14331430
if let Some(filter_bitmap) = filter
1434-
&& !filter_bitmap.contains(vector_id as u32) {
1435-
continue;
1436-
}
1431+
&& !filter_bitmap.contains(vector_id as u32)
1432+
{
1433+
continue;
1434+
}
14371435

14381436
// Use pre-computed values (vectorized above)
14391437
let ip_x0_qr = ip_x0_qr_values[i];
@@ -1551,9 +1549,10 @@ impl IvfRabitqIndex {
15511549
.zip(cluster.pending_vectors.iter())
15521550
{
15531551
if let Some(bitmap) = filter
1554-
&& !bitmap.contains(vec_id as u32) {
1555-
continue;
1556-
}
1552+
&& !bitmap.contains(vec_id as u32)
1553+
{
1554+
continue;
1555+
}
15571556

15581557
// Unpack binary code
15591558
let binary_code = qvec.unpack_binary_code();
@@ -1606,7 +1605,7 @@ impl IvfRabitqIndex {
16061605
}
16071606

16081607
#[cfg(test)]
1609-
pub(crate) fn search_with_diagnostics(
1608+
pub(crate) fn _search_with_diagnostics(
16101609
&self,
16111610
query: &[f32],
16121611
params: SearchParams,
@@ -1618,7 +1617,7 @@ impl IvfRabitqIndex {
16181617
}
16191618

16201619
#[cfg(test)]
1621-
pub(crate) fn search_naive(
1620+
pub(crate) fn _search_naive(
16221621
&self,
16231622
query: &[f32],
16241623
params: SearchParams,
@@ -2712,7 +2711,7 @@ impl ClusterData {
27122711
}
27132712

27142713
/// Reconstruct all `QuantizedVector` values (batched + pending).
2715-
fn collect_quantized_vectors(&self, padded_dim: usize) -> Vec<QuantizedVector> {
2714+
fn _collect_quantized_vectors(&self, padded_dim: usize) -> Vec<QuantizedVector> {
27162715
let ex_bits = self.ex_bits;
27172716
let dim_bytes = padded_dim / 8;
27182717
let mut result = Vec::with_capacity(self.total_vectors());
@@ -3023,7 +3022,7 @@ mod batch_search_tests {
30233022
query_precomp.build_lut(padded_dim);
30243023

30253024
// Debug LUT parameters
3026-
if let Some(lut) = query_precomp.lut {
3025+
if let Some(ref lut) = query_precomp.lut {
30273026
println!("\n=== L2 Test LUT params ===");
30283027
println!(
30293028
"delta={:.6}, sum_vl_lut={:.6}, k1x_sum_q={:.6}",

rust/lakesoul-vector/src/rabitq/kmeans.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,10 @@ fn assign_points_for_update(
524524
state.counts[cluster] += 1;
525525
let vector = &data_chunk[row * dim..(row + 1) * dim];
526526
let sum_offset = cluster * dim;
527-
for (sum, &v) in state.sums[sum_offset..sum_offset + dim].iter_mut().zip(vector.iter()) {
527+
for (sum, &v) in state.sums[sum_offset..sum_offset + dim]
528+
.iter_mut()
529+
.zip(vector.iter())
530+
{
528531
*sum += v;
529532
}
530533
insert_candidate(&mut chunk_candidates, (dist, row));
@@ -553,13 +556,13 @@ fn insert_candidate(candidates: &mut Vec<(f32, usize)>, candidate: (f32, usize))
553556
return;
554557
}
555558
if let Some((last_dist, _)) = candidates.last()
556-
&& candidate.0 > *last_dist {
557-
candidates.pop();
558-
candidates.push(candidate);
559-
candidates.sort_unstable_by(|a, b| {
560-
b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal)
561-
});
562-
}
559+
&& candidate.0 > *last_dist
560+
{
561+
candidates.pop();
562+
candidates.push(candidate);
563+
candidates
564+
.sort_unstable_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));
565+
}
563566
}
564567

565568
fn update_centroids(

rust/lakesoul-vector/src/rabitq/manifest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ fn hup_ref(h: &mut Option<&mut Hasher>, data: &[u8]) {
141141

142142
// ---- conversions ----
143143

144-
fn u2u64(v: usize) -> Result<u64, RabitqError> {
144+
fn _u2u64(v: usize) -> Result<u64, RabitqError> {
145145
u64::try_from(v).map_err(|_| RabitqError::InvalidPersistence("usize exceeds u64"))
146146
}
147147
fn uf64(v: u64) -> Result<usize, RabitqError> {

0 commit comments

Comments
 (0)