Jd 20240614 6010 bundle entry search score missing (#6011)

* 6010 test

* 6010 add bundle entry search score

* 6010 support other fhir versions for search score

* 6010 changelog

* 6116 mvn spotless

* 6010 address code review comments

---------

Co-authored-by: jdar <justin.dar@smiledigitalhealth.com>
This commit is contained in:
jdar8 2024-06-24 08:56:00 -07:00 committed by GitHub
parent bce0313458
commit 11d61a5f72
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 134 additions and 11 deletions

View File

@ -31,6 +31,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -80,6 +81,15 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
*/ */
public static final ResourceMetadataKeyEnum<BundleEntrySearchModeEnum> ENTRY_SEARCH_MODE = public static final ResourceMetadataKeyEnum<BundleEntrySearchModeEnum> ENTRY_SEARCH_MODE =
new ResourceMetadataKeyEnum<>("ENTRY_SEARCH_MODE", BundleEntrySearchModeEnum.class) {}; new ResourceMetadataKeyEnum<>("ENTRY_SEARCH_MODE", BundleEntrySearchModeEnum.class) {};
/**
* If present and populated with a decimal value, contains the "bundle entry search score", which is the value of the status field in the Bundle entry containing this resource.
* The value for this key corresponds to field <code>Bundle.entry.search.score</code>. This value represents the search ranking score, where 1.0 is relevant and 0.0 is irrelevant.
* <p>
* Note that status is only used in FHIR DSTU2 and later.
* </p>
*/
public static final ResourceMetadataKeyEnum<BigDecimal> ENTRY_SEARCH_SCORE =
new ResourceMetadataKeyEnum<>("ENTRY_SEARCH_SCORE", BigDecimal.class) {};
/** /**
* If present and populated with a {@link BundleEntryTransactionMethodEnum}, contains the "bundle entry transaction operation", which is the value of the status field in the Bundle entry * If present and populated with a {@link BundleEntryTransactionMethodEnum}, contains the "bundle entry transaction operation", which is the value of the status field in the Bundle entry
* containing this resource. The value for this key corresponds to field <code>Bundle.entry.transaction.operation</code>. This value can be set in resources being transmitted to a server to * containing this resource. The value for this key corresponds to field <code>Bundle.entry.transaction.operation</code>. This value can be set in resources being transmitted to a server to

View File

@ -50,6 +50,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@ -578,42 +579,53 @@ public class BundleUtil {
BaseRuntimeElementCompositeDefinition<?> searchChildContentsDef = BaseRuntimeElementCompositeDefinition<?> searchChildContentsDef =
(BaseRuntimeElementCompositeDefinition<?>) searchChildDef.getChildByName("search"); (BaseRuntimeElementCompositeDefinition<?>) searchChildDef.getChildByName("search");
BaseRuntimeChildDefinition searchModeChildDef = searchChildContentsDef.getChildByName("mode"); BaseRuntimeChildDefinition searchModeChildDef = searchChildContentsDef.getChildByName("mode");
BaseRuntimeChildDefinition searchScoreChildDef = searchChildContentsDef.getChildByName("score");
List<SearchBundleEntryParts> retVal = new ArrayList<>(); List<SearchBundleEntryParts> retVal = new ArrayList<>();
for (IBase nextEntry : entries) { for (IBase nextEntry : entries) {
SearchBundleEntryParts parts = getSearchBundleEntryParts( SearchBundleEntryParts parts = getSearchBundleEntryParts(
fullUrlChildDef, resourceChildDef, searchChildDef, searchModeChildDef, nextEntry); fullUrlChildDef,
resourceChildDef,
searchChildDef,
searchModeChildDef,
searchScoreChildDef,
nextEntry);
retVal.add(parts); retVal.add(parts);
} }
return retVal; return retVal;
} }
private static SearchBundleEntryParts getSearchBundleEntryParts( private static SearchBundleEntryParts getSearchBundleEntryParts(
BaseRuntimeChildDefinition fullUrlChildDef, BaseRuntimeChildDefinition theFullUrlChildDef,
BaseRuntimeChildDefinition resourceChildDef, BaseRuntimeChildDefinition theResourceChildDef,
BaseRuntimeChildDefinition searchChildDef, BaseRuntimeChildDefinition theSearchChildDef,
BaseRuntimeChildDefinition searchModeChildDef, BaseRuntimeChildDefinition theSearchModeChildDef,
BaseRuntimeChildDefinition theSearchScoreChildDef,
IBase entry) { IBase entry) {
IBaseResource resource = null; IBaseResource resource = null;
String matchMode = null; String matchMode = null;
BigDecimal searchScore = null;
String fullUrl = fullUrlChildDef String fullUrl = theFullUrlChildDef
.getAccessor() .getAccessor()
.getFirstValueOrNull(entry) .getFirstValueOrNull(entry)
.map(t -> ((IPrimitiveType<?>) t).getValueAsString()) .map(t -> ((IPrimitiveType<?>) t).getValueAsString())
.orElse(null); .orElse(null);
for (IBase nextResource : resourceChildDef.getAccessor().getValues(entry)) { for (IBase nextResource : theResourceChildDef.getAccessor().getValues(entry)) {
resource = (IBaseResource) nextResource; resource = (IBaseResource) nextResource;
} }
for (IBase nextSearch : searchChildDef.getAccessor().getValues(entry)) { for (IBase nextSearch : theSearchChildDef.getAccessor().getValues(entry)) {
for (IBase nextUrl : searchModeChildDef.getAccessor().getValues(nextSearch)) { for (IBase nextUrl : theSearchModeChildDef.getAccessor().getValues(nextSearch)) {
matchMode = ((IPrimitiveType<?>) nextUrl).getValueAsString(); matchMode = ((IPrimitiveType<?>) nextUrl).getValueAsString();
} }
for (IBase nextUrl : theSearchScoreChildDef.getAccessor().getValues(nextSearch)) {
searchScore = (BigDecimal) ((IPrimitiveType<?>) nextUrl).getValue();
}
} }
return new SearchBundleEntryParts(fullUrl, resource, matchMode); return new SearchBundleEntryParts(fullUrl, resource, matchMode, searchScore);
} }
/** /**

View File

@ -22,15 +22,20 @@ package ca.uhn.fhir.util.bundle;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import java.math.BigDecimal;
public class SearchBundleEntryParts { public class SearchBundleEntryParts {
private final IBaseResource myResource; private final IBaseResource myResource;
private final BundleEntrySearchModeEnum mySearchMode; private final BundleEntrySearchModeEnum mySearchMode;
private final BigDecimal mySearchScore;
private final String myFullUrl; private final String myFullUrl;
public SearchBundleEntryParts(String theFullUrl, IBaseResource theResource, String theSearchMode) { public SearchBundleEntryParts(
String theFullUrl, IBaseResource theResource, String theSearchMode, BigDecimal theSearchScore) {
myFullUrl = theFullUrl; myFullUrl = theFullUrl;
myResource = theResource; myResource = theResource;
mySearchMode = BundleEntrySearchModeEnum.forCode(theSearchMode); mySearchMode = BundleEntrySearchModeEnum.forCode(theSearchMode);
mySearchScore = theSearchScore;
} }
public String getFullUrl() { public String getFullUrl() {
@ -44,4 +49,8 @@ public class SearchBundleEntryParts {
public BundleEntrySearchModeEnum getSearchMode() { public BundleEntrySearchModeEnum getSearchMode() {
return mySearchMode; return mySearchMode;
} }
public BigDecimal getSearchScore() {
return mySearchScore;
}
} }

View File

@ -0,0 +1,5 @@
---
type: add
issue: 6010
jira: SMILE-8214
title: "When populated, the search score field will now be included in the entries of a response Bundle."

View File

@ -43,6 +43,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -148,10 +149,15 @@ public class Dstu2_1BundleFactory implements IVersionSpecificBundleFactory {
} }
} }
// Populate Bundle.entry.search
BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource); BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
if (searchMode != null) { if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); entry.getSearch().getModeElement().setValueAsString(searchMode.getCode());
} }
BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource);
if (searchScore != null) {
entry.getSearch().getScoreElement().setValue(searchScore);
}
} }
/* /*

View File

@ -41,6 +41,7 @@ import jakarta.annotation.Nonnull;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -138,10 +139,15 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
} }
populateBundleEntryFullUrl(next, entry); populateBundleEntryFullUrl(next, entry);
// Populate Bundle.entry.search
BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next); BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
if (searchMode != null) { if (searchMode != null) {
entry.getSearch().getModeElement().setValue(searchMode.getCode()); entry.getSearch().getModeElement().setValue(searchMode.getCode());
} }
BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(next);
if (searchScore != null) {
entry.getSearch().getScoreElement().setValue(searchScore);
}
} }
/* /*

View File

@ -44,6 +44,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -173,6 +174,10 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
if (searchMode != null) { if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); entry.getSearch().getModeElement().setValueAsString(searchMode.getCode());
} }
BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource);
if (searchScore != null) {
entry.getSearch().getScoreElement().setValue(searchScore);
}
} }
/* /*

View File

@ -45,6 +45,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -178,6 +179,10 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
if (searchMode != null) { if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); entry.getSearch().getModeElement().setValueAsString(searchMode.getCode());
} }
BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource);
if (searchScore != null) {
entry.getSearch().getScoreElement().setValue(searchScore);
}
} }
/* /*

View File

@ -45,6 +45,7 @@ import org.hl7.fhir.r4.model.DomainResource;
import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.Resource;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -181,6 +182,10 @@ public class R4BundleFactory implements IVersionSpecificBundleFactory {
if (searchMode != null) { if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); entry.getSearch().getModeElement().setValueAsString(searchMode.getCode());
} }
BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource);
if (searchScore != null) {
entry.getSearch().getScoreElement().setValue(searchScore);
}
} }
/* /*

View File

@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.CsvSource;
import java.math.BigDecimal;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -550,6 +551,55 @@ public class BundleUtilTest {
assertNotNull(searchBundleEntryParts.get(0).getResource()); assertNotNull(searchBundleEntryParts.get(0).getResource());
} }
@Test
public void testConvertingToSearchBundleEntryPartsReturnsScore() {
//Given
String bundleString = """
{
"resourceType": "Bundle",
"id": "bd194b7f-ac1e-429a-a206-ee2c470f23b5",
"type": "searchset",
"total": 1,
"link": [
{
"relation": "self",
"url": "http://localhost:8000/Condition?_count=1"
}
],
"entry": [
{
"fullUrl": "http://localhost:8000/Condition/1626",
"resource": {
"resourceType": "Condition",
"id": "1626",
"identifier": [
{
"system": "urn:hssc:musc:conditionid",
"value": "1064115000.1.5"
}
]
},
"search": {
"mode": "match",
"score": 1
}
}
]
}""";
Bundle bundle = ourCtx.newJsonParser().parseResource(Bundle.class, bundleString);
//When
List<SearchBundleEntryParts> searchBundleEntryParts = BundleUtil.getSearchBundleEntryParts(ourCtx, bundle);
//Then
assertThat(searchBundleEntryParts).hasSize(1);
assertEquals(BundleEntrySearchModeEnum.MATCH, searchBundleEntryParts.get(0).getSearchMode());
assertEquals(new BigDecimal(1), searchBundleEntryParts.get(0).getSearchScore());
assertThat(searchBundleEntryParts.get(0).getFullUrl()).contains("Condition/1626");
assertNotNull(searchBundleEntryParts.get(0).getResource());
}
@Test @Test
public void testTransactionSorterReturnsDeletesInCorrectProcessingOrder() { public void testTransactionSorterReturnsDeletesInCorrectProcessingOrder() {
Bundle b = new Bundle(); Bundle b = new Bundle();

View File

@ -44,6 +44,7 @@ import org.hl7.fhir.r4b.model.DomainResource;
import org.hl7.fhir.r4b.model.IdType; import org.hl7.fhir.r4b.model.IdType;
import org.hl7.fhir.r4b.model.Resource; import org.hl7.fhir.r4b.model.Resource;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -177,6 +178,10 @@ public class R4BBundleFactory implements IVersionSpecificBundleFactory {
if (searchMode != null) { if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); entry.getSearch().getModeElement().setValueAsString(searchMode.getCode());
} }
BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource);
if (searchScore != null) {
entry.getSearch().getScoreElement().setValue(searchScore);
}
} }
/* /*

View File

@ -44,6 +44,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.Resource;
import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -177,6 +178,10 @@ public class R5BundleFactory implements IVersionSpecificBundleFactory {
if (searchMode != null) { if (searchMode != null) {
entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); entry.getSearch().getModeElement().setValueAsString(searchMode.getCode());
} }
BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource);
if (searchScore != null) {
entry.getSearch().getScoreElement().setValue(searchScore);
}
} }
/* /*