挑战
在使用FHIR进行开发的过程中,我们会面对海量的FHIR规范中定义的数据结构,具体来说在FHIR规范中定义了超过150个资源、700多个资源内元素。每个定义里都包括了对自身结构的描述以及数据约束、数据绑定值集等。对于一个开发人员要记住这些内容非常困难。
同时FHIR数据,特别是Json格式的FHIR数据是典型的“有向图”结构,它的资源中嵌套元素定义、集合以及复杂的资源间“关系”,在这些复杂结构的数据间导航并操作,非常困难。
解决方案
在InterSystems IRIS for Health 2024.1之前,我们会将FHIR数据以Json文档的方式载入 %DynamicAbstractObject,例如下面的代码
set dynObject1 = ##class(%DynamicObject).%New()
set dynObject1.SomeNumber = 42
set dynObject1.SomeString = "a string"
set dynObject1.SomeArray = ##class(%DynamicArray).%New()
set dynObject1.SomeArray."0" = "an array element"
set dynObject1.SomeArray."1" = 123
set dynObject2 = {"SomeNumber":42,"SomeString":"a string"}
set dynObject2.SomeArray = ["an array element",123]
write "object 1: "_dynObject1.%ToJSON(),!,"object 2: "_dynObject2.%ToJSON()
ObjectScriptObjectScript
可以看到这样的处理有下面不方便的地方:
- 只能使用自己的方式解析、导航FHIR结构
- 不能从IDE中获取提示信息
- 没有数据类型安全
- 没有上下文敏感的文档
- 没有调试debug的支持
在InterSystems IRIS for Health 2024.1版本之后,引入了FHIR 对象模型。
FHIR 对象模型概览
在 HS.FHIRModel.R4 包下,每一个FHIR R4资源都对应一个ObjectScript类。比如 HS.FHIRModel.R4.AllergyIntolerance类对应于 AllergyIntolerance 资源。这些类通过为资源和组成元素提供一个共享的、可预测的数据结构和方法框架,简化了开发过程。
这样的结构,在VS Code环境下,会给出很好的提示
下面具体举例如何使用FHIR 对象模型:
1. 获取数据
按索引获取有序序列中的值
Set patientName = patient.name.get(0)
ObjectScriptObjectScript
获取嵌套定义中的值
Set patientIdValue = patient.identifier.get(0).get(“value”)
Set patientCity = patient.address.get(0).city
ObjectScriptObjectScript
在获取数据前判断键key是否存在
If patient.name.get(0).contains("family") {
Set familyName = patient.name.get(0).get(“family”)
}
ObjectScriptObjectScript
2. 更新数据
添加新的键值对
Do patient.name.get(0).put("use","official")
ObjectScriptObjectScript
更新存在的值
Do observation.put("status","final")
ObjectScriptObjectScript
3. 添加对象的引用
Set claim = ##class(HS.FHIRModel.R4.Claim).%New()
Do claim.IncludeType()
Do claim.type.IncludeCoding()
ObjectScriptObjectScript
4. 导航对象
Set contactItr = patient.contact.get(0).telecom.iterator()
While contactItr.hasNext() {
Set contact = contactItr.next().value
if contact.system = "phone" {
Write !, contact.value
}
}
ObjectScriptObjectScript
5 读取JSON格式 FHIR 数据
Set rType = dao.resourceType
Set cls = $CLASSMETHOD("I4H.FhirR4."_rType,"fromDao",dao)
ObjectScriptObjectScript
6. 将FHIR 数据输出为 JSON 格式
Set newDao = cls.toDao()
ObjectScriptObjectScript
转为字符串
Set payload = cls.toString()
ObjectScriptObjectScript
7. 创建新的 FHIR 元素
Do claim.IncludeType()
Do claim.type.IncludeCoding()
Set coding = ..MakeCoding("http://terminology.hl7.org/CodeSystem/claim-type","institutional")
Do claim.type.coding.add(coding)
ObjectScriptObjectScript
8. “图” 结构查询引擎
Set key = "system"
Set value = "email"
Set query = "$[*]?(@."_key_"=='"_value_"')"
Set email = doctor.telecom.apply(query)
ObjectScriptObjectScript
更多资料
FHIR Object Model 在线文档:https://docs.intersystems.com/irisforhealth20242/csp/docbook/DocBook.UI.Page.cls?KEY=HXFHIR_data#HXFHIR_data_classes
下面是一个完整的创建患者资源的例子
ClassMethod MakeCoding(system As %String, code As %String, display As %String = "") As HS.FHIRModel.R4.Coding
{
Set rec = ##class(HS.FHIRModel.R4.Coding).%New()
Set rec.system = system
Set rec.code = code
If (display'="") Set rec.display = display
Return rec
}
// Create a Patient using FHIR Object Model classes from R4 and then post
// this patient on the FHIR server
ClassMethod CreatePatient(ident As %String) As %Status
{
#dim patient As HS.FHIRModel.R4.Patient
Set patient = ##class(HS.FHIRModel.R4.Patient).%New()
Do patient.IncludeName()
Set name = patient.name.MakeEntry()
Set firstName = "John"
Do name.IncludeGiven()
Do name.given.add(firstName)
Set name.family = "Doe"
Do patient.name.add(name)
If patient.name.get(0).contains("family") {
Set fName = patient.name.get(0).get("family")
}
Do patient.name.get(0).put("use","official")
Set patient.gender = "male"
Set patient.birthDate = "1985-05-15"
Do patient.IncludeAddress()
Set addr = patient.address.MakeEntry()
Do addr.IncludeLine()
Set line1 = "123 Main Street"
Do addr.line.add(line1)
Set addr.city = "Boston"
Set addr.state = "MA"
Set addr.postalCode = "02111"
Do patient.address.add(addr)
Set address = ##class(HS.FHIRModel.R4.Address).%New()
Do patient.put("address", address)
Do patient.IncludeContact()
Set contact = patient.contact.MakeEntry()
Do contact.IncludeTelecom()
Set telecom = contact.telecom.MakeEntry()
Set telecom.system = "phone"
Set telecom.value = "555-555-5555"
Set telecom.use = "mobile"
Do contact.telecom.add(telecom)
Do patient.contact.add(contact)
Set contactItr = patient.contact.get(0).telecom.iterator()
While contactItr.hasNext() {
Set contact = contactItr.next().value
if contact.system = "phone" {
Write !, contact.value
}
}
Do patient.IncludeIdentifier()
Set id = patient.identifier.MakeEntry()
Set coding = ..MakeCoding("http://terminology.hl7.org/CodeSystem/v2-0203", "MR")
Set id.type = coding
Set id.value = ident
Do patient.identifier.add(id)
Set patientJSON = patient.toDao()
Set service = ##class(HS.FHIRServer.Service).EnsureInstance(1)
Do service.interactions.Add(patientJSON)
Return $$$OK
}
ObjectScriptObjectScript