查找

Article
· Sep 30 3m read

InterSystems IRIS: o que é, quando usar e um hands-on de 15 minutos

Oi pessoal!  Esse artigo é para quem está começando com InterSystems IRIS. Espero que ajude!

O InterSystems IRIS é uma plataforma de dados unificada: uma base de dados de alta performance com ferramentas de interoperabilidade e análise integradas em um só produto. Você tem SQL e NoSQL na mesma máquina, além de jeitos nativos de rodar Python com seus dados. Em resumo: menos peças móveis, mais capacidade de processamento.

Por que engenheiros escolhem IRIS

  • Multi-modelo, uma máquina. Funciona com tabelas relacionais ,objetos, globais, sem precisar alterar o contexto.
  • Python onde os dados vivem. O “Embedded Python” permite que você escreva métodos Python server-side que rodam dentro do IRIS (e não por um gateway externo). Você também pode chamar o IRIS do Python usando o móduloiris
  • Escalabilidade vertical e horizontal. Inicie em apenas uma instância, e então adicione fragmentos (shards) para dividir o armazenamento e cache horizontalmente em vários nós, para acelerar queries e ingestão.
  • Feito para aplicações em tempo real. Mira em alta performance para altos volumes de cargas de trabalho, mantendo transações e análises juntas.

Quando o IRIS brilha (e quando não)

Use quando você precisa de cargas de trabalho em estilo HTAP (operacional e analítico em um só lugar), consistência estrita e ferramentas de integração incorporadas. Se você simplesmente precisa de um cache ou um sandbox de análise avulso, uma base de dados maias leve pode ser mais simples.


Mão na massa (≈15 minutos)

1) Suba o IRIS localmente (Docker)

# pull e rode Community Edition
docker run --name iris \
  -d -p 52773:52773 -p 1972:1972 \
  intersystems/iris-community
  • Portal de Administração Web: http://localhost:52773/csp/sys/UtilHome.csp
  • As credenciais padrão do container são definidas na primeira execução, siga os prompts da tela.
    (Community Edition é gratuita para desenvolvimento e teste e está disponível no Docker Hub, com a documentação oficial do container de "primeiro contato".) 

2) Crie uma tabela e consulte(SQL)

No Portal de Administração (System Explorer → SQL), execute:

CREATE TABLE demo.orders(
  id INT PRIMARY KEY,
  customer VARCHAR(80),
  total DECIMAL(10,2),
  created_at TIMESTAMP
);

INSERT INTO demo.orders VALUES
 (1,'Kai',125.50,NOW()),
 (2,'Amaka',78.90,NOW());

SELECT customer, total
FROM demo.orders
WHERE total > 100;

(O IRIS SQL lida com conversões de tipos e formatos lógico/display por baixo dos panos.) 

3) Execute o Python dentro do IRIS(Embedded Python)

Crie um método de classe que retorna uma métrica rápida:

Class Demo.Utils
{

ClassMethod BigSpender(threshold As %Numeric) As %Integer [ Language = python ]
{
    import iris
    # Simple count using embedded SQL
    sql = "SELECT COUNT(*) FROM demo.orders WHERE total > ?"
    rs = iris.sql.exec(sql, threshold)
    return list(rs)[0][0]
}

}

Chame-o do terminal:

do ##class(Demo.Utils).BigSpender(100)

Isso é um Python server-side compilado e executado na máquina IRIS (sem ponte externa), e você pode chamar do ObjectScript ou SQL conforme necessidade. 

4) Notas sobre escalabilidade (para quando você crescer)

Se as consultas ou ingestão começarem a atingir os limites, adicione shards. O IRIS particiona dados e cache através de nós, fornecendo escalabilidade horizontal sem reescrever - e você pode misturar escalabilidade vertical e horizontal conforme necessidade.

 

Dicas direcionadas a produção

  • Mantenha unificado. Resista separar OLTP e análises cedo; o IRIS foi feito para mantê-las juntas até que um gargalo real ocorra.
  • Use Python com moderação, mas estrategicamente. Coloque lógica de negócio ou analítica que se beneficie das libs do Python no Embeddede Python; deixe o ETL pesado para trabalhos agendados.
  • Planeje shards antes de necessitar deles. Escolha chaves de shard que você não irá se arrepender (IDs imutáveis, time buckets) e teste em um sandbox de 3 nós.
Discussion (0)1
Log in or sign up to continue
Question
· Sep 30

Move to immutable backups

At the moment, we have 10 HealthShare instance servers (5 x mirrored pairs), where we implement an External Backup approach, using the freeze/thaw commands against whichever server of the pair is the backup mirror member, to complete a VM level backup. These backups are stored to a disk within our control, to purge as required. This approach allows us to deliver a zero downtime backup approach.

Our future backup solution will be storing immutable backups, with the main concern being that the 10 HealthShare instance servers are quite sizable and that our current approach of backing up the current backup mirror member at the time of the backup could generate 10 immutable VM backups and the size this would consume, when the ideal solution would be to only be capturing one side of each pair consistently.

With this in mind, I don't know whether anybody has any experience in a similar situation as to what would be an optimal solution? I have considered the following options and potential concerns:

1) To only ever backup one side of each pair, with a manual effort to keep one server as the preferred primary - the concerns being if it's the current primary that did get backed up, the freeze/thaw will impact system use, or if there was a VM issue for any period of time, we might have no valid backup

2) Implement online backups of the primary member to a centralised drive location and backup the drive, rather than the entire VM - the concerns being the time to complete such large DB backups, the time to restore and in a DR situation, the time to implement a new VM environment to restore the databases

3) Look at implementing a Concurrent External Backup approach - similar concerns to approach 2

Any thoughts are experiences would be gratefully received.

Regards,

Mark

2 Comments
Discussion (2)1
Log in or sign up to continue
Article
· Sep 30 11m read

Implementing a FHIR project - ÚNICAS

Welcome, dear members of the Community!

In this article, we will present an example of a project implementing a FHIR-based solution. This project will be based on the national project (Spanish national project), known as ÚNICAS.

What is ÚNICAS?

In his own words:

A project whose objective is to create an ecosystem of partnerships to improve healthcare for pediatric patients with complex rare diseases (RMDs). This project is being implemented through the network within the National Health System (NHS) to improve the diagnosis and care of patients with rare diseases.

Technical characteristics of ÚNICAS

To summarize, the architecture designed for this project is composed of:

Central node

The objective is to enable the sharing of clinical data on patients affected by these rare diseases. This clinical data will be found in the regional nodes.

Autonomous node

Each autonomous community will have its own node (or more than one, depending on the volume of data). These nodes will be responsible for obtaining patients' clinical information from the various available data sources and providing it to the central node.

Interoperability model

The interoperability model chosen for this case is FHIR (R5), which provides a set of standardized resources for the exchange of clinical data.

Each autonomous community is free to choose how to implement this model, either by deploying a FHIR repository into which to upload clinical data for subsequent sharing or by using a FHIR facade that will transform clinical information into FHIR resources when a sharing request is processed.

FHIR resources in ÚNICAS

Since this project has very specific objectives, it has defined a set of FHIR resources to be used:

Mensajería estandardizada en ÚNICAS en FHIR

While for reports:

Mensajería existente de informes en HL7 CDA

Implementing ÚNICAS with Health Connect

After reviewing the technical aspects of the ÚNICAS project, it's time to examine how it fits with Health Connect and what possible adaptations would be necessary to facilitate implementation.

FHIR R5 Model

In order to implement the interoperability model in FHIR R5, the client has two options:

  • FHIR Facade.
  • FHIR Repository.

FHIR Facade

The FHIR facade will deploy the business components necessary for a REST API within our Health Connect instance that allows us to receive calls from third-party systems with the various FHIR resources. With this option, we can extract the resources in %DynamicObject format and transform them into our specific clinical data format.

Queries received against the FHIR facade must be built manually, since there is no FHIR repository linked below, so we must implement each feature ourselves. For ÚNICAS, this would not be the best option, since the query-level requirements would require us to implement the functionality we already have in the FHIR repository.

FHIR Repository

Health Connect permite (consultar condiciones de la licencia) desplegar un repositorio FHIR R5 de una forma sencilla desde un namespace con la opción de interoperabilidad habilitada desde el menú Health -> FHIR Server Management

Al pulsar sobre la opción de añadir nuevo servidor se nos mostrará una pantalla con las diversas opciones de configuración:

As you can see, we have the option to import Packages into our configuration. This will be useful for importing the ValueSets and Profiles defined by the ÚNICAS IG. We can perform this import at any time by running the following command:

// Rutas a modificar por el usuario
do ##class(HS.FHIRMeta.Load.NpmLoader).importPackages($lb("/iris-shared/packages/hl7.terminology.r5-6.5.0/package", "/iris-shared/packages/hl7.fhir.uv.extensions.r5-5.2.0/package","/iris-shared/packages/full-ig/package"))

In the ÚNICAS IG, there is a small discrepancy regarding the dependencies defined for the full-IG . The following dependencies are listed within the package:

"dependencies" : {
    "hl7.terminology.r5" : "6.5.0",
    "hl7.fhir.uv.extensions.r5" : "5.2.0",
    "hl7.fhir.uv.ips" : "1.1.0"
  }

Version 1.1.0 of  hl7.fhir.uv.ips  is only supported on FHIR R4, and attempting to import it onto an R5 server will result in an error, as it also has several R4 dependencies that will cause incompatibilities. For our implementation, we'll remove it from the dependency list and import only those related to R5...and let God take care of it.

Adapting FHIR repository to ÚNICAS

One of the most interesting features of the FHIR repository in Health Connect is the ability to use the interoperability engine to capture and transform every interaction with the FHIR repository to adapt it to our needs. To do this, we only need to include the business service  HS.FHIRServer.Interop.Service  and the business operation  HS.FHIRServer.Interop.Operation from the production environment linked to the namespace where we have deployed the FHIR repository. This will be the appearance of the production environment for the namespace containing our repository:

With these components in our production, we can specify in our repository configuration that all calls using the REST API will go through this service:

Perfect! Our FHIR R5 repository is ready to begin deploying our ÚNICAS autonomic node. Let's look at an example of how we can leverage interoperability capabilities to transform HL7 v2.5.1 messaging into FHIR R5 resources.

Converting HL7 to FHIR

A typical case to be solved for any FHIR repository implementer is the conversion of HL7 (v2 or v3) messaging into FHIR resources. This conversion is not at all straightforward for two reasons: 

  1. There is no direct equivalence between FHIR resources and HL7 events. While FHIR is used for the exchange of clinical concepts in the form of resources, HL7 represents events, which contain information that may or may not be equivalent to a clinical concept.
  2. The implementation of HL7 v2 or v3 messaging is far from uniform. Each organization adapts messages to its needs, and the same event in different organizations may contain completely different data.

What tools does Health Connect provide us?

Health Connect has a series of predefined transformations that use the SDA3 data model as a gateway, very similar to CDAs. These transformations are currently developed for the R4 version, but it is not very complicated to adapt them to transform R4 resources generated in the R5 version.

So the steps to follow will be HL7 v2.5.1 -> SDA3 -> FHIR R4 -> FHIR R5. 

HL7 v2.5.1 Transformation  -> SDA3

For this first step, we'll use the  HS.Gateway.HL7.HL7ToSDA3 class  and its  GetSDA method  (you can see an example in the documentation here ). This class will transform our HL7 message into a nice SDA.

Transformation SDA3 -> FHIR R4

Once we have obtained our SDA with the clinical information extracted from the HL7 message, we will use the  HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR class  and its TransformStream  method , which will transform the SDA3 in Stream format obtained in the previous step into a %DynamicObject with a FHIR R4 Bundle resource (more information here ).

Below you can see an example of the code needed to transform an HL7 message into a FHIR R4 bundle in %DynamicObject format:

 do ##class(HS.Gateway.HL7.HL7ToSDA3).GetSDA(request, .SDAMessageTemp)
 set SDAToFHIR = #class(HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR).TransformStream(SDAMessageTemp,"HS.SDA3.Container","R4")

What about my FHIR R5?

So far we have seen that Health Connect allows for a very simple conversion from HL7 to FHIR R4 (the conversion may not fit 100% of your needs, but it will be easier to edit a resource than to build it from scratch), great, but...WHERE IS MY R5 RESOURCE?

Don't worry, because... the Developer Community is coming to the rescue!

You'll see that attached to this article is an associated OpenExchange application. This application contains the code necessary to transform FHIR R4 resources into the R5 resources used in the ÚNICAS project. To install this code, you can use IPM:

set version="latest" s r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="ISC.FeatureTracker.SSL.Config" d r.Get("/packages/zpm/"_version_"/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c")
zpm "enable -community"
zpm "install spain-unicas"

Once installed, you'll see a package called Spain  appear in the namespace where you installed it. What can you find in this package?

  • Spain.BP : BPL with example of transformation HL7 -> SDA -> FHIR R4 -> FHIR R5.
  • Spain.FHIR.DTL.vR4 : R4 to R5 transformations of the resources used by ÚNICAS.
  • Spain.FHIR.DTL.vR5 : ObjectScript classes that model ÚNICAS R5 resources.
  • Spain.Gateway.HL7 : Minor fix for HL7 to SDA3 transformation.

Transforming R4 to R5

With the tools available in the Spain package  , we can now transform our R4 resources into R5 resources. To do this, we will use the transformations for each resource type as follows:

set obj = ##class(HS.FHIR.DTL.vR4.Model.Resource.Bundle).FromJSONHelper(context.FHIRMessage, "vR4")

for i=1:1:obj.entry.Count() {
    set item = obj.entry.GetAt(i)
    if ($CLASSNAME(item.resource) = "HS.FHIR.DTL.vR4.Model.Resource.Immunization")
    {
      Set status = ##class(Spain.FHIR.DTL.vR4.vR5.Immunization).Transform(item.resource, .immunizationR5)            
      Set item.resource = immunizationR5
      Do obj.entry.SetAt(item, i)            
    }
    elseif ($CLASSNAME(item.resource) = "HS.FHIR.DTL.vR4.Model.Resource.AllergyIntolerance")
    {
      Set status = ##class(Spain.FHIR.DTL.vR4.vR5.Allergy.AllergyIntolerance).Transform(item.resource, .allergyIntoleranceR5)            
      Set item.resource = allergyIntoleranceR5
      Do obj.entry.SetAt(item, i)            
    }
    ...
 }

Remember that we had a %DynamicObject with a bundle of R4 resources, the first step we will take will be to map said %DynamicObject to a  HS.FHIR.DTL.vR4.Model.Resource.Bundle class using the FromJSONHelper method , the objective of this transformation is to be able to later recognize the type of resource present in the bundle by the name of its class.

With the bundle mapped to its ObjectScript class, we will simply go through each entry, identifying the resource. If it corresponds to one of the resources used in ÚNICAS, we proceed to invoke a specific DTL to perform the transformation from R4 to R5.

With the resource already transformed, we reintroduce it into the Bundle replacing the previous R4 resource.

Finally, we will only have to transform the Bundle into a Stream (in this case called QuickStream) and send it within a message of type HS.FHIRServer.Interop.Request  to the business operation HS.FHIRServer.Interop.Operation

 Set context.FHIRRequest.Request.RequestMethod = "POST"
 Set context.FHIRRequest.Request.RequestPath = ""
 Set context.FHIRRequest.Request.RequestFormatCode= "JSON"
 Set context.FHIRRequest.Request.SessionApplication = "/csp/healthshare/fhirserver/fhir/r5"
 Set context.FHIRRequest.Request.IsRecursive = 0
 set qs=##class(HS.SDA3.QuickStream).%New()
 set context.FHIRRequest.QuickStreamId = qs.%Id()
 do qs.CopyFrom(context.FHIRStream)
 

Here we would have the call:

Well, that's it! We now have our small example of HL7 to R5 conversions and its registration in our Health Connect repository. Let's see it in action.

Example of transformation from HL7 to R5

Let's start with an HL7 v2.5.1 message

MSH|^~\&|CLINIC_SYS|HOSPITAL123|EHR_SYSTEM|REGION1|20250826143000||ADT^A08^ADT_A01|MSG00002|P|2.5.1
EVN|A08|20250826143000
PID|1||123456^^^HOSPITAL123^MR||GOMEZ^MARIA^LUISA||19750523|F|||AVDA UNIVERSIDAD 45^^MADRID^^28040^ESP||(555)1234567|||S||12345678Z^^^ESP^NI
AL1|1|DA|70618^Penicillin^RXNORM|SV|Rash|20200115|Clinician noted severe rash after administration
AL1|2|FA|256349002^Peanut protein^SCT|SE|Anaphylaxis|20181201|Reported after accidental exposure
AL1|3|MA|300916003^Latex^SCT|MO|Skin irritation|20191010|Observed during procedure with latex gloves

Here we have a simple ADT_A08 with information related to a patient and their allergies, as diligent implementers that we are, we know that this message will contain (at least) one resource of type Patient and 3 of type AllergyIntolerance. 

We introduced a business service into our production to capture HL7 files and send them to our BPL:

Once captured, we transform our HL7 to R4 through SDA, let's take a look at the AlleryIntolerance resource generated in R4:

{
                                                              
	"request": {
		"method": "POST",
		"url": "AllergyIntolerance"
	},
	"fullUrl": "urn:uuid:4b9f7b2e-9dd0-11f0-870e-c6b593068383",
	"resource": {
		"resourceType": "AllergyIntolerance",
		"category": [
			"food"
		],
		"clinicalStatus": {
			"coding": [
				{
					"code": "active",
					"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical"
				}
			]
		},
		"code": {
			"coding": [
				{
					"code": "256349002",
					"display": "Peanut protein",
					"system": "http://snomed.info/sct"
				}
			]
		},
		"extension": [
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-discovery-time",
				"valueDateTime": "2018-12-01T00:00:00+00:00"
			},
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-entered-at",
				"valueReference": {
					"reference": "urn:uuid:4b9e97f6-9dd0-11f0-870e-c6b593068383"
				}
			}
		],
		"onsetDateTime": "2018-12-01T00:00:00+00:00",
		"patient": {
			"reference": "urn:uuid:4b9edb58-9dd0-11f0-870e-c6b593068383"
		},
		"reaction": [
			{
				"extension": [
					{
						"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-severity",
						"valueCodeableConcept": {
							"coding": [
								{
									"code": "SE"
								}
							]
						}
					}
				],
				"manifestation": [
					{
			
						"coding": [
							{
								"code": "Anaphylaxis"
							}
						]
 
					}
				]
			}
		]
	}
}

And now the same resource but adapted to the R5 version:

{
	"fullUrl": "urn:uuid:4b9f7b2e-9dd0-11f0-870e-c6b593068383",
	"request": {
		"method": "POST",
		"url": "AllergyIntolerance"
	},
													  
	"resource": {
		"resourceType": "AllergyIntolerance",
		"category": [
			"food"
		],
		"clinicalStatus": {
			"coding": [
				{
					"code": "active",
					"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical"
				}
			]
		},
		"code": {
			"coding": [
				{
					"code": "256349002",
					"display": "Peanut protein",
					"system": "http://snomed.info/sct"
				}
			]
		},
		"extension": [
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-discovery-time",
				"valueDateTime": "2018-12-01T00:00:00+00:00"
			},
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-entered-at",
				"valueReference": {
					"reference": "urn:uuid:4b9e97f6-9dd0-11f0-870e-c6b593068383"
				}
			}
		],
		"onsetDateTime": "2018-12-01T00:00:00+00:00",
		"patient": {
			"reference": "urn:uuid:4b9edb58-9dd0-11f0-870e-c6b593068383"
		},
		"reaction": [
			{
				"extension": [
					{
						"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-severity",
						"valueCodeableConcept": {
							"coding": [
								{
									"code": "SE"
								}
							]
						}
					}
				],
				"manifestation": [
					{
						"concept": {
							"coding": [
								{
									"code": "Anaphylaxis"
								}
							]
						}
					}
				]
			}
		]
	}
}

As you can see, the reaction property has been modified, adapting it to the R5 definition:

Conclusion

As you've seen, Health Connect offers endless possibilities for adapting to the various FHIR-based projects we may encounter. With the interoperability engine, we can adapt our data however we need, taking into account any requirements that deviate from standard behavior.

I hope you find it useful!

2 Comments
Discussion (2)3
Log in or sign up to continue
Article
· Sep 30 12m read

Implementando un proyecto FHIR - ÚNICAS

¡Bienvenidos, estimados miembros de la Comunidad!

En este artículo vamos a presentar un ejemplo de un proyecto de implementación de una solución basada en FHIR. Este proyecto se basará en el proyecto nacional (nacional de España), conocido como ÚNICAS.

¿Qué es ÚNICAS?

Usando sus propias palabras:

Un proyecto cuyo objetivo es crear un ecosistema de alianzas para mejorar la atención sanitaria de pacientes pediátricos con enfermedades minoritarias (EEMM) complejas. A través de la red en el Sistema Nacional de Salud (SNS) para mejorar el diagnóstico y la asistencia de pacientes con enfermedades minoritarias.

Características técnicas de ÚNICAS

Resumiendo un poco, la arquitectura diseñada para este proyecto está compuesto por:

Nodo central

Cuyo objetivo es permitir la compartición de datos clínicos de los pacientes afectados por estas enfermedades raras. Estos datos clínicos se encontrarán en los nodos autonómicos.

Nodo autonómico

Cada comunidad autonómica contará con su propio nodo (o más de uno, dependiendo el volumen de datos). Estos nodos serán los encargados de obtener la información clínica de los pacientes de los diversos orígenes de datos disponibles y proveerlos al nodo central.

Modelo de interoperabilidad

El modelo de interoperabilidad elegido para este caso es FHIR en su versión R5, el cual proporciona un conjunto de recursos normalizados para el intercambio de datos clínicos.

Cada comunidad autónoma es libre de elegir como implementar dicho modelo, ya sea mediante el despliegue de un repositorio FHIR sobre el que ir volcando los datos clínicos para su posterior compartición o bien con una fachada FHIR que transformará la información clínica a recursos FHIR en el momento que se atienda a una petición de compartición.

Recursos FHIR en ÚNICAS

Dado que este proyecto tiene unos objetivos muy concretos, ha definido un conjunto de recursos FHIR a utilizar:

Mensajería estandardizada en ÚNICAS en FHIR

Mientras que para informes:

Mensajería existente de informes en HL7 CDA

Implementando ÚNICAS con Health Connect

Tras revisar el aspecto técnico del proyecto ÚNICAS es el momento de ver el encaje con Health Connect y las posibles adaptaciones que serían necesarias para facilitar la implementación.

Modelo FHIR R5

De cara a la implementación del modelo de interoperabilidad en FHIR R5 el cliente tiene dos opciones:

  • Fachada FHIR.
  • Repositorio FHIR.

Fachada FHIR

La fachada FHIR nos desplegará dentro de nuestra instancia de Health Connect los componentes de negocio necesarios para disponer de una API REST que nos permita recibir llamadas desde terceros sistemas con los distintos recursos FHIR. Con esta opción podremos extraer los recursos en formato %DynamicObject y transformarlo a nuestro formato particular de datos clínicos.

Las consultas recibidas sobre la fachada FHIR se deberán construir a mano, ya que al no existir un repositorio FHIR vinculado por debajo deberemos implementar nosotros mismos cada funcionalidad. Para el caso de ÚNICAS esta no sería la mejor opción ya que los requisitos a nivel de consultas nos implicaría tener que implementar la funcionalidad que tenemos ya con el repositorio FHIR.

Repositorio FHIR

Health Connect permite (consultar condiciones de la licencia) desplegar un repositorio FHIR R5 de una forma sencilla desde un namespace con la opción de interoperabilidad habilitada desde el menú Health -> FHIR Server Management

Al pulsar sobre la opción de añadir nuevo servidor se nos mostrará una pantalla con las diversas opciones de configuración:

Como veis tenemos la opción de importar Packages a nuestra configuración. Esto será útil de cara a importar los ValueSet y Profiles definidos por el IG de ÚNICAS. Esta importación podremos realizarla en cualquier momento ejecutando el siguiente comando:

// Rutas a modificar por el usuario
do ##class(HS.FHIRMeta.Load.NpmLoader).importPackages($lb("/iris-shared/packages/hl7.terminology.r5-6.5.0/package", "/iris-shared/packages/hl7.fhir.uv.extensions.r5-5.2.0/package","/iris-shared/packages/full-ig/package"))

En el IG de ÚNICAS hay una pequeña discrepancia en relación a las dependencias definidas para el full-ig. Dentro del paquete se indican las siguiente dependencias:

"dependencies" : {
    "hl7.terminology.r5" : "6.5.0",
    "hl7.fhir.uv.extensions.r5" : "5.2.0",
    "hl7.fhir.uv.ips" : "1.1.0"
  }

La versión 1.1.0 de hl7.fhir.uv.ips sólo es compatible para la R4 de FHIR y si se intenta importar sobre un servidor R5 se producirá un error ya que a su vez lleva asociada una serie de dependencias con R4 que provocarán incompatibilidades. Para nuestra implementación procederemos a eliminarlo de la lista de dependencias e importar exclusivamente las relativas a la R5...y que sea lo que Dios quiera.

Adaptando repositorio FHIR a ÚNICAS

Una de las características más interesantes del repositorio FHIR en Health Connect es la posibilidad de utilizar el motor de interoperabilidad para capturar y transformar toda interacción con el repositorio FHIR para adaptarlo a nuestras necesidades. Para ello sólo necesitamos, desde la producción ligada al namespace donde tenemos desplegado el repositorio FHIR, incluir el business service HS.FHIRServer.Interop.Service y el business operation HS.FHIRServer.Interop.Operation. Esta será la pinta de la producción para el namespace que contiene nuestro repositorio:

Con estos componentes en nuestra producción podremos indicar en la configuración de nuestro repositorio que todas las llamadas mediante la API REST pasarán por dicho servicio:

¡Perfecto! Nuestro repositorio FHIR R5 está preparado para empezar a implementar nuestro nodo autonómico ÚNICAS. Veamos un ejemplo de cómo podemos aprovechar las capacidades de interoperabilidad para transformar mensajería HL7 v2.5.1 a recursos FHIR R5.

Convirtiendo HL7 a FHIR

Un caso típico que todo implementador de un repositorio FHIR tiene que afrontar antes o después es el de la conversión de mensajería HL7 (v2 o v3) en recursos FHIR. Esta conversión no es para nada sencilla por dos motivos: 

  1. No existe equivalencia directa entre recursos FHIR y eventos de HL7. Mientras FHIR se utiliza para el intercambio de conceptos clínicos en forma de recursos, HL7 representa eventos, los cuales contienen información que puede equivaler o no a un concepto clínico.
  2. La implementación de la mensajería HL7 v2 o v3 no es para nada homogenea. Cada organización adapta los mensajes a sus necesidades, siendo posible que el mismo evento en distintas organizaciones contengan datos totalmente distintos.

¿Qué herramientas nos proporciona Health Connect?

Health Connect dispone de una serie de transformaciones predefinidas que utiliza como pasarela el modelo de datos SDA3, muy similar a los CDAs. Actualmente dichas transformaciones están desarrolladas para la versión R4, pero no es muy complicado adaptarlo para transformar los recursos R4 generados en la versión R5.

Por lo que los pasos a seguir serán HL7 v2.5.1 -> SDA3 -> FHIR R4 -> FHIR R5. 

Transformación HL7 v2.5.1 -> SDA3

Para este primer paso haremos uso de la clase HS.Gateway.HL7.HL7ToSDA3 y su método GetSDA (podéis ver un ejemplo en la documentación aquí). Esta clase transformará nuestro mensaje HL7 en un bonito SDA.

Transformación SDA3 -> FHIR R4

Una vez obtenido nuestro SDA con la información clínica extraida del mensaje HL7 usaremos la clase HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR y su método TransformStream que transformará el SDA3 en formato Stream obtenido en el paso anterior en un %DynamicObject con un recurso Bundle de FHIR R4 (más información aquí).

A continuación podéis ver un ejemplo del código necesario para transformar un mensaje HL7 a un bundle de FHIR R4 en formato %DynamicObject:

 do ##class(HS.Gateway.HL7.HL7ToSDA3).GetSDA(request, .SDAMessageTemp)
 set SDAToFHIR = #class(HS.FHIR.DTL.Util.API.Transform.SDA3ToFHIR).TransformStream(SDAMessageTemp,"HS.SDA3.Container","R4")

¿Y mi FHIR R5?

Hasta ahora hemos visto que Health Connect permite de una forma muy sencilla la conversión de HL7 a FHIR R4 (es posible que la conversión no se ajuste al 100% de tus necesidades, pero te resultará más fácil editar un recurso que construirlo de 0), genial, pero...¿DONDE ESTÁ MI RECURSO R5?

No te preocupes porque... ¡la Comunidad de Desarrolladores viene al rescate!

Verás que adjunto a este artículo hay una aplicación de OpenExchange asociada, dicha aplicación contiene el código necesario para transformar los recursos FHIR R4 a los recursos R5 utilizados en el proyecto ÚNICAS. Para instalar dicho código podéis hacer uso de IPM:

set version="latest" s r=##class(%Net.HttpRequest).%New(),r.Server="pm.community.intersystems.com",r.SSLConfiguration="ISC.FeatureTracker.SSL.Config" d r.Get("/packages/zpm/"_version_"/installer"),$system.OBJ.LoadStream(r.HttpResponse.Data,"c")
zpm "enable -community"
zpm "install spain-unicas"

Una vez instalado veréis que ha aparecido un package llamado Spain en el namespace donde hayais hecho la instalación. ¿Que podéis encontrar en dicho package?

  • Spain.BP: BPL con ejemplo de transformación  HL7 -> SDA -> FHIR R4 -> FHIR R5.
  • Spain.FHIR.DTL.vR4: Transformaciones de R4 a R5 de los recursos empleados por ÚNICAS.
  • Spain.FHIR.DTL.vR5: Clases de ObjectScript que modelan los recursos R5 de ÚNICAS.
  • Spain.Gateway.HL7: Pequeña corrección para la transformación HL7 a SDA3.

Transformando R4 a R5

Con las herramientas disponibles en el package Spain ya podemos transformar nuestros recursos R4 a R5, para ello usaremos las transformaciones de cada tipo de recurso del siguiente modo:

set obj = ##class(HS.FHIR.DTL.vR4.Model.Resource.Bundle).FromJSONHelper(context.FHIRMessage, "vR4")

for i=1:1:obj.entry.Count() {
    set item = obj.entry.GetAt(i)
    if ($CLASSNAME(item.resource) = "HS.FHIR.DTL.vR4.Model.Resource.Immunization")
    {
      Set status = ##class(Spain.FHIR.DTL.vR4.vR5.Immunization).Transform(item.resource, .immunizationR5)            
      Set item.resource = immunizationR5
      Do obj.entry.SetAt(item, i)            
    }
    elseif ($CLASSNAME(item.resource) = "HS.FHIR.DTL.vR4.Model.Resource.AllergyIntolerance")
    {
      Set status = ##class(Spain.FHIR.DTL.vR4.vR5.Allergy.AllergyIntolerance).Transform(item.resource, .allergyIntoleranceR5)            
      Set item.resource = allergyIntoleranceR5
      Do obj.entry.SetAt(item, i)            
    }
    ...
 }

Recordad que disponíamos de un %DynamicObject con un bundle de recursos R4, el primer paso que daremos será mapear dicho %DynamicObject a una clase HS.FHIR.DTL.vR4.Model.Resource.Bundle usando el método FromJSONHelper, el objetivo de esta transformación es poder reconocer posteriormente el tipo de recurso presente en el bundle por el nombre de su clase.

Con el bundle mapeado a su clase de ObjectScript nos limitaremos a recorrer cada entrada del mismo identificando el recurso, si corresponde a uno de los recursos usados en ÚNICAS procedemos a invocar a un DTL específico para realizar la transformación de R4 a R5.

Con el recurso ya transformado, lo volvemos a introducir en el Bundle sustituyendo al anterior recurso R4.

Finalmente sólo nos quedará transformar el Bundle a un Stream (en este caso llamado QuickStream) y enviarlo dentro de un mensaje de tipo HS.FHIRServer.Interop.Request al business operation HS.FHIRServer.Interop.Operation

 Set context.FHIRRequest.Request.RequestMethod = "POST"
 Set context.FHIRRequest.Request.RequestPath = ""
 Set context.FHIRRequest.Request.RequestFormatCode= "JSON"
 Set context.FHIRRequest.Request.SessionApplication = "/csp/healthshare/fhirserver/fhir/r5"
 Set context.FHIRRequest.Request.IsRecursive = 0
 set qs=##class(HS.SDA3.QuickStream).%New()
 set context.FHIRRequest.QuickStreamId = qs.%Id()
 do qs.CopyFrom(context.FHIRStream)
 

Aquí tendríamos la llamada:

¡Pues ya estaría! Ya tenemos nuestro pequeño ejemplo de conversiones de HL7 a R5 y su registro en nuestro repositorio desplegado en Health Connect, veamoslo en funcionamiento.

Ejemplo de transformación de HL7 a R5

Empecemos con un mensaje HL7 v2.5.1

MSH|^~\&|CLINIC_SYS|HOSPITAL123|EHR_SYSTEM|REGION1|20250826143000||ADT^A08^ADT_A01|MSG00002|P|2.5.1
EVN|A08|20250826143000
PID|1||123456^^^HOSPITAL123^MR||GOMEZ^MARIA^LUISA||19750523|F|||AVDA UNIVERSIDAD 45^^MADRID^^28040^ESP||(555)1234567|||S||12345678Z^^^ESP^NI
AL1|1|DA|70618^Penicillin^RXNORM|SV|Rash|20200115|Clinician noted severe rash after administration
AL1|2|FA|256349002^Peanut protein^SCT|SE|Anaphylaxis|20181201|Reported after accidental exposure
AL1|3|MA|300916003^Latex^SCT|MO|Skin irritation|20191010|Observed during procedure with latex gloves

Aquí tenemos un sencillo ADT_A08 con información relativa a un paciente y sus alergias, como unos implementadores aplicados que somos, sabemos que este mensaje contendrá (por lo menos) un recurso del tipo Patient y 3 del tipo AllergyIntolerance. 

Introducimos en nuestra producción un business service para capturar ficheros de HL7 y enviarlo a nuestro BPL:

Una vez capturado, transformamos nuestro HL7 a R4 pasando por SDA, echemos un vistazo al recurso AlleryIntolerance generado en R4:

{
                                                              
	"request": {
		"method": "POST",
		"url": "AllergyIntolerance"
	},
	"fullUrl": "urn:uuid:4b9f7b2e-9dd0-11f0-870e-c6b593068383",
	"resource": {
		"resourceType": "AllergyIntolerance",
		"category": [
			"food"
		],
		"clinicalStatus": {
			"coding": [
				{
					"code": "active",
					"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical"
				}
			]
		},
		"code": {
			"coding": [
				{
					"code": "256349002",
					"display": "Peanut protein",
					"system": "http://snomed.info/sct"
				}
			]
		},
		"extension": [
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-discovery-time",
				"valueDateTime": "2018-12-01T00:00:00+00:00"
			},
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-entered-at",
				"valueReference": {
					"reference": "urn:uuid:4b9e97f6-9dd0-11f0-870e-c6b593068383"
				}
			}
		],
		"onsetDateTime": "2018-12-01T00:00:00+00:00",
		"patient": {
			"reference": "urn:uuid:4b9edb58-9dd0-11f0-870e-c6b593068383"
		},
		"reaction": [
			{
				"extension": [
					{
						"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-severity",
						"valueCodeableConcept": {
							"coding": [
								{
									"code": "SE"
								}
							]
						}
					}
				],
				"manifestation": [
					{
			
						"coding": [
							{
								"code": "Anaphylaxis"
							}
						]
 
					}
				]
			}
		]
	}
}

Y ahora el mismo recurso pero adaptado a la versión R5:

{
	"fullUrl": "urn:uuid:4b9f7b2e-9dd0-11f0-870e-c6b593068383",
	"request": {
		"method": "POST",
		"url": "AllergyIntolerance"
	},
													  
	"resource": {
		"resourceType": "AllergyIntolerance",
		"category": [
			"food"
		],
		"clinicalStatus": {
			"coding": [
				{
					"code": "active",
					"system": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical"
				}
			]
		},
		"code": {
			"coding": [
				{
					"code": "256349002",
					"display": "Peanut protein",
					"system": "http://snomed.info/sct"
				}
			]
		},
		"extension": [
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-discovery-time",
				"valueDateTime": "2018-12-01T00:00:00+00:00"
			},
			{
				"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-entered-at",
				"valueReference": {
					"reference": "urn:uuid:4b9e97f6-9dd0-11f0-870e-c6b593068383"
				}
			}
		],
		"onsetDateTime": "2018-12-01T00:00:00+00:00",
		"patient": {
			"reference": "urn:uuid:4b9edb58-9dd0-11f0-870e-c6b593068383"
		},
		"reaction": [
			{
				"extension": [
					{
						"url": "http://intersystems.com/fhir/extn/sda3/lib/allergy-severity",
						"valueCodeableConcept": {
							"coding": [
								{
									"code": "SE"
								}
							]
						}
					}
				],
				"manifestation": [
					{
						"concept": {
							"coding": [
								{
									"code": "Anaphylaxis"
								}
							]
						}
					}
				]
			}
		]
	}
}

Como podéis ver, la propiedad reaction ha sido modificada, adaptándose a la definición R5 del mismo:

Conclusión

Como habéis visto, las posibilidades que nos ofrece Health Connect para adaptarnos a los distintos proyectos basados en FHIR que nos puedan surgir son infinitas. Con el motor de interoperabilidad podremos adaptar nuestros datos del modo que necesitemos, teniendo en consideración cualquier requisito que se salga del comportamiento estándar.

¡Espero que os resulte de utilidad!

6 Comments
Discussion (6)3
Log in or sign up to continue
Announcement
· Sep 30

Nuevo tutorial práctico: RAG usando la búsqueda vectorial de InterSystems IRIS

Hola comunidad,

Estamos encantados de compartir un nuevo tutorial en Instruqt:

🧑‍🏫 RAG usando la búsqueda vectorial de InterSystems IRIS

Este tutorial práctico os guía en la creación de un chatbot de IA con Recuperación Aumentada por Generación (RAG) impulsado por la búsqueda vectorial de InterSystems IRIS. Veréis cómo se puede aprovechar la búsqueda vectorial para ofrecer respuestas actualizadas y precisas, combinando las fortalezas de IRIS con la IA generativa.

✨ ¿Por qué probarlo?

  • Aprended cómo funciona en la práctica IRIS Vector Search.
  • Explorad cómo RAG puede mejorar la fiabilidad y la relevancia de las respuestas proporcionadas por la búsqueda vectorial.
  • Adquirid experiencia práctica paso a paso al construir una solución impulsada por IA.

>> Iniciad el tutorial aquí <<

Nos encantaría saber qué pensáis. Dejad vuestro feedback al final del tutorial o compartid vuestras impresiones en los comentarios de esta publicación.

¡Feliz aprendizaje!

Discussion (0)1
Log in or sign up to continue