This page is part of the Vulcan FHIR to OMOP FHIR Implementation Guide (v1.0.0-ballot: INFORMATIVE 1 Ballot 1) based on FHIR (HL7® FHIR® Standard) v5.0.0. No current official version has been published yet. For a full list of available versions, see the Directory of published versions
Official URL: http://hl7.org/fhir/uv/omop/StructureMap/EncounterVisitMap | Version: 1.0.0-ballot | |||
Standards status: Informative | Maturity Level: 1 | Computable Name: EncounterVisitMap |
This mapping maps FHIR Encounter instances to OMOP Visit Occurence Table objects.
Transforming FHIR Encounter resources to OMOP presents several unique challenges that require careful consideration of data semantics, structural differences, and compliance requirements. The mapping decisions made during this transformation process can significantly impact data integrity, traceability, and analytical capabilities in the target OMOP database.
The transformation of FHIR Encounter.identifier
elements exemplifies the complexity found in FHIR to OMOP mappings. Healthcare systems use hese identifiers for a variety of business use cases. Encounters ids may contain various types of unique values including medical record numbers, encounter IDs, or proprietary tracking identifiers, any of which may be used for patient re-identification, and thus present a risk to privacy requirements. The challenge lies in determining the most appropriate target field(s) in OMOP while maintaining data integrity and compliance standards.
Two primary mapping approaches present themselves, each with distinct considerations:
Primary Key Mapping Approach: One potential solution involves mapping to the visit_occurrence_id
field in OMOP, which serves as the unique identifier for each record in the visit_occurrence
table. This approach aligns semantically with the identifier's purpose in FHIR. However, this field is typically generated as a sequence in OMOP implementations to ensure anonymity and maintain referential integrity across the database instance. This practice supports de-identification requirements and compliance standards but may compromise direct traceability to source systems.
Source Value Mapping Approach: An alternative approach maps FHIR Encounter.identifier
to the OMOP visit_source_value
field, which is designed to hold original values from source data describing the encounter type. This approach preserves the original identifier value but raises concerns about semantic appropriateness. The visit_source_value
field typically aligns with descriptive FHIR elements such as Encounter.period
or Encounter.type
, which represent codeable concepts rather than unique business identifiers.
Several technical constraints can impact identifier transformation:
Data Type and Length Constraints: OMOP database fields may have limitations that prevent the storage of certain identifier types. For example, the visit_source_value
field's varchar(50) character limit may be insufficient for storing GUIDs or complex identifier strings that exceed this constraint.
Contextual Ambiguity: Identifiers in source systems may lack sufficient context to determine their meaning or appropriate usage. Simple identifier structures can be particularly ambiguous:
"identifier": [
{
"system": "https://hospital.makedata.ai",
"value": "b9d8ed9e-1b1a-4e30-9ee3-af66f4a61cff"
}
]
Complex Nested Structures: FHIR identifiers can contain multiple nested elements that provide additional context but may not have direct equivalents in OMOP:
<identifier>
<use value="official" />
<system value="http://www.acmehosp.com/patients" />
<value value="44552" />
<period>
<start value="2003-05-03" />
</period>
</identifier>
Given the complexity and variability of identifier structures in FHIR, a standardized one-size-fits-all mapping approach to OMOP is not practical. Implementation teams should consider the following strategies:
Case-by-Case Evaluation: Conduct thorough analysis of available FHIR identifier elements and their semantic meaning within the source system context. This evaluation should inform the selection of appropriate target fields in OMOP while considering data integrity and compliance requirements.
Extension Table Implementation: Consider developing separate, extension tables specifically designed to store source identifiers. This approach preserves identifier information while maintaining the integrity of core OMOP tables and provides flexibility for complex identifier structures.
Selective Transformation: In some cases, the most appropriate approach may be to exclude certain identifiers from the transformation process altogether, particularly when they provide no analytical value or when technical constraints make their storage impractical.
The decision regarding which approach to adopt should be based on the specific requirements of the implementation, including analytical use cases, compliance requirements, and the technical capabilities of the target system. Each approach presents trade-offs between data preservation, system performance, and analytical utility that must be carefully weighed in the context of the overall transformation strategy.
/// url = 'http://hl7.org/fhir/uv/omop/StructureMap/EncounterVisitMap' /// name = 'EncounterVisitMap' /// title = 'Mapping Encounter resource to VisitOccurrence OMOP Domain' /// status = 'draft' uses "http://hl7.org/fhir/StructureDefinition/Encounter" alias Encounter as source uses "http://hl7.org/fhir/uv/omop/StructureDefinition/VisitOccurrence" alias VisitTable as target group VisitOccurrence(source src : Encounter, target tgt : VisitTable) { src.class as s -> tgt then { s.coding as sc -> tgt then { sc.code as a -> tgt.visit_concept_id = a, tgt.visit_source_value = a, tgt.visit_source_concept_id = a; }; }; // src.id as id -> tgt.visit_occurrence_id = cast(id, "integer"); src.actualPeriod as s -> tgt then { s.start as std -> tgt.visit_start_date = cast(std, 'date'), tgt.visit_start_datetime = std; s.end as ed -> tgt.visit_end_date = cast(ed, 'date'), tgt.visit_end_datetime = ed; }; src.admission as s -> tgt then { s.admitSource as sa -> tgt then { sa.coding as sc -> tgt then { sc.code as code -> tgt.admitted_from_concept_id = code, tgt.admitted_from_source_value = code; }; }; s.dischargeDisposition as sd -> tgt then { sd.coding as sc -> tgt then { sc.code as code -> tgt.discharged_to_concept_id = code, tgt.discharged_to_source_value = code; }; }; }; }