An entity resolution service determines when two records refer to the same real-world entity and assigns them a shared identifier. The MDM module provides a powerful and flexible interface for performing entity resolution within the repository, but it is not required if an external entity resolution service is available. External services are often very mature, with matching rules that have been extensively tested against live data feeds over years of operation. This page shows how the system can be integrated with such an external service.
The most common entity resolution service in healthcare is the Enterprise Master Patient Index (EMPI), which resolves patient records, so the following examples use Patient. The same principles apply to other resource types if a suitable external service exists.
Calls to an external entity resolution service can be integrated into the workflow by providing a custom interceptor that listens for the STORAGE_PRESTORAGE_RESOURCE_CREATED pointcut. This pointcut allows changes to be made to an incoming resource before it is stored in the repository. The same approach can be applied to updates of resources already in the repository by adding a hook for the STORAGE_PRESTORAGE_RESOURCE_UPDATED pointcut, if the EMPI identifier could be changed or removed during an update.
An interceptor class for calling an external entity resolution service will have the following structure.
package com.example;
@Service
public class EntityResolutionInterceptor {
private static final String EID_URI = "http://my-organization.org/namespaces/patient-eid";
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED)
public void resolveEntity(IBaseResource theBaseResource) {
// We only need to process resource types that the external system is capable of handling
if (theBaseResource instanceof Patient patient) {
// If the resource already has an identifier with the EID system, there's no need for further processing
if (patient.getIdentifier().stream()
.map(Identifier::getSystem)
.noneMatch(EID_URI::equals)) {
// Make an appropriate call against the API of the external system
Identifier eidIdentifier = callEntityResolutionService();
// Update the incoming resource with the new identifier
patient.addIdentifier(eidIdentifier);
}
}
}
}
The details of calling the external service will vary. The administrator of that service should provide documentation of its API. In general, this integration involves the following steps.
Place the JAR containing the custom interceptor in the customerlib directory to make it available on the module's classpath.
Set the interceptor_bean_types property on the persistence module to the class name of the interceptor:
module.persistence.config.interceptor_bean_types=com.example.EntityResolutionInterceptor
Restart the persistence module for the new configuration to take effect.
The interceptor will now apply entity resolution to every incoming resource of the types it handles (Patient, in this example).
Calling the external entity resolution service may incur a cost from network latency or the service's processing speed, so avoid making the call unnecessarily. If the incoming resource already has an EID assigned, or is not of a resource type that the service can handle, the interceptor should return immediately without modifying the resource.
Once the EID is in place, linked records can be retrieved in two ways: the built-in EID-based MDM expansion, which expands queries automatically but requires the MDM module, or manual EID queries, which work without the MDM module but put the linking burden on the caller.
When MDM Rules include property eidSystems and MDM is configured in MATCH_ONLY mode, EID-based MDM expansion is supported by the operations that allow MDM expansion. These include search queries with MDM expansion parameters,
the Group bulk export operation, and the $everything operation.
In this configuration, MDM expansion is performed exclusively based on matching EIDs. Other MDM matching rules are not evaluated during MDM expansion in this configuration.
To enable EID-based MDM expansion in MATCH_ONLY mode, you must:
MATCH_ONLYeidSystems in your MDM Rules Definitionmdm.search_expansion.enabled)For example, in your MDM Rules:
{
"version": "1",
"mdmTypes": ["Patient"],
"eidSystems": {
"Patient": "http://example.org/eid-system"
}
}
A resource type may also be given an array of EID systems, in which case a resource is expanded on any of them. See Using several EID systems for one resource type.
When a resource has an identifier with a system matching one of the configured eidSystems, MDM expansion operations will:
Resources without the configured EID, or with non-matching EID values, are not expanded.
Expansion is a single step, not a chain. Where a resource type has several configured EID systems, records may carry different subsets of them, and expansion reaches only those sharing an EID with the record you start from.
Take three records for one person: one carrying only an MRN, one carrying only an NPI, and one carrying both. Expanding from the MRN-only record reaches the record carrying both, but not the NPI-only record - the starting record has no NPI to match on. Expanding from the record carrying both reaches all three.
Expansion is therefore complete only where every record for an entity carries the same EID system. Where that does not hold, expand from the most completely identified record, or query the EID values directly. Records are only ever missing from an expansion, never wrongly included: the search is qualified by EID system as well as value, so a value shared by two different systems does not pull in an unrelated record.
Important: In MATCH_ONLY mode with EID-based expansion:
:mdm ParameterUse the :mdm search parameter modifier to expand reference searches based on EID matching.
Example:
GET [base]/Observation?patient:mdm=Patient/123
If Patient/123 has an EID value of "12345" with system http://example.org/eid-system, this search will return:
Patient/123http://example.org/eid-system|12345Without EID: If Patient/123 does not have the configured EID, only Observations for that specific patient are returned.
Non-matching EID: If Patient/123 has a different EID value than other patients, only Observations for that specific patient are returned.
$everything Operation with _mdm ParameterThe $everything operation can be expanded to include resources for all patients with matching EIDs.
Example:
GET [base]/Patient/123/$everything?_mdm=true
If Patient/123 has an EID that matches other patients, the response will include:
Patient/123 and all patients with matching EIDs)Note: The _mdm parameter must be set to true to enable expansion.
_mdm ParameterGroup export operations support EID-based expansion to include resources for all patients with matching EIDs.
Example:
POST [base]/Group/456/$export
Content-Type: application/fhir+json
{
"resourceType": "Parameters",
"parameter": [
{
"name": "_outputFormat",
"valueString": "application/fhir+ndjson"
},
{
"name": "_mdm",
"valueBoolean": true
},
{
"name": "_type",
"valueString": "Patient,Observation"
}
]
}
If Group/456 contains patients with EIDs, the export will include:
Behavior:
EID-based MDM expansion in MATCH_ONLY mode is useful when:
EID-based expansion in MATCH_ONLY mode is more efficient than full MATCH_AND_LINK mode because:
If the MDM module is not available, callers can perform the linking themselves by including the EID as a search parameter in every query.
GET http://my-server.com/Patient?identifier=http://my-organization.org/namespaces/patient-eid|1234
GET http://my-server.com/Observation?patient.identifier=http://my-organization.org/namespaces/patient-eid|1234
If the caller does not know the EID, it can be discovered by first querying for Patient by name or other demographic fields. Note that the search result bundle for this query may include unrelated patients who coincidentally share the queried demographic fields, and may omit relevant patient resources that share the EID but differ in their demographic details. For this reason, once the EID is discovered, the caller should make another call to obtain all relevant patient records. This can be done using the Patient query shown above, or by adding the parameter _include=Observation:patient to the query for Observations (or equivalently for any other resource type).
Once the data has been retrieved, it is the caller's responsibility to perform any merge or collate operations that are necessary to prepare the data for display or further processing.
You are about to leave the Smile Digital Health documentation and navigate to the Open Source HAPI-FHIR Documentation.