@@ -4008,6 +4008,71 @@ def test_bitmap_uncommitted_segments_can_be_committed_from_python(tmp_path):
40084008 )
40094009
40104010
4011+ def test_zonemap_segment_merge_and_commit_from_python (tmp_path ):
4012+ ds = generate_multi_fragment_dataset (
4013+ tmp_path , num_fragments = 4 , rows_per_fragment = 40
4014+ )
4015+
4016+ index_name = "id_zonemap_segments"
4017+ fragment_ids = [fragment .fragment_id for fragment in ds .get_fragments ()]
4018+ with pytest .raises (ValueError , match = "create_index_uncommitted" ):
4019+ ds .create_scalar_index (
4020+ column = "id" ,
4021+ index_type = "ZONEMAP" ,
4022+ fragment_ids = [fragment_ids [0 ]],
4023+ )
4024+
4025+ staged_segments = [
4026+ ds .create_index_uncommitted (
4027+ column = "id" ,
4028+ index_type = "ZONEMAP" ,
4029+ name = index_name ,
4030+ fragment_ids = [fragment_id ],
4031+ )
4032+ for fragment_id in fragment_ids
4033+ ]
4034+
4035+ assert len ({segment .uuid for segment in staged_segments }) == len (staged_segments )
4036+ for segment , fragment_id in zip (staged_segments , fragment_ids ):
4037+ files = segment .files
4038+ assert files is not None
4039+ assert segment .fragment_ids == {fragment_id }
4040+ assert any (file .path == "zonemap.lance" for file in files )
4041+ assert all (not file .path .startswith ("part_" ) for file in files )
4042+
4043+ merged_segment = ds .merge_existing_index_segments (staged_segments )
4044+ merged_files = merged_segment .files
4045+ assert merged_files is not None
4046+ assert merged_segment .uuid not in {segment .uuid for segment in staged_segments }
4047+ assert merged_segment .fragment_ids == set (fragment_ids )
4048+ assert any (file .path == "zonemap.lance" for file in merged_files )
4049+ assert all (not file .path .startswith ("part_" ) for file in merged_files )
4050+
4051+ ds = ds .commit_existing_index_segments (index_name , "id" , [merged_segment ])
4052+ descriptions = {index .name : index for index in ds .describe_indices ()}
4053+ assert descriptions [index_name ].index_type == "ZoneMap"
4054+ assert len (descriptions [index_name ].segments ) == 1
4055+
4056+ filter_expr = "id >= 20 AND id < 90"
4057+ without_index = ds .scanner (
4058+ filter = filter_expr ,
4059+ columns = ["id" , "text" ],
4060+ use_scalar_index = False ,
4061+ ).to_table ()
4062+ with_index = ds .scanner (
4063+ filter = filter_expr ,
4064+ columns = ["id" , "text" ],
4065+ use_scalar_index = True ,
4066+ ).to_table ()
4067+
4068+ assert with_index .num_rows == without_index .num_rows
4069+ assert with_index ["id" ].to_pylist () == without_index ["id" ].to_pylist ()
4070+ assert (
4071+ "ScalarIndexQuery"
4072+ in ds .scanner (filter = filter_expr , use_scalar_index = True ).explain_plan ()
4073+ )
4074+
4075+
40114076def test_merge_index_metadata_btree_soft_break (tmp_path ):
40124077 ds = generate_multi_fragment_dataset (
40134078 tmp_path , num_fragments = 2 , rows_per_fragment = 100
0 commit comments