Terminology Services
OctoFHIR integrates with external terminology servers to provide ValueSet expansion, code system validation, and advanced search modifiers like :in, :not-in, :below, and :above.
Configuration
Section titled “Configuration”Enable terminology services in your octofhir.toml:
[terminology]enabled = trueserver_url = "https://tx.fhir.org/r4"cache_ttl_secs = 3600 # Cache expansions for 1 hourSupported Terminology Servers
Section titled “Supported Terminology Servers”- tx.fhir.org - HL7 FHIR Terminology Server
- Ontoserver - CSIRO Terminology Server
- Any FHIR R4 compatible terminology server
ValueSet Search Modifiers
Section titled “ValueSet Search Modifiers”:in Modifier
Section titled “:in Modifier”Search for resources with codes that are members of a ValueSet:
# Find observations with codes in the vital-signs ValueSetGET /Observation?code:in=http://hl7.org/fhir/ValueSet/observation-vitalsignresult
# Find conditions with ICD-10 diabetes codesGET /Condition?code:in=http://example.org/ValueSet/diabetes-icd10:not-in Modifier
Section titled “:not-in Modifier”Exclude resources with codes in a ValueSet:
# Find observations that are NOT vital signsGET /Observation?code:not-in=http://hl7.org/fhir/ValueSet/observation-vitalsignresultHierarchy Modifiers (SNOMED CT)
Section titled “Hierarchy Modifiers (SNOMED CT)”:below Modifier
Section titled “:below Modifier”Search for a code and all its descendants (subsumption):
# Find conditions with diabetes or any subtype# 73211009 = Diabetes mellitusGET /Condition?code:below=http://snomed.info/sct|73211009This matches:
- 73211009 - Diabetes mellitus
- 44054006 - Type 2 diabetes mellitus
- 46635009 - Type 1 diabetes mellitus
- And all other descendants
:above Modifier
Section titled “:above Modifier”Search for a code and all its ancestors:
# Find conditions that subsume Type 2 diabetesGET /Condition?code:above=http://snomed.info/sct|44054006This matches:
- 44054006 - Type 2 diabetes mellitus
- 73211009 - Diabetes mellitus (parent)
- And all other ancestors up to the root
Code Systems
Section titled “Code Systems”SNOMED CT
Section titled “SNOMED CT”# Exact SNOMED CT code matchGET /Condition?code=http://snomed.info/sct|73211009
# Hierarchy searchGET /Condition?code:below=http://snomed.info/sct|73211009SNOMED CT hierarchy is resolved using ECL (Expression Constraint Language):
:belowuses<< code(descendants and self):aboveuses>> code(ancestors and self)
# Exact LOINC codeGET /Observation?code=http://loinc.org|8867-4
# LOINC panel members (via ValueSet)GET /Observation?code:in=http://loinc.org/vs/LP200060-1ICD-10
Section titled “ICD-10”# ICD-10-CM diagnosis codeGET /Condition?code=http://hl7.org/fhir/sid/icd-10-cm|E11.9Performance Optimization
Section titled “Performance Optimization”Automatic Optimization
Section titled “Automatic Optimization”OctoFHIR automatically optimizes large ValueSet expansions:
| Expansion Size | Strategy |
|---|---|
| < 500 codes | SQL IN clause |
| >= 500 codes | Temporary table with JOIN |
The temporary table approach provides significant performance improvements for large ValueSets.
Caching
Section titled “Caching”ValueSet expansions are cached based on cache_ttl_secs:
[terminology]cache_ttl_secs = 3600 # 1 hourCache behavior:
- First request expands the ValueSet (may take 1-2 seconds)
- Subsequent requests use cached expansion (milliseconds)
- Cache invalidates after TTL expires
Large ValueSets
Section titled “Large ValueSets”For very large ValueSets (>1000 codes), the server:
- Expands the ValueSet from the terminology server
- Bulk inserts codes into a temporary table
- Uses an efficient
JOINinstead of largeINclause - Automatically cleans up temporary data after 1 hour
$expand Operation
Section titled “$expand Operation”Explicitly expand a ValueSet:
GET /ValueSet/$expand?url=http://hl7.org/fhir/ValueSet/observation-vitalsignresult
POST /ValueSet/$expandContent-Type: application/fhir+json
{ "resourceType": "Parameters", "parameter": [ {"name": "url", "valueUri": "http://hl7.org/fhir/ValueSet/observation-vitalsignresult"}, {"name": "filter", "valueString": "heart"}, {"name": "count", "valueInteger": 100} ]}Expansion Parameters
Section titled “Expansion Parameters”| Parameter | Type | Description |
|---|---|---|
url | uri | ValueSet URL to expand |
filter | string | Filter expansion by display text |
count | integer | Limit number of codes returned |
offset | integer | Pagination offset |
includeDesignations | boolean | Include alternate designations |
$validate-code Operation
Section titled “$validate-code Operation”Validate a code against a ValueSet:
GET /ValueSet/$validate-code?url=http://hl7.org/fhir/ValueSet/observation-vitalsignresult&system=http://loinc.org&code=8867-4
POST /ValueSet/$validate-codeContent-Type: application/fhir+json
{ "resourceType": "Parameters", "parameter": [ {"name": "url", "valueUri": "http://hl7.org/fhir/ValueSet/observation-vitalsignresult"}, {"name": "coding", "valueCoding": { "system": "http://loinc.org", "code": "8867-4" }} ]}Response:
{ "resourceType": "Parameters", "parameter": [ {"name": "result", "valueBoolean": true}, {"name": "display", "valueString": "Heart rate"} ]}Error Handling
Section titled “Error Handling”Terminology Server Unavailable
Section titled “Terminology Server Unavailable”When the terminology server is unavailable:
{ "resourceType": "OperationOutcome", "issue": [{ "severity": "error", "code": "exception", "diagnostics": "Terminology server unavailable: connection timeout" }]}Invalid ValueSet
Section titled “Invalid ValueSet”{ "resourceType": "OperationOutcome", "issue": [{ "severity": "error", "code": "not-found", "diagnostics": "ValueSet not found: http://example.org/ValueSet/invalid" }]}Terminology Disabled
Section titled “Terminology Disabled”If terminology is disabled in configuration:
{ "resourceType": "OperationOutcome", "issue": [{ "severity": "error", "code": "not-supported", "diagnostics": "Terminology services are disabled. Enable in configuration to use :in, :not-in, :below, :above modifiers." }]}Examples
Section titled “Examples”Search for Vital Signs
Section titled “Search for Vital Signs”# All vital sign observationsGET /Observation?code:in=http://hl7.org/fhir/ValueSet/observation-vitalsignresult
# Heart rate observations (LOINC 8867-4) and childrenGET /Observation?code:below=http://loinc.org|8867-4Search for Diabetes Conditions
Section titled “Search for Diabetes Conditions”# All diabetes and subtypesGET /Condition?code:below=http://snomed.info/sct|73211009
# Only Type 2 diabetes specificallyGET /Condition?code=http://snomed.info/sct|44054006Exclude Specific Codes
Section titled “Exclude Specific Codes”# Observations that are NOT in the vitals ValueSetGET /Observation?code:not-in=http://hl7.org/fhir/ValueSet/observation-vitalsignresult
# Conditions that are NOT diabetes-relatedGET /Condition?code:not-in=http://example.org/ValueSet/diabetes-allCombined with Other Parameters
Section titled “Combined with Other Parameters”# Final vital sign observations from January 2024GET /Observation?status=final&code:in=http://hl7.org/fhir/ValueSet/observation-vitalsignresult&date=ge2024-01-01&date=lt2024-02-01
# Active diabetes conditions for a specific patientGET /Condition?patient=Patient/123&clinical-status=active&code:below=http://snomed.info/sct|73211009Troubleshooting
Section titled “Troubleshooting”Slow Expansions
Section titled “Slow Expansions”If ValueSet expansions are slow:
- Check terminology server latency
- Increase
cache_ttl_secsfor frequently used ValueSets - Consider using a local terminology server
- Monitor for large ValueSets (>1000 codes)
Memory Usage
Section titled “Memory Usage”Large ValueSet expansions are handled efficiently:
- Codes are streamed to temporary tables
- Temporary data is cleaned up automatically
- No in-memory caching of expansion results (uses Redis/local cache)
Debugging
Section titled “Debugging”Enable debug logging:
[logging]level = "debug"Look for:
terminology.expansion- Expansion requeststerminology.cache- Cache hits/missesterminology.temp_table- Temp table operations