Searching for the terminology abstraction: relation, function, or server — Sep 8, 2026
The question: what abstraction do runners actually need?
With no formal agenda and the WGM approaching, the group spent the hour on a single question that has been building for weeks: what is the right level of abstraction for terminology inside a SQL on FHIR runner? John Grimes split it in two — what abstraction do you need to query terminology, and, a related problem Nikolai Ryzhikov has been circling, how do you distribute terminology in the first place. Nobody resolved either, but the candidates got a lot sharper.
John's first data point was Pathling's local terminology mode: roughly seven relational tables, loaded into an in-memory index on roaring bitmaps, holding everything analytic terminology needs. His own caveat — you don't get to write SQL against them, and that's the catch. The tables are real; the abstraction they expose is not a relational one.
The FHIRPath route is still live in parallel. Pathling has had terminology functions inside FHIRPath for years, and Brian Postlethwaite's cleanup proposal (including an inline translate) would make them more usable — but Grahame Grieve is wary of pushing terminology-query complexity onto query authors, and John relayed that concern.
Some things might be pretty simple, other things might actually require quite a chunk of SQL to actually do them properly.
Candidate one: the value set as a relation
Nikolai's core claim: for most analytic queries, a pre-expanded value set is just a table you join against. You don't have to care whether it was materialised at load time, refreshed from a terminology server, or expanded on the fly — the abstraction is the relation.
He walked through the FHIR-to-OMOP case as the concrete driver. In OMOP, a single FHIR Observation can land in either the measurement or the observation table depending on the domain of the concept ID — which forces a terminology join that FHIRPath cannot express cleanly. And once you're writing multi-floor SQL to build a cohort ("women with these diseases, on these medications, with this temporal expression"), there is no place to slot a FHIRPath expression. The abstraction has to be relational at that layer.
We can treat the pre-expanded value set as a relation. How we got there, we don't know — fresh from terminology, expansion in our database, or pre-loaded. But it is abstraction, and I can join with it.
Candidate two: don't pre-bake anything
John's counter-proposal cuts the other way. Pathling requires no pre-baking at all: it implements a subset of VCL, compiles a value set's compose into VCL, then VCL into ECL, and hands that to Snowman. You get query-time expansion over arbitrary value sets without ever declaring up front which ones you care about — which matters, because ad-hoc analytics is exactly the case where you don't know your value sets in advance.
That is the real trade-off in the room. Pre-baked expansions are simple and portable but require a closed list; dynamic expansion is open-ended but drags a terminology engine into every implementation. When Nikolai asked what specifically in VCL worries Grahame Grieve, John's answer was that it depends entirely on where you draw the scope line — which value sets you promise to support determines how much of the language you have to translate into SQL.
OMOP's Athena as the reference model — and where FHIR is a superset
Nikolai pointed at OMOP's Athena as evidence that a relational terminology distribution works in production — and at UMLS as the older, uglier, but similarly successful version of the same idea. His argument: informationally, FHIR terminology (code systems, concepts with properties, value sets with compose, concept maps) maps to a normal form of relational tables. Athena is essentially a subset of what FHIR can express; a FHIR-shaped relational model is more generic than Athena, not less.
Guillermo Rodriguez, from Health Samurai's terminology team, confirmed the current relational model in Termbox covers "almost everything" from the FHIR terminology resources at the logical layer. Storage and modelling are not the blocker — the additional processing to serve terminology operations in a form directly consumable from SQL is.
Right now I think we're able to express almost everything from the FHIR terminology resources into our model. But that's only the foundation. Serving the terminology operations in something like consumable information for analytics directly from SQL — we still need to make additional processing.
Where the model stops being enough: value sets that compose on other value sets and other code systems, very large code systems where you want an expression to pick out subsets, and anything that needs a classifier. Guillermo was blunt that some of these are not just performance issues — they raise real questions about feasibility of the query itself in pure SQL without an extra engine keeping a materialisation current. Nikolai's rough split: the relational model covers maybe 20% of what FHIR terminology can express, and that 20% serves about 90% of users.
The other half: how do you even ship terminology?
The distribution problem got less airtime than it deserved, and Nikolai flagged it as one of the biggest in the area. Loading terminology into a server took his team the better part of a year — that is the status quo, and it is nobody's idea of an established practice. Athena, by contrast, ships in a form people can actually consume, which is a large part of why the OMOP community doesn't have this complaint.
His ask is modest and concrete: stop distributing terminology as a two-gigabyte JSON blob. Ship NDJSON, CSV or Parquet, standardise the shape, and let consumers load it into whatever they want — in-memory index, Postgres tables, Parquet files. Guillermo's number makes it tangible: Athena packaged for import into Termbox is about 1.5 GB, mapped onto their own relational model rather than the FHIR one.
Arjun Sanyal pointed at Brian Kaney as the person to pull into this thread — he worked on value set distribution and wrote a VCL-like language last year, and distribution in particular is his area.
Reproducibility: NCQA locks value sets per year
Michael E Campbell asked Arjun Sanyal — dropping by for the first time in a while — how measure-level value sets actually get pinned in practice. Arjun explained that NCQA locks value sets per measure on a yearly basis: the measure is published, the expansion is frozen, and if you want to re-run a prior year you need to have kept both the measure and that year's value-set expansion as a pair.
If you want to rerun something from the past, this is one of the most challenging parts of a HEDIS-style calculation — you have to have persistence of that year's value sets for that measure. You have to keep them as a pair.
Guillermo pointed out that the FHIR way to handle this is version-pinning inside the value set itself, plus capturing the expansion input parameters on the resulting ValueSet.expansion.parameter — a well-behaved terminology server should be able to reproduce the same expansion later, even against newer content. Nikolai's summary: expansion is logically a pure function of pinned versions plus parameters, and its output is a table of concepts. Which loops back to the "value set as relation" framing.
Where a classifier still has to take over
Gino Canessa pushed back on how much of the SNOMED semantics you actually want to lift into SQL. His example — myocardial infarction as a finding of a body site and an associated morphology of an infarction — is the kind of query where SNOMED distributes classified relationships in RF2 precisely because you can't rederive the hierarchy from the source data with a generic tool. SNOMED ships pre-classified: all the inference over is-a relationships happens before the release is packaged, so consumers load the result rather than deriving it. Post-coordinated expressions and MRCM validation — the syntax used to author SNOMED itself and to validate expressions — are a separate layer entirely.
Nikolai's counter: for pre-coordinated codes and the is-a graph, you can express it in a relational model — that is exactly what OMOP cohorts do all day against the ancestry table, and it works. Post-coordination is a smaller share of the real-world usage than the discussion made it sound.
If we're talking about this very crazy stuff, it's probably one percent of the real world.
John landed the boundary: pre-coordinated subsumption and membership can be relational; post-coordination and MRCM need a classifier and should be handed off to a terminology engine.
Four levels, and where to draw the line
Steve Munini asked the framing question directly: is this problem only surfacing because SQL on FHIR added SQLQuery alongside ViewDefinition, or is it genuinely impossible to do performantly through view definitions and a terminology server? Nikolai's answer: both. There is a flattening phase — building the marts — where you can normalise terminology (translating ICD to SNOMED, for example) with FHIRPath-style helpers before the data lands in a table. And there is a querying phase — building cohorts and measures — where the terminology has to be joinable in SQL because the query itself is SQL.
John's synthesis after the back-and-forth was that the two approaches co-exist and can be powered from the same source of truth. A SQL on FHIR runner can:
- Ship a pre-baked expansion table for the simple, common case — you know your value sets up front and just want membership joins
- Layer FHIRPath terminology functions (
memberOf,subsumes,translate) for query-time work at the view layer - Expose a table-based abstraction — value sets as relations, closure tables for subsumption, concept-map tables for translation — for the complex analytic case where you need to join into cohort SQL
- Hand off to a terminology server or classifier for post-coordination and anything MRCM-shaped
Two arguments kept the terminology server from simply winning. Its API was designed for runtime lookups one code at a time, not for bulk joins against millions of patient rows — so performance is a genuine blocker, not a tuning detail. And in trusted research environments, live API access to an outside server may not be available at all, which makes local tables a requirement rather than an optimisation.
Whether you export tables from a terminology server, load a distribution like Athena, or query the server live, the same governance and content pipeline can sit behind all three surfaces. Gino's caution stands as the summary of the whole hour: whatever you choose, the complexity does not disappear — it just moves. The design question is where you want the query author to meet it.
Next step: a proof of concept, not a decision
Nothing was decided, and John was explicit that the way to settle it is a proof of concept in the reference implementation — with a diverse enough set of use cases to show whether the tabular abstraction earns its keep. Nikolai proposed starting with SQL-based quality measures, where terminology is a plain membership join, and then attempting FHIR-to-OMOP as the hard case that needs concept-domain lookups and cross-terminology mapping. One open question they left hanging: whether the PoC should use Athena's tables directly or build a FHIR-shaped abstraction on top of them.
Alongside it, Nikolai will spend more time with Athena and Health Samurai's terminology team continues the FHIR-shaped relational model in Termbox. Gino took an action to reread the relational terminology interface document with today's discussion in mind, and Nikolai is coordinating with him on timing for an agent-harness lightning round. John still owes the group a dedicated terminology session with Grahame Grieve, Michael Lawley, Brian Postlethwaite and Brian Kaney — pushed by the WGM, not dropped.