From 24acb57f3b70e5abf3ee429f6a5d003ec5307bf8 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 2 Mar 2020 17:56:25 -0500 Subject: [PATCH 01/10] began with failing test. test now passes. --- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 47 ++-------- .../fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java | 3 + .../FhirResourceDaoDstu3ContainedTest.java | 17 ++-- .../dstu3/ResourceProviderDstu3Test.java | 86 +++++++++++++++++++ .../jpa/searchparam/MatchUrlServiceTest.java | 8 +- .../jpa/searchparam/SearchParameterMap.java | 79 +++++++++++++---- 6 files changed, 173 insertions(+), 67 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index e120a964a8d..348c1ed2c26 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -29,29 +29,17 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao; import ca.uhn.fhir.jpa.dao.data.IResourceTagDao; import ca.uhn.fhir.jpa.dao.index.IdHelperService; -import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilder; -import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilderFactory; -import ca.uhn.fhir.jpa.dao.predicate.QueryRoot; -import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinEnum; -import ca.uhn.fhir.jpa.dao.predicate.SearchBuilderJoinKey; +import ca.uhn.fhir.jpa.dao.predicate.*; import ca.uhn.fhir.jpa.entity.ResourceSearchView; import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails; import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; -import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique; -import ca.uhn.fhir.jpa.model.entity.ResourceLink; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.model.entity.ResourceTag; +import ca.uhn.fhir.jpa.model.entity.*; import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails; import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; -import ca.uhn.fhir.jpa.util.BaseIterator; -import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; -import ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster; -import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; -import ca.uhn.fhir.jpa.util.SqlQueryList; +import ca.uhn.fhir.jpa.util.*; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; @@ -77,7 +65,6 @@ import org.apache.commons.lang3.Validate; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.query.Query; -import org.hl7.fhir.dstu3.model.Location; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -92,27 +79,10 @@ import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; +import javax.persistence.criteria.*; +import java.util.*; -import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.apache.commons.lang3.StringUtils.isBlank; -import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.*; /** * The SearchBuilder is responsible for actually forming the SQL query that handles @@ -189,9 +159,8 @@ public class SearchBuilder implements ISearchBuilder { // Remove any empty parameters theParams.clean(); - if (myResourceType == Location.class) { - theParams.setLocationDistance(); - } + // Pull out near-distance first so when it comes time to evaluate near, we already know the distance + theParams.setNearDistance(myResourceType); /* * Check if there is a unique key associated with the set diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java index bdc3ad3a2d7..703e04d21fc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3Test.java @@ -148,6 +148,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest { @Qualifier("myLocationDaoDstu3") protected IFhirResourceDao myLocationDao; @Autowired + @Qualifier("myPractitionerRoleDaoDstu3") + protected IFhirResourceDao myPractitionerRoleDao; + @Autowired @Qualifier("myMediaDaoDstu3") protected IFhirResourceDao myMediaDao; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ContainedTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ContainedTest.java index 941900ae49d..c24c0825d8a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ContainedTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ContainedTest.java @@ -1,5 +1,9 @@ package ca.uhn.fhir.jpa.dao.dstu3; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.TokenParamModifier; +import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Reference; @@ -7,8 +11,8 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; import org.junit.Test; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.util.TestUtil; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder; public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ContainedTest.class); @@ -47,12 +51,9 @@ public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test { ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(o2)); - SearchParameterMap map; - -// map = new SearchParameterMap(); -// map.add(Observation.SP_CODE, new TokenParam(null, "some observation").setModifier(TokenParamModifier.TEXT)); -// assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2))); - + SearchParameterMap map = new SearchParameterMap(); + map.add(Observation.SP_CODE, new TokenParam(null, "some observation").setModifier(TokenParamModifier.TEXT)); + assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(oid1, oid2))); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index 45edc4928c7..72f5af8299d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -4280,6 +4280,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } + // FIXME KHS move distance tests to distance test class @Test public void testNearSearchApproximate() { Location loc = new Location(); @@ -4329,6 +4330,91 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } + + @Test + public void testNearSearchDistanceNoDistanceChained() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_CHIN; + double longitude = CoordCalculatorTest.LONGITUDE_CHIN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + IIdType locId = ourClient.create().resource(loc).execute().getId().toUnqualifiedVersionless(); + + PractitionerRole pr = new PractitionerRole(); + pr.addLocation().setReference(locId.getValue()); + IIdType prId = ourClient.create().resource(pr).execute().getId().toUnqualifiedVersionless(); + + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + latitude + URLEncoder.encode(":") + longitude; + + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(prId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + + @Test + public void testNearSearchApproximateChained() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_UHN; + double longitude = CoordCalculatorTest.LONGITUDE_UHN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + myCaptureQueriesListener.clear(); + IIdType locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless(); + myCaptureQueriesListener.logInsertQueries(); + + PractitionerRole pr = new PractitionerRole(); + pr.addLocation().setReference(locId.getValue()); + IIdType prId = myPractitionerRoleDao.create(pr).getId().toUnqualifiedVersionless(); + + { // In the box + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + "location." + Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(prId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + { // Outside the box + double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + "location." + Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(0, actual.getEntry().size()); + } + } + private String toStr(Date theDate) { return new InstantDt(theDate).getValueAsString(); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java index e79e49fe76e..42f9925406c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java @@ -58,7 +58,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { Location.SP_NEAR + "=1000.0:2000.0" + "&" + Location.SP_NEAR_DISTANCE + "=" + kmDistance + "|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - map.setLocationDistance(); + map.setNearDistance(Location.class); QuantityParam nearDistanceParam = map.getNearDistanceParam(); assertEquals(1, map.size()); @@ -75,7 +75,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { "&" + Location.SP_NEAR_DISTANCE + "=2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - map.setLocationDistance(); + map.setNearDistance(Location.class); fail(); } catch (IllegalArgumentException e) { @@ -92,7 +92,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { "," + "2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - map.setLocationDistance(); + map.setNearDistance(Location.class); fail(); } catch (IllegalArgumentException e) { @@ -100,6 +100,8 @@ public class MatchUrlServiceTest extends BaseJpaTest { } } + // FIXME KHS add chaining test + @Override protected FhirContext getContext() { return ourCtx; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java index 958e58c2f1c..ebbc64f7b8d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.StringUtils; @@ -16,6 +17,7 @@ import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.dstu3.model.Location; +import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.Serializable; import java.util.*; @@ -505,30 +507,73 @@ public class SearchParameterMap implements Serializable { return myNearDistanceParam; } - public void setLocationDistance() { - if (containsKey(Location.SP_NEAR_DISTANCE)) { + // FIXME KHS extract to helper class + public void setNearDistance(Class theResourceType) { + if (theResourceType == Location.class && containsKey(Location.SP_NEAR_DISTANCE)) { List> paramAndList = get(Location.SP_NEAR_DISTANCE); - - if (paramAndList.isEmpty()) { - return; - } - if (paramAndList.size() > 1) { - throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); - } - List paramOrList = paramAndList.get(0); - if (paramOrList.isEmpty()) { - return; - } - if (paramOrList.size() > 1) { - throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); - } - setNearDistanceParam((QuantityParam) paramOrList.get(0)); + QuantityParam quantityParam = getNearDistanceParam(paramAndList); + setNearDistanceParam(quantityParam); // Need to remove near-distance or it we'll get a hashcode predicate for it remove(Location.SP_NEAR_DISTANCE); + } else if (containsKey("location")) { + List> paramAndList = get("location"); + ReferenceParam referenceParam = getChainedLocationNearDistanceParam(paramAndList); + if (referenceParam != null) { + QuantityParam quantityParam = new QuantityParam(referenceParam.getValue()); + setNearDistanceParam(quantityParam); + } } } + private ReferenceParam getChainedLocationNearDistanceParam(List> theParamAndList) { + ReferenceParam retval = null; + List andParamToRemove = null; + for (List paramOrList : theParamAndList) { + IQueryParameterType orParamToRemove = null; + for (IQueryParameterType param : paramOrList) { + if (param instanceof ReferenceParam) { + ReferenceParam referenceParam = (ReferenceParam) param; + if (Location.SP_NEAR_DISTANCE.equals(referenceParam.getChain())) { + if (retval != null) { + throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + } else { + retval = referenceParam; + orParamToRemove = param; + } + } + } + } + if (orParamToRemove != null) { + paramOrList.remove(orParamToRemove); + if (paramOrList.isEmpty()) { + andParamToRemove = paramOrList; + } + } + } + if (andParamToRemove != null) { + theParamAndList.remove(andParamToRemove); + } + return retval; + } + + private QuantityParam getNearDistanceParam(List> theParamAndList) { + if (theParamAndList.isEmpty()) { + return null; + } + if (theParamAndList.size() > 1) { + throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + } + List paramOrList = theParamAndList.get(0); + if (paramOrList.isEmpty()) { + return null; + } + if (paramOrList.size() > 1) { + throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + } + return (QuantityParam) paramOrList.get(0); + } + public enum EverythingModeEnum { /* * Don't reorder! We rely on the ordinals From 27dcafe4f247fd1c43c5078168d5252b6bc6c2c0 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 2 Mar 2020 21:27:50 -0500 Subject: [PATCH 02/10] all but unit tests --- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 3 +- ...hirResourceDaoDstu3SearchDistanceTest.java | 134 +++++++++++++++ .../FhirResourceDaoDstu3SearchNoFtTest.java | 113 ------------- .../FhirResourceDaoR4SearchDistanceTest.java | 140 ++++++++++++++++ .../r4/FhirResourceDaoR4SearchNoFtTest.java | 155 +----------------- .../ResourceProviderDstu3DistanceTest.java | 153 +++++++++++++++++ .../dstu3/ResourceProviderDstu3Test.java | 137 ---------------- .../jpa/searchparam/MatchUrlServiceTest.java | 3 - .../jpa/searchparam/SearchParameterMap.java | 70 -------- .../jpa/searchparam/util/DistanceHelper.java | 79 +++++++++ 10 files changed, 513 insertions(+), 474 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchDistanceTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchDistanceTest.java create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3DistanceTest.java create mode 100644 hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 348c1ed2c26..69a1418971c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -39,6 +39,7 @@ import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; +import ca.uhn.fhir.jpa.searchparam.util.DistanceHelper; import ca.uhn.fhir.jpa.util.*; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IResource; @@ -160,7 +161,7 @@ public class SearchBuilder implements ISearchBuilder { theParams.clean(); // Pull out near-distance first so when it comes time to evaluate near, we already know the distance - theParams.setNearDistance(myResourceType); + DistanceHelper.setNearDistance(myResourceType, theParams); /* * Check if there is a unique key associated with the set diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchDistanceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchDistanceTest.java new file mode 100644 index 00000000000..5867db54e93 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchDistanceTest.java @@ -0,0 +1,134 @@ +package ca.uhn.fhir.jpa.dao.dstu3; + +import ca.uhn.fhir.jpa.searchparam.MatchUrlService; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.util.CoordCalculatorTest; +import ca.uhn.fhir.rest.param.TokenParam; +import org.hl7.fhir.dstu3.model.Location; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.InvalidDataAccessApiUsageException; + +import java.util.List; + +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.*; + +public class FhirResourceDaoDstu3SearchDistanceTest extends BaseJpaDstu3Test { + @Autowired + MatchUrlService myMatchUrlService; + + @Before + public void beforeDisableResultReuse() { + myDaoConfig.setReuseCachedSearchResultsForMillis(null); + } + + @Test + public void testNearSearchDistanceNoDistance() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_CHIN; + double longitude = CoordCalculatorTest.LONGITUDE_CHIN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); + + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + latitude + ":" + longitude, + myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids, contains(locId)); + } + + @Test + public void testNearSearchDistanceZero() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_CHIN; + double longitude = CoordCalculatorTest.LONGITUDE_CHIN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); + + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + latitude + ":" + longitude + + "&" + + Location.SP_NEAR_DISTANCE + "=0||", + myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids, contains(locId)); + } + + @Test + public void testNearSearchApproximate() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_UHN; + double longitude = CoordCalculatorTest.LONGITUDE_UHN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); + + { // In the box + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + ":" + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + "|http://unitsofmeasure.org|km", myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids, contains(locId)); + } + { // Outside the box + double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; + + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + ":" + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + "|http://unitsofmeasure.org|km", myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids.size(), is(0)); + } + + } + + @Test + public void testBadCoordsFormat() { + assertInvalidNearFormat("1:2:3"); + assertInvalidNearFormat("1:"); + assertInvalidNearFormat(":"); + assertInvalidNearFormat(""); + } + + private void assertInvalidNearFormat(String theCoords) { + SearchParameterMap map = new SearchParameterMap(); + map.add(Location.SP_NEAR, new TokenParam(theCoords)); + map.setLoadSynchronous(true); + try { + myLocationDao.search(map); + fail(); + } catch (InvalidDataAccessApiUsageException e) { + assertEquals("Invalid position format '" + theCoords + "'. Required format is 'latitude:longitude'", e.getCause().getMessage()); + } + } + + @Test + public void testNearMissingLat() { + SearchParameterMap map = new SearchParameterMap(); + map.add(Location.SP_NEAR, new TokenParam(":2")); + map.setLoadSynchronous(true); + try { + myLocationDao.search(map); + fail(); + } catch (InvalidDataAccessApiUsageException e) { + assertEquals("Invalid position format ':2'. Both latitude and longitude must be provided.", e.getCause().getMessage()); + } + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java index 461e75c5806..7a1224f98fd 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java @@ -2,11 +2,9 @@ package ca.uhn.fhir.jpa.dao.dstu3; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.model.entity.*; -import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParamConstants; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; -import ca.uhn.fhir.jpa.util.CoordCalculatorTest; import ca.uhn.fhir.jpa.util.TestUtil; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; @@ -36,8 +34,6 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; @@ -59,9 +55,6 @@ import static org.mockito.Mockito.mock; public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3SearchNoFtTest.class); - @Autowired - MatchUrlService myMatchUrlService; - @Before public void beforeDisableResultReuse() { myDaoConfig.setReuseCachedSearchResultsForMillis(null); @@ -3473,112 +3466,6 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { assertThat(ids.toString(), ids, contains("Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB")); } - @Test - public void testNearSearchDistanceNoDistance() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_CHIN; - double longitude = CoordCalculatorTest.LONGITUDE_CHIN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); - - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + latitude + ":" + longitude, - myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids, contains(locId)); - } - - @Test - public void testNearSearchDistanceZero() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_CHIN; - double longitude = CoordCalculatorTest.LONGITUDE_CHIN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); - - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + latitude + ":" + longitude + - "&" + - Location.SP_NEAR_DISTANCE + "=0||", - myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids, contains(locId)); - } - - @Test - public void testNearSearchApproximate() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_UHN; - double longitude = CoordCalculatorTest.LONGITUDE_UHN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); - - { // In the box - double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + ":" + CoordCalculatorTest.LONGITUDE_CHIN + - "&" + - Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + "|http://unitsofmeasure.org|km", myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids, contains(locId)); - } - { // Outside the box - double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; - - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + ":" + CoordCalculatorTest.LONGITUDE_CHIN + - "&" + - Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + "|http://unitsofmeasure.org|km", myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids.size(), is(0)); - } - - } - - @Test - public void testBadCoordsFormat() { - assertInvalidNearFormat("1:2:3"); - assertInvalidNearFormat("1:"); - assertInvalidNearFormat(":"); - assertInvalidNearFormat(""); - } - - private void assertInvalidNearFormat(String theCoords) { - SearchParameterMap map = new SearchParameterMap(); - map.add(Location.SP_NEAR, new TokenParam(theCoords)); - map.setLoadSynchronous(true); - try { - myLocationDao.search(map); - fail(); - } catch (InvalidDataAccessApiUsageException e) { - assertEquals("Invalid position format '" + theCoords + "'. Required format is 'latitude:longitude'", e.getCause().getMessage()); - } - } - - @Test - public void testNearMissingLat() { - SearchParameterMap map = new SearchParameterMap(); - map.add(Location.SP_NEAR, new TokenParam(":2")); - map.setLoadSynchronous(true); - try { - myLocationDao.search(map); - fail(); - } catch (InvalidDataAccessApiUsageException e) { - assertEquals("Invalid position format ':2'. Both latitude and longitude must be provided.", e.getCause().getMessage()); - } - } - private String toStringMultiline(List theResults) { StringBuilder b = new StringBuilder(); for (Object next : theResults) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchDistanceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchDistanceTest.java new file mode 100644 index 00000000000..3401fc3fbef --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchDistanceTest.java @@ -0,0 +1,140 @@ +package ca.uhn.fhir.jpa.dao.r4; + +import ca.uhn.fhir.jpa.searchparam.MatchUrlService; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.util.CoordCalculatorTest; +import org.hl7.fhir.r4.model.Location; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.contains; +import static org.junit.Assert.assertThat; + +public class FhirResourceDaoR4SearchDistanceTest extends BaseJpaR4Test { + @Before + public void beforeDisableResultReuse() { + myDaoConfig.setReuseCachedSearchResultsForMillis(null); + } + + @Autowired + MatchUrlService myMatchUrlService; + + @Test + public void testNearSearchDistanceNoDistance() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_CHIN; + double longitude = CoordCalculatorTest.LATITUDE_CHIN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); + + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + latitude + "|" + longitude, + myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids, contains(locId)); + } + + @Test + public void testNearSearchDistanceZero() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_CHIN; + double longitude = CoordCalculatorTest.LATITUDE_CHIN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); + { + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + latitude + "|" + longitude + "|0", + myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids, contains(locId)); + } + { + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + latitude + "|" + longitude + "|0.0", + myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids, contains(locId)); + } + } + + @Test + public void testNearSearchApproximate() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_UHN; + double longitude = CoordCalculatorTest.LONGITUDE_UHN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); + + { // In the box + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" + + CoordCalculatorTest.LONGITUDE_CHIN + "|" + + bigEnoughDistance, myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids, contains(locId)); + } + { // Outside the box + double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; + + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" + + CoordCalculatorTest.LONGITUDE_CHIN + "|" + + tooSmallDistance, myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids.size(), is(0)); + } + + } + + @Test + public void testNearSearchApproximateNearAntiMeridian() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_TAVEUNI; + double longitude = CoordCalculatorTest.LONGITIDE_TAVEUNI; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); + + { // We match even when the box crosses the anti-meridian + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_TAVEUNI; + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_TAVEUNI + "|" + + CoordCalculatorTest.LONGITIDE_TAVEUNI + "|" + + bigEnoughDistance, myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids, contains(locId)); + } + { // We don't match outside a box that crosses the anti-meridian + double tooSmallDistance = CoordCalculatorTest.DISTANCE_TAVEUNI; + SearchParameterMap map = myMatchUrlService.translateMatchUrl( + "Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" + + CoordCalculatorTest.LONGITUDE_CHIN + "|" + + tooSmallDistance, myFhirCtx.getResourceDefinition("Location")); + + List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); + assertThat(ids.size(), is(0)); + } + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index 54973d1d502..db077f8bfd7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -3,18 +3,10 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.entity.Search; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri; -import ca.uhn.fhir.jpa.model.entity.ResourceLink; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.model.entity.*; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum; -import ca.uhn.fhir.jpa.util.CoordCalculatorTest; import ca.uhn.fhir.jpa.util.TestUtil; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; @@ -40,11 +32,7 @@ import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender; import org.hl7.fhir.r4.model.Observation.ObservationStatus; import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType; import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; @@ -55,30 +43,11 @@ import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; import java.util.stream.Collectors; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasItems; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; import static org.mockito.Mockito.mock; @SuppressWarnings({"unchecked", "Duplicates"}) @@ -4264,120 +4233,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertThat(toUnqualifiedVersionlessIdValues(outcome), contains(crId)); } - @Test - public void testNearSearchDistanceNoDistance() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_CHIN; - double longitude = CoordCalculatorTest.LATITUDE_CHIN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); - - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + latitude + "|" + longitude, - myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids, contains(locId)); - } - - @Test - public void testNearSearchDistanceZero() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_CHIN; - double longitude = CoordCalculatorTest.LATITUDE_CHIN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); - { - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + latitude + "|" + longitude + "|0", - myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids, contains(locId)); - } - { - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + latitude + "|" + longitude + "|0.0", - myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids, contains(locId)); - } - } - - @Test - public void testNearSearchApproximate() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_UHN; - double longitude = CoordCalculatorTest.LONGITUDE_UHN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); - - { // In the box - double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" - + CoordCalculatorTest.LONGITUDE_CHIN + "|" + - bigEnoughDistance, myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids, contains(locId)); - } - { // Outside the box - double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; - - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" - + CoordCalculatorTest.LONGITUDE_CHIN + "|" + - tooSmallDistance, myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids.size(), is(0)); - } - - } - - @Test - public void testNearSearchApproximateNearAntiMeridian() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_TAVEUNI; - double longitude = CoordCalculatorTest.LONGITIDE_TAVEUNI; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - String locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless().getValue(); - - { // We match even when the box crosses the anti-meridian - double bigEnoughDistance = CoordCalculatorTest.DISTANCE_TAVEUNI; - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_TAVEUNI + "|" - + CoordCalculatorTest.LONGITIDE_TAVEUNI + "|" + - bigEnoughDistance, myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids, contains(locId)); - } - { // We don't match outside a box that crosses the anti-meridian - double tooSmallDistance = CoordCalculatorTest.DISTANCE_TAVEUNI; - SearchParameterMap map = myMatchUrlService.translateMatchUrl( - "Location?" + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" - + CoordCalculatorTest.LONGITUDE_CHIN + "|" + - tooSmallDistance, myFhirCtx.getResourceDefinition("Location")); - - List ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map)); - assertThat(ids.size(), is(0)); - } - } - @Test public void testCircularReferencesDontBreakRevIncludes() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3DistanceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3DistanceTest.java new file mode 100644 index 00000000000..75f492e45da --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3DistanceTest.java @@ -0,0 +1,153 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +import ca.uhn.fhir.jpa.util.CoordCalculatorTest; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Location; +import org.hl7.fhir.dstu3.model.PractitionerRole; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.Test; + +import java.net.URLEncoder; + +import static org.junit.Assert.assertEquals; + +public class ResourceProviderDstu3DistanceTest extends BaseResourceProviderDstu3Test { + + @Override + public void before() throws Exception { + super.before(); + myDaoConfig.setReuseCachedSearchResultsForMillis(null); + } + + @Test + public void testNearSearchApproximate() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_UHN; + double longitude = CoordCalculatorTest.LONGITUDE_UHN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + IIdType locId = ourClient.create().resource(loc).execute().getId().toUnqualifiedVersionless(); + + { // In the box + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; + String url = "/Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); + + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(locId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + { // Outside the box + double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; + String url = "/Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(0, actual.getEntry().size()); + } + } + + @Test + public void testNearSearchDistanceNoDistanceChained() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_CHIN; + double longitude = CoordCalculatorTest.LONGITUDE_CHIN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + IIdType locId = ourClient.create().resource(loc).execute().getId().toUnqualifiedVersionless(); + + PractitionerRole pr = new PractitionerRole(); + pr.addLocation().setReference(locId.getValue()); + IIdType prId = ourClient.create().resource(pr).execute().getId().toUnqualifiedVersionless(); + + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + latitude + URLEncoder.encode(":") + longitude; + + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(prId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + + @Test + public void testNearSearchApproximateChained() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_UHN; + double longitude = CoordCalculatorTest.LONGITUDE_UHN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + myCaptureQueriesListener.clear(); + IIdType locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless(); + myCaptureQueriesListener.logInsertQueries(); + + PractitionerRole pr = new PractitionerRole(); + pr.addLocation().setReference(locId.getValue()); + IIdType prId = myPractitionerRoleDao.create(pr).getId().toUnqualifiedVersionless(); + { // In the box + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + "location." + Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(prId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + + { // Outside the box + double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + + "&" + + "location." + Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(0, actual.getEntry().size()); + } + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java index 72f5af8299d..d73e4a5ef4a 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3Test.java @@ -5,7 +5,6 @@ import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test; import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl; -import ca.uhn.fhir.jpa.util.CoordCalculatorTest; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; @@ -66,7 +65,6 @@ import java.math.BigDecimal; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; -import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.*; @@ -4280,141 +4278,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test { } - // FIXME KHS move distance tests to distance test class - @Test - public void testNearSearchApproximate() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_UHN; - double longitude = CoordCalculatorTest.LONGITUDE_UHN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - IIdType locId = ourClient.create().resource(loc).execute().getId().toUnqualifiedVersionless(); - - { // In the box - double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; - String url = "/Location?" + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + - "&" + - Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); - - Bundle actual = ourClient - .search() - .byUrl(ourServerBase + "/" + url) - .encodedJson() - .prettyPrint() - .returnBundle(Bundle.class) - .execute(); - - assertEquals(1, actual.getEntry().size()); - assertEquals(locId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); - } - { // Outside the box - double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; - String url = "/Location?" + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + - "&" + - Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); - - myCaptureQueriesListener.clear(); - Bundle actual = ourClient - .search() - .byUrl(ourServerBase + "/" + url) - .encodedJson() - .prettyPrint() - .returnBundle(Bundle.class) - .execute(); - myCaptureQueriesListener.logSelectQueries(); - - assertEquals(0, actual.getEntry().size()); - } - - } - - - @Test - public void testNearSearchDistanceNoDistanceChained() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_CHIN; - double longitude = CoordCalculatorTest.LONGITUDE_CHIN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - IIdType locId = ourClient.create().resource(loc).execute().getId().toUnqualifiedVersionless(); - - PractitionerRole pr = new PractitionerRole(); - pr.addLocation().setReference(locId.getValue()); - IIdType prId = ourClient.create().resource(pr).execute().getId().toUnqualifiedVersionless(); - - String url = "PractitionerRole?location." + - Location.SP_NEAR + "=" + latitude + URLEncoder.encode(":") + longitude; - - Bundle actual = ourClient - .search() - .byUrl(ourServerBase + "/" + url) - .encodedJson() - .prettyPrint() - .returnBundle(Bundle.class) - .execute(); - - assertEquals(1, actual.getEntry().size()); - assertEquals(prId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); - } - - @Test - public void testNearSearchApproximateChained() { - Location loc = new Location(); - double latitude = CoordCalculatorTest.LATITUDE_UHN; - double longitude = CoordCalculatorTest.LONGITUDE_UHN; - Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); - loc.setPosition(position); - myCaptureQueriesListener.clear(); - IIdType locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless(); - myCaptureQueriesListener.logInsertQueries(); - - PractitionerRole pr = new PractitionerRole(); - pr.addLocation().setReference(locId.getValue()); - IIdType prId = myPractitionerRoleDao.create(pr).getId().toUnqualifiedVersionless(); - - { // In the box - double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; - String url = "PractitionerRole?location." + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + - "&" + - "location." + Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); - - myCaptureQueriesListener.clear(); - Bundle actual = ourClient - .search() - .byUrl(ourServerBase + "/" + url) - .encodedJson() - .prettyPrint() - .returnBundle(Bundle.class) - .execute(); - myCaptureQueriesListener.logSelectQueries(); - - assertEquals(1, actual.getEntry().size()); - assertEquals(prId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); - } - { // Outside the box - double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; - String url = "PractitionerRole?location." + - Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + URLEncoder.encode(":") + CoordCalculatorTest.LONGITUDE_CHIN + - "&" + - "location." + Location.SP_NEAR_DISTANCE + "=" + tooSmallDistance + URLEncoder.encode("|http://unitsofmeasure.org|km"); - - myCaptureQueriesListener.clear(); - Bundle actual = ourClient - .search() - .byUrl(ourServerBase + "/" + url) - .encodedJson() - .prettyPrint() - .returnBundle(Bundle.class) - .execute(); - myCaptureQueriesListener.logSelectQueries(); - - assertEquals(0, actual.getEntry().size()); - } - } - private String toStr(Date theDate) { return new InstantDt(theDate).getValueAsString(); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java index 42f9925406c..471392134c4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java @@ -58,7 +58,6 @@ public class MatchUrlServiceTest extends BaseJpaTest { Location.SP_NEAR + "=1000.0:2000.0" + "&" + Location.SP_NEAR_DISTANCE + "=" + kmDistance + "|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - map.setNearDistance(Location.class); QuantityParam nearDistanceParam = map.getNearDistanceParam(); assertEquals(1, map.size()); @@ -75,7 +74,6 @@ public class MatchUrlServiceTest extends BaseJpaTest { "&" + Location.SP_NEAR_DISTANCE + "=2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - map.setNearDistance(Location.class); fail(); } catch (IllegalArgumentException e) { @@ -92,7 +90,6 @@ public class MatchUrlServiceTest extends BaseJpaTest { "," + "2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - map.setNearDistance(Location.class); fail(); } catch (IllegalArgumentException e) { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java index ebbc64f7b8d..a3a384936d4 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java @@ -9,15 +9,12 @@ import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.QuantityParam; -import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.hl7.fhir.dstu3.model.Location; -import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.Serializable; import java.util.*; @@ -507,73 +504,6 @@ public class SearchParameterMap implements Serializable { return myNearDistanceParam; } - // FIXME KHS extract to helper class - public void setNearDistance(Class theResourceType) { - if (theResourceType == Location.class && containsKey(Location.SP_NEAR_DISTANCE)) { - List> paramAndList = get(Location.SP_NEAR_DISTANCE); - QuantityParam quantityParam = getNearDistanceParam(paramAndList); - setNearDistanceParam(quantityParam); - - // Need to remove near-distance or it we'll get a hashcode predicate for it - remove(Location.SP_NEAR_DISTANCE); - } else if (containsKey("location")) { - List> paramAndList = get("location"); - ReferenceParam referenceParam = getChainedLocationNearDistanceParam(paramAndList); - if (referenceParam != null) { - QuantityParam quantityParam = new QuantityParam(referenceParam.getValue()); - setNearDistanceParam(quantityParam); - } - } - } - - private ReferenceParam getChainedLocationNearDistanceParam(List> theParamAndList) { - ReferenceParam retval = null; - List andParamToRemove = null; - for (List paramOrList : theParamAndList) { - IQueryParameterType orParamToRemove = null; - for (IQueryParameterType param : paramOrList) { - if (param instanceof ReferenceParam) { - ReferenceParam referenceParam = (ReferenceParam) param; - if (Location.SP_NEAR_DISTANCE.equals(referenceParam.getChain())) { - if (retval != null) { - throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); - } else { - retval = referenceParam; - orParamToRemove = param; - } - } - } - } - if (orParamToRemove != null) { - paramOrList.remove(orParamToRemove); - if (paramOrList.isEmpty()) { - andParamToRemove = paramOrList; - } - } - } - if (andParamToRemove != null) { - theParamAndList.remove(andParamToRemove); - } - return retval; - } - - private QuantityParam getNearDistanceParam(List> theParamAndList) { - if (theParamAndList.isEmpty()) { - return null; - } - if (theParamAndList.size() > 1) { - throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); - } - List paramOrList = theParamAndList.get(0); - if (paramOrList.isEmpty()) { - return null; - } - if (paramOrList.size() > 1) { - throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); - } - return (QuantityParam) paramOrList.get(0); - } - public enum EverythingModeEnum { /* * Don't reorder! We rely on the ordinals diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java new file mode 100644 index 00000000000..3d1c40e82eb --- /dev/null +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java @@ -0,0 +1,79 @@ +package ca.uhn.fhir.jpa.searchparam.util; + +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import org.hl7.fhir.dstu3.model.Location; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import java.util.List; + +// FIXME KHS write unit test +public class DistanceHelper { + public static void setNearDistance(Class theResourceType, SearchParameterMap theParams) { + if (theResourceType == Location.class && theParams.containsKey(Location.SP_NEAR_DISTANCE)) { + List> paramAndList = theParams.get(Location.SP_NEAR_DISTANCE); + QuantityParam quantityParam = getNearDistanceParam(paramAndList); + theParams.setNearDistanceParam(quantityParam); + + // Need to remove near-distance or it we'll get a hashcode predicate for it + theParams.remove(Location.SP_NEAR_DISTANCE); + } else if (theParams.containsKey("location")) { + List> paramAndList = theParams.get("location"); + ReferenceParam referenceParam = getChainedLocationNearDistanceParam(paramAndList); + if (referenceParam != null) { + QuantityParam quantityParam = new QuantityParam(referenceParam.getValue()); + theParams.setNearDistanceParam(quantityParam); + } + } + } + + private static ReferenceParam getChainedLocationNearDistanceParam(List> theParamAndList) { + ReferenceParam retval = null; + List andParamToRemove = null; + for (List paramOrList : theParamAndList) { + IQueryParameterType orParamToRemove = null; + for (IQueryParameterType param : paramOrList) { + if (param instanceof ReferenceParam) { + ReferenceParam referenceParam = (ReferenceParam) param; + if (Location.SP_NEAR_DISTANCE.equals(referenceParam.getChain())) { + if (retval != null) { + throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + } else { + retval = referenceParam; + orParamToRemove = param; + } + } + } + } + if (orParamToRemove != null) { + paramOrList.remove(orParamToRemove); + if (paramOrList.isEmpty()) { + andParamToRemove = paramOrList; + } + } + } + if (andParamToRemove != null) { + theParamAndList.remove(andParamToRemove); + } + return retval; + } + + private static QuantityParam getNearDistanceParam(List> theParamAndList) { + if (theParamAndList.isEmpty()) { + return null; + } + if (theParamAndList.size() > 1) { + throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + } + List paramOrList = theParamAndList.get(0); + if (paramOrList.isEmpty()) { + return null; + } + if (paramOrList.size() > 1) { + throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + } + return (QuantityParam) paramOrList.get(0); + } +} From d1b54a966827956bec879a6e01c004957ab8533a Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 2 Mar 2020 21:32:27 -0500 Subject: [PATCH 03/10] pre-review cleanup --- .../java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java index 471392134c4..b27b966a394 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.jpa.config.TestDstu3Config; import ca.uhn.fhir.jpa.dao.BaseJpaTest; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; +import ca.uhn.fhir.jpa.searchparam.util.DistanceHelper; import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.util.TestUtil; @@ -58,6 +59,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { Location.SP_NEAR + "=1000.0:2000.0" + "&" + Location.SP_NEAR_DISTANCE + "=" + kmDistance + "|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); + DistanceHelper.setNearDistance(Location.class, map); QuantityParam nearDistanceParam = map.getNearDistanceParam(); assertEquals(1, map.size()); @@ -74,6 +76,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { "&" + Location.SP_NEAR_DISTANCE + "=2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); + DistanceHelper.setNearDistance(Location.class, map); fail(); } catch (IllegalArgumentException e) { @@ -90,6 +93,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { "," + "2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); + DistanceHelper.setNearDistance(Location.class, map); fail(); } catch (IllegalArgumentException e) { From 4e1be80525bcbe178183b09ad46033cdd0c20258 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 2 Mar 2020 21:39:28 -0500 Subject: [PATCH 04/10] fixme --- .../java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java | 2 -- .../java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java | 1 - 2 files changed, 3 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java index b27b966a394..8d37227f734 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java @@ -101,8 +101,6 @@ public class MatchUrlServiceTest extends BaseJpaTest { } } - // FIXME KHS add chaining test - @Override protected FhirContext getContext() { return ourCtx; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java index 3d1c40e82eb..f66e4a8b122 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java @@ -9,7 +9,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.List; -// FIXME KHS write unit test public class DistanceHelper { public static void setNearDistance(Class theResourceType, SearchParameterMap theParams) { if (theResourceType == Location.class && theParams.containsKey(Location.SP_NEAR_DISTANCE)) { From a56df030556d4d60fa194c60f1b4fcc4b5e58fe8 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 2 Mar 2020 21:50:57 -0500 Subject: [PATCH 05/10] restrict near-distance extraction to DSTU3 --- .../src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 69a1418971c..75d6eca09b2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.dao; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.interceptor.api.HookParams; @@ -160,8 +161,10 @@ public class SearchBuilder implements ISearchBuilder { // Remove any empty parameters theParams.clean(); - // Pull out near-distance first so when it comes time to evaluate near, we already know the distance - DistanceHelper.setNearDistance(myResourceType, theParams); + // For DSTU3, pull out near-distance first so when it comes time to evaluate near, we already know the distance + if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) { + DistanceHelper.setNearDistance(myResourceType, theParams); + } /* * Check if there is a unique key associated with the set From bce1716c6cd6cb9dff4943b23bc0ad4fa38729c0 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Mon, 2 Mar 2020 22:13:21 -0500 Subject: [PATCH 06/10] documentation --- .../ca/uhn/hapi/fhir/changelog/4_3_0/near-chain.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/near-chain.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/near-chain.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/near-chain.yaml new file mode 100644 index 00000000000..64c83acf7ad --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/4_3_0/near-chain.yaml @@ -0,0 +1,4 @@ +--- +type: fix +title: "DSTU3 searches using near-distance only worked on Location resources directly. It now works on chained searches on resources with a location. +E.g. PractitionerRole?location.near-distance=1.0 now works properly." From 705735b9c08c82e5c2eda46e81bb0acf7f2140c7 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Tue, 3 Mar 2020 13:29:10 -0500 Subject: [PATCH 07/10] review feedback: javadoc --- .../java/ca/uhn/fhir/jpa/dao/SearchBuilder.java | 4 ++-- .../jpa/searchparam/MatchUrlServiceTest.java | 8 ++++---- ...tanceHelper.java => Dstu3DistanceHelper.java} | 16 ++++++++++++---- 3 files changed, 18 insertions(+), 10 deletions(-) rename hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/{DistanceHelper.java => Dstu3DistanceHelper.java} (74%) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 75d6eca09b2..008b0a8fe7e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -40,7 +40,7 @@ import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage; import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; -import ca.uhn.fhir.jpa.searchparam.util.DistanceHelper; +import ca.uhn.fhir.jpa.searchparam.util.Dstu3DistanceHelper; import ca.uhn.fhir.jpa.util.*; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IResource; @@ -163,7 +163,7 @@ public class SearchBuilder implements ISearchBuilder { // For DSTU3, pull out near-distance first so when it comes time to evaluate near, we already know the distance if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) { - DistanceHelper.setNearDistance(myResourceType, theParams); + Dstu3DistanceHelper.setNearDistance(myResourceType, theParams); } /* diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java index 8d37227f734..6ee336af59c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/searchparam/MatchUrlServiceTest.java @@ -5,7 +5,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.jpa.config.TestDstu3Config; import ca.uhn.fhir.jpa.dao.BaseJpaTest; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; -import ca.uhn.fhir.jpa.searchparam.util.DistanceHelper; +import ca.uhn.fhir.jpa.searchparam.util.Dstu3DistanceHelper; import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.util.TestUtil; @@ -59,7 +59,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { Location.SP_NEAR + "=1000.0:2000.0" + "&" + Location.SP_NEAR_DISTANCE + "=" + kmDistance + "|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - DistanceHelper.setNearDistance(Location.class, map); + Dstu3DistanceHelper.setNearDistance(Location.class, map); QuantityParam nearDistanceParam = map.getNearDistanceParam(); assertEquals(1, map.size()); @@ -76,7 +76,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { "&" + Location.SP_NEAR_DISTANCE + "=2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - DistanceHelper.setNearDistance(Location.class, map); + Dstu3DistanceHelper.setNearDistance(Location.class, map); fail(); } catch (IllegalArgumentException e) { @@ -93,7 +93,7 @@ public class MatchUrlServiceTest extends BaseJpaTest { "," + "2|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location")); - DistanceHelper.setNearDistance(Location.class, map); + Dstu3DistanceHelper.setNearDistance(Location.class, map); fail(); } catch (IllegalArgumentException e) { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java similarity index 74% rename from hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java rename to hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java index f66e4a8b122..950e8b1fcfa 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/DistanceHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java @@ -9,7 +9,15 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.List; -public class DistanceHelper { + +/** + * In DSTU3, the near-distance search parameter is separate from near. In this utility method, + * we search for near-distance search parameters and if we find any, remove them from the list + * of search parameters and store it in a dedicated field in {@link SearchParameterMap}. This is so that + * when the "near" search parameter is processed, we have access to this near-distance value. + * This requires at most one near-distance parameter. If more are found, we throw an {@link IllegalArgumentException}. + */ +public class Dstu3DistanceHelper { public static void setNearDistance(Class theResourceType, SearchParameterMap theParams) { if (theResourceType == Location.class && theParams.containsKey(Location.SP_NEAR_DISTANCE)) { List> paramAndList = theParams.get(Location.SP_NEAR_DISTANCE); @@ -38,7 +46,7 @@ public class DistanceHelper { ReferenceParam referenceParam = (ReferenceParam) param; if (Location.SP_NEAR_DISTANCE.equals(referenceParam.getChain())) { if (retval != null) { - throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present"); } else { retval = referenceParam; orParamToRemove = param; @@ -64,14 +72,14 @@ public class DistanceHelper { return null; } if (theParamAndList.size() > 1) { - throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present"); } List paramOrList = theParamAndList.get(0); if (paramOrList.isEmpty()) { return null; } if (paramOrList.size() > 1) { - throw new IllegalArgumentException("Only one " + ca.uhn.fhir.model.dstu2.resource.Location.SP_NEAR_DISTANCE + " parameter may be present"); + throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present"); } return (QuantityParam) paramOrList.get(0); } From ccd983ab429c9c97dbc1821db246de218b4ebc04 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Tue, 3 Mar 2020 13:46:23 -0500 Subject: [PATCH 08/10] review feedback: code improvement --- .../searchparam/util/Dstu3DistanceHelper.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java index 950e8b1fcfa..8482a2b5ff3 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.rest.param.ReferenceParam; import org.hl7.fhir.dstu3.model.Location; import org.hl7.fhir.instance.model.api.IBaseResource; +import java.util.Collection; import java.util.List; @@ -68,19 +69,17 @@ public class Dstu3DistanceHelper { } private static QuantityParam getNearDistanceParam(List> theParamAndList) { - if (theParamAndList.isEmpty()) { + long sum = theParamAndList.stream().mapToLong(Collection::size).sum(); + + // No near-distance Param + if (sum == 0) { return null; - } - if (theParamAndList.size() > 1) { + // A single near-distance Param + } else if (sum == 1) { + return (QuantityParam) theParamAndList.get(0).get(0); + // Too many near-distance params + } else { throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present"); } - List paramOrList = theParamAndList.get(0); - if (paramOrList.isEmpty()) { - return null; - } - if (paramOrList.size() > 1) { - throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present"); - } - return (QuantityParam) paramOrList.get(0); } } From d104fbdf7d4a0b454f8c0836537f7e45b7691498 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Tue, 3 Mar 2020 16:40:31 -0500 Subject: [PATCH 09/10] fixed a copy/paste error in bulk export with test --- .../java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImpl.java | 4 ++-- .../ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImpl.java index 5800362ed58..072565cdc43 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImpl.java @@ -347,14 +347,14 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc { requestBuilder.append("&").append(JpaConstants.PARAM_EXPORT_SINCE).append("=").append(new InstantType(since).setTimeZoneZulu(true).getValueAsString()); } if (theFilters != null && theFilters.size() > 0) { - requestBuilder.append("&").append(JpaConstants.PARAM_EXPORT_TYPE).append("=").append(String.join(",", theFilters)); + requestBuilder.append("&").append(JpaConstants.PARAM_EXPORT_TYPE_FILTER).append("=").append(String.join(",", theFilters)); } String request = requestBuilder.toString(); Date cutoff = DateUtils.addMilliseconds(new Date(), -myReuseBulkExportForMillis); Pageable page = PageRequest.of(0, 10); Slice existing = myBulkExportJobDao.findExistingJob(page, request, cutoff, BulkJobStatusEnum.ERROR); - if (existing.isEmpty() == false) { + if (!existing.isEmpty()) { return toSubmittedJobInfo(existing.iterator().next()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java index 436627a67b2..34e835f5e83 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportSvcImplR4Test.java @@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.entity.BulkExportCollectionFileEntity; import ca.uhn.fhir.jpa.entity.BulkExportJobEntity; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.test.utilities.UnregisterScheduledProcessor; import com.google.common.base.Charsets; import com.google.common.collect.Sets; import org.apache.commons.lang3.time.DateUtils; @@ -23,7 +22,6 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.TestPropertySource; import java.util.Date; import java.util.UUID; @@ -34,6 +32,7 @@ import static org.junit.Assert.*; public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test { private static final Logger ourLog = LoggerFactory.getLogger(BulkDataExportSvcImplR4Test.class); + public static final String TEST_FILTER = "Patient?gender=female"; @Autowired private IBulkExportJobDao myBulkExportJobDao; @Autowired @@ -134,13 +133,13 @@ public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test { createResources(); // Create a bulk job - IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(null, Sets.newHashSet("Patient", "Observation"), null, null); + IBulkDataExportSvc.JobInfo jobDetails = myBulkDataExportSvc.submitJob(null, Sets.newHashSet("Patient", "Observation"), null, Sets.newHashSet(TEST_FILTER)); assertNotNull(jobDetails.getJobId()); // Check the status IBulkDataExportSvc.JobInfo status = myBulkDataExportSvc.getJobStatusOrThrowResourceNotFound(jobDetails.getJobId()); assertEquals(BulkJobStatusEnum.SUBMITTED, status.getStatus()); - assertEquals("/$export?_outputFormat=application%2Ffhir%2Bndjson&_type=Observation,Patient", status.getRequest()); + assertEquals("/$export?_outputFormat=application%2Ffhir%2Bndjson&_type=Observation,Patient&_typeFilter="+TEST_FILTER, status.getRequest()); // Run a scheduled pass to build the export myBulkDataExportSvc.buildExportFiles(); From e819d404148d62bb8cd01ea175b518e7ba02a98f Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Tue, 3 Mar 2020 18:33:19 -0500 Subject: [PATCH 10/10] fix near searching for R4 --- .../predicate/PredicateBuilderReference.java | 4 + .../ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java | 3 + .../r4/ResourceProviderR4DistanceTest.java | 146 ++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4DistanceTest.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java index ca72f0a6f56..0381a5fcba9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/PredicateBuilderReference.java @@ -666,6 +666,10 @@ class PredicateBuilderReference extends BasePredicateBuilder { qp = new ReferenceParam(); break; case SPECIAL: + if ("Location.position".equals(theParam.getPath())) { + qp = new SpecialParam(); + break; + } case URI: case HAS: default: diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java index 26f5bd9b4da..d1f85382684 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4Test.java @@ -206,6 +206,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest { @Qualifier("myLocationDaoR4") protected IFhirResourceDao myLocationDao; @Autowired + @Qualifier("myPractitionerRoleDaoR4") + protected IFhirResourceDao myPractitionerRoleDao; + @Autowired @Qualifier("myMediaDaoR4") protected IFhirResourceDao myMediaDao; @Autowired diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4DistanceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4DistanceTest.java new file mode 100644 index 00000000000..653f7b7a258 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4DistanceTest.java @@ -0,0 +1,146 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import ca.uhn.fhir.jpa.util.CoordCalculatorTest; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Location; +import org.hl7.fhir.r4.model.PractitionerRole; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ResourceProviderR4DistanceTest extends BaseResourceProviderR4Test { + @Override + public void before() throws Exception { + super.before(); + myDaoConfig.setReuseCachedSearchResultsForMillis(null); + } + + @Test + public void testNearSearchApproximate() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_UHN; + double longitude = CoordCalculatorTest.LONGITUDE_UHN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + IIdType locId = ourClient.create().resource(loc).execute().getId().toUnqualifiedVersionless(); + + { // In the box + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; + String url = "/Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" + CoordCalculatorTest.LONGITUDE_CHIN + + "|" + bigEnoughDistance; + + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(locId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + { // Outside the box + double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; + String url = "/Location?" + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" + CoordCalculatorTest.LONGITUDE_CHIN + + "|" + tooSmallDistance; + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(0, actual.getEntry().size()); + } + } + + @Test + public void testNearSearchDistanceNoDistanceChained() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_CHIN; + double longitude = CoordCalculatorTest.LONGITUDE_CHIN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + IIdType locId = ourClient.create().resource(loc).execute().getId().toUnqualifiedVersionless(); + + PractitionerRole pr = new PractitionerRole(); + pr.addLocation().setReference(locId.getValue()); + IIdType prId = ourClient.create().resource(pr).execute().getId().toUnqualifiedVersionless(); + + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + latitude + "|" + longitude; + + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(prId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + + @Test + public void testNearSearchApproximateChained() { + Location loc = new Location(); + double latitude = CoordCalculatorTest.LATITUDE_UHN; + double longitude = CoordCalculatorTest.LONGITUDE_UHN; + Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude); + loc.setPosition(position); + myCaptureQueriesListener.clear(); + IIdType locId = myLocationDao.create(loc).getId().toUnqualifiedVersionless(); + myCaptureQueriesListener.logInsertQueries(); + + PractitionerRole pr = new PractitionerRole(); + pr.addLocation().setReference(locId.getValue()); + IIdType prId = myPractitionerRoleDao.create(pr).getId().toUnqualifiedVersionless(); + { // In the box + double bigEnoughDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN * 2; + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" + CoordCalculatorTest.LONGITUDE_CHIN + + "|" + bigEnoughDistance; + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(1, actual.getEntry().size()); + assertEquals(prId.getIdPart(), actual.getEntry().get(0).getResource().getIdElement().getIdPart()); + } + + { // Outside the box + double tooSmallDistance = CoordCalculatorTest.DISTANCE_KM_CHIN_TO_UHN / 2; + String url = "PractitionerRole?location." + + Location.SP_NEAR + "=" + CoordCalculatorTest.LATITUDE_CHIN + "|" + CoordCalculatorTest.LONGITUDE_CHIN + + "|" + tooSmallDistance; + + myCaptureQueriesListener.clear(); + Bundle actual = ourClient + .search() + .byUrl(ourServerBase + "/" + url) + .encodedJson() + .prettyPrint() + .returnBundle(Bundle.class) + .execute(); + myCaptureQueriesListener.logSelectQueries(); + + assertEquals(0, actual.getEntry().size()); + } + } +}