From 4f7db37d8e9ed9b6800c790e063c9afcbb468ecb Mon Sep 17 00:00:00 2001 From: Diederik Muylwyk Date: Wed, 25 Sep 2019 21:36:36 -0400 Subject: [PATCH] Additional progress for ancestor/descendant filters; only ancestor/in remains. --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 97 +++++++++++++++---- .../jpa/term/TerminologySvcImplDstu3Test.java | 4 + 2 files changed, 82 insertions(+), 19 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 017fbb6a329..57220a5ab49 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -876,9 +876,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, handleFilterLoincParentChild(theQb, theBool, theFilter); break; case "ancestor": + isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); + handleFilterLoincAncestor(theSystem, theQb, theBool, theFilter); + break; case "descendant": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); - handleFilterLoincAncestorDescendant(theQb, theBool, theFilter); + handleFilterLoincDescendant(theSystem, theQb, theBool, theFilter); break; case "copyright": isCodeSystemLoingOrThrowInvalidRequestException(theSystem, theFilter.getProperty()); @@ -942,12 +945,15 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } private void handleFilterLoincParentChild(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { - addLoincFilterParentChildEqual(theBool, theFilter.getProperty(), theFilter.getValue()); - } else if (theFilter.getOp() == ValueSet.FilterOperator.IN) { - addLoincFilterParentChildIn(theBool, theFilter); - } else { - throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); + switch (theFilter.getOp()) { + case EQUAL: + addLoincFilterParentChildEqual(theBool, theFilter.getProperty(), theFilter.getValue()); + break; + case IN: + addLoincFilterParentChildIn(theBool, theFilter); + break; + default: + throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } @@ -970,28 +976,81 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, return new Term(TermConceptPropertyFieldBridge.CONCEPT_FIELD_PROPERTY_PREFIX + theProperty, theValue); } - private void handleFilterLoincAncestorDescendant(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { - if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) { - addLoincFilterAncestorDescendantEqual(theBool, theFilter.getProperty(), theFilter.getValue()); - } else if (theFilter.getOp() == ValueSet.FilterOperator.IN) { - addLoincFilterAncestorDescendantIn(theBool, theFilter); - } else { - throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); + private void handleFilterLoincAncestor(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + switch (theFilter.getOp()) { + case EQUAL: + addLoincFilterAncestorEqual(theSystem, theQb, theBool, theFilter); + break; + case IN: + addLoincFilterAncestorIn(theSystem, theQb, theBool, theFilter); + break; + default: + throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); } } - private void addLoincFilterAncestorDescendantEqual(BooleanJunction theBool, String theProperty, String theValue) { - logFilteringValueOnProperty(theValue, theProperty); - // FIXME: DM 2019-09-25 - Filter with op=EQUAL on ancestor/descendant + private void addLoincFilterAncestorEqual(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + TermConcept code = findCode(theSystem, theFilter.getValue()) + .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + theSystem + "}" + theFilter.getValue())); + + logFilteringValueOnProperty(theFilter.getValue(), theFilter.getProperty()); + theBool.must(theQb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery()); } - private void addLoincFilterAncestorDescendantIn(BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + private void addLoincFilterAncestorIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { String[] values = theFilter.getValue().split(","); List terms = new ArrayList<>(); for (String value : values) { logFilteringValueOnProperty(value, theFilter.getProperty()); - // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor/descendant + // FIXME: DM 2019-09-25 - Filter with op=IN on ancestor } + theBool.must(new TermsQuery(terms)); + } + + private void handleFilterLoincDescendant(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + switch (theFilter.getOp()) { + case EQUAL: + addLoincFilterDescendantEqual(theSystem, theBool, theFilter); + break; + case IN: + addLoincFilterDescendantIn(theSystem, theQb, theBool, theFilter); + break; + default: + throw new InvalidRequestException("Don't know how to handle op=" + theFilter.getOp() + " on property " + theFilter.getProperty()); + } + } + + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + addLoincFilterDescendantEqual(theSystem, theBool, theFilter.getProperty(), theFilter.getValue()); + } + + private void addLoincFilterDescendantEqual(String theSystem, BooleanJunction theBool, String theProperty, String theValue) { + List terms = getDescendantTerms(theSystem, theProperty, theValue); + theBool.must(new TermsQuery(terms)); + } + + private void addLoincFilterDescendantIn(String theSystem, QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { + String[] values = theFilter.getValue().split(","); + List terms = new ArrayList<>(); + for (String value : values) { + terms.addAll(getDescendantTerms(theSystem, theFilter.getProperty(), value)); + } + theBool.must(new TermsQuery(terms)); + } + + private List getDescendantTerms(String theSystem, String theProperty, String theValue) { + List retVal = new ArrayList<>(); + + TermConcept code = findCode(theSystem, theValue) + .orElseThrow(() -> new InvalidRequestException("Invalid filter criteria - code does not exist: {" + theSystem + "}" + theValue)); + + String[] parentPids = code.getParentPidsAsString().split(" "); + for (String parentPid : parentPids) { + retVal.add(new Term("myId", parentPid)); + } + logFilteringValueOnProperty(theValue, theProperty); + + return retVal; } private void handleFilterLoincCopyright(QueryBuilder theQb, BooleanJunction theBool, ValueSet.ConceptSetFilterComponent theFilter) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index b38e178ac22..35b92796f17 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -150,6 +151,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { LOINC_URI, code2.getCode(), code2.getDisplay()); + code1.addChild(code2, TermConceptParentChildLink.RelationshipTypeEnum.ISA); cs.getConcepts().add(code1); code2.addPropertyString("SYSTEM", "Ser"); @@ -164,11 +166,13 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { LOINC_URI, code3.getCode(), code3.getDisplay()); + code2.addChild(code3, TermConceptParentChildLink.RelationshipTypeEnum.ISA); code2.addPropertyCoding( "child", LOINC_URI, code4.getCode(), code4.getDisplay()); + code2.addChild(code4, TermConceptParentChildLink.RelationshipTypeEnum.ISA); cs.getConcepts().add(code2); code3.addPropertyString("SYSTEM", "Ser");