Community social mapping by Community Eye Health, on Flickr |
One of the cool things about FHIR is the sheer amount of data about the standard that is available in computable (or nearly so) form. For example, all the HL7 Version 2, Version 3 and CDA mappings. One of my day to day challenges is converting things to and from FHIR, and so I've been thinking about the problem of how to handle conversion in a generalized way. FHIR has a Mapping Language, but it requires expression of a large amount of information that I find to be somewhat repetitive. It's essential to have all of the information it requires to get the mapping to work, but there should be an easier way to do this.
In thinking through this problem, I recall a tracker item I created for the ConceptMap translation operation structure that was related to how one might select a concept mapping based not just on a code, but also on other values (dependencies) that might be present in an instance that needs to be mapped. ConceptMap has both dependsOn and product, which reflect additional data in translation. A given mapping that uses dependsOn and product well can be made reversible, which is a REALLY nice feature to have laying around. NOTE: I said "can be made", not "is". You have to work at it, because mappings that generalize in one direction, will wind up specializing in another direction, which can be a bad thing. It might be OK to generalize up a level for some things, but specializing down can often lead to incorrect conclusions.
The devil is in the details, but that is where dependsOn and product fields in a concept can help, because they can provide details to produce mappings that have only equivalents, instead of other less precise mappings. So, if we can create a reversible concept mapping table, would it also be possible to create a reversible structure mapping table? NOTE: Reversibility isn't a requirement of what I'm trying to do, but it would be nice if the outcome was something that could allow me to express a mapping that could be reversed.
The basic process of mapping takes one or more components (or perhaps zero or more if mapping to a fixed value, but I digress) from the source structure, and maps it, possibly through some transformation, to one or more components of a target structure.
I think that there are two kinds of mappings that I need to worry about: Type mappings, and instance mappings. In a type mapping, I can say "this source type" can be mapped to "that target type". I would express those as MSH -> MessageHeader. Read that as "MSH produces MessageHeader".
In an instance mapping, I want to say that "this source element" is mapped to "that target element", or more directly: Bundle.entry.resource <= MSH(0). Read this as Bundle.entry.resource takes the values produced by mapping MSH.
Some things might generate two or more objects when going from source to target. For example, the V2 XCN data type should generate both a HumanName data type, as well as an Identifier Data type. The XCN data type in question could be generating a Patient, Practitioner, or RelatedPerson, but from a mapping perspective, the mapping should reflect the specific level of detail we know. Other context, such as where the XCN data type is used would then tell us which one of those resources is actually being produced. This might be expressed in the form: XCN -> HumanName, Identifier.
In the previous example, there's really only one edge case in most HL7 messages where an XCN might be other than Practitioner (In TXA, where the document originator might be the patient or an authorized representative), so it's not a huge problem here. For most uses of XCN, we'd just probably want to say something like: source-field -> destination-field, for example: PV1-7 -> Practitioner, and also PV1-7 -> Encounter.participant.individual. But in this odd-ball case, we'd probably need something else to tell us that what we are generating is Patient or RelatedPerson.
Type specific mapping rules can handle certain cases, in the above for example, to address the case of referencing the generated practitioner in the case of PV1-7.
One thing can map to two items, but in some cases, the one thing has to map to two parts of the same item. For example, CWE-1 to CWE-3 map to a single coding. I might say that the compound {CWE-1, CWE-2, CWE-3} -> Coding. Figuring out where to put this is sometimes complicated. It might go into a coding element, a CodeableConcept, or be (concept) mapped to a FHIR code in the resulting structure.
I'm leaving a lot of details out of my mapping on purpose, because what I want to be able to do is INFER a lot of the detail with the data I have, either from context, from more general rules, or from configuration. I ought to be able do some of that inference based on data types and potential data type conversions. To explain: If I say that CWE -> CodeableConcept, the system that interprets my mapping structures should know fairly well how to down-convert a codeable concept to a coding or a code, and the mapping context should be able to help. That way, any time CWE The same thing might also be true for certain HL7 data types which in some versions are simply strings, but in later versions are more complex data types where the value found in the earlier version is stored in the first component of the data type (e.g., compare for example, XCN-1 in HL7 2.3.1 vs. 2.5.1 where FN replaces ST, and thus, one could "down-convert" FN to ST by taking the value of its first component).
Done right, one might be able to express a mapping from one structure to another fairly concisely with some default rules (e.g., to handle various type conversions), in a way that could enable definition of a one-to-one and onto mapping that might well be reversible.
I'm still struggling with various parts of this, but I think I'm somewhat close to having something working.
Keith
P.S. Don't worry, I'll get back to that other exercise I'm doing on Risk Analysis, I just haven't had the time to do some of the additional research I need.
In thinking through this problem, I recall a tracker item I created for the ConceptMap translation operation structure that was related to how one might select a concept mapping based not just on a code, but also on other values (dependencies) that might be present in an instance that needs to be mapped. ConceptMap has both dependsOn and product, which reflect additional data in translation. A given mapping that uses dependsOn and product well can be made reversible, which is a REALLY nice feature to have laying around. NOTE: I said "can be made", not "is". You have to work at it, because mappings that generalize in one direction, will wind up specializing in another direction, which can be a bad thing. It might be OK to generalize up a level for some things, but specializing down can often lead to incorrect conclusions.
The devil is in the details, but that is where dependsOn and product fields in a concept can help, because they can provide details to produce mappings that have only equivalents, instead of other less precise mappings. So, if we can create a reversible concept mapping table, would it also be possible to create a reversible structure mapping table? NOTE: Reversibility isn't a requirement of what I'm trying to do, but it would be nice if the outcome was something that could allow me to express a mapping that could be reversed.
The basic process of mapping takes one or more components (or perhaps zero or more if mapping to a fixed value, but I digress) from the source structure, and maps it, possibly through some transformation, to one or more components of a target structure.
I think that there are two kinds of mappings that I need to worry about: Type mappings, and instance mappings. In a type mapping, I can say "this source type" can be mapped to "that target type". I would express those as MSH -> MessageHeader. Read that as "MSH produces MessageHeader".
In an instance mapping, I want to say that "this source element" is mapped to "that target element", or more directly: Bundle.entry.resource <= MSH(0). Read this as Bundle.entry.resource takes the values produced by mapping MSH.
Some things might generate two or more objects when going from source to target. For example, the V2 XCN data type should generate both a HumanName data type, as well as an Identifier Data type. The XCN data type in question could be generating a Patient, Practitioner, or RelatedPerson, but from a mapping perspective, the mapping should reflect the specific level of detail we know. Other context, such as where the XCN data type is used would then tell us which one of those resources is actually being produced. This might be expressed in the form: XCN -> HumanName, Identifier.
In the previous example, there's really only one edge case in most HL7 messages where an XCN might be other than Practitioner (In TXA, where the document originator might be the patient or an authorized representative), so it's not a huge problem here. For most uses of XCN, we'd just probably want to say something like: source-field -> destination-field, for example: PV1-7 -> Practitioner, and also PV1-7 -> Encounter.participant.individual. But in this odd-ball case, we'd probably need something else to tell us that what we are generating is Patient or RelatedPerson.
Type specific mapping rules can handle certain cases, in the above for example, to address the case of referencing the generated practitioner in the case of PV1-7.
One thing can map to two items, but in some cases, the one thing has to map to two parts of the same item. For example, CWE-1 to CWE-3 map to a single coding. I might say that the compound {CWE-1, CWE-2, CWE-3} -> Coding. Figuring out where to put this is sometimes complicated. It might go into a coding element, a CodeableConcept, or be (concept) mapped to a FHIR code in the resulting structure.
I'm leaving a lot of details out of my mapping on purpose, because what I want to be able to do is INFER a lot of the detail with the data I have, either from context, from more general rules, or from configuration. I ought to be able do some of that inference based on data types and potential data type conversions. To explain: If I say that CWE -> CodeableConcept, the system that interprets my mapping structures should know fairly well how to down-convert a codeable concept to a coding or a code, and the mapping context should be able to help. That way, any time CWE The same thing might also be true for certain HL7 data types which in some versions are simply strings, but in later versions are more complex data types where the value found in the earlier version is stored in the first component of the data type (e.g., compare for example, XCN-1 in HL7 2.3.1 vs. 2.5.1 where FN replaces ST, and thus, one could "down-convert" FN to ST by taking the value of its first component).
Done right, one might be able to express a mapping from one structure to another fairly concisely with some default rules (e.g., to handle various type conversions), in a way that could enable definition of a one-to-one and onto mapping that might well be reversible.
I'm still struggling with various parts of this, but I think I'm somewhat close to having something working.
Keith
P.S. Don't worry, I'll get back to that other exercise I'm doing on Risk Analysis, I just haven't had the time to do some of the additional research I need.
No comments:
Post a Comment