Skip to content

Upload: parquet / hive / Delta sources, and suggested column mapping in the wizard - #262

Merged
fcollman merged 2 commits into
masterfrom
parquet-upload-sources
Oct 26, 2026
Merged

fcollman merged 2 commits into
masterfrom
parquet-upload-sources

Conversation

@fcollman

Copy link
Copy Markdown
Collaborator

Parquet sources

A cloud-location upload (gs:// or public s3://) can now be parquet as well as CSV:

  • One file: a single .parquet file.
  • A folder: read recursively. Hive key=value directories become columns. Entries starting with _ or . are skipped.
  • A Delta table: a folder with a _delta_log. Only the table's current files are read, so files replaced by an optimize or overwrite aren't counted twice.

How the work is split:

  • Parts: built from the parquet footers, about 1.5M rows each (UPLOAD_PARQUET_PART_ROWS). A part is whole row groups, or a slice of a row group larger than the target.
  • Pipeline: the parts run through the same part tasks, SchemaProcessor, CSV parts and GCS compose as CSV uploads.
  • Row counts: known up front, so generated IDs get exact offsets. A part that reads a different number of rows than its footer promised fails.
  • Footers: read with one suffix-range request each. Planning the 315-file minnie v1941 synapse export took 151 s through pyarrow's GCS filesystem and takes about 8 s this way.
  • Preview: the inspect endpoint returns columns, a first row, a file count and a row count for parquet sources.

Suggested column mapping (wizard step 2)

  • When suggestions are made: fields with no column are filled with the best-matching column name when a schema is chosen; a "Suggest matches" button runs it again.
  • They never override a choice: suggestions are marked until the user changes them, and a column the user picked is never replaced.
  • How names are compared: names are split into components on _, -, spaces and camelCase. A component matches when it is equal or a prefix (pos ~ position). The score is 2 × matched characters / (total characters).
  • Threshold: a pair is suggested only if every component of one name matched and the score is at least 0.4. So pre_x maps to pre_pt_position_x, but pre_pt_root_id and id_partition → id are not suggested.

Tests

  • Parquet sources: hive folders, a Delta table after an overwrite, single files, row-group slices, empty files, output byte-identical to the CSV path.
  • Matcher: run through Node, skipped without it.
  • The full suite passes locally on fresh PostGIS 18 and Redis: 550 passed, 2 skipped.

🤖 Generated with Claude Code

fcollman and others added 2 commits October 26, 2026 00:53
…a Delta table

A cloud-location upload (gs:// or public s3://) can now be parquet as well
as CSV: one .parquet file, a folder read recursively (key=value directories
become columns; _/. entries are skipped), or a folder with a _delta_log,
read as a Delta table so files an optimize or overwrite replaced are never
read twice.

process_csv plans parts from the parquet footers (UPLOAD_PARQUET_PART_ROWS,
default 1.5M rows): whole row groups, or slices of a row group larger than
that, so each process:process_parquet_part stays about a minute. The
footers give each part's row count, so generated ids get exact offsets and
a part that reads a different count fails. Parts go through the same
SchemaProcessor, CSV parts and GCS compose as CSV uploads.

Footers are read with one suffix-range request each: pyarrow's GCS
filesystem serialized concurrent opens (315 footers of the minnie v1941
synapse export: 151 s, now ~8 s). The inspect endpoint previews parquet
sources (columns, first row, files, rows) for the column mapping.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When a schema is chosen (or the position format toggled), fields with no
column yet get the column whose name matches best; a "Suggest matches"
button runs it again. Suggestions are marked until changed, and never
replace a column the user chose.

columnMatch.js splits names into components (_, -, spaces, camelCase). A
column component matches a field component when equal or a prefix of it
("pos" ~ "position"), worth the shorter one's characters; the score is
2 * matched / (characters in both names), so identical names score 1. A pair
is suggested only when every component of one name matched and it scores at
least 0.4, so pre_x -> pre_pt_position_x (0.44) but not pre_pt_root_id, and
not id_partition -> id (0.31). Plain in-order character matching would not
do: "post_x" spells out a subsequence of "pre_pt_position_x".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@fcollman
fcollman merged commit 2f33392 into master Oct 26, 2026
1 check passed
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