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()); + } + } +}