multiple operators, now in correct nesting, more than 3 allowed, mixed operators supported
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
index 473e80d..cebb22f 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/AqlTree.java
@@ -56,6 +56,10 @@
*/
LinkedList<LinkedHashMap<String,Object>> objectStack = new LinkedList<LinkedHashMap<String,Object>>();
/**
+ * Keeps track of operands that are to be integrated into yet uncreated objects.
+ */
+ LinkedList<LinkedHashMap<String,Object>> operandStack = new LinkedList<LinkedHashMap<String,Object>>();
+ /**
* Keeps track of explicitly (by #-var definition) or implicitly (number as reference) introduced entities (for later reference by #-operator)
*/
Map<String, LinkedHashMap<String,Object>> variableReferences = new LinkedHashMap<String, LinkedHashMap<String,Object>>();
@@ -72,9 +76,6 @@
* order (e.g. the IN() operator) compared to their AST representation.
*/
private LinkedList<ArrayList<Object>> invertedOperandsLists = new LinkedList<ArrayList<Object>>();
-
- private LinkedList<ArrayList<ArrayList<Object>>> distributedOperandsLists = new LinkedList<ArrayList<ArrayList<Object>>>();
-
/**
* Keeps track of how many objects there are to pop after every recursion of {@link #processNode(ParseTree)}
*/
@@ -224,7 +225,6 @@
// stackedObjects++;
// putIntoSuperObject(andGroup,1);
// }
- System.out.println(globalLingTermNodes.size());
if (globalLingTermNodes.size() > 1) {
LinkedHashMap<String, Object> zeroTextDistance = makeGroup("sequence");
ArrayList<Object> distances = new ArrayList<Object>();
@@ -263,12 +263,10 @@
}
if (nodeCat.equals("n_ary_linguistic_term")) {
- // get referenced operands
-
// get operator and determine type of group (sequence/treeRelation/relation/...)
- List<ParseTree> operatorNodes = getChildrenWithCat(node, "operator");
- int i = 1;
- for (ParseTree operatorNode : operatorNodes) {
+ // It's possible in Annis QL to concatenate operators, so there may be several operators under one n_ary_linguistic_term node.
+ // Counter 'i' will point to all operator nodes under this node.
+ for (int i=1; i<node.getChildCount(); i = i+2) {
ParseTree operand1 = node.getChild(i-1);
ParseTree operand2 = node.getChild(i+1);
LinkedHashMap<String, Object> operatorTree = parseOperatorNode(node.getChild(i).getChild(0));
@@ -290,31 +288,63 @@
}
// insert referenced nodes into operands list
List<Object> operands = (List<Object>) group.get("operands");
- if (!getNodeCat(operand1.getChild(0)).equals("variableExpr")) {
- String ref = operand1.getChild(0).toStringTree(parser).substring(1);
- operands.add(variableReferences.get(ref));
- } else {
-
- }
- // also process second operand, but only if there is no subsequent operator
- // that will claim the second operand as its first!
- if (i == node.getChildCount()-2) {
+
+ // -> Case distinction:
+ // Things are easy when there's just one operator (thus 3 children incl. operands)...
+ if (node.getChildCount()==3) {
+ if (!getNodeCat(operand1.getChild(0)).equals("variableExpr")) {
+ String ref1 = operand1.getChild(0).toStringTree(parser).substring(1);
+ operands.add(variableReferences.get(ref1));
+ }
if (!getNodeCat(operand2.getChild(0)).equals("variableExpr")) {
- String ref = operand2.getChild(0).toStringTree(parser).substring(1);
- operands.add(variableReferences.get(ref));
+ String ref2 = operand2.getChild(0).toStringTree(parser).substring(1);
+ operands.add(variableReferences.get(ref2));
+ }
+ putIntoSuperObject(group);
+ objectStack.push(group);
+ stackedObjects++;
+ // ...but stuff gets a little more complicated here. The AST is of this form: (operand1 operator 1 operand2 operator2 operand3 operator3 ...)
+ // but we'll have to serialize it in a nested, binary way: (((operand1 operator1 operand2) operator2 operand3) operator3 ...)
+ // the following code will do just that:
+ } else {
+ // for the first operator, include both operands
+ if (i == 1) {
+ if (!getNodeCat(operand1.getChild(0)).equals("variableExpr")) {
+ String ref1 = operand1.getChild(0).toStringTree(parser).substring(1);
+ operands.add(variableReferences.get(ref1));
+ }
+ if (!getNodeCat(operand2.getChild(0)).equals("variableExpr")) {
+ String ref2= operand2.getChild(0).toStringTree(parser).substring(1);
+ operands.add(variableReferences.get(ref2));
+ }
+ // Don't put this into the super object directly but store on operandStack
+ // (because this group will have to be an operand of a subsequent operator)
+ operandStack.push(group);
+ // for all subsequent operators, only take the 2nd operand (first was already added by previous operator)
+ // This is the last operator. Include 2nd operand and insert previously stored group at first position
+ } else if (i == node.getChildCount()-2) {
+ if (!getNodeCat(operand2.getChild(0)).equals("variableExpr")) {
+ String ref2 = operand2.getChild(0).toStringTree(parser).substring(1);
+ operands.add(variableReferences.get(ref2));
+ }
+ putIntoSuperObject(group);
+ if (!operandStack.isEmpty()) {
+ operands.add(0, operandStack.pop());
+ operandStack.clear();
+ }
+ objectStack.push(group);
+ stackedObjects++;
+ // for all intermediate operators, include other previous groups and 2nd operand. Store this on the operandStack, too.
} else {
-
+ if (!getNodeCat(operand2.getChild(0)).equals("variableExpr")) {
+ String ref2 = operand2.getChild(0).toStringTree(parser).substring(1);
+ operands.add(variableReferences.get(ref2));
+ }
+ operands.add(0, operandStack.pop());
+ operandStack.push(group);
}
}
- putIntoSuperObject(group);
- objectStack.push(group);
- stackedObjects++;
-
- i += 2;
}
-
-
-
}
if (nodeCat.equals("variableExpr")) {
@@ -361,7 +391,6 @@
if (object != null) {
if (! operandOnlyNodeRefs.contains(variableCounter.toString())) {
putIntoSuperObject(object);
- System.out.println(object.toString()+" "+objectStack.toString());
}
variableReferences.put(variableCounter.toString(), object);
variableCounter++;
@@ -704,14 +733,16 @@
"node & #1:root",
"pos=\"N\" & pos=\"V\" & pos=\"N\" & #1 . #2 & #2 . #3",
"cat=\"NP\" & #1:tokenarity=2",
- "node & node & node & #1 . #2 . #3"
+ "node & node & node & #1 . #2 . #3",
+ "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & #1 > #2 > #3",
+ "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & cat=\"DP\" & #1 > #2 > #3 > #4"
// "cnx/cat=\"NP\" > node",
// "node > node",
// "cat=/NP/ > node",
// "/Mann/",
// "node > tok=\"foo\"",
};
-// AqlTree.verbose=true;
+ AqlTree.verbose=true;
for (String q : queries) {
try {
System.out.println(q);
diff --git a/src/test/java/AqlTreeTest.java b/src/test/java/AqlTreeTest.java
index 4ad1a35..f7af8cc 100644
--- a/src/test/java/AqlTreeTest.java
+++ b/src/test/java/AqlTreeTest.java
@@ -164,19 +164,36 @@
@Test
public void testMultipleDominance() throws QueryException {
- query = "cat=\"NP\" & cat=\"VP\" & cat=\"NP\" & #1 > #2 > #3";
+ query = "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & #1 > #2 > #3";
String dom1 =
"{@type=korap:group, operation=operation:relation, operands=[" +
- "{@type=korap:span, layer=cat, key=NP, match=match:eq}," +
"{@type=korap:group, operation=operation:relation, operands=[" +
- "{@type=korap:span, layer=cat, key=VP, match=match:eq}," +
- "{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
- "], relation={@type=korap:treeRelation, reltype=dominance}}" +
+ "{@type=korap:span, layer=cat, key=CP, match=match:eq}," +
+ "{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}," +
+ "{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
"], relation={@type=korap:treeRelation, reltype=dominance}" +
"}";
aqlt = new AqlTree(query);
map = aqlt.getRequestMap().get("query").toString();
assertEquals(dom1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "cat=\"CP\" & cat=\"VP\" & cat=\"NP\" & cat=\"DP\" & #1 > #2 > #3 > #4";
+ String dom2 =
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:span, layer=cat, key=CP, match=match:eq}," +
+ "{@type=korap:span, layer=cat, key=VP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}," +
+ "{@type=korap:span, layer=cat, key=NP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}," +
+ "{@type=korap:span, layer=cat, key=DP, match=match:eq}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(dom2.replaceAll(" ", ""), map.replaceAll(" ", ""));
}
@Test
@@ -252,15 +269,15 @@
String seq4 =
"{@type=korap:group, operation=operation:sequence," +
"operands=[" +
- "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Mond, match=match:eq}}," +
- "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sterne, match=match:eq}}" +
- "], distances=[" +
- "{@type=korap:distance, key=w, min=0, max=4}" +
- "], inOrder=true}" +
+ "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
+ "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Mond, match=match:eq}}" +
+ "], distances=[" +
+ "{@type=korap:distance, key=w, min=0, max=2}" +
+ "], inOrder=true}," +
+ "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sterne, match=match:eq}}" +
"],distances=[" +
- "{@type=korap:distance, key=w, min=0, max=2}" +
+ "{@type=korap:distance, key=w, min=0, max=4}" +
"], inOrder=true" +
"}";
aqlt = new AqlTree(query);
@@ -270,14 +287,14 @@
query = "node & node & node & #1 . #2 .1,3 #3";
String seq5 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
- "{@type=korap:span}," +
"{@type=korap:group, operation=operation:sequence, operands=[" +
"{@type=korap:span}," +
"{@type=korap:span}" +
- "], distances=[" +
+ "], inOrder=true}," +
+ "{@type=korap:span}" +
+ "], distances=[" +
"{@type=korap:distance, key=w, min=1, max=3}" +
- "], inOrder=true}" +
- "], inOrder=true" +
+ "], inOrder=true" +
"}";
aqlt = new AqlTree(query);
map = aqlt.getRequestMap().get("query").toString();
@@ -285,6 +302,26 @@
}
@Test
+ public void testMultipleMixedOperators() throws QueryException {
+ query = "tok=\"Sonne\" & tok=\"Mond\" & tok=\"Sterne\" & #1 > #2 .0,4 #3";
+ String seq4 =
+ "{@type=korap:group, operation=operation:sequence," +
+ "operands=[" +
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sonne, match=match:eq}}," +
+ "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Mond, match=match:eq}}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}}," +
+ "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Sterne, match=match:eq}}" +
+ "], distances=[" +
+ "{@type=korap:distance, key=w, min=0, max=4}" +
+ "], inOrder=true" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(seq4.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ }
+
+ @Test
public void testPositions() throws QueryException {
query = "node & node & #2 _=_ #1";
String pos1 =