|
1 | 1 | // SPDX-License-Identifier: Apache-2.0 |
2 | 2 | // SPDX-FileCopyrightText: Copyright The Lance Authors |
3 | 3 |
|
| 4 | +use lance_core::utils::row_addr_remap::RowAddrRemap; |
4 | 5 | use std::{ |
5 | 6 | any::Any, |
6 | 7 | cmp::Reverse, |
@@ -795,7 +796,7 @@ impl ScalarIndex for BitmapIndex { |
795 | 796 | /// Remap the row ids, creating a new remapped version of this index in `dest_store` |
796 | 797 | async fn remap( |
797 | 798 | &self, |
798 | | - mapping: &HashMap<u64, Option<u64>>, |
| 799 | + mapping: &RowAddrRemap, |
799 | 800 | dest_store: &dyn IndexStore, |
800 | 801 | ) -> Result<CreatedIndex> { |
801 | 802 | let state = self.load_bitmap_index_state().await?; |
@@ -1549,18 +1550,15 @@ impl BitmapIndexPlugin { |
1549 | 1550 | /// Remaps every bitmap in a materialized bitmap-index state using row-id mappings. |
1550 | 1551 | pub(crate) fn remap_bitmap_state( |
1551 | 1552 | state: HashMap<ScalarValue, RowAddrTreeMap>, |
1552 | | - mapping: &HashMap<u64, Option<u64>>, |
| 1553 | + mapping: &RowAddrRemap, |
1553 | 1554 | ) -> HashMap<ScalarValue, RowAddrTreeMap> { |
1554 | 1555 | state |
1555 | 1556 | .into_iter() |
1556 | 1557 | .map(|(key, bitmap)| { |
1557 | 1558 | let remapped_bitmap = |
1558 | 1559 | RowAddrTreeMap::from_iter(bitmap.row_addrs().unwrap().filter_map(|addr| { |
1559 | 1560 | let addr_as_u64 = u64::from(addr); |
1560 | | - mapping |
1561 | | - .get(&addr_as_u64) |
1562 | | - .copied() |
1563 | | - .unwrap_or(Some(addr_as_u64)) |
| 1561 | + mapping.get(addr_as_u64).unwrap_or(Some(addr_as_u64)) |
1564 | 1562 | })); |
1565 | 1563 | (key, remapped_bitmap) |
1566 | 1564 | }) |
@@ -1909,7 +1907,7 @@ mod tests { |
1909 | 1907 | use lance_core::utils::{address::RowAddress, tempfile::TempObjDir}; |
1910 | 1908 | use lance_io::object_store::ObjectStore; |
1911 | 1909 | use lance_select::RowSetOps; |
1912 | | - use std::collections::HashMap; |
| 1910 | + use rstest::rstest; |
1913 | 1911 |
|
1914 | 1912 | fn assert_state_roundtrips(state: &BitmapIndexState) { |
1915 | 1913 | let mut buf = Vec::new(); |
@@ -2460,8 +2458,44 @@ mod tests { |
2460 | 2458 | assert_eq!(red_rows_2, vec![0, 3, 6, 10, 11]); |
2461 | 2459 | } |
2462 | 2460 |
|
| 2461 | + // frags 1 and 2 (3 rows each) are compacted into frag 3: the 6 rows are |
| 2462 | + // rewritten in order to frag 3 offsets 0..6. |
| 2463 | + fn bitmap_remap_compact() -> RowAddrRemap { |
| 2464 | + use lance_core::utils::row_addr_remap::GroupInput; |
| 2465 | + use roaring::RoaringTreemap; |
| 2466 | + RowAddrRemap::compact([GroupInput { |
| 2467 | + rewritten_old_row_addrs: RoaringTreemap::from_iter( |
| 2468 | + (0..3) |
| 2469 | + .map(|o| u64::from(RowAddress::new_from_parts(1, o))) |
| 2470 | + .chain((0..3).map(|o| u64::from(RowAddress::new_from_parts(2, o)))), |
| 2471 | + ), |
| 2472 | + old_frag_ids: vec![1, 2], |
| 2473 | + new_frags: vec![(3, 6)], |
| 2474 | + }]) |
| 2475 | + .unwrap() |
| 2476 | + } |
| 2477 | + |
| 2478 | + fn bitmap_remap_explicit() -> RowAddrRemap { |
| 2479 | + // The same mapping, listed out explicitly. |
| 2480 | + RowAddrRemap::direct( |
| 2481 | + (0..6u32) |
| 2482 | + .map(|i| { |
| 2483 | + let (f, o) = if i < 3 { (1, i) } else { (2, i - 3) }; |
| 2484 | + ( |
| 2485 | + u64::from(RowAddress::new_from_parts(f, o)), |
| 2486 | + Some(u64::from(RowAddress::new_from_parts(3, i))), |
| 2487 | + ) |
| 2488 | + }) |
| 2489 | + .collect(), |
| 2490 | + ) |
| 2491 | + } |
| 2492 | + |
| 2493 | + // remap must behave identically whether the mapping is compact or explicit. |
| 2494 | + #[rstest] |
| 2495 | + #[case(bitmap_remap_compact())] |
| 2496 | + #[case(bitmap_remap_explicit())] |
2463 | 2497 | #[tokio::test] |
2464 | | - async fn test_remap_bitmap_with_null() { |
| 2498 | + async fn test_remap_bitmap_with_null(#[case] remap: RowAddrRemap) { |
2465 | 2499 | use arrow_array::UInt32Array; |
2466 | 2500 |
|
2467 | 2501 | // Create a temporary store. |
@@ -2526,38 +2560,8 @@ mod tests { |
2526 | 2560 | assert_eq!(index.index_map.len(), 2); // 2 non-null values (1 and 2) |
2527 | 2561 | assert!(!index.null_map.is_empty()); // Should have null values |
2528 | 2562 |
|
2529 | | - // Create a remap that simulates compaction of frags 1 and 2 into frag 3 |
2530 | | - let mut row_addr_map = HashMap::<u64, Option<u64>>::new(); |
2531 | | - row_addr_map.insert( |
2532 | | - RowAddress::new_from_parts(1, 0).into(), |
2533 | | - Some(RowAddress::new_from_parts(3, 0).into()), |
2534 | | - ); |
2535 | | - row_addr_map.insert( |
2536 | | - RowAddress::new_from_parts(1, 1).into(), |
2537 | | - Some(RowAddress::new_from_parts(3, 1).into()), |
2538 | | - ); |
2539 | | - row_addr_map.insert( |
2540 | | - RowAddress::new_from_parts(1, 2).into(), |
2541 | | - Some(RowAddress::new_from_parts(3, 2).into()), |
2542 | | - ); |
2543 | | - row_addr_map.insert( |
2544 | | - RowAddress::new_from_parts(2, 0).into(), |
2545 | | - Some(RowAddress::new_from_parts(3, 3).into()), |
2546 | | - ); |
2547 | | - row_addr_map.insert( |
2548 | | - RowAddress::new_from_parts(2, 1).into(), |
2549 | | - Some(RowAddress::new_from_parts(3, 4).into()), |
2550 | | - ); |
2551 | | - row_addr_map.insert( |
2552 | | - RowAddress::new_from_parts(2, 2).into(), |
2553 | | - Some(RowAddress::new_from_parts(3, 5).into()), |
2554 | | - ); |
2555 | | - |
2556 | 2563 | // Perform remap |
2557 | | - index |
2558 | | - .remap(&row_addr_map, test_store.as_ref()) |
2559 | | - .await |
2560 | | - .unwrap(); |
| 2564 | + index.remap(&remap, test_store.as_ref()).await.unwrap(); |
2561 | 2565 |
|
2562 | 2566 | // Reload and check |
2563 | 2567 | let reloaded_idx = BitmapIndex::load(test_store, None, &LanceCache::no_cache()) |
|
0 commit comments