parent
b2f9dd4578
commit
24acb57f3b
|
@ -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
|
||||
|
|
|
@ -148,6 +148,9 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
|
|||
@Qualifier("myLocationDaoDstu3")
|
||||
protected IFhirResourceDao<Location> myLocationDao;
|
||||
@Autowired
|
||||
@Qualifier("myPractitionerRoleDaoDstu3")
|
||||
protected IFhirResourceDao<PractitionerRole> myPractitionerRoleDao;
|
||||
@Autowired
|
||||
@Qualifier("myMediaDaoDstu3")
|
||||
protected IFhirResourceDao<Media> myMediaDao;
|
||||
@Autowired
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,28 +507,71 @@ 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<? extends IBaseResource> theResourceType) {
|
||||
if (theResourceType == Location.class && containsKey(Location.SP_NEAR_DISTANCE)) {
|
||||
List<List<IQueryParameterType>> paramAndList = get(Location.SP_NEAR_DISTANCE);
|
||||
QuantityParam quantityParam = getNearDistanceParam(paramAndList);
|
||||
setNearDistanceParam(quantityParam);
|
||||
|
||||
if (paramAndList.isEmpty()) {
|
||||
return;
|
||||
// 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<List<IQueryParameterType>> paramAndList = get("location");
|
||||
ReferenceParam referenceParam = getChainedLocationNearDistanceParam(paramAndList);
|
||||
if (referenceParam != null) {
|
||||
QuantityParam quantityParam = new QuantityParam(referenceParam.getValue());
|
||||
setNearDistanceParam(quantityParam);
|
||||
}
|
||||
if (paramAndList.size() > 1) {
|
||||
}
|
||||
}
|
||||
|
||||
private 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 " + 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<List<IQueryParameterType>> 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<IQueryParameterType> paramOrList = paramAndList.get(0);
|
||||
List<IQueryParameterType> paramOrList = theParamAndList.get(0);
|
||||
if (paramOrList.isEmpty()) {
|
||||
return;
|
||||
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");
|
||||
}
|
||||
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);
|
||||
}
|
||||
return (QuantityParam) paramOrList.get(0);
|
||||
}
|
||||
|
||||
public enum EverythingModeEnum {
|
||||
|
|
Loading…
Reference in New Issue