r4 works. But distance calculation still needs to be fixed.
This commit is contained in:
parent
5da2823553
commit
9413e8f198
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue