FHIR-to-OMOP in miniature — one Condition, three possible tables, terminology decides which — May 26, 2026
A work-in-progress FHIR-to-OMOP mapping — built out of SQL on FHIR pieces
- A FHIR profile that guarantees a conforming resource is translatable
- Value sets reverse-engineered out of the OMOP Athena tables
- Concept maps for the small mappings like status and gender
- A ViewDefinition to flatten
- A SQL query to join it all together
Nikolai also announced he was abandoning his first approach — diffing his output against the official Synthea-to-OMOP ETL — because that ETL is wrong in enough places that it isn't a ground truth. Hand-written test cases replace it.
The Condition walkthrough — the whole problem in miniature
OMOP's condition_occurrence wants a SNOMED concept ID, but your Condition might be coded in ICD-10, so you look the code up in Athena's mapping tables.
- One ICD-10 code can map to several SNOMED codes → several rows
- Then check the domain of the concept you landed on: if it's a condition it goes to
condition_occurrence, if it's an observation or a measurement it goes somewhere else entirely, with different fields
In SQL that whole decision is a join against the terminology tables — which is also why it stays fast over millions of rows.
Arjun — this isn't type X to type Y
This isn't type X to type Y, because both the type and the cardinality change. Plenty of ICD-10 codes aren't diagnoses at all — a BMI is an observation — and some are two conditions bundled into one code, which you can never map cleanly into a more precise system, because the more precise system asks for information the source never had.
Nikolai — why SQL, not a mapping language
The logic is genuinely procedural: look up the default vocabulary, split if the mapping is one-to-many, check the domain, then apply a different transformation depending on the answer. I don't think that's expressible in FHIR Mapping Language without reaching for something else.
Even if translate could carry it, it wouldn't survive bulk transformation — people would burn CPUs. In SQL it's a join.
Steve — spec inconsistencies from implementing operation-by-operation
Had them clarified before filing:
- The binary-versus-raw-payload question — Nikolai conceded this is an official hack, recommended by Gino, and that FHIR core probably needs an issue about it
- Whether a Bundle passed in should be unwrapped
- The return shape diverging between
$viewdefinition-runand$sqlquery-run - Streaming guidance that only exists on one operation
Nikolai's line on all of them: if we said it once, it should be said everywhere it applies.