Merge branch 'master' into ja_20200206_multitenancy
This commit is contained in:
commit
58ee76549c
|
@ -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."
|
|
@ -347,14 +347,14 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc {
|
||||||
requestBuilder.append("&").append(JpaConstants.PARAM_EXPORT_SINCE).append("=").append(new InstantType(since).setTimeZoneZulu(true).getValueAsString());
|
requestBuilder.append("&").append(JpaConstants.PARAM_EXPORT_SINCE).append("=").append(new InstantType(since).setTimeZoneZulu(true).getValueAsString());
|
||||||
}
|
}
|
||||||
if (theFilters != null && theFilters.size() > 0) {
|
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();
|
String request = requestBuilder.toString();
|
||||||
|
|
||||||
Date cutoff = DateUtils.addMilliseconds(new Date(), -myReuseBulkExportForMillis);
|
Date cutoff = DateUtils.addMilliseconds(new Date(), -myReuseBulkExportForMillis);
|
||||||
Pageable page = PageRequest.of(0, 10);
|
Pageable page = PageRequest.of(0, 10);
|
||||||
Slice<BulkExportJobEntity> existing = myBulkExportJobDao.findExistingJob(page, request, cutoff, BulkJobStatusEnum.ERROR);
|
Slice<BulkExportJobEntity> existing = myBulkExportJobDao.findExistingJob(page, request, cutoff, BulkJobStatusEnum.ERROR);
|
||||||
if (existing.isEmpty() == false) {
|
if (!existing.isEmpty()) {
|
||||||
return toSubmittedJobInfo(existing.iterator().next());
|
return toSubmittedJobInfo(existing.iterator().next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.interceptor.api.HookParams;
|
import ca.uhn.fhir.interceptor.api.HookParams;
|
||||||
|
@ -29,29 +30,18 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceSearchViewDao;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
import ca.uhn.fhir.jpa.dao.data.IResourceTagDao;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.jpa.dao.predicate.PredicateBuilder;
|
import ca.uhn.fhir.jpa.dao.predicate.*;
|
||||||
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.entity.ResourceSearchView;
|
import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
||||||
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
||||||
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
import ca.uhn.fhir.jpa.model.entity.*;
|
||||||
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.search.SearchRuntimeDetails;
|
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||||
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
|
||||||
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
import ca.uhn.fhir.jpa.searchparam.JpaRuntimeSearchParam;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.jpa.util.BaseIterator;
|
import ca.uhn.fhir.jpa.searchparam.util.Dstu3DistanceHelper;
|
||||||
import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener;
|
import ca.uhn.fhir.jpa.util.*;
|
||||||
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.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.Include;
|
import ca.uhn.fhir.model.api.Include;
|
||||||
|
@ -77,7 +67,6 @@ import org.apache.commons.lang3.Validate;
|
||||||
import org.hibernate.ScrollMode;
|
import org.hibernate.ScrollMode;
|
||||||
import org.hibernate.ScrollableResults;
|
import org.hibernate.ScrollableResults;
|
||||||
import org.hibernate.query.Query;
|
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.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -92,27 +81,10 @@ import javax.persistence.EntityManager;
|
||||||
import javax.persistence.PersistenceContext;
|
import javax.persistence.PersistenceContext;
|
||||||
import javax.persistence.PersistenceContextType;
|
import javax.persistence.PersistenceContextType;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.*;
|
||||||
import javax.persistence.criteria.CriteriaQuery;
|
import java.util.*;
|
||||||
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 static org.apache.commons.lang3.StringUtils.defaultString;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The SearchBuilder is responsible for actually forming the SQL query that handles
|
* The SearchBuilder is responsible for actually forming the SQL query that handles
|
||||||
|
@ -189,8 +161,9 @@ public class SearchBuilder implements ISearchBuilder {
|
||||||
// Remove any empty parameters
|
// Remove any empty parameters
|
||||||
theParams.clean();
|
theParams.clean();
|
||||||
|
|
||||||
if (myResourceType == Location.class) {
|
// For DSTU3, pull out near-distance first so when it comes time to evaluate near, we already know the distance
|
||||||
theParams.setLocationDistance();
|
if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU3) {
|
||||||
|
Dstu3DistanceHelper.setNearDistance(myResourceType, theParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -666,6 +666,10 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
qp = new ReferenceParam();
|
qp = new ReferenceParam();
|
||||||
break;
|
break;
|
||||||
case SPECIAL:
|
case SPECIAL:
|
||||||
|
if ("Location.position".equals(theParam.getPath())) {
|
||||||
|
qp = new SpecialParam();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case URI:
|
case URI:
|
||||||
case HAS:
|
case HAS:
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -9,7 +9,6 @@ import ca.uhn.fhir.jpa.entity.BulkExportCollectionFileEntity;
|
||||||
import ca.uhn.fhir.jpa.entity.BulkExportJobEntity;
|
import ca.uhn.fhir.jpa.entity.BulkExportJobEntity;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
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.base.Charsets;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
@ -23,7 +22,6 @@ import org.junit.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.context.TestPropertySource;
|
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -34,6 +32,7 @@ import static org.junit.Assert.*;
|
||||||
public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test {
|
public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(BulkDataExportSvcImplR4Test.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(BulkDataExportSvcImplR4Test.class);
|
||||||
|
public static final String TEST_FILTER = "Patient?gender=female";
|
||||||
@Autowired
|
@Autowired
|
||||||
private IBulkExportJobDao myBulkExportJobDao;
|
private IBulkExportJobDao myBulkExportJobDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -134,13 +133,13 @@ public class BulkDataExportSvcImplR4Test extends BaseJpaR4Test {
|
||||||
createResources();
|
createResources();
|
||||||
|
|
||||||
// Create a bulk job
|
// 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());
|
assertNotNull(jobDetails.getJobId());
|
||||||
|
|
||||||
// Check the status
|
// Check the status
|
||||||
IBulkDataExportSvc.JobInfo status = myBulkDataExportSvc.getJobStatusOrThrowResourceNotFound(jobDetails.getJobId());
|
IBulkDataExportSvc.JobInfo status = myBulkDataExportSvc.getJobStatusOrThrowResourceNotFound(jobDetails.getJobId());
|
||||||
assertEquals(BulkJobStatusEnum.SUBMITTED, status.getStatus());
|
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
|
// Run a scheduled pass to build the export
|
||||||
myBulkDataExportSvc.buildExportFiles();
|
myBulkDataExportSvc.buildExportFiles();
|
||||||
|
|
|
@ -148,6 +148,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
||||||
@Qualifier("myLocationDaoDstu3")
|
@Qualifier("myLocationDaoDstu3")
|
||||||
protected IFhirResourceDao<Location> myLocationDao;
|
protected IFhirResourceDao<Location> myLocationDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("myPractitionerRoleDaoDstu3")
|
||||||
|
protected IFhirResourceDao<PractitionerRole> myPractitionerRoleDao;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myMediaDaoDstu3")
|
@Qualifier("myMediaDaoDstu3")
|
||||||
protected IFhirResourceDao<Media> myMediaDao;
|
protected IFhirResourceDao<Media> myMediaDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.dao.dstu3;
|
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.Observation;
|
||||||
import org.hl7.fhir.dstu3.model.Patient;
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
import org.hl7.fhir.dstu3.model.Reference;
|
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.AfterClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
|
||||||
|
|
||||||
public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test {
|
public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ContainedTest.class);
|
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));
|
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(o2));
|
||||||
|
|
||||||
|
|
||||||
SearchParameterMap map;
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
|
map.add(Observation.SP_CODE, new TokenParam(null, "some observation").setModifier(TokenParamModifier.TEXT));
|
||||||
// map = new SearchParameterMap();
|
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(oid1, oid2)));
|
||||||
// map.add(Observation.SP_CODE, new TokenParam(null, "some observation").setModifier(TokenParamModifier.TEXT));
|
|
||||||
// assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(map)), containsInAnyOrder(toValues(id1, id2)));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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<String> 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<String> 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<String> 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<String> 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,11 +2,9 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.model.entity.*;
|
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.SearchParamConstants;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
|
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.jpa.util.TestUtil;
|
||||||
import ca.uhn.fhir.model.api.Include;
|
import ca.uhn.fhir.model.api.Include;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
|
@ -36,8 +34,6 @@ import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
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.TransactionStatus;
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
@ -59,9 +55,6 @@ import static org.mockito.Mockito.mock;
|
||||||
public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3SearchNoFtTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3SearchNoFtTest.class);
|
||||||
|
|
||||||
@Autowired
|
|
||||||
MatchUrlService myMatchUrlService;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void beforeDisableResultReuse() {
|
public void beforeDisableResultReuse() {
|
||||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
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"));
|
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<String> 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<String> 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<String> 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<String> 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) {
|
private String toStringMultiline(List<?> theResults) {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
for (Object next : theResults) {
|
for (Object next : theResults) {
|
||||||
|
|
|
@ -206,6 +206,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||||
@Qualifier("myLocationDaoR4")
|
@Qualifier("myLocationDaoR4")
|
||||||
protected IFhirResourceDao<Location> myLocationDao;
|
protected IFhirResourceDao<Location> myLocationDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("myPractitionerRoleDaoR4")
|
||||||
|
protected IFhirResourceDao<PractitionerRole> myPractitionerRoleDao;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myMediaDaoR4")
|
@Qualifier("myMediaDaoR4")
|
||||||
protected IFhirResourceDao<Media> myMediaDao;
|
protected IFhirResourceDao<Media> myMediaDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map));
|
||||||
|
assertThat(ids.size(), is(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,18 +3,10 @@ package ca.uhn.fhir.jpa.dao.r4;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.entity.Search;
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
|
import ca.uhn.fhir.jpa.model.entity.*;
|
||||||
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.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
|
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.jpa.util.TestUtil;
|
||||||
import ca.uhn.fhir.model.api.Include;
|
import ca.uhn.fhir.model.api.Include;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
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.Observation.ObservationStatus;
|
||||||
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
|
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
|
||||||
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
|
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
|
||||||
import org.junit.After;
|
import org.junit.*;
|
||||||
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.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.TransactionStatus;
|
import org.springframework.transaction.TransactionStatus;
|
||||||
import org.springframework.transaction.support.TransactionCallback;
|
import org.springframework.transaction.support.TransactionCallback;
|
||||||
|
@ -55,30 +43,11 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.hamcrest.Matchers.contains;
|
import static org.junit.Assert.*;
|
||||||
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.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "Duplicates"})
|
@SuppressWarnings({"unchecked", "Duplicates"})
|
||||||
|
@ -4299,120 +4268,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
||||||
assertThat(toUnqualifiedVersionlessIdValues(outcome), contains(crId));
|
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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map));
|
|
||||||
assertThat(ids.size(), is(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCircularReferencesDontBreakRevIncludes() {
|
public void testCircularReferencesDontBreakRevIncludes() {
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,6 @@ import ca.uhn.fhir.jpa.dao.data.ISearchDao;
|
||||||
import ca.uhn.fhir.jpa.entity.Search;
|
import ca.uhn.fhir.jpa.entity.Search;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test;
|
||||||
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
|
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.api.TemporalPrecisionEnum;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.model.primitive.UriDt;
|
import ca.uhn.fhir.model.primitive.UriDt;
|
||||||
|
@ -66,7 +65,6 @@ import java.math.BigDecimal;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -4280,55 +4278,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private String toStr(Date theDate) {
|
private String toStr(Date theDate) {
|
||||||
return new InstantDt(theDate).getValueAsString();
|
return new InstantDt(theDate).getValueAsString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.jpa.config.TestDstu3Config;
|
import ca.uhn.fhir.jpa.config.TestDstu3Config;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
|
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.util.Dstu3DistanceHelper;
|
||||||
import ca.uhn.fhir.rest.param.QuantityParam;
|
import ca.uhn.fhir.rest.param.QuantityParam;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
|
@ -58,7 +59,7 @@ public class MatchUrlServiceTest extends BaseJpaTest {
|
||||||
Location.SP_NEAR + "=1000.0:2000.0" +
|
Location.SP_NEAR + "=1000.0:2000.0" +
|
||||||
"&" +
|
"&" +
|
||||||
Location.SP_NEAR_DISTANCE + "=" + kmDistance + "|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location"));
|
Location.SP_NEAR_DISTANCE + "=" + kmDistance + "|http://unitsofmeasure.org|km", ourCtx.getResourceDefinition("Location"));
|
||||||
map.setLocationDistance();
|
Dstu3DistanceHelper.setNearDistance(Location.class, map);
|
||||||
|
|
||||||
QuantityParam nearDistanceParam = map.getNearDistanceParam();
|
QuantityParam nearDistanceParam = map.getNearDistanceParam();
|
||||||
assertEquals(1, map.size());
|
assertEquals(1, map.size());
|
||||||
|
@ -75,7 +76,7 @@ public class MatchUrlServiceTest extends BaseJpaTest {
|
||||||
"&" +
|
"&" +
|
||||||
Location.SP_NEAR_DISTANCE + "=2|http://unitsofmeasure.org|km",
|
Location.SP_NEAR_DISTANCE + "=2|http://unitsofmeasure.org|km",
|
||||||
ourCtx.getResourceDefinition("Location"));
|
ourCtx.getResourceDefinition("Location"));
|
||||||
map.setLocationDistance();
|
Dstu3DistanceHelper.setNearDistance(Location.class, map);
|
||||||
|
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
@ -92,7 +93,7 @@ public class MatchUrlServiceTest extends BaseJpaTest {
|
||||||
"," +
|
"," +
|
||||||
"2|http://unitsofmeasure.org|km",
|
"2|http://unitsofmeasure.org|km",
|
||||||
ourCtx.getResourceDefinition("Location"));
|
ourCtx.getResourceDefinition("Location"));
|
||||||
map.setLocationDistance();
|
Dstu3DistanceHelper.setNearDistance(Location.class, map);
|
||||||
|
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
import org.hl7.fhir.dstu3.model.Location;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -505,30 +504,6 @@ public class SearchParameterMap implements Serializable {
|
||||||
return myNearDistanceParam;
|
return myNearDistanceParam;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLocationDistance() {
|
|
||||||
if (containsKey(Location.SP_NEAR_DISTANCE)) {
|
|
||||||
List<List<IQueryParameterType>> 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<IQueryParameterType> 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));
|
|
||||||
|
|
||||||
// Need to remove near-distance or it we'll get a hashcode predicate for it
|
|
||||||
remove(Location.SP_NEAR_DISTANCE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum EverythingModeEnum {
|
public enum EverythingModeEnum {
|
||||||
/*
|
/*
|
||||||
* Don't reorder! We rely on the ordinals
|
* Don't reorder! We rely on the ordinals
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
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.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<? extends IBaseResource> theResourceType, SearchParameterMap theParams) {
|
||||||
|
if (theResourceType == Location.class && theParams.containsKey(Location.SP_NEAR_DISTANCE)) {
|
||||||
|
List<List<IQueryParameterType>> 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<List<IQueryParameterType>> 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<List<IQueryParameterType>> theParamAndList) {
|
||||||
|
ReferenceParam retval = null;
|
||||||
|
List<IQueryParameterType> andParamToRemove = null;
|
||||||
|
for (List<IQueryParameterType> 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 " + 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<List<IQueryParameterType>> theParamAndList) {
|
||||||
|
long sum = theParamAndList.stream().mapToLong(Collection::size).sum();
|
||||||
|
|
||||||
|
// No near-distance Param
|
||||||
|
if (sum == 0) {
|
||||||
|
return null;
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue