Table of Contents |
---|
References
Query data node using cps-path
In query of data node, when we can query using <,>,>=,<= comparative operators . Also we can combine “AND/OR” conditions with comparative operators . Below are the some examples
Comparative Operators Condition
# | cps-path | Output | |||||||
---|---|---|---|---|---|---|---|---|---|
1 | Using "<" condition cps-path : //books[@price<15] |
| |||||||
2 | Using "<" with OR condition cps-path : //books[@price<10 or @lang="German"] |
| |||||||
3 | Using ">" with AND condition cps-path : //books[@price>13 and @title="A Book with No Language"] |
| |||||||
4 | Using ">=" with combination of OR/AND condition cps-path : //books[@price>=13 or @lang="Spanish" and @title="Good Omens"] |
|
Issues & Decisions
#
Notes
Decision
Antlr changes to be made to recognize OR as input in cps-path
Adding OR operator to the antlr grammer file
PATH : cps-path-parser/src/main/antlr4/org/onap/cps/cpspath/parser/antlr4/CpsPath.g4
Solution : listElementRef : OB leafCondition ( ( KW_AND | KW_OR) leafCondition)* CB
multipleLeafConditions : OB leafCondition ( ( KW_AND | KW_OR) leafCondition)* CB
It is tested by antlr test
Code changes made to recognise the operatoe i.e., and ,or
PATH:cps-path-parser/src/main/java/org/onap/cps/cpspath/parser/CpsPathBuilder.java
Solution:
String parent = ctx.getParent().getText();
String payload = ctx.getPayload().getText();
payloads.add(payload);
String operator = findOperator(parent, payloads);
appendCondition(normalizedXpathBuilder, ctx.leafName().getText(), comparisonValue, operator);
if (processingAncestorAxis) {
appendCondition(normalizedAncestorPathBuilder, ctx.leafName().getText(), comparisonValue, operator);
}
}
private String findOperator(String parent, List<String> payloads) {
StringBuilder parentStringBuilder = new StringBuilder(parent);
try {
payloads.forEach(payload -> parentStringBuilder.delete(parentStringBuilder.indexOf(payload), parentStringBuilder.indexOf(payload) + payload.length()));
parentStringBuilder.delete(0, parentStringBuilder.indexOf("[") + 1);
return parentStringBuilder.toString().trim();
} catch (RuntimeException e) {
return null;
}
}
private void appendCondition(final StringBuilder currentNormalizedPathBuilder, final String name,
final Object value, String operator) {
final char lastCharacter = currentNormalizedPathBuilder.charAt(currentNormalizedPathBuilder.length() - 1);
currentNormalizedPathBuilder.append(lastCharacter == '[' ? "" : " " + operator + " ");
currentNormalizedPathBuilder.append("@");
currentNormalizedPathBuilder.append(name);
currentNormalizedPathBuilder.append("='");
currentNormalizedPathBuilder.append(value);
currentNormalizedPathBuilder.append("'");
}
}
PATH:cps-path-parser/src/test/groovy/org/onap/cps/cpspath/parser/CpsPathQuerySpec.groovy
1. def 'Parse cps path having OR operator containing #scenario.'() {
when: 'the given cps path is parsed'
def result = CpsPathUtil.getCpsPathQuery(cpsPath)
then: 'the query has the right normalized xpath type'
assert result.normalizedXpath == expectedNormalizedXPath
where: 'the following data is used'
scenario | cpsPath || expectedNormalizedXPath
'parent & child with more than one attribute' | '/parent/child[@key1=1 or @key2="abc"]/child2' || "/parent/child[@key1='1' or @key2='abc']/child2"
}
2. def 'Parse cps path that ends with a yang list containing #scenario and having or operator '() {
when: 'the given cps path is parsed'
def result = CpsPathQuery.createFrom(cpsPath)
then: 'the query has the right xpath type'
result.cpsPathPrefixType == DESCENDANT
and: 'the right parameters are set'
result.descendantName == "child"
result.leavesData.size() == expectedNumberOfLeaves
where: 'the following data is used'
scenario | cpsPath || expectedNumberOfLeaves
'more than one attribute' | '//child[@int-leaf=5 or @leaf-name="leaf value"]' || 2
}
Testcases in
CpsDataPersistenceQueryDataNodeSpec.groovy
def 'Cps Path query using descendant anywhere with #scenario OR condition(s) for a container element.'() {
when: 'a query is executed to get a data node by the given cps path'
def result = objectUnderTest.queryDataNodes(DATASPACE_NAME, ANCHOR_FOR_SHOP_EXAMPLE, cpsPath, OMIT_DESCENDANTS)
then: 'the correct number of data nodes are retrieved'
result.size() == expectedXPaths.size()
and: 'xpaths of the retrieved data nodes are as expected'
for (int i = 0; i < result.size(); i++) {
assert result[i].getXpath() == expectedXPaths[i]
}
where: 'the following data is used'
scenario
|
|
|
|
|
|
|
|
|
|
'leaves has OR condition' | '//author[@FirstName="Joe" or @Firstname="Jane"]' || ["/shops/shop[@id=''1'']/categories[@code=''1'']/book/author[@FirstName='Joe']", "/shops/shop[@id=''1'']/categories[@code=''2'']/book/author[@FirstName=''Jane'']"]
}
So, this groovy testcase is based on the Query where it as
"SELECT * FROM FRAGMENT WHERE anchor_id = :anchorId AND xpath ~ :xpathRegex AND attributes @> :leafDataAsJson\\:\\:jsonb
The problem is we are unable to find the logic how AND condition has been implemented , Also OR works as AND
In this query they are passing anchor id,xpatRegex,Attributes but no where we see either of two operators(i.e, and/or).
Here we had analysis that whatever the attributes having jsondata is taking as graphdatabase that inbuilt functions as AND
| |||||||||
5 | Using "<=" with combination of AND/OR condition cps-path : //books[@price<=15 and @title="Annihilation" or @lang="Spanish"] |
|
---|
Implementation of Comparative Operator
1.Update antlr parser to recognize <,>,<=,>= in leaf-condition
2.Implement required (native) query
3.Add Integration tests for comparative operators
4.Update documentation
5.demo to team
Query used : SELECT * FROM fragment WHERE anchor_id = :anchorId AND xpath ~ :xpathRegex AND ((attributes ->> 'price')\:\:int > '13' and attributes @> '{"title":"A Book with No Language"}')
Limitations
1.Using comparative operators with string values will lead to an error at runtime. This error can't be validated earlier as the datatype is unknown until the execution phaseWould like to get help with it.