Skip to content

feat(parquet): support page index filter for nested column type (paimon side)#409

Closed
zhf999 wants to merge 5 commits into
alibaba:mainfrom
zhf999:nest-fix-paimon
Closed

feat(parquet): support page index filter for nested column type (paimon side)#409
zhf999 wants to merge 5 commits into
alibaba:mainfrom
zhf999:nest-fix-paimon

Conversation

@zhf999

@zhf999 zhf999 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

feat: support page index filter for nested column type

Purpose

Linked issue: close #xxx

Previously, page index filtering was only applied to flat (scalar) columns. When the read schema contained any nested field (struct, list, or map), the entire page index filter was disabled as a workaround (has_nested_field check in ParquetFileBatchReader::SetReadSchema), causing nested columns to fall back to rowgroup-level filtering only and miss fine-grained page-level I/O skipping.

This PR extends page index filtering to nested column types by implementing nested array assembly on the Paimon side. The core idea is to read each top-level field (flat or nested) via its leaf RecordReaders, then manually assemble the nested Arrow array using def/rep levels — mimicking Arrow's StructReader::BuildArray and ListReader::BuildArray logic.

Key implementation changes:

  1. PageFilteredRowGroupReader: The ReadFilteredRowGroup entry point now accepts parquet::arrow::FileReader* (instead of ParquetFileReader*) to access SchemaManifest. Leaf column indices are grouped into top-level fields via SchemaManifest::GetFieldIndices, and each field is read through the new ReadAndAssembleField method.

  2. ReadAndAssembleField (new): Recursively reads a top-level field and assembles the result array:

    • Leaf fields: Delegates to ReadLeafColumn. Top-level leaf columns get data_page_filter + compressed ranges for I/O-level page skipping. Leaf columns within nested types skip data_page_filter (to preserve def/rep level synchronization across sibling leaves) and rely on decode-level skipping only.
    • Struct fields: Reads all requested child fields recursively, then builds the validity bitmap from def levels using DefLevelsToBitmap (or DefRepLevelsToBitmap if a repeated descendant exists).
    • List/Map fields: Reads the child field, then builds offsets and validity from def/rep levels using DefRepLevelsToList. Map is handled as list<struct<key, value>> per Parquet's storage model.
  3. BuildProjectedField / BuildProjectedSchema (new): Recursively builds a projected Arrow schema from SchemaField trees, including only requested leaf columns. This enables sub-column projection (e.g., reading only struct<x> from struct<x, y>).

  4. ReadLeafColumn (refactored from ReadFilteredColumn): Returns both the ChunkedArray and the RecordReader so callers can access def/rep levels for nested array assembly. The enable_page_filter flag controls whether I/O-level page skipping is applied.

  5. FileReaderWrapper: Removes BuildPageFilteredSchema and page_filtered_read_schema_ construction logic — schema projection is now delegated to PageFilteredRowGroupReader::BuildProjectedSchema.

  6. ParquetFileBatchReader: Removes the has_nested_field check that previously disabled page index filter for nested fields.

Tests

UT — PageFilteredRowGroupReaderTest (src/paimon/format/parquet/page_filtered_row_group_reader_test.cpp):

  • NestedStructColumnRowGroupFilter — rowgroup-level filtering on struct columns (fixed expected row count: 50 → 30, since page-level filtering now correctly applies)
  • NestedStructColumnOnlyReadIdField — reading only the flat id field from a file with nested struct
  • NestedListColumnRowGroupFilter — rowgroup-level filtering on list column (fixed expected row count)
  • NestedMapColumnRowGroupFilter — rowgroup-level filtering on map column (fixed expected row count)
  • MultipleAdjacentNestedColumns — multiple adjacent nested columns (fixed expected row count)
  • MultipleNestedColumns (new) — predicate pushdown with all nested types (struct, list, map) and page-level filtering across multiple row groups
  • NestedStructSubColumnProjection (new) — sub-column projection of a struct type (struct<x> from struct<x, y>) with page-level filtering

Test helper functions were refactored for reusability: MakeIdColumn, MakeNestedStructData, MakeListColumnData, and MakeMapColumnData now each build a single column (instead of wrapping with an id column), allowing flexible composition across test cases.

API and Format

  • Internal API change: PageFilteredRowGroupReader::ReadFilteredRowGroup signature changed — now takes parquet::arrow::FileReader* instead of parquet::ParquetFileReader*. This is an internal API (not in include/).
  • Internal API change: FileReaderWrapper::BuildPageFilteredSchema is removed; schema projection is now handled by PageFilteredRowGroupReader::BuildProjectedSchema.
  • No changes to Paimon's public API in include/.
  • No storage format or protocol changes.
  • No Arrow dependency patch changes.

Documentation

No new user-facing feature documentation needed — this is an internal performance optimization that is transparent to users.

Generative AI tooling

Generated-by: GLM 5.2

@zhf999 zhf999 closed this Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant