Looking for implementation strategy for create/update of Organizations in FHIR server
Hi community,
I am looking for an implementation strategy to create/update organizations in a local ISC based FHIR server. Thoase organizations do have a certain dependancy to one another. For Organization resources the "partOf" property symbolizes that. It is planned, that a Interoperability Production (supporting FHIR Server) receives a bundle which contains a batch export of all organizations of a customer (Hospital). The task is to bring those data into the FHIR server create and/or update organization.
I was looking at conditional create/update since there a two major challanges.
1) The recieved bundle containing the export of the customers organizations does not know anything on id´s of a resource on the fhir server
2) Logic to implement to make sure that "head" organization resources are inserted and correctly linked to "child" organizations would be quite complex.
Following is an example of the bundle (batch export of customers organizations) that shall be processed by the FHIR server. Both organizations ""EFG" an "AEAP" are to be childs of organization "HEAD".
The given bundle allows for conditional create against the ISC FHIR server. When queried via GET references will be resolved by the FHIR server with the FHIR resource id assigned by the FHIR server.
{
"entry":
[
{
"request":
{
"ifNoneExist": "identifier=PDQ",
"method": "POST",
"url": "Organization"
},
"resource":
{
"active": true,
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "EFG"
}
],
"name": "Allgemeine Einrichtung EFG.",
"partOf":
{
"display": "HEAD",
"reference": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type":
[
{
"coding":
[
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
},
{
"request":
{
"ifNoneExist": "identifier=AEAP",
"method": "POST",
"url": "Organization"
},
"resource":
{
"active": true,
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "AEAP"
}
],
"name": "Allgemeine Einrichtung AEAP",
"partOf":
{
"display": "HEAD",
"reference": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type":
[
{
"coding":
[
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
},
{
"fullUrl": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd",
"request":
{
"ifNoneExist": "identifier=HEAD",
"method": "POST",
"url": "Organization"
},
"resource":
{
"active": true,
"address":
[
{
"line":
[
"Some street 2"
],
"type": "postal",
"use": "work"
}
],
"id": "87a8f88a-5223-11ee-8c2c-005056b163cd",
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "HEAD"
}
],
"name": "Some Hospital",
"partOf":
{
"display": "NULL"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
}
],
"type":
[
{
"coding":
[
{
"code": "0",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "SHOS",
"display": "Some Hospital",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
}
],
"id": "214a4092-eeb9-440d-b645-0ec3b5b30bbe",
"resourceType": "Bundle",
"type": "transaction"
}As for a conditional update I am not completetly shure what how to adjust that bundle to support this interaction too. Thats why my idea is to query the FHIR server first (with a bundle) for the given identifiers of the organization. If organization is not found in the fhir server create it, otherwise check for necessatiy to update the object by traversing object contents and match what has been received in the first place. If different update the ressource (using id assigned by fhir server).
What would be your approach to the problem? Is it possibly to modify the above given bundle to allow for conditional create/update directyl? I´ve did extensive testing and studying docs but weren´t able to find anything for my scenario.
best reageads,
Sebastiam
Comments
Well, in theory if you define the identifier in the request section and define the method as "PUT" the server should create the organization if it doesn't exist or updated if it do.
<?xml version="1.0" encoding="UTF-8"?><Bundlexmlns="http://hl7.org/fhir"><typevalue="transaction"></type><entry><resource><Organization><idvalue="17C7D86E-664F-4FE2-91D7-AF9A8E47311E"></id><text><statusvalue="generated"/><divxmlns="http://www.w3.org/1999/xhtml"> LITTLE CLINIC 2000 PATIENT DRIVE ANN
ARBOR MI US - NOI # 888333111 </div></text><identifier><systemvalue="www.nationalorgidentifier.gov"/><valuevalue="888333111"/></identifier><namevalue="LITTLE CLINIC"/><address><linevalue="2000 CLINIC DRIVE"/><cityvalue="ANN ARBOR"/><statevalue="MI"/><countryvalue="US"/></address></Organization></resource><request><methodvalue="PUT"/><urlvalue="Organization?identifier=www.nationalorgidentifier.gov|888333111"/></request></entry><entry><resource><Patient><text><statusvalue="generated"/><divxmlns="http://www.w3.org/1999/xhtml"> EVERYWOMAN EVE 2000 PATIENT DRIVE ANN
ARBOR MI MPI #12312311 </div></text><identifier><systemvalue="www.mypatientidentifier.com"/><valuevalue="12312311"/></identifier><name><familyvalue="EVERYWOMAN"/><givenvalue="EVE"/></name><address><linevalue="2000 PATIENT DRIVE"/><cityvalue="ANN ARBOR"/><statevalue="MI"/><countryvalue="US"/></address><managingOrganization><referencevalue="Organization/17C7D86E-664F-4FE2-91D7-AF9A8E47311E"></reference></managingOrganization></Patient></resource><request><methodvalue="PUT"/><urlvalue="Patient?identifier=www.mypatientidentifier.com|12312311"/></request></entry></Bundle>But this is the theory...not sure if it will work for your FHIR server.
The FHIR server is the Intersystems FHIR Server. The above depicted bundle does the conditional create this works when using "method": "POST". Replacing PUT with POST requires (of course) an ID - I´ll try it the way you done it in your sample using
Organization?identifier=www.nationalorgidentifier.gov|888333111"That adjustment didn´t work either. Maybe there need to be another adjustment in the bundle to supoort my scenariao? This is recieving the bundle (i can manipulate it) send it to the InterSystems FHIR server and conditionaly create and/or conditionaly update the resources in the FHIR server.
How would you (based on the organization scenario) would your bundle look like?
{
"entry":
[
{
"request":
{
"method": "PUT",
"url": "Organization?identifier=EFG"
},
"resource":
{
"active": true,
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "EFG"
}
],
"name": "Allgemeine Einrichtung EFG.",
"partOf":
{
"display": "HEAD",
"reference": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type":
[
{
"coding":
[
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
},
{
"request":
{
"method": "PUT",
"url": "Organization?identifier=AEAP"
},
"resource":
{
"active": true,
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "AEAP"
}
],
"name": "Allgemeine Einrichtung AEAP",
"partOf":
{
"display": "HEAD",
"reference": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type":
[
{
"coding":
[
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
},
{
"fullUrl": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd",
"request":
{
"method": "PUT",
"url": "Organization?identifier=HEAD"
},
"resource":
{
"active": true,
"address":
[
{
"line":
[
"Some street 2"
],
"type": "postal",
"use": "work"
}
],
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "HEAD"
}
],
"name": "Some Hospital",
"partOf":
{
"display": "NULL"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
}
],
"type":
[
{
"coding":
[
{
"code": "0",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "SHOS",
"display": "Some Hospital",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
}
],
"id": "214a4092-eeb9-440d-b645-0ec3b5b30bbe",
"resourceType": "Bundle",
"type": "transaction"
}Here is your bundle with conditional update instead of conditional create.
Result on IRIS 2023.2 :
First call :
{
"resourceType": "Bundle",
"id": "409996a4-644b-11ee-8e3a-0242ac160003",
"type": "transaction-response",
"timestamp": "2023-10-06T13:07:35Z",
"entry": [
{
"response": {
"status": "201",
"location": "https://localhost:4443/fhir/r4/Organization/2",
"etag": "W/\"1\"",
"lastModified": "2023-10-06T13:07:35Z"
}
},
{
"response": {
"status": "201",
"location": "https://localhost:4443/fhir/r4/Organization/3",
"etag": "W/\"1\"",
"lastModified": "2023-10-06T13:07:35Z"
}
},
{
"fullUrl": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd",
"response": {
"status": "201",
"location": "https://localhost:4443/fhir/r4/Organization/1",
"etag": "W/\"1\"",
"lastModified": "2023-10-06T13:07:35Z"
}
}
]
}as you can see we have 201 (mean created)
Second call :
{
"resourceType": "Bundle",
"id": "ccc5c35a-644b-11ee-8f77-0242ac160003",
"type": "transaction-response",
"timestamp": "2023-10-06T13:08:47Z",
"entry": [
{
"response": {
"status": "200",
"location": "https://localhost:4443/fhir/r4/Organization/2",
"etag": "W/\"2\"",
"lastModified": "2023-10-06T13:08:48Z"
}
},
{
"response": {
"status": "200",
"location": "https://localhost:4443/fhir/r4/Organization/3",
"etag": "W/\"2\"",
"lastModified": "2023-10-06T13:08:48Z"
}
},
{
"fullUrl": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd",
"response": {
"status": "200",
"location": "https://localhost:4443/fhir/r4/Organization/1",
"etag": "W/\"2\"",
"lastModified": "2023-10-06T13:08:47Z"
}
}
]
}we can see 200 mean ok/updated.
Get all organization :
{
"resourceType": "Bundle",
"id": "56ccfcb4-644a-11ee-8f78-0242ac160003",
"type": "searchset",
"timestamp": "2023-10-06T13:09:50Z",
"total": 3,
"link": [
{
"relation": "self",
"url": "https://localhost:4443/fhir/r4/Organization"
}
],
"entry": [
{
"fullUrl": "https://localhost:4443/fhir/r4/Organization/1",
"resource": {
"active": true,
"address": [
{
"line": [
"Some street 2"
],
"type": "postal",
"use": "work"
}
],
"identifier": [
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "HEAD"
}
],
"name": "Some Hospital",
"partOf": {
"display": "NULL"
},
"resourceType": "Organization",
"telecom": [
{
"system": "phone",
"value": "some-phonenr"
}
],
"type": [
{
"coding": [
{
"code": "0",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "SHOS",
"display": "Some Hospital",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
],
"id": "1",
"meta": {
"lastUpdated": "2023-10-06T13:08:47Z",
"versionId": "2"
}
},
"search": {
"mode": "match"
}
},
{
"fullUrl": "https://localhost:4443/fhir/r4/Organization/2",
"resource": {
"active": true,
"identifier": [
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "EFG"
}
],
"name": "Allgemeine Einrichtung EFG.",
"partOf": {
"display": "HEAD",
"reference": "Organization/1"
},
"resourceType": "Organization",
"telecom": [
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type": [
{
"coding": [
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
],
"id": "2",
"meta": {
"lastUpdated": "2023-10-06T13:08:48Z",
"versionId": "2"
}
},
"search": {
"mode": "match"
}
},
{
"fullUrl": "https://localhost:4443/fhir/r4/Organization/3",
"resource": {
"active": true,
"identifier": [
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "AEAP"
}
],
"name": "Allgemeine Einrichtung AEAP",
"partOf": {
"display": "HEAD",
"reference": "Organization/1"
},
"resourceType": "Organization",
"telecom": [
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type": [
{
"coding": [
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
],
"id": "3",
"meta": {
"lastUpdated": "2023-10-06T13:08:48Z",
"versionId": "2"
}
},
"search": {
"mode": "match"
}
}
]
}As you can see "partOf" are referencing internal id of HEAD.
TIP :
To quickly test on a fresh server, you can wipe data like this (only do it on dev server, your laptop, never in production !!! )
Set appKey = "/fhir/r4"Set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint(appKey)
Set options("deleteDataOnly") = 1Do strategy.Delete(.options)Hi Guillaume,
thanks for the example. As pointet Customer runs version 2022.1.3 insgesamt of 2023.x is there any way to accomplish this in Version 2022.1? From your second call i‘ve noticed That the resources were updated despite no modificatons in the resource contents were made. Is there any way to update only when modifications were made?
best Regards,
sebastian
It seems that 2022.x have the same capability that 2023.x, I mean by that 2022.x support conditional Update and Create.
Then if you want to update only that is needed to be updated you have to go with JSON Patch :
PATCH https://localhost:4443/fhir/r4/Organization?identifier=HEAD
Accept: application/fhir+json
Content-Type: application/json-patch+json
[
{
"op": "add",
"path": "/telecom/0",
"value": {
"system": "phone",
"value": "some-other-phone"
}
}
]Result :
GET https://localhost:4443/fhir/r4/Organization?identifier=HEAD&_elements=telecom
Accept: application/fhir+json{
"resourceType": "Bundle",
"id": "9962a9aa-668b-11ee-8f79-0242ac160003",
"type": "searchset",
"timestamp": "2023-10-09T10:05:38Z",
"total": 1,
"link": [
{
"relation": "self",
"url": "https://localhost:4443/fhir/r4/Organization?_elements=telecom&identifier=HEAD"
}
],
"entry": [
{
"fullUrl": "https://localhost:4443/fhir/r4/Organization/1",
"resource": {
"resourceType": "Organization",
"telecom": [
{
"system": "phone",
"value": "some-other-phone"
},
{
"system": "phone",
"value": "some-other-phone"
}
],
"meta": {
"tag": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue",
"code": "SUBSETTED",
"display": "Resource content reduced because _elements search parameter"
}
]
}
},
"search": {
"mode": "match"
}
}
]
}Attention JSON patch only work with direct access to the resource, if you need json path in a bundle you have to pass it as a base64 :
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "Patient/1",
"resource": {
"resourceType": "Binary",
"contentType": "application/json-patch+json",
"data": "WyB7ICJvcCI6InJlcGxhY2UiLCAicGF0aCI6Ii9hY3RpdmUiLCAidmFsdWUiOmZhbHNlIH0gXQ=="
},
"request": {
"method": "PATCH",
"url": "Patient/1"
}
}
]
}
Example of a json path in a bundle for the patient 1 with this patch :
[ { "op":"replace", "path":"/active", "value":false } ]That Wolf require to know what has been changed wich is problematic in a batch Import since i would need to fetch, analyse, prepare etc. Resources and push it to the fhir Server. Isn‘t there any more simple options for this?
I don't think so.
FHIR is an API rest. FHIR has not been designed to apply deduplication and data merge business rules. This is the role of HealthShare not the role of an FHIR endpoint.
1) The recieved bundle containing the export of the customers organizations does not know anything on id´s of a resource on the fhir server
You can check in FHIR Http - FHIR v5.0.0 (hl7.org) for Conditional Reference, you can reference to another resource using a search parameter, such as identifier.
2) Logic to implement to make sure that "head" organization resources are inserted and correctly linked to "child" organizations would be quite complex.
Using the previous method, you can be confident that, even if the child does not exist and the head exists they will be linked because of the conditional reference. It is needed to implement some strategies for incoming "child" that reference an Organization that is not present in the server.