aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Gleeson <luke.gleeson@est.tech>2023-05-31 11:07:57 +0000
committerGerrit Code Review <gerrit@onap.org>2023-05-31 11:07:57 +0000
commit53ae115e95876f8b51a142574cbf308f42cccbc6 (patch)
tree15b5d5b22ec4df344807e4904005999f6c1d6246
parentd123807549a7f4670ecd273641c46ed101f62893 (diff)
parent2696de664b097c8dceb4332e9896417835e77178 (diff)
Merge "Add <,> operators support to cps-path"
-rw-r--r--cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g410
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java58
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathComparativeOperator.java64
-rw-r--r--cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java3
-rw-r--r--cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy28
-rw-r--r--cps-ri/pom.xml2
-rw-r--r--cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java49
-rw-r--r--integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy34
8 files changed, 194 insertions, 54 deletions
diff --git a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4 b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
index 86c1705617..c88a822654 100644
--- a/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
+++ b/cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
@@ -47,13 +47,15 @@ listElementRef : OB leafCondition ( booleanOperators leafCondition)* CB ;
multipleLeafConditions : OB leafCondition ( booleanOperators leafCondition)* CB ;
-leafCondition : AT leafName EQ ( IntegerLiteral | StringLiteral) ;
+leafCondition : AT leafName comparativeOperators ( IntegerLiteral | StringLiteral) ;
leafName : QName ;
booleanOperators : ( KW_AND | KW_OR ) ;
-invalidPostFix : (AT | CB | COLONCOLON | EQ ).+ ;
+comparativeOperators : ( EQ | GT | LT | GE | LE ) ;
+
+invalidPostFix : (AT | CB | COLONCOLON | comparativeOperators ).+ ;
/*
* Lexer Rules
@@ -70,6 +72,10 @@ SLASH : '/' ;
COMMA : ',' ;
OP : '(' ;
CP : ')' ;
+GT : '>' ;
+LT : '<' ;
+GE : '>=' ;
+LE : '<=' ;
// KEYWORDS
diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
index f44e310a1f..5c47127375 100644
--- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
@@ -25,10 +25,8 @@ import static org.onap.cps.cpspath.parser.CpsPathPrefixType.DESCENDANT;
import java.util.ArrayList;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Queue;
import org.onap.cps.cpspath.parser.antlr4.CpsPathBaseListener;
import org.onap.cps.cpspath.parser.antlr4.CpsPathParser;
import org.onap.cps.cpspath.parser.antlr4.CpsPathParser.AncestorAxisContext;
@@ -59,7 +57,7 @@ public class CpsPathBuilder extends CpsPathBaseListener {
final List<String> booleanOperators = new ArrayList<>();
- final Queue<String> booleanOperatorsQueue = new LinkedList<>();
+ final List<String> comparativeOperators = new ArrayList<>();
@Override
public void exitInvalidPostFix(final CpsPathParser.InvalidPostFixContext ctx) {
@@ -83,25 +81,20 @@ public class CpsPathBuilder extends CpsPathBaseListener {
@Override
public void exitLeafCondition(final LeafConditionContext ctx) {
- Object comparisonValue = null;
+ Object comparisonValue;
if (ctx.IntegerLiteral() != null) {
comparisonValue = Integer.valueOf(ctx.IntegerLiteral().getText());
- }
- if (ctx.StringLiteral() != null) {
- final boolean wasWrappedInDoubleQuote = ctx.StringLiteral().getText().startsWith("\"");
+ } else if (ctx.StringLiteral() != null) {
+ final boolean wasWrappedInDoubleQuote = ctx.StringLiteral().getText().startsWith("\"");
comparisonValue = stripFirstAndLastCharacter(ctx.StringLiteral().getText());
if (wasWrappedInDoubleQuote) {
comparisonValue = String.valueOf(comparisonValue).replace("'", "\\'");
}
- } else if (comparisonValue == null) {
- throw new PathParsingException("Unsupported comparison value encountered in expression" + ctx.getText());
- }
- leavesData.put(ctx.leafName().getText(), comparisonValue);
- final String booleanOperator = booleanOperatorsQueue.poll();
- appendCondition(normalizedXpathBuilder, ctx.leafName().getText(), booleanOperator, comparisonValue);
- if (processingAncestorAxis) {
- appendCondition(normalizedAncestorPathBuilder, ctx.leafName().getText(), booleanOperator, comparisonValue);
+ } else {
+ throw new PathParsingException(
+ "Unsupported comparison value encountered in expression" + ctx.getText());
}
+ leafContext(ctx.leafName(), comparisonValue);
}
@Override
@@ -109,8 +102,13 @@ public class CpsPathBuilder extends CpsPathBaseListener {
final CpsPathBooleanOperatorType cpsPathBooleanOperatorType = CpsPathBooleanOperatorType.fromString(
ctx.getText());
booleanOperators.add(cpsPathBooleanOperatorType.getValues());
- booleanOperatorsQueue.add(cpsPathBooleanOperatorType.getValues());
- cpsPathQuery.setBooleanOperatorsType(booleanOperators);
+ }
+
+ @Override
+ public void exitComparativeOperators(final CpsPathParser.ComparativeOperatorsContext ctx) {
+ final CpsPathComparativeOperator cpsPathComparativeOperator = CpsPathComparativeOperator.fromString(
+ ctx.getText());
+ comparativeOperators.add(cpsPathComparativeOperator.getLabel());
}
@Override
@@ -174,6 +172,8 @@ public class CpsPathBuilder extends CpsPathBaseListener {
CpsPathQuery build() {
cpsPathQuery.setNormalizedXpath(normalizedXpathBuilder.toString());
cpsPathQuery.setContainerNames(containerNames);
+ cpsPathQuery.setBooleanOperators(booleanOperators);
+ cpsPathQuery.setComparativeOperators(comparativeOperators);
return cpsPathQuery;
}
@@ -192,14 +192,30 @@ public class CpsPathBuilder extends CpsPathBaseListener {
}
}
+ private void leafContext(final CpsPathParser.LeafNameContext ctx, final Object comparisonValue) {
+ leavesData.put(ctx.getText(), comparisonValue);
+ appendCondition(normalizedXpathBuilder, ctx.getText(), comparisonValue);
+ if (processingAncestorAxis) {
+ appendCondition(normalizedAncestorPathBuilder, ctx.getText(), comparisonValue);
+ }
+ }
+
private void appendCondition(final StringBuilder currentNormalizedPathBuilder, final String name,
- final String booleanOperator, final Object value) {
+ final Object value) {
final char lastCharacter = currentNormalizedPathBuilder.charAt(currentNormalizedPathBuilder.length() - 1);
- currentNormalizedPathBuilder.append(lastCharacter == '[' ? "" : " " + booleanOperator + " ")
- .append("@")
+ final boolean isStartOfExpression = lastCharacter == '[';
+ if (!isStartOfExpression) {
+ currentNormalizedPathBuilder.append(" ").append(getLastElement(booleanOperators)).append(" ");
+ }
+ currentNormalizedPathBuilder.append("@")
.append(name)
- .append("='")
+ .append(getLastElement(comparativeOperators))
+ .append("'")
.append(value)
.append("'");
}
+
+ private String getLastElement(final List<String> listOfStrings) {
+ return listOfStrings.get(listOfStrings.size() - 1);
+ }
}
diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathComparativeOperator.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathComparativeOperator.java
new file mode 100644
index 0000000000..c7ffd0d7ec
--- /dev/null
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathComparativeOperator.java
@@ -0,0 +1,64 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2023 Tech Mahindra Ltd
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.cps.cpspath.parser;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum CpsPathComparativeOperator {
+ EQ("="),
+ GT(">"),
+ LT("<"),
+ GE(">="),
+ LE("<=");
+
+ private final String label;
+
+ CpsPathComparativeOperator(final String label) {
+ this.label = label;
+ }
+
+ public final String getLabel() {
+ return this.label;
+ }
+
+ private static final Map<String, CpsPathComparativeOperator> cpsPathComparativeOperatorPerLabel = new HashMap<>();
+
+ static {
+ for (final CpsPathComparativeOperator cpsPathComparativeOperator : CpsPathComparativeOperator.values()) {
+ cpsPathComparativeOperatorPerLabel.put(cpsPathComparativeOperator.label, cpsPathComparativeOperator);
+ }
+ }
+
+ /**
+ * Finds the value of the given enumeration.
+ *
+ * @param label value of the enum
+ * @return a comparativeOperatorType
+ */
+ public static CpsPathComparativeOperator fromString(final String label) {
+ if (!cpsPathComparativeOperatorPerLabel.containsKey(label)) {
+ throw new PathParsingException("Incomplete leaf condition (no operator)");
+ }
+ return cpsPathComparativeOperatorPerLabel.get(label);
+ }
+}
+
diff --git a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java
index 418b5ec55b..3c3cbccf7e 100644
--- a/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java
+++ b/cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathQuery.java
@@ -43,7 +43,8 @@ public class CpsPathQuery {
private String ancestorSchemaNodeIdentifier = "";
private String textFunctionConditionLeafName;
private String textFunctionConditionValue;
- private List<String> booleanOperatorsType;
+ private List<String> booleanOperators;
+ private List<String> comparativeOperators;
private String containsFunctionConditionLeafName;
private String containsFunctionConditionValue;
diff --git a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
index 9552a4d342..9ab5491b5d 100644
--- a/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
+++ b/cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
@@ -71,6 +71,10 @@ class CpsPathQuerySpec extends Specification {
'yang container' | '/cps-path' || '/cps-path'
'descendant anywhere' | '//cps-path' || '//cps-path'
'descendant with leaf condition' | '//cps-path[@key=1]' || "//cps-path[@key='1']"
+ 'descendant with leaf condition has ">" operator' | '//cps-path[@key>9]' || "//cps-path[@key>'9']"
+ 'descendant with leaf condition has "<" operator' | '//cps-path[@key<10]' || "//cps-path[@key<'10']"
+ 'descendant with leaf condition has ">=" operator' | '//cps-path[@key>=8]' || "//cps-path[@key>='8']"
+ 'descendant with leaf condition has "<=" operator' | '//cps-path[@key<=12]' || "//cps-path[@key<='12']"
'descendant with leaf value and ancestor' | '//cps-path[@key=1]/ancestor:parent[@key=1]' || "//cps-path[@key='1']/ancestor:parent[@key='1']"
'parent & child' | '/parent/child' || '/parent/child'
'parent leaf of type Integer & child' | '/parent/child[@code=1]/child2' || "/parent/child[@code='1']/child2"
@@ -108,16 +112,22 @@ class CpsPathQuerySpec extends Specification {
result.descendantName == "child"
result.leavesData.size() == expectedNumberOfLeaves
and: 'the given operator(s) returns in the correct order'
- result.booleanOperatorsType == expectedOperators
+ result.booleanOperators == expectedOperators
+ and: 'the given comparativeOperator(s) returns in the correct order'
+ result.comparativeOperators == expectedComparativeOperator
where: 'the following data is used'
- scenario | cpsPath || expectedNumberOfLeaves || expectedOperators
- 'one attribute' | '//child[@common-leaf-name-int=5]' || 1 || null
- 'more than one attribute has AND operator' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2 || ['and']
- 'more than one attribute has OR operator' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' || 2 || ['or']
- 'more than one attribute has combinations and operators' | '//child[@int-leaf=5 and @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['and', 'and']
- 'more than one attribute has combinations or operators' | '//child[@int-leaf=5 or @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['or', 'or']
- 'more than one attribute has combinations and/or combination' | '//child[@int-leaf=5 and @leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 2 || ['and', 'or']
- 'more than one attribute has combinations or/and combination' | '//child[@int-leaf=5 or @leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 2 || ['or', 'and']
+ scenario | cpsPath || expectedNumberOfLeaves || expectedOperators || expectedComparativeOperator
+ 'one attribute' | '//child[@common-leaf-name-int=5]' || 1 || [] || ['=']
+ 'more than one attribute has AND operator' | '//child[@int-leaf=5 and @leaf-name="leaf value"]' || 2 || ['and'] || ['=', '=']
+ 'more than one attribute has OR operator' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' || 2 || ['or'] || ['=', '=']
+ 'more than one attribute has combinations AND operators' | '//child[@int-leaf=5 and @common-leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 3 || ['and', 'and'] || ['=', '=', '=']
+ 'more than one attribute has combinations OR operators' | '//child[@int-leaf=5 or @common-leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 3 || ['or', 'or'] || ['=', '=', '=']
+ 'more than one attribute has combinations AND/OR combination' | '//child[@int-leaf=5 and @common-leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 3 || ['and', 'or'] || ['=', '=', '=']
+ 'more than one attribute has combinations OR/AND combination' | '//child[@int-leaf=5 or @common-leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 3 || ['or', 'and'] || ['=', '=', '=']
+ 'more than one attribute has AND/> operators' | '//child[@int-leaf>15 and @leaf-name="leaf value"]' || 2 || ['and'] || ['>', '=']
+ 'more than one attribute has OR/< operators' | '//child[@int-leaf<5 or @leaf-name="leaf value"]' || 2 || ['or'] || ['<', '=']
+ 'more than one attribute has combinations AND/>= operators' | '//child[@int-leaf>=18 and @common-leaf-name="leaf value" and @leaf-name="leaf value1" ]' || 3 || ['and', 'and'] || ['>=', '=', '=']
+ 'more than one attribute has combinations OR/<= operators' | '//child[@int-leaf<=25 or @common-leaf-name="leaf value" or @leaf-name="leaf value1" ]' || 3 || ['or', 'or'] || ['<=', '=', '=']
}
def 'Parse #scenario cps path with text function condition'() {
diff --git a/cps-ri/pom.xml b/cps-ri/pom.xml
index 25dc91c6fd..aa86a7fc06 100644
--- a/cps-ri/pom.xml
+++ b/cps-ri/pom.xml
@@ -33,7 +33,7 @@
<artifactId>cps-ri</artifactId>
<properties>
- <minimum-coverage>0.54</minimum-coverage>
+ <minimum-coverage>0.53</minimum-coverage>
<!-- Additional coverage is provided by integration-test module -->
</properties>
diff --git a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java
index 72750dcc92..ba94d56b1c 100644
--- a/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java
+++ b/cps-ri/src/main/java/org/onap/cps/spi/repository/FragmentQueryBuilder.java
@@ -23,7 +23,6 @@ package org.onap.cps.spi.repository;
import java.util.HashMap;
import java.util.LinkedList;
-import java.util.List;
import java.util.Map;
import java.util.Queue;
import javax.persistence.EntityManager;
@@ -36,6 +35,7 @@ import org.onap.cps.cpspath.parser.CpsPathQuery;
import org.onap.cps.spi.entities.AnchorEntity;
import org.onap.cps.spi.entities.DataspaceEntity;
import org.onap.cps.spi.entities.FragmentEntity;
+import org.onap.cps.spi.exceptions.CpsPathException;
import org.onap.cps.utils.JsonObjectMapper;
import org.springframework.stereotype.Component;
@@ -141,25 +141,40 @@ public class FragmentQueryBuilder {
private void addLeafConditions(final CpsPathQuery cpsPathQuery, final StringBuilder sqlStringBuilder) {
if (cpsPathQuery.hasLeafConditions()) {
- sqlStringBuilder.append(" AND (");
- final List<String> queryBooleanOperatorsType = cpsPathQuery.getBooleanOperatorsType();
- final Queue<String> booleanOperatorsQueue = (queryBooleanOperatorsType == null) ? null : new LinkedList<>(
- queryBooleanOperatorsType);
- cpsPathQuery.getLeavesData().entrySet().forEach(entry -> {
- sqlStringBuilder.append(" attributes @> ");
- sqlStringBuilder.append("'");
- sqlStringBuilder.append(jsonObjectMapper.asJsonString(entry));
- sqlStringBuilder.append("'");
- if (!(booleanOperatorsQueue == null || booleanOperatorsQueue.isEmpty())) {
- sqlStringBuilder.append(" ");
- sqlStringBuilder.append(booleanOperatorsQueue.poll());
- sqlStringBuilder.append(" ");
- }
- });
- sqlStringBuilder.append(")");
+ queryLeafConditions(cpsPathQuery, sqlStringBuilder);
}
}
+ private void queryLeafConditions(final CpsPathQuery cpsPathQuery, final StringBuilder sqlStringBuilder) {
+ sqlStringBuilder.append(" AND (");
+ final Queue<String> booleanOperatorsQueue = new LinkedList<>(cpsPathQuery.getBooleanOperators());
+ final Queue<String> comparativeOperatorQueue = new LinkedList<>(cpsPathQuery.getComparativeOperators());
+ cpsPathQuery.getLeavesData().entrySet().forEach(entry -> {
+ final String nextComparativeOperator = comparativeOperatorQueue.poll();
+ if (entry.getValue() instanceof Integer) {
+ sqlStringBuilder.append("(attributes ->> ");
+ sqlStringBuilder.append("'").append(entry.getKey()).append("')\\:\\:int");
+ sqlStringBuilder.append(" ").append(nextComparativeOperator).append(" ");
+ sqlStringBuilder.append("'").append(jsonObjectMapper.asJsonString(entry.getValue())).append("'");
+ } else {
+ if ("=".equals(nextComparativeOperator)) {
+ sqlStringBuilder.append(" attributes @> ");
+ sqlStringBuilder.append("'");
+ sqlStringBuilder.append(jsonObjectMapper.asJsonString(entry));
+ sqlStringBuilder.append("'");
+ } else {
+ throw new CpsPathException(" can use only " + nextComparativeOperator + " with integer ");
+ }
+ }
+ if (!booleanOperatorsQueue.isEmpty()) {
+ sqlStringBuilder.append(" ");
+ sqlStringBuilder.append(booleanOperatorsQueue.poll());
+ sqlStringBuilder.append(" ");
+ }
+ });
+ sqlStringBuilder.append(")");
+ }
+
private static void addTextFunctionCondition(final CpsPathQuery cpsPathQuery,
final StringBuilder sqlStringBuilder,
final Map<String, Object> queryParameters) {
diff --git a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy
index d64617caf8..233c58fb6e 100644
--- a/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy
+++ b/integration-test/src/test/groovy/org/onap/cps/integration/functional/CpsQueryServiceIntegrationSpec.groovy
@@ -55,13 +55,13 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
def 'Cps Path query using combinations of OR operator #scenario.'() {
when: 'a query is executed to get response by the given cps path'
- def result = objectUnderTest.queryDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, cpspath, OMIT_DESCENDANTS)
+ def result = objectUnderTest.queryDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, cpsPath, OMIT_DESCENDANTS)
then: 'the result contains expected number of nodes'
assert result.size() == expectedResultSize
and: 'the cps-path of queryDataNodes has the expectedLeaves'
assert result.leaves.sort() == expectedLeaves.sort()
where: 'the following data is used'
- scenario | cpspath || expectedResultSize | expectedLeaves
+ scenario | cpsPath || expectedResultSize | expectedLeaves
'the "OR" condition' | '//books[@lang="English" or @price=15]' || 6 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]],
[lang: "English", price: 15, title: "The Gruffalo", authors: ["Julia Donaldson"], editions: [1999]],
[lang: "English", price: 14, title: "The Light Fantastic", authors: ["Terry Pratchett"], editions: [1986]],
@@ -78,6 +78,29 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
'combination of OR/AND' | '//books[@title="Annihilation" or @price=39 and @lang="arabic"]' || 1 | [[lang: "English", price: 15, title: "Annihilation", authors: ["Jeff VanderMeer"], editions: [2014]]]
}
+ def 'cps-path query using combinations of Comparative Operators #scenario.'() {
+ when: 'a query is executed to get response by the given cpsPath'
+ def result = objectUnderTest.queryDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, cpsPath, OMIT_DESCENDANTS)
+ then: 'the result contains expected number of nodes'
+ assert result.size() == expectedResultSize
+ and: 'xpaths of the retrieved data nodes are as expected'
+ def bookTitles = result.collect { it.getLeaves().get('title') }
+ assert bookTitles.sort() == expectedBookTitles.sort()
+ where: 'the following data is used'
+ scenario | cpsPath || expectedResultSize | expectedBookTitles
+ 'the ">" condition' | '//books[@price>13 ]' || 5 | ['A Book with No Language', 'Annihilation', 'Debian GNU/Linux', 'The Gruffalo', 'The Light Fantastic']
+ 'the "<" condition ' | '//books[@price<15]' || 5 | ['Good Omens', 'Logarithm tables', 'Matilda', 'The Colour of Magic', 'The Light Fantastic']
+ 'the "<=" condition' | '//books[@price<=15]' || 7 | ['Annihilation', 'Good Omens', 'Logarithm tables', 'Matilda', 'The Colour of Magic', 'The Gruffalo', 'The Light Fantastic']
+ 'the ">=" condition' | '//books[@price>=20]' || 2 | ['A Book with No Language', 'Debian GNU/Linux']
+ 'the "<" condition where result does not exist' | '//books[@price<5]' || 0 | []
+ 'the ">" condition where result does not exist' | '//books[@price>1000]' || 0 | []
+ 'the ">" condition with AND condition' | '//books[@price>13 and @title="A Book with No Language"]' || 1 | ['A Book with No Language']
+ 'the "<" condition with OR condition' | '//books[@price<10 or @lang="German"]' || 1 | ['Debian GNU/Linux']
+ 'the "<=" condition with AND/OR condition' | '//books[@price<=15 and @title="Annihilation" or @lang="Spanish"]' || 1 | ['Annihilation']
+ 'the ">=" condition with OR/AND condition' | '//books[@price>=13 or @lang="Spanish" and @title="Good Omens"]' || 6 | ['A Book with No Language', 'Annihilation', 'Good Omens', 'Debian GNU/Linux', 'The Gruffalo', 'The Light Fantastic']
+ 'Mix of integer and string condition ' | '//books[@lang="German" and @price>38]' || 1 | ['Debian GNU/Linux']
+ }
+
def 'Cps Path query for leaf value(s) with #scenario.'() {
when: 'a query is executed to get a data node by the given cps path'
def result = objectUnderTest.queryDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, cpsPath, fetchDescendantsOption)
@@ -170,6 +193,7 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
where: 'the following data is used'
scenario | cpsPath || expectedBookTitles
'one leaf' | '//books[@price=14]' || ['The Light Fantastic']
+ 'one leaf with ">" condition' | '//books[@price>14]' || ['A Book with No Language', 'Annihilation', 'Debian GNU/Linux', 'The Gruffalo']
'one text' | '//books/authors[text()="Terry Pratchett"]' || ['Good Omens', 'The Colour of Magic', 'The Light Fantastic']
'more than one leaf' | '//books[@price=12 and @lang="English"]' || ['The Colour of Magic']
'more than one leaf has "OR" condition' | '//books[@lang="English" or @price=15]' || ['Annihilation', 'Good Omens', 'Matilda', 'The Colour of Magic', 'The Gruffalo', 'The Light Fantastic']
@@ -229,9 +253,13 @@ class CpsQueryServiceIntegrationSpec extends FunctionalSpecBase {
def 'Cps Path query with syntax error throws a CPS Path Exception.'() {
when: 'trying to execute a query with a syntax (parsing) error'
- objectUnderTest.queryDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, 'cpsPath that cannot be parsed' , OMIT_DESCENDANTS)
+ objectUnderTest.queryDataNodes(FUNCTIONAL_TEST_DATASPACE_1, BOOKSTORE_ANCHOR_1, cpsPath, OMIT_DESCENDANTS)
then: 'a cps path exception is thrown'
thrown(CpsPathException)
+ where: 'the following data is used'
+ scenario | cpsPath
+ 'cpsPath that cannot be parsed' | 'cpsPath that cannot be parsed'
+ 'String with comparative operator' | '//books[@lang>"German" and @price>10]'
}
def 'Cps Path query across anchors with #scenario.'() {