Upstream data
Job-posting corpus
47 raw scrapes, 143 MB, nine boards
Technical overviewData engineering and dimensional modelling
A job-posting corpus landed immutably, modelled in Spark, and loaded into PostgreSQL by an idempotent write.
Source code
Inspect the implementation, commit history, and project documentation.
01
A batch pipeline over 24,636 landed observations of 18,969 distinct job postings. Raw scrapes land immutably by ingest date; a Spark job conforms and deduplicates them into two fact tables and four dimensions; a loader mirrors the model into PostgreSQL. The grain is written before any transform, and every rerun is idempotent.
02
Upstream data
47 raw scrapes, 143 MB, nine boards
Selection
Globs the real scrapes, excludes derived files
Immutable store
One partition per ingest date, never mutated
Durability
Temp file then rename, gzip mtime zero
Exclusion
The emails column never enters the warehouse
Processing
Runs in the official Spark image, not on Windows
Business logic
Pure functions: company folding, location resolution
Schema
Two fact tables and four dimensions
Database
wh schema, real foreign keys, grain in table comments
Loader
Full load in one transaction, or replace one ingest date
Enforcement
Idempotency and referential integrity checked on each push
03
The grain of each fact is committed to the README before any transform code, because a landing step that quietly repairs its input destroys the evidence of what the source actually sent.
The observation fact keeps one row per posting, per source, per ingest date. Collapsing to one row per posting first would permanently destroy whether a posting was open on a given date.
The rules that fold company names and resolve locations import nothing from Spark, so 61 tests exercise every rule in 0.06 seconds. A rule that only runs by starting a session is a rule nobody runs.
Folding Inc, Ltd, and Corp merges 127 spellings of the same employer. Folding Canada or USA would merge KPMG with KPMG Canada, a global brand and a national entity a job-market warehouse keeps apart.
04
$ spark-submit warehouse/transform.py
fact_posting_observation 24,519 · fact_posting 18,969
dim_company 5,878 · dim_location 1,534 · dim_source 9 · dim_date 123
$ python -m warehouse.load --full (then re-run)
reload leaves identical counts · 0 orphan keys
✓ idempotent on the real corpus05