From e032914d742ae3e7365967babe0047401ff0f811 Mon Sep 17 00:00:00 2001 From: Ian Date: Tue, 8 Oct 2024 17:36:20 -0400 Subject: [PATCH] Initial pass at some of the sorting work --- .../FhirMedicationDispenseDaoImpl_2_6.java | 3 +- .../openmrs/module/fhir2/FhirConstants.java | 4 +- .../module/fhir2/api/dao/impl/BaseDao.java | 99 ++++++++++++------- .../fhir2/api/dao/impl/BaseFhirDao.java | 50 +++++----- .../fhir2/api/dao/impl/BasePersonDao.java | 22 ++--- .../impl/FhirAllergyIntoleranceDaoImpl.java | 5 +- .../api/dao/impl/FhirConditionDaoImpl.java | 7 +- .../dao/impl/FhirDiagnosticReportDaoImpl.java | 5 +- .../api/dao/impl/FhirEncounterDaoImpl.java | 5 +- .../api/dao/impl/FhirLocationDaoImpl.java | 13 +-- .../api/dao/impl/FhirObservationDaoImpl.java | 5 +- .../api/dao/impl/FhirPatientDaoImpl.java | 29 +++--- .../dao/impl/FhirRelatedPersonDaoImpl.java | 66 ++++++------- .../fhir2/api/dao/impl/FhirVisitDaoImpl.java | 5 +- .../dao/impl/OpenmrsFhirCriteriaContext.java | 4 +- .../api/search/PersonSearchQueryTest.java | 34 ++++--- .../impl/ValueSetTranslatorImplTest.java | 4 +- 17 files changed, 208 insertions(+), 152 deletions(-) diff --git a/api-2.6/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirMedicationDispenseDaoImpl_2_6.java b/api-2.6/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirMedicationDispenseDaoImpl_2_6.java index f606c254d6..998cc6eb90 100644 --- a/api-2.6/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirMedicationDispenseDaoImpl_2_6.java +++ b/api-2.6/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirMedicationDispenseDaoImpl_2_6.java @@ -11,6 +11,7 @@ import javax.annotation.Nonnull; import javax.persistence.criteria.From; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import java.util.List; @@ -92,7 +93,7 @@ protected Optional handleLastUpdated(OpenmrsFhirCriteriaContex } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { return super.paramToProp(criteriaContext, param); } } diff --git a/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java b/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java index 42a35c5053..e4272164c6 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java +++ b/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java @@ -182,9 +182,9 @@ private FhirConstants() { public static final String GLOBAL_PROPERTY_MODERATE = "allergy.concept.severity.moderate"; public static final String GLOBAL_PROPERTY_OTHER = "allergy.concept.severity.other"; - + public static final String GLOBAL_PROPERTY_DEFAULT_CONCEPT_MAP_TYPE = "concept.defaultConceptMapType"; - + public static final String GLOBAL_PROPERTY_URI_PREFIX = "fhir2.uriPrefix"; public static final String ENCOUNTER_REFERENCE_SEARCH_HANDLER = "encounter.reference.search.handler"; diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseDao.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseDao.java index 58f980db6d..f060ff24c5 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseDao.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseDao.java @@ -16,6 +16,7 @@ import javax.persistence.criteria.From; import javax.persistence.criteria.Join; import javax.persistence.criteria.Order; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; @@ -244,8 +245,14 @@ protected , U extends IQueryParameterType> Option if (andListParam == null) { return Optional.empty(); } + + Predicate[] predicates = toCriteriaArray(handleAndListParam(andListParam).map(handler)); + + if (predicates.length == 0) { + return Optional.empty(); + } - return Optional.of(criteriaBuilder.and((toCriteriaArray(handleAndListParam(andListParam).map(handler))))); + return Optional.of(criteriaBuilder.and(predicates)); } protected , U extends IQueryParameterType> Optional handleAndListParamAsStream( @@ -254,9 +261,15 @@ protected , U extends IQueryParameterType> Option if (andListParam == null) { return Optional.empty(); } - - return Optional.of(criteriaBuilder.and((toCriteriaArray(handleAndListParam(andListParam) - .map(orListParam -> handleOrListParamAsStream(criteriaBuilder, orListParam, handler)))))); + + Predicate[] predicates = toCriteriaArray(handleAndListParam(andListParam) + .map(orListParam -> handleOrListParamAsStream(criteriaBuilder, orListParam, handler))); + + if (predicates.length == 0) { + return Optional.empty(); + } + + return Optional.of(criteriaBuilder.and(predicates)); } /** @@ -273,8 +286,14 @@ protected Optional handleOrListParam( if (orListParam == null) { return Optional.empty(); } - - return Optional.of(criteriaBuilder.or(toCriteriaArray(handleOrListParam(orListParam).map(handler)))); + + Predicate[] predicates = toCriteriaArray(handleOrListParam(orListParam).map(handler)); + + if ( predicates.length == 0) { + return Optional.empty(); + } + + return Optional.of(criteriaBuilder.or(predicates)); } protected Optional handleOrListParamAsStream(CriteriaBuilder criteriaBuilder, @@ -282,8 +301,14 @@ protected Optional handleOrListParamA if (orListParam == null) { return Optional.empty(); } - - return Optional.of(criteriaBuilder.or(toCriteriaArray(handleOrListParam(orListParam).flatMap(handler)))); + + Predicate[] predicates = toCriteriaArray(handleOrListParam(orListParam).flatMap(handler)); + + if (predicates.length == 0) { + return Optional.empty(); + } + + return Optional.of(criteriaBuilder.or(predicates)); } /** @@ -710,10 +735,13 @@ protected Optional handleCodeableConcept(OpenmrsFhirCriteriaCo return handleAndListParamBySystem(criteriaContext.getCriteriaBuilder(), concepts, (system, tokens) -> { if (system.isEmpty()) { - - Predicate inConceptId = criteriaContext.getCriteriaBuilder().in(conceptAlias.get("conceptId")).value(criteriaContext.getCriteriaBuilder().literal(tokensToParams(tokens).map(NumberUtils::toInt).collect(Collectors.toList()))); - Predicate inUuid = criteriaContext.getCriteriaBuilder().in(conceptAlias.get("uuid")).value(criteriaContext.getCriteriaBuilder().literal(tokensToList(tokens))); - + + Predicate inConceptId = criteriaContext.getCriteriaBuilder().in(conceptAlias.get("conceptId")) + .value(criteriaContext.getCriteriaBuilder() + .literal(tokensToParams(tokens).map(NumberUtils::toInt).collect(Collectors.toList()))); + Predicate inUuid = criteriaContext.getCriteriaBuilder().in(conceptAlias.get("uuid")) + .value(criteriaContext.getCriteriaBuilder().literal(tokensToList(tokens))); + return Optional.of(criteriaContext.getCriteriaBuilder().or(inConceptId, inUuid)); } else { Join conceptMapAliasJoin = criteriaContext.addJoin(conceptAlias, "conceptMappings", conceptMapAlias); @@ -891,7 +919,7 @@ protected Optional handleMedicationReference(OpenmrsFhirCriter } protected Optional handleMedicationRequestReference(OpenmrsFhirCriteriaContext criteriaContext, - @Nonnull From drugOrderAlias, ReferenceAndListParam drugOrderReference) { + @Nonnull From drugOrderAlias, ReferenceAndListParam drugOrderReference) { if (drugOrderReference == null) { return Optional.empty(); } @@ -914,16 +942,17 @@ protected void handleSort(OpenmrsFhirCriteriaContext criteriaContex handleSort(criteriaContext, sort, this::paramToProps).ifPresent(l -> l.forEach(criteriaContext::addOrder)); } - protected Optional> handleSort( + protected Optional> handleSort( OpenmrsFhirCriteriaContext criteriaContext, SortSpec sort, - BiFunction, SortState, Collection> paramToProp) { - List orderings = new ArrayList<>(); + BiFunction, SortState, Collection> paramToProp) { + + List orderings = new ArrayList<>(); SortSpec sortSpec = sort; while (sortSpec != null) { - SortState state = SortState.builder().context(criteriaContext).sortOrder(sortSpec.getOrder()) + SortState state = SortState.builder().context(criteriaContext).sortOrder(sortSpec.getOrder()) .parameter(sortSpec.getParamName().toLowerCase()).build(); - Collection orders = paramToProp.apply(criteriaContext, state); + Collection orders = paramToProp.apply(criteriaContext, state); if (orders != null) { orderings.addAll(orders); } @@ -1029,17 +1058,17 @@ protected TokenAndListParam convertStringStatusToBoolean(TokenAndListParam statu * @param sortState a {@link SortState} object describing the current sort state * @return the corresponding ordering(s) needed for this property */ - protected Collection paramToProps(OpenmrsFhirCriteriaContext criteriaContext, - @Nonnull SortState sortState) { - Collection prop = paramToProps(criteriaContext, sortState.getParameter()); + protected Collection paramToProps(@Nonnull OpenmrsFhirCriteriaContext criteriaContext, + @Nonnull SortState sortState) { + Collection> prop = paramToProps(criteriaContext, sortState.getParameter()); if (prop != null) { switch (sortState.getSortOrder()) { case ASC: - return prop.stream().map(s -> criteriaContext.getCriteriaBuilder().asc(criteriaContext.getRoot().get(s))) + return prop.stream().map(p -> criteriaContext.getCriteriaBuilder().asc(p)) .collect(Collectors.toList()); case DESC: return prop.stream() - .map(s -> criteriaContext.getCriteriaBuilder().desc(criteriaContext.getRoot().get(s))) + .map(p -> criteriaContext.getCriteriaBuilder().desc(p)) .collect(Collectors.toList()); } } @@ -1054,9 +1083,9 @@ protected Collection paramToProps(OpenmrsFhirCriteriaContext * @param param the FHIR parameter to map * @return the name of the corresponding property from the current query */ - protected Collection paramToProps(OpenmrsFhirCriteriaContext criteriaContext, + protected Collection> paramToProps(@Nonnull OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { - String prop = paramToProp(criteriaContext, param); + Path prop = paramToProp(criteriaContext, param); if (prop != null) { return Collections.singleton(prop); @@ -1072,7 +1101,7 @@ protected Collection paramToProps(OpenmrsFhirCriteriaContext String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { return null; } @@ -1126,15 +1155,15 @@ protected Stream handleOrListParam(IQueryPara @SafeVarargs @SuppressWarnings("unused") - protected final Predicate[] toCriteriaArray(Optional... predicate) { + protected final @Nonnull Predicate[] toCriteriaArray(@Nonnull Optional... predicate) { return toCriteriaArray(Arrays.stream(predicate)); } - protected Predicate[] toCriteriaArray(Collection> collection) { + protected @Nonnull Predicate[] toCriteriaArray(@Nonnull Collection> collection) { return toCriteriaArray(collection.stream()); } - protected Predicate[] toCriteriaArray(Stream> predicateStream) { + protected @Nonnull Predicate[] toCriteriaArray(@Nonnull Stream> predicateStream) { return predicateStream.filter(Optional::isPresent).map(Optional::get).toArray(Predicate[]::new); } @@ -1144,9 +1173,9 @@ protected Predicate[] toCriteriaArray(Stream> pred @Data @Builder @EqualsAndHashCode - public static final class SortState { + public static final class SortState { - private OpenmrsFhirCriteriaContext context; + private OpenmrsFhirCriteriaContext context; private SortOrderEnum sortOrder; @@ -1272,7 +1301,7 @@ protected OpenmrsFhirCriteriaContext createCriteriaContext(Class cq = (CriteriaQuery) cb.createQuery(rootType); @SuppressWarnings("unchecked") Root root = (Root) cq.from(rootType); - + return new OpenmrsFhirCriteriaContext<>(em, cb, cq, root); } @@ -1284,13 +1313,13 @@ protected OpenmrsFhirCriteriaContext createCriteriaContext(Class From getRootOrJoin(OpenmrsFhirCriteriaContext criteriaContext, From alias) { + + protected From getRootOrJoin(OpenmrsFhirCriteriaContext criteriaContext, From alias) { if (alias == null) { return criteriaContext.getRoot(); } else { return criteriaContext.getJoin(alias).orElseThrow(() -> new IllegalStateException( - "Tried to reference alias " + alias + " before creating a join with that name")); + "Tried to reference alias " + alias + " before creating a join with that name")); } } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java index 7079db0b92..d55d2018cf 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java @@ -18,6 +18,7 @@ import javax.persistence.EntityManager; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Selection; @@ -158,20 +159,20 @@ protected OpenmrsFhirCriteriaContext getSearchResultCriteria(SearchPar return criteriaContext; } - + @Override @SuppressWarnings({ "unchecked", "UnstableApiUsage" }) public List getSearchResults(@Nonnull SearchParameterMap theParams) { List results; OpenmrsFhirCriteriaContext criteriaContext = getSearchResultCriteria(theParams); String idProperty = getIdPropertyName(criteriaContext); - + handleSort(criteriaContext, theParams.getSortSpec()); handleIdPropertyOrdering(criteriaContext, idProperty); - + TypedQuery executableQuery = (TypedQuery) criteriaContext.getEntityManager() - .createQuery(criteriaContext.finalizeQuery()); - + .createQuery(criteriaContext.finalizeQuery()); + executableQuery.setFirstResult(theParams.getFromIndex()); if (theParams.getToIndex() != Integer.MAX_VALUE) { int maxResults = theParams.getToIndex() - theParams.getFromIndex(); @@ -183,38 +184,39 @@ public List getSearchResults(@Nonnull SearchParameterMap theParams) { executableQuery.setMaxResults(negative); } } - + if (hasDistinctResults()) { - results = (List) criteriaContext.getEntityManager().createQuery(criteriaContext.getCriteriaQuery()) - .getResultList(); + results = executableQuery.getResultList(); } else { List> selectionList = new ArrayList<>(); selectionList.add(criteriaContext.getRoot().get(idProperty).alias("id")); criteriaContext.getCriteriaQuery().multiselect(selectionList).distinct(true); - + criteriaContext.getCriteriaQuery().select(criteriaContext.getRoot().get(getIdPropertyName(criteriaContext))) - .distinct(true); - + .distinct(true); + List ids = new ArrayList<>(); if (selectionList.size() > 1) { - for (Object[] o : ((List) criteriaContext.getEntityManager().createQuery(criteriaContext.getCriteriaQuery()).getResultList())) { + for (Object[] o : ((List) criteriaContext.getEntityManager() + .createQuery(criteriaContext.getCriteriaQuery()).getResultList())) { ids.add((Integer) o[0]); } } else { - ids = (List) criteriaContext.getEntityManager().createQuery(criteriaContext.getCriteriaQuery()).getResultList(); + ids = (List) criteriaContext.getEntityManager().createQuery(criteriaContext.getCriteriaQuery()) + .getResultList(); } - + // Use distinct ids from the original query to return entire objects OpenmrsFhirCriteriaContext wrapperQuery = createCriteriaContext(typeToken.getRawType()); wrapperQuery.getCriteriaQuery().where(wrapperQuery.getRoot().get(idProperty).in(ids)); - + // Need to reapply ordering handleSort(criteriaContext, theParams.getSortSpec()); handleIdPropertyOrdering(criteriaContext, idProperty); - + results = wrapperQuery.getEntityManager().createQuery(wrapperQuery.getCriteriaQuery()).getResultList(); } - + return results.stream().map(this::deproxyResult).collect(Collectors.toList()); } @@ -329,7 +331,7 @@ protected void setupSearchParams(OpenmrsFhirCriteriaContext criteriaCo @Override protected Collection paramToProps( - OpenmrsFhirCriteriaContext criteriaContext, @Nonnull SortState sortState) { + OpenmrsFhirCriteriaContext criteriaContext, @Nonnull SortState sortState) { String param = sortState.getParameter(); if (FhirConstants.SP_LAST_UPDATED.equalsIgnoreCase(param)) { @@ -337,12 +339,10 @@ protected Collection paramToProps( switch (sortState.getSortOrder()) { case ASC: return Collections - .singletonList((javax.persistence.criteria.Order) criteriaContext.getCriteriaQuery().orderBy( - criteriaContext.getCriteriaBuilder().asc(criteriaContext.getRoot().get("dateCreated")))); + .singletonList(criteriaContext.getCriteriaBuilder().asc(criteriaContext.getRoot().get("dateCreated"))); case DESC: return Collections.singletonList( - (javax.persistence.criteria.Order) criteriaContext.getCriteriaQuery().orderBy( - criteriaContext.getCriteriaBuilder().desc(criteriaContext.getRoot().get("dateCreated")))); + criteriaContext.getCriteriaBuilder().desc(criteriaContext.getRoot().get("dateCreated"))); } } @@ -358,14 +358,14 @@ protected Collection paramToProps( } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { if (DomainResource.SP_RES_ID.equals(param)) { - return "uuid"; + return criteriaContext.getRoot().get("uuid"); } return super.paramToProp(criteriaContext, param); } - + @SuppressWarnings("unchecked") protected void applyExactTotal(OpenmrsFhirCriteriaContext criteriaContext, SearchParameterMap theParams) { List> exactTotal = theParams.getParameters(EXACT_TOTAL_SEARCH_PARAMETER); diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BasePersonDao.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BasePersonDao.java index d8fa1f48e6..83b24a3da3 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BasePersonDao.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BasePersonDao.java @@ -24,6 +24,7 @@ import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Order; +import javax.persistence.criteria.Path; import java.util.ArrayList; import java.util.Collection; @@ -66,7 +67,7 @@ public abstract class BasePersonDao extends @Override protected Collection paramToProps(OpenmrsFhirCriteriaContext criteriaContext, - @Nonnull SortState sortState) { + @Nonnull SortState sortState) { String param = sortState.getParameter(); if (param == null) { @@ -148,7 +149,7 @@ protected Collection paramToProps(OpenmrsFhirCriteriaContext // first patient name if there are no preferred patient name cb.and(cb.not(cb.exists(personNameSecondSubquery.finalizeQuery())), cb.equal(personNameJoin.get("personNameId"), personNameThirdSubquery.finalizeQuery())), - // ultimate fallback, I guess? + // ultimate fallback, I guess? cb.isNull(personNameJoin.get("personNameId"))))); String[] properties = null; @@ -179,29 +180,28 @@ protected Collection paramToProps(OpenmrsFhirCriteriaContext break; } - criteriaContext.getCriteriaQuery().orderBy(sortStateOrders); return sortStateOrders; - } return super.paramToProps(criteriaContext, sortState); } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { From person = getPersonProperty(criteriaContext); - From address = criteriaContext.addJoin(person, "addresses", "pad"); + From address = criteriaContext.getJoin("pad").orElseGet(() -> criteriaContext.addJoin(person, "addresses", "pad")); + switch (param) { case SP_BIRTHDATE: - return "birthdate"; + return person.get("birthdate"); case SP_ADDRESS_CITY: - address.get("cityVillage"); + return address.get("cityVillage"); case SP_ADDRESS_STATE: - address.get("stateProvince"); + return address.get("stateProvince"); case SP_ADDRESS_POSTALCODE: - address.get("postalCode"); + return address.get("postalCode"); case SP_ADDRESS_COUNTRY: - address.get("country"); + return address.get("country"); default: return null; } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirAllergyIntoleranceDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirAllergyIntoleranceDaoImpl.java index 00b1d5a067..83859e39ce 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirAllergyIntoleranceDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirAllergyIntoleranceDaoImpl.java @@ -12,6 +12,7 @@ import javax.annotation.Nonnull; import javax.persistence.criteria.From; import javax.persistence.criteria.Join; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import java.util.Map; @@ -180,9 +181,9 @@ private Optional handleAllergenCategory(OpenmrsFhirCriteriaCon } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { if (AllergyIntolerance.SP_SEVERITY.equals(param)) { - return "severity"; + return criteriaContext.getRoot().get("severity"); } return super.paramToProp(criteriaContext, param); diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl.java index 9439143263..b70c0007be 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl.java @@ -11,6 +11,7 @@ import javax.annotation.Nonnull; import javax.persistence.criteria.From; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import java.util.List; @@ -136,12 +137,12 @@ protected Optional handleLastUpdated(OpenmrsFhirCriteriaContex } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { switch (param) { case org.hl7.fhir.r4.model.Condition.SP_ONSET_DATE: - return "onsetDate"; + return criteriaContext.getRoot().get("onsetDate"); case org.hl7.fhir.r4.model.Condition.SP_RECORDED_DATE: - return "dateCreated"; + return criteriaContext.getRoot().get("dateCreated"); } return super.paramToProp(criteriaContext, param); } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirDiagnosticReportDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirDiagnosticReportDaoImpl.java index 13ee7f0d24..b8e0705a2a 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirDiagnosticReportDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirDiagnosticReportDaoImpl.java @@ -11,6 +11,7 @@ import javax.persistence.criteria.From; import javax.persistence.criteria.Join; +import javax.persistence.criteria.Path; import java.util.Optional; @@ -86,9 +87,9 @@ private void handleObservationReference(OpenmrsFhirCriteriaContext String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { if (DiagnosticReport.SP_ISSUED.equals(param)) { - return "issued"; + return criteriaContext.getRoot().get("issued"); } return super.paramToProp(criteriaContext, param); diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirEncounterDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirEncounterDaoImpl.java index 93814dcb4f..7890419930 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirEncounterDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirEncounterDaoImpl.java @@ -18,6 +18,7 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.From; import javax.persistence.criteria.Join; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; @@ -111,10 +112,10 @@ protected void handleParticipant(OpenmrsFhirCriteriaContext cr } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { switch (param) { case SP_DATE: - return "encounterDatetime"; + return criteriaContext.getRoot().get("encounterDatetime"); default: return null; } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirLocationDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirLocationDaoImpl.java index cb2f4e6eeb..1db9833df6 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirLocationDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirLocationDaoImpl.java @@ -11,6 +11,7 @@ import javax.annotation.Nonnull; import javax.persistence.criteria.From; +import javax.persistence.criteria.Path; import java.util.List; @@ -141,18 +142,18 @@ private void handleParentLocation(OpenmrsFhirCriteriaContext cr } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { switch (param) { case org.hl7.fhir.r4.model.Location.SP_NAME: - return "name"; + return criteriaContext.getRoot().get("name"); case org.hl7.fhir.r4.model.Location.SP_ADDRESS_CITY: - return "cityVillage"; + return criteriaContext.getRoot().get("cityVillage"); case org.hl7.fhir.r4.model.Location.SP_ADDRESS_STATE: - return "stateProvince"; + return criteriaContext.getRoot().get("stateProvince"); case org.hl7.fhir.r4.model.Location.SP_ADDRESS_COUNTRY: - return "country"; + return criteriaContext.getRoot().get("country"); case org.hl7.fhir.r4.model.Location.SP_ADDRESS_POSTALCODE: - return "postalCode"; + return criteriaContext.getRoot().get("postalCode"); default: return super.paramToProp(criteriaContext, param); diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirObservationDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirObservationDaoImpl.java index 8523d8b594..73a4dcfa9a 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirObservationDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirObservationDaoImpl.java @@ -12,6 +12,7 @@ import javax.annotation.Nonnull; import javax.persistence.criteria.From; import javax.persistence.criteria.Join; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import java.util.Date; @@ -290,9 +291,9 @@ private void handleValueCodedConcept(OpenmrsFhirCriteriaContext crit } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String paramName) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String paramName) { if (Observation.SP_DATE.equals(paramName)) { - return "obsDatetime"; + return criteriaContext.getRoot().get("obsDatetime"); } return null; diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java index be78e0ed13..fe232cb21c 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java @@ -12,13 +12,10 @@ import static org.hl7.fhir.r4.model.Patient.SP_DEATH_DATE; import javax.annotation.Nonnull; -import javax.persistence.EntityManager; import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import java.util.ArrayList; import java.util.Collection; @@ -48,7 +45,8 @@ public class FhirPatientDaoImpl extends BasePersonDao implements FhirPa @Override public Patient getPatientById(@Nonnull Integer id) { OpenmrsFhirCriteriaContext criteriaContext = createCriteriaContext(Patient.class); - criteriaContext.getCriteriaQuery().select(criteriaContext.getRoot()).where(criteriaContext.getCriteriaBuilder().equal(criteriaContext.getRoot().get("patientId"), id)); + criteriaContext.getCriteriaQuery().select(criteriaContext.getRoot()) + .where(criteriaContext.getCriteriaBuilder().equal(criteriaContext.getRoot().get("patientId"), id)); TypedQuery query = criteriaContext.getEntityManager().createQuery(criteriaContext.getCriteriaQuery()); return query.getResultList().stream().findFirst().orElse(null); @@ -65,10 +63,19 @@ public List getPatientsByIds(@Nonnull Collection ids) { @Override public PatientIdentifierType getPatientIdentifierTypeByNameOrUuid(String name, String uuid) { - OpenmrsFhirCriteriaContext criteriaContext = createCriteriaContext(PatientIdentifierType.class); - criteriaContext.getCriteriaQuery().select(criteriaContext.getRoot()).where(criteriaContext.getCriteriaBuilder().or(criteriaContext.getCriteriaBuilder().and(criteriaContext.getCriteriaBuilder().equal(criteriaContext.getRoot().get("name"), name), - criteriaContext.getCriteriaBuilder().equal(criteriaContext.getRoot().get("retired"), false)), criteriaContext.getCriteriaBuilder().equal(criteriaContext.getRoot().get("uuid"), uuid))); - List identifierTypes = criteriaContext.getEntityManager().createQuery(criteriaContext.getCriteriaQuery()).getResultList(); + OpenmrsFhirCriteriaContext criteriaContext = createCriteriaContext( + PatientIdentifierType.class); + criteriaContext.getCriteriaQuery().select(criteriaContext.getRoot()) + .where( + criteriaContext + .getCriteriaBuilder().or( + criteriaContext.getCriteriaBuilder().and( + criteriaContext.getCriteriaBuilder().equal(criteriaContext.getRoot().get("name"), name), + criteriaContext.getCriteriaBuilder().equal(criteriaContext.getRoot().get("retired"), + false)), + criteriaContext.getCriteriaBuilder().equal(criteriaContext.getRoot().get("uuid"), uuid))); + List identifierTypes = criteriaContext.getEntityManager() + .createQuery(criteriaContext.getCriteriaQuery()).getResultList(); if (identifierTypes.isEmpty()) { return null; @@ -188,9 +195,9 @@ protected void handleIdentifier(OpenmrsFhirCriteriaContext crite } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { if (SP_DEATH_DATE.equalsIgnoreCase(param)) { - return "deathDate"; + return criteriaContext.getRoot().get("deathDate"); } return super.paramToProp(criteriaContext, param); } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImpl.java index 137685bd92..388becd498 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImpl.java @@ -64,9 +64,9 @@ protected void setupSearchParams(OpenmrsFhirCriteriaContext .ifPresent(c -> criteriaContext.addPredicate(c).finalizeQuery())); break; case FhirConstants.DATE_RANGE_SEARCH_HANDLER: - entry.getValue().forEach( - param -> handleDateRange(criteriaContext, personJoin, (DateRangeParam) param.getParam()) - .ifPresent(c -> criteriaContext.addPredicate(c).finalizeQuery())); + entry.getValue() + .forEach(param -> handleDateRange(criteriaContext, personJoin, (DateRangeParam) param.getParam()) + .ifPresent(c -> criteriaContext.addPredicate(c).finalizeQuery())); break; case FhirConstants.ADDRESS_SEARCH_HANDLER: handleAddresses(criteriaContext, entry); @@ -78,23 +78,25 @@ protected void setupSearchParams(OpenmrsFhirCriteriaContext } }); } - + // TODO: find a way of integrating this with the handleDateRange functionality in BaseDao! - private Optional handleDateRange(OpenmrsFhirCriteriaContext criteriaContext, From personJoin, DateRangeParam param) { + private Optional handleDateRange(OpenmrsFhirCriteriaContext criteriaContext, + From personJoin, DateRangeParam param) { if (param == null) { return Optional.empty(); } - + return Optional.ofNullable(criteriaContext.getCriteriaBuilder() - .and(toCriteriaArray(Stream.of(handleDate(criteriaContext, personJoin, param.getLowerBound()), - handleDate(criteriaContext, personJoin, param.getUpperBound()))))); - } - - private Optional handleDate(OpenmrsFhirCriteriaContext criteriaContext, From personJoin, DateParam dateParam) { + .and(toCriteriaArray(Stream.of(handleDate(criteriaContext, personJoin, param.getLowerBound()), + handleDate(criteriaContext, personJoin, param.getUpperBound()))))); + } + + private Optional handleDate(OpenmrsFhirCriteriaContext criteriaContext, + From personJoin, DateParam dateParam) { if (dateParam == null) { return Optional.empty(); } - + int calendarPrecision = dateParam.getPrecision().getCalendarConstant(); if (calendarPrecision > Calendar.SECOND) { calendarPrecision = Calendar.SECOND; @@ -102,42 +104,40 @@ private Optional handleDate(OpenmrsFhirCriteriaContext Collection paramToProps(OpenmrsFhirCriteriaContext criteriaContext, - @Nonnull SortState sortState) { + @Nonnull SortState sortState) { String param = sortState.getParameter(); if (param == null) { @@ -209,7 +209,7 @@ protected Collection paramToProps(OpenmrsFhirCriteriaContext } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @Nonnull String param) { From personJoin = criteriaContext.addJoin("personA", "m"); From pad = criteriaContext.addJoin(personJoin, "addresses", "pad", javax.persistence.criteria.JoinType.LEFT); switch (param) { diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirVisitDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirVisitDaoImpl.java index 5a95356aef..bc2e1374df 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirVisitDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirVisitDaoImpl.java @@ -13,6 +13,7 @@ import javax.persistence.criteria.From; import javax.persistence.criteria.Join; +import javax.persistence.criteria.Path; import java.util.Optional; @@ -54,10 +55,10 @@ protected void handleParticipant(OpenmrsFhirCriteriaContext criter } @Override - protected String paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { + protected Path paramToProp(OpenmrsFhirCriteriaContext criteriaContext, @NonNull String param) { switch (param) { case SP_DATE: - return "startDatetime"; + return criteriaContext.getRoot().get("startDatetime"); default: return null; } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/OpenmrsFhirCriteriaContext.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/OpenmrsFhirCriteriaContext.java index d7221938e0..a6e6d53aae 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/OpenmrsFhirCriteriaContext.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/OpenmrsFhirCriteriaContext.java @@ -214,8 +214,8 @@ public OpenmrsFhirCriteriaContext addResults(T result) { public Optional> getJoin(String alias) { return Optional.ofNullable(aliases.get(alias)); } - - public Optional> getJoin(From alias) { + + public Optional> getJoin(From alias) { return Optional.ofNullable(aliases.get(alias.getAlias())); } diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/search/PersonSearchQueryTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/search/PersonSearchQueryTest.java index 57f37f3619..30ced70785 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/search/PersonSearchQueryTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/search/PersonSearchQueryTest.java @@ -113,22 +113,34 @@ public class PersonSearchQueryTest extends BaseModuleContextSensitiveTest { ret = compareWithNullAsGreatest(o1.getFamily(), o2.getFamily()); // familyName if (ret == 0) { // familyName2 - ret = compareWithNullAsGreatest(o1.getExtension().get(2).getValue().toString(), - o2.getExtension().get(2).getValue().toString()); + if (o1.hasExtension(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyName2") && + o2.hasExtension(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyName2")) { + ret = compareWithNullAsGreatest( + o1.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyName2").getValue().toString(), + o2.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyName2").getValue().toString()); + } } - + if (ret == 0) { // givenName + middleName ret = compareWithNullAsGreatest(o1.getGivenAsSingleString(), o2.getGivenAsSingleString()); } - + if (ret == 0) { // familyNamePrefix - ret = compareWithNullAsGreatest(o1.getExtension().get(1).getValue().toString(), - o2.getExtension().get(1).getValue().toString()); + if (o1.hasExtension(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyNamePrefix") && + o2.hasExtension(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyNamePrefix")) { + ret = compareWithNullAsGreatest( + o1.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyNamePrefix").getValue().toString(), + o2.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyNamePrefix").getValue().toString()); + } } - + if (ret == 0) { // familyNameSuffix - ret = compareWithNullAsGreatest(o1.getExtension().get(3).getValue().toString(), - o2.getExtension().get(3).getValue().toString()); + if (o1.hasExtension(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyNameSuffix") && + o2.hasExtension(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyNameSuffix")) { + ret = compareWithNullAsGreatest( + o1.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyNameSuffix").getValue().toString(), + o2.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_NAME + "#familyNameSuffix").getValue().toString()); + } } if (ret == 0) { @@ -137,7 +149,7 @@ public class PersonSearchQueryTest extends BaseModuleContextSensitiveTest { return ret; }); - + @Autowired private FhirPersonDao dao; @@ -478,7 +490,7 @@ public void shouldAddPersonLinksToResultListWhenIncluded() { } @Test - public void shouldReturnCollectionOfPeopleSortedByGivenName() { + public void shouldReturnCollectionOfPeopleSortedByName() { SortSpec sort = new SortSpec(); sort.setParamName(SP_NAME); sort.setOrder(SortOrderEnum.ASC); diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ValueSetTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ValueSetTranslatorImplTest.java index 03a01aa1a2..2e084e0ba5 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ValueSetTranslatorImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ValueSetTranslatorImplTest.java @@ -53,7 +53,7 @@ public class ValueSetTranslatorImplTest extends BaseModuleContextSensitiveTest { @Mock private Concept concept; - + @Autowired private FhirGlobalPropertyService globalPropertyService; @@ -63,7 +63,7 @@ public class ValueSetTranslatorImplTest extends BaseModuleContextSensitiveTest { public void setup() { valueSetTranslator.setConceptSourceService(conceptSourceService); } - + @Before public void setGlobalProperty() { when(globalPropertyService.getGlobalProperty(GLOBAL_PROPERTY_DEFAULT_CONCEPT_MAP_TYPE)).thenReturn("NARROWER-THAN");