From 11d61a5f72a1e4b0ae5e726eb35c2f945368edf4 Mon Sep 17 00:00:00 2001 From: jdar8 <69840459+jdar8@users.noreply.github.com> Date: Mon, 24 Jun 2024 08:56:00 -0700 Subject: [PATCH] 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 --- .../model/api/ResourceMetadataKeyEnum.java | 10 ++++ .../java/ca/uhn/fhir/util/BundleUtil.java | 32 ++++++++---- .../util/bundle/SearchBundleEntryParts.java | 11 +++- .../6010-add-support-bundle-search-score.yaml | 5 ++ .../rest/server/Dstu2_1BundleFactory.java | 6 +++ .../provider/dstu2/Dstu2BundleFactory.java | 6 +++ .../hapi/rest/server/Dstu3BundleFactory.java | 5 ++ .../dstu2hl7org/Dstu2Hl7OrgBundleFactory.java | 5 ++ .../r4/hapi/rest/server/R4BundleFactory.java | 5 ++ .../uhn/fhir/util/bundle/BundleUtilTest.java | 50 +++++++++++++++++++ .../hapi/rest/server/R4BBundleFactory.java | 5 ++ .../r5/hapi/rest/server/R5BundleFactory.java | 5 ++ 12 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6010-add-support-bundle-search-score.yaml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java index 68aa3a4279c..977a0cdf258 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java @@ -31,6 +31,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.io.Serializable; +import java.math.BigDecimal; import java.util.Date; import java.util.List; @@ -80,6 +81,15 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { */ public static final ResourceMetadataKeyEnum ENTRY_SEARCH_MODE = 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 Bundle.entry.search.score. This value represents the search ranking score, where 1.0 is relevant and 0.0 is irrelevant. + *

+ * Note that status is only used in FHIR DSTU2 and later. + *

+ */ + public static final ResourceMetadataKeyEnum 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 * containing this resource. The value for this key corresponds to field Bundle.entry.transaction.operation. This value can be set in resources being transmitted to a server to diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java index 2317feafc88..6801a25bfd6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java @@ -50,6 +50,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashSet; @@ -578,42 +579,53 @@ public class BundleUtil { BaseRuntimeElementCompositeDefinition searchChildContentsDef = (BaseRuntimeElementCompositeDefinition) searchChildDef.getChildByName("search"); BaseRuntimeChildDefinition searchModeChildDef = searchChildContentsDef.getChildByName("mode"); + BaseRuntimeChildDefinition searchScoreChildDef = searchChildContentsDef.getChildByName("score"); List retVal = new ArrayList<>(); for (IBase nextEntry : entries) { SearchBundleEntryParts parts = getSearchBundleEntryParts( - fullUrlChildDef, resourceChildDef, searchChildDef, searchModeChildDef, nextEntry); + fullUrlChildDef, + resourceChildDef, + searchChildDef, + searchModeChildDef, + searchScoreChildDef, + nextEntry); retVal.add(parts); } return retVal; } private static SearchBundleEntryParts getSearchBundleEntryParts( - BaseRuntimeChildDefinition fullUrlChildDef, - BaseRuntimeChildDefinition resourceChildDef, - BaseRuntimeChildDefinition searchChildDef, - BaseRuntimeChildDefinition searchModeChildDef, + BaseRuntimeChildDefinition theFullUrlChildDef, + BaseRuntimeChildDefinition theResourceChildDef, + BaseRuntimeChildDefinition theSearchChildDef, + BaseRuntimeChildDefinition theSearchModeChildDef, + BaseRuntimeChildDefinition theSearchScoreChildDef, IBase entry) { IBaseResource resource = null; String matchMode = null; + BigDecimal searchScore = null; - String fullUrl = fullUrlChildDef + String fullUrl = theFullUrlChildDef .getAccessor() .getFirstValueOrNull(entry) .map(t -> ((IPrimitiveType) t).getValueAsString()) .orElse(null); - for (IBase nextResource : resourceChildDef.getAccessor().getValues(entry)) { + for (IBase nextResource : theResourceChildDef.getAccessor().getValues(entry)) { resource = (IBaseResource) nextResource; } - for (IBase nextSearch : searchChildDef.getAccessor().getValues(entry)) { - for (IBase nextUrl : searchModeChildDef.getAccessor().getValues(nextSearch)) { + for (IBase nextSearch : theSearchChildDef.getAccessor().getValues(entry)) { + for (IBase nextUrl : theSearchModeChildDef.getAccessor().getValues(nextSearch)) { 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); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/bundle/SearchBundleEntryParts.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/bundle/SearchBundleEntryParts.java index 2f38aaa1597..7687958be81 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/bundle/SearchBundleEntryParts.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/bundle/SearchBundleEntryParts.java @@ -22,15 +22,20 @@ package ca.uhn.fhir.util.bundle; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import org.hl7.fhir.instance.model.api.IBaseResource; +import java.math.BigDecimal; + public class SearchBundleEntryParts { private final IBaseResource myResource; private final BundleEntrySearchModeEnum mySearchMode; + private final BigDecimal mySearchScore; private final String myFullUrl; - public SearchBundleEntryParts(String theFullUrl, IBaseResource theResource, String theSearchMode) { + public SearchBundleEntryParts( + String theFullUrl, IBaseResource theResource, String theSearchMode, BigDecimal theSearchScore) { myFullUrl = theFullUrl; myResource = theResource; mySearchMode = BundleEntrySearchModeEnum.forCode(theSearchMode); + mySearchScore = theSearchScore; } public String getFullUrl() { @@ -44,4 +49,8 @@ public class SearchBundleEntryParts { public BundleEntrySearchModeEnum getSearchMode() { return mySearchMode; } + + public BigDecimal getSearchScore() { + return mySearchScore; + } } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6010-add-support-bundle-search-score.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6010-add-support-bundle-search-score.yaml new file mode 100644 index 00000000000..212c77a9e48 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6010-add-support-bundle-search-score.yaml @@ -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." diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java index 8eaf848f8f0..1d37f9a53d3 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java @@ -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.IPrimitiveType; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; 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); if (searchMode != null) { entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); } + BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource); + if (searchScore != null) { + entry.getSearch().getScoreElement().setValue(searchScore); + } } /* diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java index b78b8d7f3c5..30bfb849b87 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java @@ -41,6 +41,7 @@ import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -138,10 +139,15 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { } populateBundleEntryFullUrl(next, entry); + // Populate Bundle.entry.search BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next); if (searchMode != null) { entry.getSearch().getModeElement().setValue(searchMode.getCode()); } + BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(next); + if (searchScore != null) { + entry.getSearch().getScoreElement().setValue(searchScore); + } } /* diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java index af435eefe5f..49578c7c5db 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java @@ -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.IPrimitiveType; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -173,6 +174,10 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory { if (searchMode != null) { entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); } + BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource); + if (searchScore != null) { + entry.getSearch().getScoreElement().setValue(searchScore); + } } /* diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java index 90b50bea114..0deb4a5c6ce 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java @@ -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.IPrimitiveType; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -178,6 +179,10 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory { if (searchMode != null) { entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); } + BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource); + if (searchScore != null) { + entry.getSearch().getScoreElement().setValue(searchScore); + } } /* diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java index 73cd64314b1..d279f87cd44 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java @@ -45,6 +45,7 @@ import org.hl7.fhir.r4.model.DomainResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Resource; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -181,6 +182,10 @@ public class R4BundleFactory implements IVersionSpecificBundleFactory { if (searchMode != null) { entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); } + BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource); + if (searchScore != null) { + entry.getSearch().getScoreElement().setValue(searchScore); + } } /* diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java index 2fb32b59b3f..f349ffa457d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/bundle/BundleUtilTest.java @@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import java.math.BigDecimal; import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -550,6 +551,55 @@ public class BundleUtilTest { 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 = 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 public void testTransactionSorterReturnsDeletesInCorrectProcessingOrder() { Bundle b = new Bundle(); diff --git a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/rest/server/R4BBundleFactory.java b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/rest/server/R4BBundleFactory.java index 01933a7e756..b9f912450d1 100644 --- a/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/rest/server/R4BBundleFactory.java +++ b/hapi-fhir-structures-r4b/src/main/java/org/hl7/fhir/r4b/hapi/rest/server/R4BBundleFactory.java @@ -44,6 +44,7 @@ import org.hl7.fhir.r4b.model.DomainResource; import org.hl7.fhir.r4b.model.IdType; import org.hl7.fhir.r4b.model.Resource; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -177,6 +178,10 @@ public class R4BBundleFactory implements IVersionSpecificBundleFactory { if (searchMode != null) { entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); } + BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource); + if (searchScore != null) { + entry.getSearch().getScoreElement().setValue(searchScore); + } } /* diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java index 8b974912923..aae865bd163 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/R5BundleFactory.java @@ -44,6 +44,7 @@ import org.hl7.fhir.r5.model.DomainResource; import org.hl7.fhir.r5.model.IdType; import org.hl7.fhir.r5.model.Resource; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; @@ -177,6 +178,10 @@ public class R5BundleFactory implements IVersionSpecificBundleFactory { if (searchMode != null) { entry.getSearch().getModeElement().setValueAsString(searchMode.getCode()); } + BigDecimal searchScore = ResourceMetadataKeyEnum.ENTRY_SEARCH_SCORE.get(nextAsResource); + if (searchScore != null) { + entry.getSearch().getScoreElement().setValue(searchScore); + } } /*