Overview
A property data team gets the same house from many places. One feed writes Unit 5, 12 Smith St, another 5/12 Smith Street, a third Apt 5 12 SMITH ST., and a fourth leaves the postcode out. parcelkey takes CRM, listing and sales feeds for two agencies, matches each address to a table in the public G-NAF layout, and gives every property one id that stays stable for a G-NAF release. The feeds are generated, with the defects a real feed carries.
Key Features
- Normalise, then match. Unit forms, levels, lots, all the G-NAF street types and suffixes, Mount and Saint, state names, phones to E.164 and emails lower cased, with the rule ids stored on each row.
- Two rules that cannot be switched off. A different unit never matches, and a lot with no street number links to a numbered address only when a geocode is within a distance, measured with PostGIS on
geography. - Every decision on record. Each link keeps its rule and score. What cannot be decided goes to quarantine with a reason code and the candidates, and a later G-NAF load can free it.
- Contacts and permissions. Contacts merge only on a shared email or phone inside one agency, never on a name. Forced row level security keeps each agency's rows apart.
- Checks that prove the data. A reconcile command re-reads the feed files with its own reader and proves every row is linked or set aside, nothing is orphaned and no property has two ids. A freshness check exits non zero when a load is stale.
Technical Achievements
The matcher is scored against a seeded answer key that only the evaluate command reads. It reports precision, recall, and an honest recall that counts the records it deliberately sets aside as misses, so the headline number cannot hide them. A mutation check changes one guard at a time and requires a failing test for each. Independent review rounds, the last two blind, wrote new address and contact cases against the finished build, and each fault they found became a generated test class. The first measurement was too good, because the generated streets were far apart; adding look-alike streets showed the real failure, a new address one letter from an existing street.
What I Learned
An accuracy figure is only as honest as its key. The generator and the matcher share a vocabulary, so the clean classes score high by construction, and the write-up says which classes carry the matcher's own policy as the expected result.
Limits
The feeds are generated in the G-NAF table layout, and the loader reads Geoscape's file names and columns as their published guide describes them. Match time grows faster than the record count, and the run at ten times the size is the largest one measured.
Tech Stack
Python, PostgreSQL, PostGIS, pg_trgm, SQL, psycopg, Hypothesis, pytest, Docker, GitHub Actions.