began with failing test.

test now passes.
This commit is contained in:
Ken Stevens 2020-03-02 17:56:25 -05:00
parent b2f9dd4578
commit 24acb57f3b
6 changed files with 173 additions and 67 deletions

View File

@ -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

View File

@ -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

View File

@ -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)));
}

View File

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

View File

@ -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;

View File

@ -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 {