[Feature] Support shared-shredding storage layout for MAP columns
@lszskye is already working on this.
Since Sep 21, 2026.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Start with PIP-43 and trace the existing FormatWriter, FileBatchReader, AppendOnlyWriter, LeafPredicate, and PredicateConverter interfaces. Map the write and read paths before implementing the schema conversion, metadata, allocation, predicate translation, and reconstruction pieces. Done means shared-shredding MAP columns work across writing, reading, filtering, overflow, and varying K values across files.
Written by the indexing model from the issue text.
Description
Search before asking
- I searched in the issues and found nothing similar.
Motivation
In time-series / IoT / observability workloads, a common pattern is storing free-schema fields in a MAP<STRING, T> column (e.g. metrics MAP<STRING, DOUBLE>). The default MAP storage (two KV arrays) provides:
- No per-key columnar access
- No per-key statistics
- No predicate pushdown on individual keys
This makes queries like SELECT ext_map['usage'] FROM metrics WHERE ext_map['usage'] > 30 scan the entire MAP column — extremely inefficient when only 1–3 keys out of thousands are needed per query.
The PIP-43: Columnar Storage Optimization for MAP Type in Paimon proposes a new shared-shredding storage layout that stores MAP values in K reusable physical columns within a Struct, achieving near-full columnar access with per-key statistics and predicate pushdown — without changing the logical type (MAP<STRING, T>).
Solution
Physical Layout
Each MAP<STRING, T> column configured with fields.<column>.map.storage-layout = shared-shredding is physically stored as:
STRUCT<
__field_mapping: FixedSizeList<Int32, K>, -- per-row: which field_id each col holds
__col_0: T, __col_1: T, ..., __col_{K-1}: T, -- reusable typed columns
__overflow: MAP<INT32, T> -- rare fallback for rows with > K fields
>
fields.<column>.map.shared-shredding.max-columns controls K_max, and fields.<column>.map.shared-shredding.column-placement-policy controls column placement.
File metadata (footer) stores: field name↔id dictionary, field_id→physical column set S, overflow set O, K, and max row width.
Write Path
-
Schema conversion utilities — Logical MAP → physical Struct schema rewriting; metadata serialization/deserialization; shared-shredding column detection via field metadata marker.
-
FormatWriter::AddMetadata— New virtual method (default no-op) for writing key-value metadata to file footer beforeFinish(). Parquet implementation callsAddKeyValueMetadata. -
Column allocator — Per-row slot allocator that maps field IDs to up to
Kphysical columns and sends the rest to overflow. Placement policy is configurable (plain,sequential,lru; defaultplain). Accumulates file-level statistics (S, O, max row width). -
Logical→physical batch converter — Parses logical MAP, encodes field names to integer IDs (file-level dictionary), invokes allocator per row, assembles physical Struct array.
-
Writer integration — Extended DataFileWriter that performs conversion before writing + injects metadata on close. AppendOnlyWriter detects shared-shredding columns and routes accordingly. Cross-file K adaptation (P99 of recent max row widths, capped by K_max).
Read Path
-
File metadata parsing — Parse shared-shredding metadata from file footer (dictionary, S, O, K). New
GetFileKeyValueMetadata()method onFileBatchReaderwith Parquet implementation. -
Predicate translation — Translate logical predicates on MAP keys into conservative OR predicates over physical sub-columns. Requires extending
LeafPredicateto support nested field paths and updatingPredicateConverterto emit nestedFieldRef. -
Read planning — At
SetReadSchematime: look up which physical columns to read (from S), decide whether__overflowis needed (from O), translate predicates, and pass the physical schema + physical predicate down to the innerFileBatchReaderunchanged. -
Batch reconstruction — After
NextBatch: read__field_mappingper row to identify which column holds which field (fine-grained filter), gather values into logicalMAP<STRING, T>. Merge overflow when needed. Correctness relies on per-row__field_mapping, not on pushdown precision. -
Reader integration — A wrapper reader (implements
FileBatchReader) sits between the upper layer and the format-level reader. Per-file instance. Compatible with varying K across files. Orthogonal toDataEvolutionFileReader(schema evolution).
Anything else?
No response
Are you willing to submit a PR?
- I'm willing to submit a PR!
- Dominant language
- C++
- Stars
- 65
- Forks
- 29
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 78
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from apache/paimon-cpp
-
enhancement
apache/paimon-cpp#381 · 1 assignee ·
-
Difficulty 4/5 3-5 days Newbie friendliness 30/100
apache/paimon-cpp#375 · 1 assignee ·
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 45/100
apache/paimon-cpp#361 · 1 assignee ·
-
enhancement
Difficulty 4/5 3-5 days Newbie friendliness 45/100
apache/paimon-cpp#325 · 1 assignee ·
-
enhancement
Difficulty 5/5 Over a week Newbie friendliness 35/100
apache/paimon-cpp#319 · 1 reaction · 1 assignee ·
All issues in apache/paimon-cpp
Similar issues
-
ai_reviewed
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
ydb-platform/ydb#53869 · 3 comments ·
-
bug cert blocker needs triage
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
project-chip/connectedhomeip#74373 ·
-
upstream update
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
conan-io/conan-center-index#31035 ·
-
Bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
documentation
Difficulty 1/5 Under an hour Newbie friendliness 85/100
vllm-project/vllm-ascend#17329 ·