Merge pull request #1695 from jamesagnew/ks-20200131-near-search-http

Ks 20200131 near search http
This commit is contained in:
Ken Stevens 2020-02-03 10:38:29 -05:00 committed by GitHub
commit 7faf8932cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 93 additions and 27 deletions

View File

@ -65,6 +65,7 @@ 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;
@ -160,6 +161,10 @@ public class SearchBuilder implements ISearchBuilder {
// Remove any empty parameters
theParams.clean();
if (myResourceType == Location.class) {
theParams.setLocationDistance();
}
/*
* Check if there is a unique key associated with the set
* of parameters passed in
@ -181,7 +186,6 @@ public class SearchBuilder implements ISearchBuilder {
}
@Override
public Iterator<Long> createCountQuery(SearchParameterMap theParams, String theSearchUuid, RequestDetails theRequest) {
init(theParams, theSearchUuid);

View File

@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
import ca.uhn.fhir.jpa.util.CoordCalculatorTest;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt;
@ -65,6 +66,7 @@ import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@ -4282,6 +4284,55 @@ 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) {
return new InstantDt(theDate).getValueAsString();
}

View File

@ -58,6 +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();
QuantityParam nearDistanceParam = map.getNearDistanceParam();
assertEquals(1, map.size());
@ -74,6 +75,8 @@ public class MatchUrlServiceTest extends BaseJpaTest {
"&" +
Location.SP_NEAR_DISTANCE + "=2|http://unitsofmeasure.org|km",
ourCtx.getResourceDefinition("Location"));
map.setLocationDistance();
fail();
} catch (IllegalArgumentException e) {
assertEquals("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present", e.getMessage());
@ -89,7 +92,8 @@ public class MatchUrlServiceTest extends BaseJpaTest {
"," +
"2|http://unitsofmeasure.org|km",
ourCtx.getResourceDefinition("Location"));
map.setLoadSynchronous(true);
map.setLocationDistance();
fail();
} catch (IllegalArgumentException e) {
assertEquals("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present", e.getMessage());

View File

@ -26,13 +26,11 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu2.resource.Location;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.param.QuantityAndListParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.ReflectionUtil;
import ca.uhn.fhir.util.UrlUtil;
@ -115,9 +113,6 @@ public class MatchUrlService {
} else if (Constants.PARAM_SOURCE.equals(nextParamName)) {
IQueryParameterAnd<?> param = ParameterUtil.parseQueryParams(myContext, RestSearchParameterTypeEnum.TOKEN, nextParamName, paramList);
paramMap.add(nextParamName, param);
} else if (Location.SP_NEAR_DISTANCE.equals(nextParamName)) {
QuantityAndListParam nearDistanceAndListParam = (QuantityAndListParam) ParameterUtil.parseQueryParams(myContext, RestSearchParameterTypeEnum.QUANTITY, nextParamName, paramList);
paramMap.setNearDistanceParam(nearDistanceAndListParam);
} else if (nextParamName.startsWith("_")) {
// ignore these since they aren't search params (e.g. _sort)
} else {

View File

@ -5,15 +5,17 @@ import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.dstu2.resource.Location;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.param.*;
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.util.ObjectUtil;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.dstu3.model.Location;
import java.io.Serializable;
import java.util.*;
@ -495,29 +497,38 @@ public class SearchParameterMap implements Serializable {
}
}
public void setNearDistanceParam(QuantityAndListParam theQuantityAndListParam) {
List<QuantityOrListParam> orTokens = theQuantityAndListParam.getValuesAsQueryTokens();
if (orTokens.isEmpty()) {
return;
}
if (orTokens.size() > 1) {
throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present");
}
QuantityOrListParam quantityOrListParam = orTokens.get(0);
List<QuantityParam> tokens = quantityOrListParam.getValuesAsQueryTokens();
if (tokens.isEmpty()) {
return;
}
if (tokens.size() > 1) {
throw new IllegalArgumentException("Only one " + Location.SP_NEAR_DISTANCE + " parameter may be present");
}
myNearDistanceParam = tokens.get(0);
public void setNearDistanceParam(QuantityParam theQuantityParam) {
myNearDistanceParam = theQuantityParam;
}
public QuantityParam getNearDistanceParam() {
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 {
/*
* Don't reorder! We rely on the ordinals

View File

@ -40,6 +40,7 @@ import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.MetaUtil;
import ca.uhn.fhir.util.UrlUtil;
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.hl7.fhir.instance.model.api.IIdType;
@ -88,7 +89,7 @@ public class InMemoryResourceMatcher {
if (searchParameterMap.getLastUpdated() != null) {
return InMemoryMatchResult.unsupportedFromParameterAndReason(Constants.PARAM_LASTUPDATED, InMemoryMatchResult.STANDARD_PARAMETER);
}
if (searchParameterMap.getNearDistanceParam() != null) {
if (searchParameterMap.containsKey(Location.SP_NEAR)) {
return InMemoryMatchResult.unsupportedFromReason(InMemoryMatchResult.LOCATION_NEAR);
}