Commit c8c977be authored by Georg Fette's avatar Georg Fette
Browse files

added further CQL-tests

refactored aggregator operators and nested queries
parent 947931a8
......@@ -19,6 +19,7 @@ public class CQLOperatorMapper {
aMap.addOp("and", OperatorType.AND);
aMap.addOp("or", OperatorType.OR);
aMap.addOp("not", OperatorType.NOT);
aMap.addOp("alltrue", OperatorType.ALLTRUE);
aMap.addOp("anytrue", OperatorType.ANYTRUE);
......
......@@ -40,6 +40,7 @@ import org.cqframework.cql.gen.cqlParser.MemberInvocationContext;
import org.cqframework.cql.gen.cqlParser.MultipleSourceClauseContext;
import org.cqframework.cql.gen.cqlParser.MultiplicationExpressionTermContext;
import org.cqframework.cql.gen.cqlParser.NamedTypeSpecifierContext;
import org.cqframework.cql.gen.cqlParser.NotExpressionContext;
import org.cqframework.cql.gen.cqlParser.NumberLiteralContext;
import org.cqframework.cql.gen.cqlParser.ParamListContext;
import org.cqframework.cql.gen.cqlParser.ParenthesizedTermContext;
......@@ -163,8 +164,13 @@ public class CQL_2_Graph_Mapper {
}
}
private void parseQueryExpression(QueryExpressionContext qe) {
private QueryOperator parseQueryExpression(QueryExpressionContext qe) {
String prettyPrint = prettyPrint(qe);
QueryOperator lastOp = currentOp;
QueryOperator thisOp = null;
if (currentOp != query.rootClause) {
thisOp = addOp(OperatorType.QUERY);
}
QueryContext queryContext = qe.query();
SourceClauseContext sourceClause = queryContext.sourceClause();
......@@ -185,7 +191,11 @@ public class CQL_2_Graph_Mapper {
WhereClauseContext whereClause = queryContext.whereClause();
if (whereClause != null) {
ExpressionContext whereExp = whereClause.expression();
currentOp = query.rootClause;
if (thisOp == null) {
currentOp = query.rootClause;
} else {
currentOp = thisOp;
}
parseExpression(whereExp);
}
......@@ -198,6 +208,8 @@ public class CQL_2_Graph_Mapper {
if (returnClause != null) {
parseReturnClauseContext(returnClause);
}
currentOp = lastOp;
return thisOp;
}
private void parseQueryInclusionClauseContext(QueryInclusionClauseContext anIncQuery) {
......@@ -260,13 +272,17 @@ public class CQL_2_Graph_Mapper {
private void parseSortClauseContext(SortClauseContext sortClause) {
String prettyPrint = prettyPrint(sortClause);
QueryOperator queryOp = currentOp;
currentOp = currentOp.source; // exchange the query with the sortOp
queryOp.source.removeParameter(queryOp);
QueryOperator lastOp = currentOp;
addOp(OperatorType.SORT);
currentOp.addParameter(queryOp);
List<SortByItemContext> sortByItem = sortClause.sortByItem();
for (SortByItemContext anItem : sortByItem) {
ExpressionTermContext expressionTerm = anItem.expressionTerm();
// TODO: this can also be an Op !
QueryConcept asConcept = lastOp.getParameters().get(0).asConcept();
// set the currentPath to point to the iterator of the surrounding query
QueryConcept asConcept = queryOp.getParameters().get(0).asConcept();
setCurrentPath(new GraphPath(asConcept));
parseExpressionTerm(expressionTerm);
addCurrentPathToCurrentOp();
......@@ -290,10 +306,20 @@ public class CQL_2_Graph_Mapper {
QuerySourceContext querySource = aliasedQuerySource.querySource();
RetrieveContext retrieve = querySource.retrieve();
QualifiedIdentifierContext qualifiedIdentifier = querySource.qualifiedIdentifier();
ExpressionContext expression = querySource.expression();
if (retrieve != null) {
parseRetrieve(retrieve);
} else if (qualifiedIdentifier != null) {
parseQualifiedIdentifier(qualifiedIdentifier);
// the path has been added to the query, but it has vanished from the currentPath
currentPath = new GraphPath(currentOp.getParameters().get(0).asConcept());
} else if (expression != null) {
parseExpression(expression);
if (currentOp != query.rootClause) {
currentOp.addParameter(getCurrentPath().getLastElement());
}
} else {
throw new RuntimeException("unexpected");
}
String alias = aliasedQuerySource.alias().identifier().getText();
aliases.put(alias, getCurrentPath());
......@@ -383,13 +409,23 @@ public class CQL_2_Graph_Mapper {
createdOp = parseOpWithParams(expression, OperatorType.EQUALS);
} else if (anExp instanceof ExistenceExpressionContext) {
ExistenceExpressionContext ee = (ExistenceExpressionContext) anExp;
createdOp = parseExistenceExpression(ee);
TermExpressionContext te = (TermExpressionContext) ee.expression();
List<ExpressionContext> params = new ArrayList<ExpressionContext>();
params.add(te);
// TermExpressionTermContext tet = (TermExpressionTermContext) te.expressionTerm();
// ParenthesizedTermContext pt = (ParenthesizedTermContext) tet.term();
// ExpressionContext expression = pt.expression();
createdOp = parseOpWithParams(params, OperatorType.EXISTS);
// createdOp = parseExistenceExpression(ee);
} else if (anExp instanceof TypeExpressionContext) {
TypeExpressionContext te = (TypeExpressionContext) anExp;
createdOp = parseTypeExpression(te);
} else if (anExp instanceof AndExpressionContext) {
AndExpressionContext ae = (AndExpressionContext) anExp;
createdOp = parseAndExpressionContext(ae);
} else if (anExp instanceof NotExpressionContext) {
NotExpressionContext ae = (NotExpressionContext) anExp;
createdOp = parseNotExpressionContext(ae);
} else if (anExp instanceof TimingExpressionContext) {
TimingExpressionContext te = (TimingExpressionContext) anExp;
createdOp = parseTimingExpressionContext(te);
......@@ -437,11 +473,12 @@ public class CQL_2_Graph_Mapper {
String prettyPrint = prettyPrint(ee);
QueryOperator lastOp = currentOp;
QueryOperator thisOp = addOp(OperatorType.EXISTS);
currentOp = thisOp;
TermExpressionContext te = (TermExpressionContext) ee.expression();
TermExpressionTermContext tet = (TermExpressionTermContext) te.expressionTerm();
ParenthesizedTermContext pt = (ParenthesizedTermContext) tet.term();
QueryExpressionContext qe = (QueryExpressionContext) pt.expression();
parseQueryExpression(qe);
ExpressionContext expression = pt.expression();
parseExpression(expression);
currentOp = lastOp;
return thisOp;
}
......@@ -511,6 +548,17 @@ public class CQL_2_Graph_Mapper {
return thisOp;
}
private QueryOperator parseNotExpressionContext(NotExpressionContext ne) {
String prettyPrint = prettyPrint(ne);
QueryOperator lastOp = currentOp;
QueryOperator thisOp = addOp(OperatorType.NOT);
currentOp = thisOp;
ExpressionContext expression = ne.expression();
parseExpression(expression);
currentOp = lastOp;
return thisOp;
}
private QueryOperator parseExpressionTerm(ExpressionTermContext te) {
String prettyPrint = prettyPrint(te);
QueryOperator lastOp = currentOp;
......@@ -585,6 +633,10 @@ public class CQL_2_Graph_Mapper {
} else if (aChild instanceof ListSelectorTermContext) {
ListSelectorTermContext lst = (ListSelectorTermContext) aChild;
lastCreatedOp = parseListSelectorTermContext(lst);
} else if (aChild instanceof ParenthesizedTermContext) {
ParenthesizedTermContext pt = (ParenthesizedTermContext) aChild;
ExpressionContext expression = pt.expression();
lastCreatedOp = parseExpression(expression);
} else if (aChild instanceof InvocationTermContext) {
InvocationTermContext it = (InvocationTermContext) aChild;
InvocationContext invocation = it.invocation();
......@@ -710,19 +762,25 @@ public class CQL_2_Graph_Mapper {
ParamListContext paramList = function.paramList();
List<ExpressionContext> expression = paramList.expression();
QueryOperator newOp = parseOpWithParams(expression, opType);
if (opType == OperatorType.LAST) {
// TODO: the contained parameter might as well be an arbitrary expression
setCurrentPath(new GraphPath(newOp.getParameters().get(0).asConcept()));
}
if (opType == OperatorType.COUNT) {
// here should something happen that determines the scope of the aggregation op
}
return newOp;
} else {
throw new RuntimeException("unexpected");
}
}
private QueryConcept getDemotionConcept(QueryOperator anOp) {
QueryOperator currentOp = anOp;
QueryConcept result = null;
while (result == null) {
if (currentOp.getParameters().get(0).isConcept()) {
result = currentOp.getParameters().get(0).asConcept();
} else {
currentOp = currentOp.getParameters().get(0).asOperator();
}
}
return result;
}
private void parseMemberInvocationContext(MemberInvocationContext mi) {
String prettyPrint = prettyPrint(mi);
String text = mi.identifier().getText();
......@@ -732,8 +790,9 @@ public class CQL_2_Graph_Mapper {
}
if ((currentOp != null) && (currentOp.type == OperatorType.OPERATOR_RESULT_PROPERTY)) {
QueryOperator anOp = currentOp.getParameters().get(0).asOperator();
if (anOp.type == OperatorType.LAST) {
setCurrentPath(new GraphPath(anOp.getParameters().get(0).asConcept()));
if ((anOp.type == OperatorType.LAST) || (anOp.type == OperatorType.FIRST)) {
QueryConcept demotionConcept = getDemotionConcept(anOp);
setCurrentPath(new GraphPath(demotionConcept));
} else if (anOp.type == OperatorType.OPERATOR_RESULT_PROPERTY) {
setCurrentPath(new GraphPath(
anOp.getParameters().get(anOp.getParameters().size() - 1).asConcept()));
......@@ -789,8 +848,8 @@ public class CQL_2_Graph_Mapper {
return aDateString.matches(format);
}
private SimpleDateFormat onlyYearsIn, dateTimeWithSecondsIn, onlyTimeWithSecondsIn, yearsOut, monthsOut, daysOut,
hoursOut, minutesOut, secondsOut, milliSecsOut;
private SimpleDateFormat onlyYearsIn, dateTimeWithSecondsIn, onlyTimeWithSecondsIn, yearsOut,
monthsOut, daysOut, hoursOut, minutesOut, secondsOut, milliSecsOut;
private String onlyYearsInRegex, dateTimeWithSecondsInRegex, onlyTimeWithSecondsInRegex;
......
package de.uniwue.query.CQL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
......@@ -29,6 +30,21 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
private boolean nextPrimitiveIsValueSet;
@SuppressWarnings("unchecked")
private Set<OperatorType> infixOps = new HashSet<OperatorType>(
Arrays.asList(new OperatorType[] { OperatorType.MORE, OperatorType.MORE_OR_EQUAL,
OperatorType.LESS, OperatorType.LESS_OR_EQUAL, OperatorType.AND, OperatorType.EQUALS,
OperatorType.AFTER, OperatorType.ADD, OperatorType.SUBTRACT, OperatorType.MULTIPLY,
OperatorType.DIVIDE, OperatorType.POWER, OperatorType.MODULO, OperatorType.TRUNCATED_DIVIDE }));
@SuppressWarnings("unchecked")
private Set<OperatorType> prefixOps = new HashSet<OperatorType>(Arrays.asList(new OperatorType[] {
OperatorType.FIRST, OperatorType.LAST, OperatorType.NOT, OperatorType.ABS,
OperatorType.ALLTRUE, OperatorType.ANYTRUE, OperatorType.AVG, OperatorType.COUNT,
OperatorType.MAX, OperatorType.MIN, OperatorType.MEDIAN, OperatorType.CEILING,
OperatorType.EXP, OperatorType.FLOOR, OperatorType.LN, OperatorType.LOG, OperatorType.ROUND,
OperatorType.TRUNCATE }));
public Graph_2_CQL_Mapper() {
super(system);
CQLOperatorMapper.initializeOpMappings(opMap);
......@@ -65,6 +81,11 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
public String visit(GraphQuery aQuery) {
initialize(aQuery);
String result = writeRootQuery();
return result;
}
private String writeRootQuery() {
StringBuilder builder = new StringBuilder();
QueryConcept firstRoot = getFirstRoot();
if (firstRoot != null) {
......@@ -74,6 +95,8 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
aliases.put(getFirstRoot(), alias);
String conceptsString = writeRootConcepts();
builder.append(conceptsString);
// TOTO: this whould be changed/merges with writeOp, but first the root query would have to be
// refactored
String where = writeWhere(query.rootClause);
if (!where.isEmpty()) {
builder.append(" where " + where);
......@@ -91,6 +114,31 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
return builder.toString().trim();
}
private String writeNonRootQuery(QueryOperator aQuery) {
// TODO: merge this with the rootQuery-method
StringBuilder builder = new StringBuilder();
// TODO: this could as well be an operator result
QueryConcept conc2Iterate = aQuery.getParameters().get(0).asConcept();
String path = getPath(conc2Iterate);
if (aliases.containsKey(conc2Iterate)) {
String alias = aliases.get(conc2Iterate);
builder.append(alias);
} else {
builder.append(path + " ");
String alias = aliasNames[aliasIndex++];
builder.append(alias);
aliases.put(conc2Iterate, alias);
}
if (aQuery.getParameters().size() > 1) {
QueryOperator asOperator = aQuery.getParameters().get(1).asOperator();
String where = writeOp(asOperator);
if (!where.isEmpty()) {
builder.append(" where " + where);
}
}
return builder.toString().trim();
}
private String writeReturn() {
String result = "";
int returnedExps = 0;
......@@ -238,9 +286,9 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
new ArrayList<QueryConcept>(alreadyMentionedConcepts));
boolean first = true;
for (QueryOperator anOp : hullOfSuchThatOperators) {
if (anOp.type == OperatorType.EXISTS) {
anOp = anOp.getOperators().get(0);
}
// if (anOp.type == OperatorType.EXISTS) {
// anOp = anOp.getOperators().get(0);
// }
if (first) {
first = false;
} else {
......@@ -341,35 +389,64 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
return result;
}
private String writePrefixOp(QueryOperator anOp) {
StringBuilder builder = new StringBuilder();
String opString = opMap.graph_2_system_operatorMap.get(anOp.type);
builder.append(opString + "(");
boolean first = true;
for (Expression anExp : anOp.getParameters()) {
if (first) {
first = false;
} else {
builder.append(", ");
}
String expString = writeExpr(anExp);
builder.append(expString);
}
builder.append(")");
return builder.toString();
}
private String writeInfixOp(QueryOperator anOp) {
StringBuilder builder = new StringBuilder();
String opString = opMap.graph_2_system_operatorMap.get(anOp.type);
boolean first = true;
for (Expression anExp : anOp.getParameters()) {
if (first) {
first = false;
} else {
builder.append(" " + opString + " ");
}
String expString = writeExpr(anExp);
builder.append(expString);
}
return builder.toString();
}
private String writeOp(QueryOperator anOp) {
StringBuilder builder = new StringBuilder();
if (alreadyHandledInWith.contains(anOp)) {
return "";
}
String opString = opMap.graph_2_system_operatorMap.get(anOp.type);
if (anOp.type == OperatorType.QUERY) {
String queryString = writeWhere(anOp);
if (prefixOps.contains(anOp.type)) {
String string = writePrefixOp(anOp);
builder.append(string);
} else if (infixOps.contains(anOp.type)) {
String string = writeInfixOp(anOp);
builder.append(string);
} else if (anOp.type == OperatorType.QUERY) {
String queryString = writeNonRootQuery(anOp);
builder.append(queryString);
} else if (anOp.isBooleanOp()) {
if (anOp.type == OperatorType.NOT) {
// TODO
} else {
boolean first = true;
for (QueryOperator aChildOp : anOp.getOperators()) {
String childString = writeOp(aChildOp);
if (!first) {
builder.append(" " + opString + " ");
}
first = false;
builder.append(childString);
}
}
} else if (anOp.type == OperatorType.NOT) {
String expString = writeExpr(anOp.getParameters().get(0));
builder.append("not(" + expString + ")");
} else if (anOp.type == OperatorType.INTERVAL) {
String intString = writeInterval(anOp);
builder.append(intString);
} else if ((anOp.type == OperatorType.NEGATE) && anOp.getParameters().get(0).isPrimitive()) {
} else if (anOp.type == OperatorType.NEGATE) {
builder.append("-");
String aPrim = writePrimitive(anOp.getParameters().get(0).asPrimitive());
String aPrim = writeExpr(anOp.getParameters().get(0));
builder.append(aPrim);
} else if (anOp.type == OperatorType.IN_VALUE_SET) {
String string1 = writeExpr(anOp.getParameters().get(0));
......@@ -381,37 +458,18 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
} else if (anOp.type == OperatorType.LIST) {
String intString = writeList(anOp);
builder.append(intString);
} else if (anOp.type == OperatorType.SORT) {
String string = writeSomethingSorted(anOp);
builder.append(string);
} else if (anOp.type == OperatorType.QUANTITY) {
builder.append(anOp.getParameters().get(0).asPrimitive().value + " "
+ anOp.getParameters().get(1).asPrimitive().value);
} else if ((anOp.type == OperatorType.LAST) || (anOp.type == OperatorType.FIRST)) {
writeLast(builder, opString, anOp);
// } else if ((anOp.type == OperatorType.LAST) || (anOp.type == OperatorType.FIRST)) {
// writeFirstLast(builder, opString, anOp);
} else if (anOp.type == OperatorType.OPERATOR_RESULT_PROPERTY) {
String frstExp = writeExpr(anOp.getParameters().get(0));
String sndExp = anOp.getParameters().get(1).asConcept().code;
builder.append(frstExp + "." + sndExp);
} else if (anOp.getParameters().size() == 2) {
Expression exp = anOp.getParameters().get(0);
if (exp.isConcept()) {
String asConceptCode = exp.asConcept().parentConcept.code;
// ClassType classType = (ClassType) model.resolveTypeName(asConceptCode);
// if (classType != null) {
// for (ClassTypeElement anElem : classType.getElements()) {
// if (anElem.getName().equals(exp.asConcept().code)) {
// if (anElem.getType().toLabel().equals("System.Concept")) {
// nextPrimitiveIsValueSet = true;
// }
// }
// }
// }
}
String frstExp = writeExpr(anOp.getParameters().get(0));
String sndExp = writeExpr(anOp.getParameters().get(1));
if (anOp.type == OperatorType.LOG) {
builder.append(opString + "(" + frstExp + ", " + sndExp + ")");
} else {
builder.append(frstExp + " " + opString + " " + sndExp);
}
} else if (anOp.getParameters().size() == 1) {
String exp = writeExpr(anOp.getParameters().get(0));
if ((anOp.type == OperatorType.END) || (anOp.type == OperatorType.START)) {
......@@ -419,7 +477,7 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
} else if (anOp.type == OperatorType.TO_LIST) {
builder.append(exp);
} else {
builder.append(opString + "(" + exp + ")");
throw new RuntimeException("unexpected");
}
} else if (anOp.getParameters().size() == 0) {
builder.append(opString);
......@@ -429,24 +487,19 @@ public class Graph_2_CQL_Mapper extends Graph_2_System_Mapper {
return builder.toString();
}
private void writeLast(StringBuilder builder, String opString, QueryOperator anOp) {
builder.append(opString + "(");
QueryConcept aConc = anOp.getParameters().get(0).asConcept();
String exp = writeExpr(aConc);
builder.append(exp);
private String writeSomethingSorted(QueryOperator anOp) {
StringBuilder builder = new StringBuilder();
Expression source = anOp.getParameters().get(0);
String sourceExpString = writeExpr(source);
builder.append(sourceExpString);
if (anOp.getParameters().size() > 1) {
String alias = getNextAlias(aConc);
builder.append(" " + alias + " ");
QueryOperator sort = anOp.getParameters().get(1).asOperator();
QueryConcept currentConc = sort.getParameters().get(0).asConcept();
String sortPath = currentConc.code;
while (currentConc.parentConcept != aConc) {
currentConc = currentConc.parentConcept;
sortPath = currentConc.code + "." + sortPath;
}
builder.append("sort by " + sortPath);
Expression sortBy = anOp.getParameters().get(1);
String sortByString = writeExpr(sortBy);
// the first binding of the path is not mentioned
sortByString = sortByString.replaceAll("^[^\\.]*\\.", "");
builder.append(" sort by " + sortByString);
}
builder.append(")");
return builder.toString();
}
public static String getSystem() {
......
......@@ -13,8 +13,6 @@ public class QueryConcept extends Expression {
public String code;
public boolean optional;
// an identifier with which the concept can be identified inside the returned results
public String outputName;
......
......@@ -55,6 +55,14 @@ public class Graph_2_Cypher_Mapper extends Graph_2_System_Mapper {
OperatorType.EQUALS, OperatorType.AFTER, OperatorType.POWER, OperatorType.ADD,
OperatorType.MULTIPLY, OperatorType.DIVIDE, OperatorType.SUBTRACT }));
@SuppressWarnings("unchecked")
private Set<OperatorType> prefixOps = new HashSet<OperatorType>(
Arrays.asList(new OperatorType[] { OperatorType.FIRST, OperatorType.LAST,
OperatorType.NOT, OperatorType.ABS, OperatorType.ALLTRUE, OperatorType.ANYTRUE,
OperatorType.AVG, OperatorType.COUNT, OperatorType.MAX, OperatorType.MIN,
OperatorType.MEDIAN, OperatorType.CEILING, OperatorType.EXP, OperatorType.FLOOR,
OperatorType.LN, OperatorType.LOG, OperatorType.ROUND, OperatorType.TRUNCATE }));
@SuppressWarnings("unchecked")
private Set<OperatorType> aggrOps = new HashSet<OperatorType>(Arrays.asList(
new OperatorType[] { OperatorType.LAST, OperatorType.FIRST, OperatorType.COUNT }));
......@@ -124,7 +132,7 @@ public class Graph_2_Cypher_Mapper extends Graph_2_System_Mapper {
if (isMultiTypeField) {
String expandedId = id + StringUtilsUniWue.firstUpperCase(code);
if (propName.equals(expandedId)) {
return code;
return code;
}
} else {
return code;
......@@ -247,27 +255,46 @@ public class Graph_2_Cypher_Mapper extends Graph_2_System_Mapper {
}
private String writeInfixOperator(QueryOperator anOp) {
String result;
Expression anExp1 = anOp.getParameters().get(0);
result = writeExpression(anExp1);
result += " " + opMap.graph_2_system_operatorMap.get(anOp.type) + " ";
Expression anExp2 = anOp.getParameters().get(1);
result += writeExpression(anExp2);
String opString = opMap.graph_2_system_operatorMap.get(anOp.type);
String result = "";
boolean first = true;
for (Expression anExp : anOp.getParameters()) {
if (first) {
first = false;
} else {
result += " " + opString + " ";
}
result += writeExpression(anExp);
}
return result;
}
private String writePrefixOperator(QueryOperator anOp) {
String opString = opMap.graph_2_system_operatorMap.get(anOp.type);
String result = opString + "(";
boolean first = true;
for (Expression anExp : anOp.getParameters()) {
if (first) {
first = false;
} else {
result += ", ";
}
result += writeExpression(anExp);
}
result += ")";
return result;
}
private String writeLast(QueryOperator anOp) {
String result = "";
// because the last has already been aliased in the WITH clause, we only have to reference its
// alias
String alias = aliasesOps.get(anOp);
result = alias;
return result;
return alias;
}
private String writeOperator(QueryOperator anOp) {
String result;