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,
明天我會針對這個範例來詳細講解,希望讓讀者更快的進入狀況