alamb commented on code in PR #10784:
URL: https://github.com/apache/arrow-rs/pull/10784#discussion_r3847489114


##########
parquet/src/file/metadata/mod.rs:
##########
@@ -412,6 +412,176 @@ impl PageIndex {
             None
         }
     }
+
+    /// Convert this `PageIndex` into a [`PageIndexBuilder`]
+    pub fn into_builder(self) -> PageIndexBuilder {
+        self.into()
+    }
+}
+
+/// Builder for constructing [`PageIndex`] structures
+///
+/// It supports:
+/// - Allocating space for indexes based on [`PageIndexPolicy`]
+/// - Populating column indexes for predicate columns (for page filtering)
+/// - Populating offset indexes for projected columns (for direct I/O)
+/// - Automatic conversion of empty structures to `None` to save memory
+pub struct PageIndexBuilder {
+    column_indexes: Option<Vec<Vec<Option<ColumnIndexMetaData>>>>,
+    offset_indexes: Option<Vec<Vec<Option<OffsetIndexMetaData>>>>,
+}
+
+impl PageIndexBuilder {
+    /// Creates an empty index structure with space for the specified number 
of row groups and columns
+    ///
+    /// Returns `Some` containing a nested vector structure where all entries 
are initialized to `None`.
+    /// The outer vector has one entry per row group, and each inner vector 
has one entry per column.
+    fn empty_index<T>(num_row_groups: usize, num_columns: usize) -> 
Option<Vec<Vec<Option<T>>>> {
+        Some(
+            (0..num_row_groups)
+                .map(|_| {
+                    let mut idx = Vec::with_capacity(num_columns);
+                    idx.resize_with(num_columns, || None);
+                    idx
+                })
+                .collect(),
+        )
+    }
+
+    /// Creates a new [`PageIndexBuilder`] with space allocated for both 
column and offset indexes
+    ///
+    /// This allocates empty index structures for the specified number of row 
groups and columns.
+    /// All index entries are initialized to `None` and can be populated using
+    /// [`put_column_index`](Self::put_column_index) and 
[`put_offset_index`](Self::put_offset_index).
+    pub fn new(num_row_groups: usize, num_columns: usize) -> Self {
+        Self {
+            column_indexes: Self::empty_index(num_row_groups, num_columns),
+            offset_indexes: Self::empty_index(num_row_groups, num_columns),
+        }
+    }
+
+    /// Creates a new [`PageIndexBuilder`] with selective allocation based on 
policies
+    ///
+    /// This allows fine-grained control over which indexes are allocated:
+    /// - [`PageIndexPolicy::Skip`]: No allocation, the index structure is set 
to `None`
+    /// - [`PageIndexPolicy::Optional`] or [`PageIndexPolicy::Required`]: 
Allocates empty index structure
+    ///
+    /// This is more memory-efficient than [`new`](Self::new) when only one 
type of index is needed.
+    ///
+    /// # Arguments
+    /// * `num_row_groups` - Number of row groups in the file
+    /// * `num_columns` - Number of columns in the schema
+    /// * `column_index_policy` - Policy for column index allocation
+    /// * `offset_index_policy` - Policy for offset index allocation
+    pub fn new_with_policy(
+        num_row_groups: usize,
+        num_columns: usize,
+        column_index_policy: &PageIndexPolicy,
+        offset_index_policy: &PageIndexPolicy,
+    ) -> Self {
+        use reader::PageIndexPolicy;
+
+        let column_indexes = match column_index_policy {
+            PageIndexPolicy::Skip => None,
+            _ => Self::empty_index(num_row_groups, num_columns),
+        };
+
+        let offset_indexes = match offset_index_policy {
+            PageIndexPolicy::Skip => None,
+            _ => Self::empty_index(num_row_groups, num_columns),
+        };
+
+        Self {
+            column_indexes,
+            offset_indexes,
+        }
+    }
+
+    /// Creates a new [`PageIndexBuilder`] from an existing [`PageIndex`]
+    ///
+    /// This takes ownership of the index structures from the provided 
[`PageIndex`],
+    /// allowing them to be modified and rebuilt. Useful for updating existing 
page indexes.
+    pub(crate) fn new_from(page_index: PageIndex) -> Self {
+        Self {
+            column_indexes: page_index.column_indexes,
+            offset_indexes: page_index.offset_indexes,
+        }
+    }
+
+    /// Sets the column index for a specific row group and column

Review Comment:
   maybe `copy_column_index` would be a more specific name 🤔 



##########
parquet/src/file/metadata/mod.rs:
##########
@@ -412,6 +412,176 @@ impl PageIndex {
             None
         }
     }
+
+    /// Convert this `PageIndex` into a [`PageIndexBuilder`]
+    pub fn into_builder(self) -> PageIndexBuilder {
+        self.into()
+    }
+}
+
+/// Builder for constructing [`PageIndex`] structures
+///
+/// It supports:
+/// - Allocating space for indexes based on [`PageIndexPolicy`]
+/// - Populating column indexes for predicate columns (for page filtering)
+/// - Populating offset indexes for projected columns (for direct I/O)
+/// - Automatic conversion of empty structures to `None` to save memory
+pub struct PageIndexBuilder {
+    column_indexes: Option<Vec<Vec<Option<ColumnIndexMetaData>>>>,
+    offset_indexes: Option<Vec<Vec<Option<OffsetIndexMetaData>>>>,

Review Comment:
   Having some outlet for different representations (so we don't have to hard 
code one choice into the base crate that downstream projects have to accept) 
sounds like a good idea to me
   
   That way we could implement some fancy Grid structure for DataFusion for 
example, but leave a basic implementation in arrow-rs that is simple 



##########
parquet/src/file/metadata/mod.rs:
##########
@@ -412,6 +412,176 @@ impl PageIndex {
             None
         }
     }
+
+    /// Convert this `PageIndex` into a [`PageIndexBuilder`]
+    pub fn into_builder(self) -> PageIndexBuilder {
+        self.into()
+    }
+}
+
+/// Builder for constructing [`PageIndex`] structures
+///
+/// It supports:
+/// - Allocating space for indexes based on [`PageIndexPolicy`]
+/// - Populating column indexes for predicate columns (for page filtering)
+/// - Populating offset indexes for projected columns (for direct I/O)
+/// - Automatic conversion of empty structures to `None` to save memory
+pub struct PageIndexBuilder {
+    column_indexes: Option<Vec<Vec<Option<ColumnIndexMetaData>>>>,
+    offset_indexes: Option<Vec<Vec<Option<OffsetIndexMetaData>>>>,

Review Comment:
   Yeah the more I think about it, the more I like the idea of separating out 
the API for accessing the page index from the concrete implementation. 
   
   The builder needs to be part of the concrete implementation, but it would be 
really neat if we could switch out PageIndex with `Arc<dyn PageIndexSource>` or 
something 🤔  that would let downstream users provide their own imlementation
   
   Something like this perhaps
   
   ```rust
   enum PageIndexSource {
     // concrete pageIndex (our old `Option<Vec<Option<Vec<..>>>>` friend
     PageIndex(PageIndex),
     // user supplied call back
     Callback(Arc<dyn PageIndexSource>),
   }
   
   // and then wire it into the ParquetMetadata 🤔 
   struct ParquetMetadata { '
     ... 
     page_index: PageIndexSource,
   }
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to