r4 works. But distance calculation still needs to be fixed.

This commit is contained in:
Ken Stevens 2020-01-22 11:59:03 -05:00
parent 5da2823553
commit 9413e8f198
7 changed files with 156 additions and 14 deletions

View File

@ -23,7 +23,7 @@ import ca.uhn.fhir.util.CoverageIgnore;
*/
public class HasOrListParam extends BaseOrListParam<HasOrListParam, HasParam> {
public class HasOrListParam extends BaseOrListParam<HasOrListParam, HasParam> {
@CoverageIgnore
@Override

View File

@ -98,6 +98,12 @@ public class ParameterUtil {
binder = new QueryParameterAndBinder(HasAndListParam.class,
Collections.<Class<? extends IQueryParameterType>>emptyList());
break;
case SPECIAL:
binder = new QueryParameterAndBinder(SpecialAndListParam.class,
Collections.emptyList());
break;
default:
throw new IllegalArgumentException("Parameter '" + theUnqualifiedParamName + "' has type " + paramType + " which is currently not supported.");
}
// FIXME null access

View File

@ -2075,8 +2075,9 @@ public class SearchBuilder implements ISearchBuilder {
From<?, ResourceIndexedSearchParamCoords> theFrom) {
String latitudeValue;
String longitudeValue;
Double distance = 0.0;
if (theParam instanceof TokenParam) {
if (theParam instanceof TokenParam) { // DSTU3
TokenParam param = (TokenParam) theParam;
String value = param.getValue();
String[] parts = value.split(":");
@ -2088,21 +2089,40 @@ public class SearchBuilder implements ISearchBuilder {
if (isBlank(latitudeValue) || isBlank(longitudeValue)) {
throw new IllegalArgumentException("Invalid position format '" + value + "'. Both latitude and longitude must be provided.");
}
QuantityParam distanceParam = myParams.getNearDistanceParam();
if (distanceParam != null) {
distance = distanceParam.getValue().doubleValue();
}
} else if (theParam instanceof SpecialParam) { // R4
SpecialParam param = (SpecialParam) theParam;
String value = param.getValue();
String[] parts = value.split("\\|");
if (parts.length < 2 || parts.length > 4) {
throw new IllegalArgumentException("Invalid position format '" + value + "'. Required format is 'latitude|longitude' or 'latitude|longitude|distance' or 'latitude|longitude|distance|units'");
}
latitudeValue = parts[0];
longitudeValue = parts[1];
if (isBlank(latitudeValue) || isBlank(longitudeValue)) {
throw new IllegalArgumentException("Invalid position format '" + value + "'. Both latitude and longitude must be provided.");
}
if (parts.length >= 3) {
String distanceString = parts[2];
if (!isBlank(distanceString)) {
distance = Double.valueOf(distanceString);
}
}
} else {
throw new IllegalArgumentException("Invalid position type: " + theParam.getClass());
}
QuantityParam distanceParam = myParams.getNearDistanceParam();
Predicate latitudePredicate;
Predicate longitudePredicate;
if (distanceParam == null || distanceParam.getValue().doubleValue() == 0.0) {
if (distance == 0.0) {
latitudePredicate = theBuilder.equal(theFrom.get("myLatitude"), latitudeValue);
longitudePredicate = theBuilder.equal(theFrom.get("myLongitude"), longitudeValue);
} else if (distance < 0.0) {
throw new IllegalArgumentException("Invalid " + Location.SP_NEAR_DISTANCE + " parameter '" + distance + "' must be >= 0.0");
} else {
Double distance = distanceParam.getValue().doubleValue();
if (distance < 0.0) {
throw new IllegalArgumentException("Invalid " + Location.SP_NEAR_DISTANCE + " parameter '" + distance + "' must be >= 0.0");
}
Double latitude = Double.valueOf(latitudeValue);
latitudePredicate = theBuilder.and(
theBuilder.greaterThanOrEqualTo(theFrom.get("myLatitude"), latitude - distance),
@ -3063,7 +3083,11 @@ public class SearchBuilder implements ISearchBuilder {
break;
case HAS:
case SPECIAL:
// should not happen
for (List<? extends IQueryParameterType> nextAnd : theAndOrParams) {
if ("Location.position".equals(nextParamDef.getPath())) {
addPredicateCoords(theResourceName, theParamName, nextAnd);
}
}
break;
}
} else {

View File

@ -63,6 +63,7 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
@ -4142,6 +4143,87 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
assertThat(toUnqualifiedVersionlessIdValues(outcome), contains(crId));
}
@Test
public void testNearSearchDistanceNoDistance() {
Location loc = new Location();
double latitude = 1000.0;
double longitude = 2000.0;
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 = 1000.0;
double longitude = 2000.0;
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 = 1000.0;
double longitude = 2000.0;
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 offset = 50.0;
SearchParameterMap map = myMatchUrlService.translateMatchUrl(
"Location?" +
Location.SP_NEAR + "=" + (latitude + offset) + "|" +
(longitude - offset) + "|" +
(offset * 2) + "|km",
myFhirCtx.getResourceDefinition("Location"));
List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map));
assertThat(ids, contains(locId));
}
{ // Outside the box
double offset = 50.0;
SearchParameterMap map = myMatchUrlService.translateMatchUrl(
"Location?" +
Location.SP_NEAR + "=" + (latitude + offset) + "|" +
(longitude - offset) + "|" +
(offset / 2) + "|km",
myFhirCtx.getResourceDefinition("Location"));
List<String> ids = toUnqualifiedVersionlessIdValues(myLocationDao.search(map));
assertThat(ids.size(), is(0));
}
}
private String toStringMultiline(List<?> theResults) {
StringBuilder b = new StringBuilder();

View File

@ -43,7 +43,6 @@ import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.ObjectUtils;
import org.hl7.fhir.dstu3.model.Location;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
@ -52,7 +51,6 @@ import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.IdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
@ -310,6 +308,21 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.TOKEN);
}
@Override
public SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource) {
String resourceTypeName = toRootTypeName(theResource);
IExtractor<BaseResourceIndexedSearchParam> extractor = (params, searchParam, value, path) -> {
if ("Location.position".equals(path)) {
addCoords_Position(resourceTypeName, params, searchParam, value);
}
};
return extractSearchParams(theResource, extractor, RestSearchParameterTypeEnum.SPECIAL);
}
private void addUnexpectedDatatypeWarning(SearchParamSet<?> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
theParams.addWarning("Search param " + theSearchParam.getName() + " is of unexpected datatype: " + theValue.getClass());
}
@ -719,12 +732,17 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
private void addCoords_Position(String theResourceType, SearchParamSet<BaseResourceIndexedSearchParam> theParams, RuntimeSearchParam theSearchParam, IBase theValue) {
BigDecimal latitude = null;
BigDecimal longitude = null;
if (theValue instanceof Location.LocationPositionComponent) {
Location.LocationPositionComponent value = (Location.LocationPositionComponent) theValue;
if (theValue instanceof org.hl7.fhir.dstu3.model.Location.LocationPositionComponent) {
org.hl7.fhir.dstu3.model.Location.LocationPositionComponent value = (org.hl7.fhir.dstu3.model.Location.LocationPositionComponent) theValue;
latitude = value.getLatitude();
longitude = value.getLongitude();
} else if (theValue instanceof org.hl7.fhir.r4.model.Location.LocationPositionComponent) {
org.hl7.fhir.r4.model.Location.LocationPositionComponent value = (org.hl7.fhir.r4.model.Location.LocationPositionComponent) theValue;
latitude = value.getLatitude();
longitude = value.getLongitude();
}
// KHS we only accept coordinates when both are present
// We only accept coordinates when both are present
if (latitude != null && longitude != null) {
ResourceIndexedSearchParamCoords nextEntity = new ResourceIndexedSearchParamCoords(theResourceType, theSearchParam.getName(), latitude.doubleValue(), longitude.doubleValue());
theParams.add(nextEntity);

View File

@ -40,6 +40,8 @@ public interface ISearchParamExtractor {
SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamTokens(IBaseResource theResource);
SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource);
SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(IBaseResource theResource);
SearchParamSet<PathAndRef> extractResourceLinks(IBaseResource theResource);

View File

@ -83,6 +83,12 @@ public class SearchParamExtractorService {
}
}
for (BaseResourceIndexedSearchParam next : extractSearchParamSpecial(theResource)) {
if (next instanceof ResourceIndexedSearchParamCoords) {
theParams.myCoordsParams.add((ResourceIndexedSearchParamCoords) next);
}
}
populateResourceTable(theParams.myStringParams, theEntity);
populateResourceTable(theParams.myNumberParams, theEntity);
populateResourceTable(theParams.myQuantityParams, theEntity);
@ -139,6 +145,10 @@ public class SearchParamExtractorService {
return mySearchParamExtractor.extractSearchParamTokens(theResource);
}
private ISearchParamExtractor.SearchParamSet<BaseResourceIndexedSearchParam> extractSearchParamSpecial(IBaseResource theResource) {
return mySearchParamExtractor.extractSearchParamSpecial(theResource);
}
private ISearchParamExtractor.SearchParamSet<ResourceIndexedSearchParamUri> extractSearchParamUri(IBaseResource theResource) {
return mySearchParamExtractor.extractSearchParamUri(theResource);
}