Find duplicates: $match
The $match operation performs a probabilistic search using a matching model and returns potential duplicates ranked by match score.
A MatchingModel must be created before using $match.
MDMbox follows the request and response shape of the FHIR R6 Patient/$match operation: clients send a FHIR Parameters resource and receive a Bundle of type searchset. MDMbox generalizes the operation to any configured FHIR resource type and adds modelId and threshold parameters so callers can choose the matching model and score cutoff.
Unversioned routes such as /api/fhir/Patient/$match use the FHIR release selected by MDMBOX_DEFAULT_FHIR_RELEASE. Versioned routes are also available as /api/fhir/r4/:resource/$match and /api/fhir/r6/:resource/$match. Instance-level matching is available at /api/fhir/r4/:resource/:id/$match and /api/fhir/r6/:resource/:id/$match.
MDMbox validates body-based input before matching. See Profiled input resources when using meta.profile.
Match a resource
Send a FHIR Parameters resource containing the record to match:
POST https://<mdmbox-host>/api/fhir/Patient/$match
Content-Type: application/json
{
"resourceType": "Parameters",
"parameter": [
{ "name": "modelId", "valueString": "patient-model" },
{
"name": "resource",
"resource": {
"resourceType": "Patient",
"name": [{ "given": ["Freya"], "family": "Shah" }],
"birthDate": "1990-01-15",
"gender": "female"
}
}
]
}
Match an existing resource by ID
To match an existing resource against all others:
POST https://<mdmbox-host>/api/fhir/Patient/123/$match
Content-Type: application/json
{
"resourceType": "Parameters",
"parameter": [{ "name": "modelId", "valueString": "patient-model" }]
}
MDMbox retrieves Patient/123 and compares it with other records, excluding the source ID from results.
Pairs recorded with $mark-not-a-match are excluded when the input identifies an existing resource. A new resource without an ID has no recorded pair decisions to apply.
Parameters
Common request body parameters
| Name | Type | Required | Description |
|---|---|---|---|
modelId | valueString | Yes | ID of the MatchingModel to use |
resource | resource | Only for /api/fhir/:resource/$match | The FHIR resource to find matches for. Omit this parameter when matching an existing resource by ID. |
threshold | valueDecimal | No | Override the model's probable threshold. Cannot be combined with onlyCertainMatches=true or onlySingleMatch=true. |
count | valueInteger | No | Maximum number of returned entries (default: 10). Bundle.total still reports the full number of matches above the effective threshold. |
The default count is controlled by MDMBOX_MATCH_DEFAULT_COUNT and is 10 unless configured otherwise.
Profiled input resources
If input declares meta.profile, install the package containing that profile in Aidbox using its Implementation Guide installation guide. MDMbox validates the resource against the profile before searching. Invalid input returns HTTP 422 with an OperationOutcome.
R6 flag behavior
| Name | Type | Description |
|---|---|---|
onlyCertainMatches | valueBoolean | When true, uses the model's certain threshold. Cannot be combined with threshold or onlySingleMatch=true. count still applies. |
onlySingleMatch | valueBoolean | When true, returns one best candidate. Cannot be combined with threshold, count, or onlyCertainMatches=true. |
In normal R6 mode, MDMbox uses threshold when supplied, otherwise the model's probable threshold. There is no 100-entry TEFCA cap for R6.
In R6 onlySingleMatch=true mode, MDMbox selects the highest-scored candidate with a raw weight of at least 0. This mode does not use the model's probable or certain threshold, so the result can have grade possible. Ties are resolved by resource ID. If no candidate is eligible, the response is an empty searchset.
R4 flag behavior
| Name | Type | Description |
|---|---|---|
onlyCertainMatches | valueBoolean | When true, uses the model's certain threshold. Cannot be combined with threshold. count still applies. |
R4 routes do not implement onlySingleMatch; use an R6 route when that behavior is required.
In normal R4 potential-match mode (onlyCertainMatches omitted or false), MDMbox uses threshold when supplied, otherwise the model's probable threshold.
When MDMBOX_TEFCA_MODE=true, R4 potential-match responses return no more than 100 entries, even when count is larger. The TEFCA cap is not applied when onlyCertainMatches=true.
Response
The response is a FHIR Bundle of type searchset. Each entry includes:
resource— the matched FHIR resourcesearch.score— probability-like FHIR search score from 0 to 1 derived from the raw match weightsearch.extension— match grade (certain,probable, orpossible), raw match weight, and per-feature match details
{
"resourceType": "Bundle",
"type": "searchset",
"total": 1,
"entry": [
{
"resource": {
"resourceType": "Patient",
"id": "456",
"name": [{ "given": ["Freya"], "family": "Shah" }],
"birthDate": "1990-01-15",
"gender": "female"
},
"search": {
"mode": "match",
"score": 0.9999999826441174,
"extension": [
{
"url": "http://hl7.org/fhir/StructureDefinition/match-grade",
"valueCode": "certain"
},
{
"url": "https://mdm.health-samurai.io/fhir/StructureDefinition/match-weight",
"valueDecimal": 25.78
},
{
"url": "https://mdm.health-samurai.io/fhir/StructureDefinition/match-details",
"extension": [
{ "url": "dob", "valueDecimal": 10.59 },
{ "url": "name", "valueDecimal": 13.34 },
{ "url": "sex", "valueDecimal": 1.85 }
]
}
]
}
}
]
}
For large datasets, create database indexes on columns used in matching model blocks. Without indexes, $match performs a full table scan for each block, which can be very slow.
Weight and score calculation
Match weights are log2 Bayes factor sums. MDMbox exposes the raw weight in the https://mdm.health-samurai.io/fhir/StructureDefinition/match-weight extension and converts it to search.score using a sigmoid function:
probability = 1 / (1 + 2^(-weight))
See Mathematical details for the full derivation.
Audit
Successful requests record an AuditEvent before returning results, including empty results. If the event cannot be saved, the operation returns HTTP 500. See Audit for recorded fields and limits.