From 878c22192efc6683336465480f435b678c5326d4 Mon Sep 17 00:00:00 2001 From: Frank Tao Date: Wed, 18 Nov 2020 22:26:21 -0500 Subject: [PATCH] Added expand filter impl for R5 and dstu3, changed the filter logic --- .../java/ca/uhn/fhir/util/StringUtil.java | 21 +++++++ .../java/ca/uhn/fhir/util/StringUtilTest.java | 12 ++++ .../dstu3/FhirResourceDaoValueSetDstu3.java | 6 +- .../jpa/dao/r4/FhirResourceDaoValueSetR4.java | 10 ++- .../jpa/dao/r5/FhirResourceDaoValueSetR5.java | 6 +- .../ResourceProviderDstu3ValueSetTest.java | 63 +++++++++++++++++++ ...rceProviderR4ValueSetNoVerCSNoVerTest.java | 25 +++++++- .../r5/ResourceProviderR5ValueSetTest.java | 62 ++++++++++++++++++ 8 files changed, 197 insertions(+), 8 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StringUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StringUtil.java index 9222f330cb4..167c2371514 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StringUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StringUtil.java @@ -24,6 +24,7 @@ import java.io.CharArrayWriter; import java.nio.charset.StandardCharsets; import java.text.Normalizer; import java.util.Arrays; +import java.util.StringTokenizer; public class StringUtil { @@ -78,4 +79,24 @@ public class StringUtil { return new String(bytes, StandardCharsets.UTF_8); } + /** + * Check the input string has prefix token + * + * @param theInput the input string + * @param thePrefix the prefix string of a token + * @return Return true if a input string token separated by space start with the prefix + */ + public static boolean isStartsWithIgnoreCase(final String theInput, final String thePrefix) { + + if (theInput == null || thePrefix == null) + return false; + + StringTokenizer tok = new StringTokenizer(theInput); + while (tok.hasMoreTokens()) { + if (org.apache.commons.lang3.StringUtils.startsWithIgnoreCase(tok.nextToken(), thePrefix)) + return true; + } + + return false; + } } diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StringUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StringUtilTest.java index e27a23ab280..3457b2e821b 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StringUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StringUtilTest.java @@ -10,6 +10,8 @@ import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class StringUtilTest { @Test @@ -51,4 +53,14 @@ public class StringUtilTest { assertEquals("a/a", StringUtil.chompCharacter("a/a////", '/')); } + @Test + public void testIsStartsWithIgnoreCase() { + assertFalse(StringUtil.isStartsWithIgnoreCase(null, null)); + assertFalse(StringUtil.isStartsWithIgnoreCase(null, "hei")); + assertFalse(StringUtil.isStartsWithIgnoreCase("Body height", null)); + assertTrue(StringUtil.isStartsWithIgnoreCase("Body height", "he")); + assertTrue(StringUtil.isStartsWithIgnoreCase("Body height", "bo")); + assertFalse(StringUtil.isStartsWithIgnoreCase("Body height", "ei")); + assertFalse(StringUtil.isStartsWithIgnoreCase("Body height", "dy")); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java index 057caa1d8e5..b704e8137c4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoValueSetDstu3.java @@ -33,6 +33,8 @@ import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ElementUtil; +import ca.uhn.fhir.util.StringUtil; + import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IntegerType; @@ -224,7 +226,7 @@ public class FhirResourceDaoValueSetDstu3 extends BaseHapiFhirResourceDao for (int idx = 0; idx < theContains.size(); idx++) { ValueSetExpansionContainsComponent next = theContains.get(idx); - if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) { + + // e.g. For a display text of “Body height”: + // searching on "Bo" or "he" should find it + // searching on "ei" or "dy" should not find it + if (isBlank(next.getDisplay()) || !StringUtil.isStartsWithIgnoreCase(next.getDisplay(), theFilter)) { theContains.remove(idx); idx--; if (theTotalElement.getValue() != null) { @@ -218,7 +224,7 @@ public class FhirResourceDaoValueSetR4 extends BaseHapiFhirResourceDao applyFilter(theTotalElement, next.getContains(), theFilter); } } - + private void addFilterIfPresent(String theFilter, ConceptSetComponent include) { if (ElementUtil.isEmpty(include.getConcept())) { if (isNotBlank(theFilter)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java index 44a7af52312..4828ac1ab95 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoValueSetR5.java @@ -31,6 +31,8 @@ import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ElementUtil; +import ca.uhn.fhir.util.StringUtil; + import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -208,7 +210,7 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao for (int idx = 0; idx < theContains.size(); idx++) { ValueSetExpansionContainsComponent next = theContains.get(idx); - if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) { + if (isBlank(next.getDisplay()) || !StringUtil.isStartsWithIgnoreCase(next.getDisplay(), theFilter)) { theContains.remove(idx); idx--; if (theTotalElement.getValue() != null) { @@ -222,7 +224,7 @@ public class FhirResourceDaoValueSetR5 extends BaseHapiFhirResourceDao private void addFilterIfPresent(String theFilter, ConceptSetComponent include) { if (ElementUtil.isEmpty(include.getConcept())) { if (isNotBlank(theFilter)) { - include.addFilter().setProperty("display").setOp(Enumerations.FilterOperator.EQUAL).setValue(theFilter); + include.addFilter().setProperty("display").setOp(Enumerations.FilterOperator.EXISTS).setValue(theFilter); } } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 3d1daac2ae2..a66cde4c5cd 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -50,6 +50,7 @@ import java.util.List; import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM; import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest.URL_MY_VALUE_SET; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.*; @@ -857,6 +858,68 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 } + @Test + public void testExpandByValueSetWithFilterContainsPrefixValue() throws IOException { + loadAndPersistCodeSystem(); + + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("filter", new StringType("blo")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder("", "")); + } + + @Test + public void testExpandByValueSetWithFilterContainsNoPrefixValue() throws IOException { + loadAndPersistCodeSystem(); + + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("filter", new StringType("lood")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, not(stringContainsInOrder("",""))); + } + + @Test + public void testExpandByValueSetWithFilterNotContainsAnyValue() throws IOException { + loadAndPersistCodeSystem(); + + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = ourClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("filter", new StringType("loood")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, not(stringContainsInOrder("",""))); + } + @Test public void testExpandByUrlWithFilter() throws Exception { loadAndPersistCodeSystemAndValueSet(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java index dd1002c81d8..acdbadb4704 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java @@ -1195,7 +1195,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv } @Test - public void testExpandByValueSetWithFilterContainsPartialValue() throws IOException { + public void testExpandByValueSetWithFilterContainsPrefixValue() throws IOException { loadAndPersistCodeSystem(); ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); @@ -1205,7 +1205,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv .onType(ValueSet.class) .named("expand") .withParameter(Parameters.class, "valueSet", toExpand) - .andParameter("filter", new StringType("loo")) + .andParameter("filter", new StringType("blo")) .execute(); ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); @@ -1214,6 +1214,27 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv assertThat(resp, stringContainsInOrder("", "")); } + @Test + public void testExpandByValueSetWithFilterContainsNoPrefixValue() throws IOException { + loadAndPersistCodeSystem(); + + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("filter", new StringType("lood")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, not(stringContainsInOrder("",""))); + } + @Test public void testExpandByValueSetWithFilterNotContainsAnyValue() throws IOException { loadAndPersistCodeSystem(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java index 9cdf0cbab6c..a6c1b7e3eab 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java @@ -1440,6 +1440,68 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { } + @Test + public void testExpandByValueSetWithFilterContainsPrefixValue() throws IOException { + loadAndPersistCodeSystem(HTTPVerb.POST); + + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("filter", new StringType("blo")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + assertThat(resp, stringContainsInOrder("", "")); + } + + @Test + public void testExpandByValueSetWithFilterContainsNoPrefixValue() throws IOException { + loadAndPersistCodeSystem(HTTPVerb.POST); + + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("filter", new StringType("lood")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, not(stringContainsInOrder("",""))); + } + + @Test + public void testExpandByValueSetWithFilterNotContainsAnyValue() throws IOException { + loadAndPersistCodeSystem(HTTPVerb.POST); + + ValueSet toExpand = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); + + Parameters respParam = myClient + .operation() + .onType(ValueSet.class) + .named("expand") + .withParameter(Parameters.class, "valueSet", toExpand) + .andParameter("filter", new StringType("loood")) + .execute(); + ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded); + ourLog.info(resp); + + assertThat(resp, not(stringContainsInOrder("",""))); + } + @Test public void testExpandByUrlWithFilter() throws Exception { loadAndPersistCodeSystemAndValueSet(HTTPVerb.POST);