diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/SearchFilterParser.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/SearchFilterParser.java index 124c5adbfe0d..eac5b7a06e20 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/SearchFilterParser.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/predicate/SearchFilterParser.java @@ -237,14 +237,14 @@ private BaseFilter parseOpen() throws FilterSyntaxException { } private BaseFilter parseLogical(BaseFilter filter) throws FilterSyntaxException { - BaseFilter result = null; - String s; FilterLogical logical; if (filter == null) { - s = "not"; + logical = new FilterLogical(); + logical.setOperation(FilterLogicalOperation.not); + logical.setFilter1(parseOpen()); } else { - s = consumeName(); + String s = consumeName(); if ((!s.equals("or")) && (!s.equals("and")) && (!s.equals("not"))) { throw new FilterSyntaxException(Msg.code(1059) + String.format("Unexpected Name %s at %d", s, cursor)); } @@ -260,8 +260,8 @@ private BaseFilter parseLogical(BaseFilter filter) throws FilterSyntaxException } logical.setFilter2(parseOpen()); - result = logical; } + result = logical; return result; } @@ -594,8 +594,12 @@ void setFilter2(BaseFilter FFilter2) { @Override public String toString() { - return FFilter1.toString() + " " - + CODES_LogicalOperation.get(getOperation().ordinal()) + " " + FFilter2.toString(); + if ("not".equals(CODES_LogicalOperation.get(getOperation().ordinal()))) { + return CODES_LogicalOperation.get(getOperation().ordinal()) + " " + FFilter1.toString(); + } else { + return FFilter1.toString() + " " + + CODES_LogicalOperation.get(getOperation().ordinal()) + " " + FFilter2.toString(); + } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 19ba187c114a..b0af13edec3b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -98,6 +98,7 @@ import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.Expression; import com.healthmarketscience.sqlbuilder.InCondition; +import com.healthmarketscience.sqlbuilder.NotCondition; import com.healthmarketscience.sqlbuilder.SelectQuery; import com.healthmarketscience.sqlbuilder.SetOperationQuery; import com.healthmarketscience.sqlbuilder.Subquery; @@ -962,11 +963,14 @@ private Condition createPredicateFilter( theRequestPartitionId); // Right side - Condition yPredicate = createPredicateFilter( - theQueryStack3, - ((SearchFilterParser.FilterLogical) theFilter).getFilter2(), - theResourceName, - theRequestPartitionId); + Condition yPredicate = ((SearchFilterParser.FilterLogical) theFilter).getOperation() + == SearchFilterParser.FilterLogicalOperation.not + ? null + : createPredicateFilter( + theQueryStack3, + ((SearchFilterParser.FilterLogical) theFilter).getFilter2(), + theResourceName, + theRequestPartitionId); if (((SearchFilterParser.FilterLogical) theFilter).getOperation() == SearchFilterParser.FilterLogicalOperation.and) { @@ -974,6 +978,9 @@ private Condition createPredicateFilter( } else if (((SearchFilterParser.FilterLogical) theFilter).getOperation() == SearchFilterParser.FilterLogicalOperation.or) { return ComboCondition.or(xPredicate, yPredicate); + } else if (((SearchFilterParser.FilterLogical) theFilter).getOperation() + == SearchFilterParser.FilterLogicalOperation.not) { + return new NotCondition(xPredicate); } else { // Shouldn't happen throw new InvalidRequestException(Msg.code(1205) + "Don't know how to handle operation " diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/SearchFilterSyntaxTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/SearchFilterSyntaxTest.java index 7a1f95a8a3fe..cf54ed9332bb 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/SearchFilterSyntaxTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/SearchFilterSyntaxTest.java @@ -26,6 +26,11 @@ public void testToken() throws SearchFilterParser.FilterSyntaxException { testParse("name eq loinc|1234"); } + @Test + public void testTokenNot() throws SearchFilterParser.FilterSyntaxException { + testParse("not name eq loinc|1234"); + } + @Test public void testUrl() throws SearchFilterParser.FilterSyntaxException { testParse("name in http://loinc.org/vs/LP234"); @@ -56,6 +61,11 @@ public void testFilter2() throws SearchFilterParser.FilterSyntaxException { testParse("related[type eq comp and this lt that].target pr false"); } + @Test + public void testFilter2WithNot() throws SearchFilterParser.FilterSyntaxException { + testParse("related[type eq comp and not this lt that].target pr false"); + } + @Test public void testParentheses() throws SearchFilterParser.FilterSyntaxException { testParse("((userName eq \"bjensen\") or (userName eq \"jdoe\")) and (code sb snomed|diabetes)");