VI.FUME
今天要來正式介紹FUME Mapping的撰寫,前面有簡單帶過,FUME是由JSONata與FLASH(modified from FSH)所組成,
FLASH負責結構描述的部分、JSONata則負責運算、數據操作等程式部分。
還是不免俗的先將兩個Documentation附上:
https://www.fume.health/docs/release-notes/community
https://docs.jsonata.org/overview.html
https://www.fume.health/docs/flash/
FLASH在最開始的開頭,有一個很重要的概念要提及,
FLASH本身是使用 * 作為開頭,並且是依照層級區分的,因此空格會影響到輸出的正確性,請特別注意
FLASH由幾個宣告做開頭:
InstanceOf: <type>
??用來宣告這個Resource的型別是什麼,如
??InstanceOf: Patient
??InstanceOf: bp
??InstanceOf:?http://hl7.org/fhir/StructureDefinition/bp
??支援單純型態、profile的引入
?Instance: <expression>
??用來指派這個Resource的logical ID,但目前筆者不太會使用到
?* <context element> (Context Rule)
??描述FHIR Resource的核心語法,像是:
??#FUME:
??* identifier
?? ?* value = mrn
??↑ identifier下有value的屬性項,而value的值被指派為輸入端的mrn
??FUME的特性1:如果這個輸入端的mrn能夠從輸入中被找到,
#INPUT:
??{
???"mrn" : "12345"
??}
??
??FHIR輸出端就會建立
??#FHIR:
??"identifier": [
{
"value": "12345"
}
??]
?* (context).<element> (Input Context)
??這個比較複雜一點,用表達的方式會比較像是:
??今天IG欄位有一個項稱作deviceName,他的Cardinality是0..*,也就是非必填無上限
??前者的context是輸入的來源物件,後者的element是FHIR格式內的屬性
??#FUME:
??* (device_deviceName).deviceName
? ? ? ? ?* name = device_deviceName_name
? ? ? ? ?* type = device_deviceName_type
??↑ 有一個輸入項長成這樣,其中包含了兩個屬性項 device_deviceName_name與device_deviceName_type:
??
??#INPUT:
??"device_deviceName" : [
{
"device_deviceName_name" : "deviceName",
"device_deviceName_type" : "deviceType"
}
??]
??之所以這樣撰寫的原因是因為,遇到複數或複合的輸入欄位時,輸入也會需要進行調整,
??所以上面那行FLASH的意思是,找稱作device_deviceName的物件,讓下面的屬性項與device_deviceName的兩個項相映射
??
??結合剛剛講的特性1,如果device_deviceName不存在於輸入端,deviceName的FHIR欄位將不會被建立
??並且,假設device_deviceName只填入device_deviceName_name,FHIR輸出端只會建立:
??
??#FHIR:
??"deviceName": [
{
"name": "deviceName"
}
? ? ? ?]
??還有一個可以講的東西是,這裡的(context)是可以接運算式的,如:
??* (k_dat_leom != null ? k_dat_leom).extension[religion]
?? ?* valueCodeableConcept
??這個範例代表了若k_dat_leom非空,則該extension[religion]的部分由k_dat_leom的分項內容填入
?* <element>[sliceName] (Slices)
??這個也比較少用一點,這個是另一種呈現複合型態的方式,前面的element表示FHIR的屬性欄位,sliceName則是這個element的子分項,
??可以用這個方法來表達複合屬性內的屬性對應
* extension[birthPlace]
?? ?* valueAddress
??? * country = "USA"
??↑ 上面的例子代表在birthPlace這個extension內,他的valueAddress.country是"USA"
??
?
?假設某個Patient Profile規定必須要有2個identifier屬性項,其中一個對應身分證號,另一個對應病歷號,該怎麼做
?
?這時候有兩個實作思路可以選擇:
?
?1. 把複合欄位交給輸入端,FUME Mapping只用* (context). 處理
?
?EX:
#INPUT:
??"patient_identifier" : [
{
"use" : "official",
"code" : "NNxxx",
"system" : "http://terminology.hl7.org/CodeSystem/v2-0203",
"value" : "A123456789"
},
{
"use" : "official",
"code" : "MR",
"system" : "http://example.com/",
"value" : "123456789"
}
??]
#FUME:
??* (patient_identifier).identifier
?? ?* use = use
?? ?* code = code
?? ?* system = system
?? ?* value = value
?這個例子中,輸入端把資料都包含在patient_identifier中,FUME只需要用複合元素映射去對應就好
?
?2. 在FUME Mapping中把兩個identifier都先定義好,輸入端只需要少少的輸入即可
?
?EX:
#INPUT:
??"patient_identifier_id" : "A123456789",
??"patient_identifier_mr" : "123456789",
??"patient_identifier_mr_system" : "http://example.com/"
#FUME
??* identifier
?? ?* use = "official"
?? ?* code = "NNxxx"
?? ?* system = "http://terminology.hl7.org/CodeSystem/v2-0203"
?? ?* value = patient_identifier_id
??* identifier
?? ?* use = "official"
?? ?* code = "MR"
?? ?* system = patient_identifier_mr_system
?? ?* value = patient_identifier_mr
?
?兩個方法都是通的,只是實作上重心會偏向哪一方,以實作FUME提供服務的角度來說,
?我們應該盡可能讓使用者(提供源資料)的必填以外的輸入保持完整性的情形下,越少越好
?所以實務上筆者會更推薦使用第二種方法,不過這個實際上還是要跟提供資料的使用者溝通,
?畢竟很多時候欄位是很難省下來的,特別是關於Terminology的場合。這點後面會有case能夠討論。
?
?
?從這裡開始討論FUME的東西知識點會變得很發散,因為是根據筆者實作與理解的經驗彙整而成,
?要如何把這些東西語言組織化並不容易,有想到會盡量寫。
?
?根據範例內提供的Patient,其實目前所講的東西還不足以完整包含到撰寫整個Resource,
?
?明天我會針對這個範例來詳細講解,希望讓讀者更快的進入狀況