-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FM2-653 #556
FM2-653 #556
Changes from 8 commits
5b94f18
c46ba07
e5b00bd
9319b83
90ec5dd
29a5cb0
47d35d7
19723f0
b1efbfb
be996fe
370d392
aafa52a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,18 +10,23 @@ | |
package org.openmrs.module.fhir2.api.dao.impl; | ||
|
||
import static org.hibernate.criterion.Restrictions.eq; | ||
import static org.hibernate.criterion.Restrictions.or; | ||
|
||
import javax.annotation.Nonnull; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import ca.uhn.fhir.rest.param.ReferenceAndListParam; | ||
import ca.uhn.fhir.rest.param.ReferenceOrListParam; | ||
import ca.uhn.fhir.rest.param.ReferenceParam; | ||
import ca.uhn.fhir.rest.param.StringAndListParam; | ||
import ca.uhn.fhir.rest.param.TokenAndListParam; | ||
import lombok.AccessLevel; | ||
import lombok.Setter; | ||
import org.hibernate.Criteria; | ||
import org.hibernate.criterion.Criterion; | ||
import org.hibernate.sql.JoinType; | ||
import org.openmrs.Location; | ||
import org.openmrs.LocationAttribute; | ||
|
@@ -38,6 +43,8 @@ | |
@Setter(AccessLevel.PACKAGE) | ||
public class FhirLocationDaoImpl extends BaseFhirDao<Location> implements FhirLocationDao { | ||
|
||
private static final int SUPPORTED_LOCATION_HIERARCHY_SEARCH_DEPTH = 9; | ||
|
||
@Autowired | ||
LocationService locationService; | ||
|
||
|
@@ -62,7 +69,7 @@ protected void setupSearchParams(Criteria criteria, SearchParameterMap theParams | |
break; | ||
case FhirConstants.LOCATION_REFERENCE_SEARCH_HANDLER: | ||
entry.getValue() | ||
.forEach(param -> handleParentLocation(criteria, (ReferenceAndListParam) param.getParam())); | ||
.forEach(param -> handleLocationReference(criteria, (ReferenceAndListParam) param.getParam())); | ||
break; | ||
case FhirConstants.TAG_SEARCH_HANDLER: | ||
entry.getValue().forEach(param -> handleTag(criteria, (TokenAndListParam) param.getParam())); | ||
|
@@ -112,8 +119,56 @@ private void handleTag(Criteria criteria, TokenAndListParam tags) { | |
} | ||
} | ||
|
||
private void handleParentLocation(Criteria criteria, ReferenceAndListParam parent) { | ||
handleLocationReference("loc", parent).ifPresent(loc -> criteria.createAlias("parentLocation", "loc").add(loc)); | ||
private void handleLocationReference(Criteria criteria, ReferenceAndListParam locationAndReferences) { | ||
|
||
if (locationAndReferences == null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All this through 148 is basically a series null / size checks to make sure that ReferenceAnd list only contains a single reference. We probably should change this parameter to just a ReferenceParam but I didn't want to untangle the locationReference utility method at this point. Basically, the "partof:below" functionality I built currently only works correctly for a single parameter (and I don't have the need to build more now). Technically the plain "partof" works for multiple "partof", but would always return null since a location partof is 0..1 |
||
return; | ||
} | ||
|
||
List<ReferenceOrListParam> locationOrReference = locationAndReferences.getValuesAsQueryTokens(); | ||
|
||
if (locationOrReference == null || locationOrReference.isEmpty()) { | ||
return; | ||
} | ||
|
||
if (locationOrReference.size() > 1) { | ||
throw new IllegalArgumentException("Only one location reference is supported"); | ||
} | ||
|
||
List<ReferenceParam> locationReferences = locationOrReference.get(0).getValuesAsQueryTokens(); | ||
|
||
if (locationReferences == null || locationReferences.isEmpty()) { | ||
return; | ||
} | ||
|
||
if (locationReferences.size() > 1) { | ||
throw new IllegalArgumentException("Only one location reference is supported"); | ||
} | ||
|
||
ReferenceParam locationReference = locationReferences.get(0); | ||
|
||
// **NOTE: this is a *bug* in the current HAPI FHIR implementation, "below" should be the "queryParameterQualifier", not the resource type; likely need update this when/fix the HAPI FHIR implementation is fixed** | ||
// this is to support queries of the type "Location?partof=below:uuid" | ||
if ("below".equalsIgnoreCase(locationReference.getResourceType())) { | ||
List<Criterion> belowReferenceCriteria = new ArrayList<>(); | ||
|
||
// we need to add a join to the parentLocation for each level of hierarchy we want to search, and add "equals" criterion for each level | ||
int depth = 1; | ||
while (depth <= SUPPORTED_LOCATION_HIERARCHY_SEARCH_DEPTH) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be a for loop? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You mean aesthetically, or is there a bug here I'm missing? :) For some reason I tend to prefer while loops. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just aesthetics. It looks like it works. |
||
belowReferenceCriteria.add(eq("ancestor" + depth + ".uuid", locationReference.getIdPart())); | ||
criteria.createAlias(depth == 1 ? "parentLocation" : "ancestor" + (depth - 1) + ".parentLocation", | ||
"ancestor" + depth, JoinType.LEFT_OUTER_JOIN); | ||
depth++; | ||
} | ||
|
||
// "or" these call together so that we return the location if any of the joined ancestor location uuids match | ||
criteria.add(or(belowReferenceCriteria.toArray(new Criterion[0]))); | ||
} else { | ||
// this is to support queries of the type "Location?partof=uuid" or chained search like "Location?partof:Location=Location:name=xxx" | ||
handleLocationReference("loc", locationAndReferences) | ||
.ifPresent(loc -> criteria.createAlias("parentLocation", "loc").add(loc)); | ||
} | ||
|
||
} | ||
|
||
@Override | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically, I use a succession of joins to go back up the ancestor tree. A bit annoying/hacky, but from a practical matter, seems unlikely there would ever be a hierarchy of more than 9 levels. We could make this a configurable parameter if we wanted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we turn this into a GP? That way an implementation can adjust it to the actual depth of their location hierarchy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, I will do that (and since it's configurable, set a more normal default of like 5)