multiple operators, e.g. #1 . #2 . #3 or #1 > #2 > #3
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 a6e4686..473e80d 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
@@ -58,7 +58,7 @@
/**
* Keeps track of explicitly (by #-var definition) or implicitly (number as reference) introduced entities (for later reference by #-operator)
*/
- Map<String, Object> variableReferences = new LinkedHashMap<String, Object>();
+ Map<String, LinkedHashMap<String,Object>> variableReferences = new LinkedHashMap<String, LinkedHashMap<String,Object>>();
/**
* Counter for variable definitions.
*/
@@ -151,6 +151,7 @@
throw new NullPointerException("Parser has not been instantiated!");
}
log.info("Processing Annis query.");
+ log.info("AST is: "+tree.toStringTree(parser));
System.out.println("Processing Annis QL");
if (verbose) System.out.println(tree.toStringTree(parser));
processNode(tree);
@@ -200,7 +201,7 @@
List<ParseTree> globalLingTermNodes = new ArrayList<ParseTree>();
for (ParseTree exprNode : getChildrenWithCat(node,"expr")) {
List<ParseTree> lingTermNodes = new ArrayList<ParseTree>();
- lingTermNodes.addAll(getChildrenWithCat(exprNode, "unary_linguistic_term"));
+// lingTermNodes.addAll(getChildrenWithCat(exprNode, "unary_linguistic_term"));
lingTermNodes.addAll(getChildrenWithCat(exprNode, "n_ary_linguistic_term"));
globalLingTermNodes.addAll(lingTermNodes);
// Traverse refOrNode nodes under *ary_linguistic_term nodes and extract references
@@ -255,62 +256,65 @@
}
if (nodeCat.equals("unary_linguistic_term")) {
-
+ LinkedHashMap<String, Object> unaryOperator = parseUnaryOperator(node);
+ String reference = node.getChild(0).toStringTree(parser).substring(1);
+ LinkedHashMap<String, Object> object = variableReferences.get(reference);
+ object.putAll(unaryOperator);
}
if (nodeCat.equals("n_ary_linguistic_term")) {
// get referenced operands
- // TODO generalize operator
- // TODO capture variableExprs
// get operator and determine type of group (sequence/treeRelation/relation/...)
- LinkedHashMap<String, Object> operatorTree = parseOperatorNode(node.getChild(1).getChild(0));
- String groupType;
- try {
- groupType = (String) operatorTree.get("groupType");
- } catch (ClassCastException | NullPointerException n) {
- groupType = "relation";
- }
- LinkedHashMap<String, Object> group = makeGroup(groupType);
- if (groupType.equals("relation") || groupType.equals("treeRelation")) {
- LinkedHashMap<String, Object> relationGroup = new LinkedHashMap<String, Object>();
- putAllButGroupType(relationGroup, operatorTree);
- group.put("relation", relationGroup);
- } else if (groupType.equals("sequence")) {
- putAllButGroupType(group, operatorTree);
- } else if (groupType.equals("position")) {
- String frame = (String) operatorTree.get("frame");
-// if (!mirroredPositionFrames.contains(frame.substring(6))) { //remove leading "frame:"
+ List<ParseTree> operatorNodes = getChildrenWithCat(node, "operator");
+ int i = 1;
+ for (ParseTree operatorNode : operatorNodes) {
+ ParseTree operand1 = node.getChild(i-1);
+ ParseTree operand2 = node.getChild(i+1);
+ LinkedHashMap<String, Object> operatorTree = parseOperatorNode(node.getChild(i).getChild(0));
+ String groupType;
+ try {
+ groupType = (String) operatorTree.get("groupType");
+ } catch (ClassCastException | NullPointerException n) {
+ groupType = "relation";
+ }
+ LinkedHashMap<String, Object> group = makeGroup(groupType);
+ if (groupType.equals("relation") || groupType.equals("treeRelation")) {
+ LinkedHashMap<String, Object> relationGroup = new LinkedHashMap<String, Object>();
+ putAllButGroupType(relationGroup, operatorTree);
+ group.put("relation", relationGroup);
+ } else if (groupType.equals("sequence")) {
putAllButGroupType(group, operatorTree);
-// } else {
-// group = makeGroup("or");
-// LinkedHashMap<String, Object> group1 = makeGroup(groupType);
-// LinkedHashMap<String, Object> group2 = makeGroup(groupType);
-// putAllButGroupType(group1, operatorTree);
-// putAllButGroupType(group2, operatorTree);
-// ArrayList<Object> groupOperands = (ArrayList<Object>) group.get("operands");
-// groupOperands.add(group1);
-// groupOperands.add(group2);
-// ArrayList<ArrayList<Object>> distOperandsList = new ArrayList<ArrayList<Object>>();
-// distOperandsList.add((ArrayList<Object>) group1.get("operands"));
-// distOperandsList.add((ArrayList<Object>) group2.get("operands"));
-// invertedOperandsLists.push((ArrayList<Object>) group2.get("operands"));
-// distributedOperandsLists.push(distOperandsList);
-// }
- }
- // insert referenced nodes into operands list
- List<Object> operands = (List<Object>) group.get("operands");
- for (ParseTree refOrNode : getChildrenWithCat(node, "refOrNode")) {
- if (!getNodeCat(refOrNode.getChild(0)).equals("variableExpr")) {
- String ref = refOrNode.getChild(0).toStringTree(parser).substring(1);
+ } else if (groupType.equals("position")) {
+ putAllButGroupType(group, operatorTree);
+ }
+ // 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) {
+ if (!getNodeCat(operand2.getChild(0)).equals("variableExpr")) {
+ String ref = operand2.getChild(0).toStringTree(parser).substring(1);
+ operands.add(variableReferences.get(ref));
+ } else {
+
+ }
+ }
+ putIntoSuperObject(group);
+ objectStack.push(group);
+ stackedObjects++;
+
+ i += 2;
}
- putIntoSuperObject(group);
- objectStack.push(group);
- stackedObjects++;
+
+
+
}
if (nodeCat.equals("variableExpr")) {
@@ -322,6 +326,7 @@
} else if (firstChildNodeCat.equals("tok")) {
object = makeToken();
LinkedHashMap<String, Object> term = makeTerm();
+ term.put("layer", "orth");
object.put("wrap", term);
} else if (firstChildNodeCat.equals("qName")) { // only (foundry/)?layer specified
// may be token or span, depending on indicated layer! (e.g. cnx/cat=NP or mate/pos=NN)
@@ -391,6 +396,27 @@
}
+ /**
+ * Parses a unary_linguistic_operator node. Possible operators are: root, arity, tokenarity.
+ * Operators are embedded into a korap:term, in turn wrapped by an 'attr' property in a korap:span.
+ * @param node The unary_linguistic_operator node
+ * @return A map containing the attr key, to be inserted into korap:span
+ */
+ private LinkedHashMap<String, Object> parseUnaryOperator(ParseTree node) {
+ LinkedHashMap<String, Object> attr = new LinkedHashMap<String, Object>();
+ LinkedHashMap<String, Object> term = makeTerm();
+ String op = node.getChild(1).toStringTree(parser).substring(1);
+ if (op.equals("arity") || op.equals("tokenarity")) {
+ LinkedHashMap<String, Object> boundary = boundaryFromRangeSpec(node.getChild(3), false);
+ term.put(op, boundary);
+ } else {
+ term.put(op, true);
+ }
+
+ attr.put("attr", term);
+ return attr;
+ }
+
private LinkedHashMap<String, Object> parseOperatorNode(ParseTree operatorNode) {
LinkedHashMap<String, Object> relation = null;
String operator = getNodeCat(operatorNode);
@@ -522,8 +548,13 @@
}
private LinkedHashMap<String, Object> boundaryFromRangeSpec(ParseTree rangeSpec) {
+ return boundaryFromRangeSpec(rangeSpec, true);
+ }
+
+ private LinkedHashMap<String, Object> boundaryFromRangeSpec(ParseTree rangeSpec, boolean expandToMax) {
Integer min = Integer.parseInt(rangeSpec.getChild(0).toStringTree(parser));
- Integer max = MAXIMUM_DISTANCE;
+ Integer max = min;
+ if (expandToMax) max = MAXIMUM_DISTANCE;
if (rangeSpec.getChildCount()==3)
max = Integer.parseInt(rangeSpec.getChild(2).toStringTree(parser));
return makeBoundary(min, max);
@@ -671,13 +702,19 @@
*/
String[] queries = new String[] {
"node & #1:root",
- "pos=\"N\" & pos=\"V\" & pos=\"N\" & #1 . #2 & #2 . #3"
+ "pos=\"N\" & pos=\"V\" & pos=\"N\" & #1 . #2 & #2 . #3",
+ "cat=\"NP\" & #1:tokenarity=2",
+ "node & node & node & #1 . #2 . #3"
+// "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);
-// System.out.println(AqlTree.parseAnnisQuery(q).toStringTree(AqlTree.parser));
AqlTree at = new AqlTree(q);
System.out.println(at.parseAnnisQuery(q).toStringTree(at.parser));
System.out.println();
diff --git a/src/test/java/AqlTreeTest.java b/src/test/java/AqlTreeTest.java
index a39ccc4..4ad1a35 100644
--- a/src/test/java/AqlTreeTest.java
+++ b/src/test/java/AqlTreeTest.java
@@ -34,7 +34,7 @@
// [orth!=Frau]
query = "tok!=\"Frau\"";
- String token2 = "{@type=korap:token, wrap={@type=korap:term, key=Frau, match=match:ne}}";
+ String token2 = "{@type=korap:token, wrap={@type=korap:term, layer=orth, key=Frau, match=match:ne}}";
assertTrue(equalsQueryContent(token2, query));
// Mann
@@ -163,6 +163,23 @@
}
@Test
+ public void testMultipleDominance() throws QueryException {
+ query = "cat=\"NP\" & 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}}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(dom1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ }
+
+ @Test
public void testPointingRelations() throws QueryException {
query = "node & node & #2 ->label[coref=\"true\"] #1";
String dom1 =
@@ -190,7 +207,7 @@
@Test
public void testSequence() throws QueryException {
query = "node & node & #1 . #2";
- String dom1 =
+ String seq1 =
"{@type=korap:group, operation=operation:sequence, " +
"operands=[" +
"{@type=korap:span}," +
@@ -199,10 +216,10 @@
"}";
aqlt = new AqlTree(query);
map = aqlt.getRequestMap().get("query").toString();
- assertEquals(dom1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ assertEquals(seq1.replaceAll(" ", ""), map.replaceAll(" ", ""));
query = "node & node & #1 .* #2";
- String dom2 =
+ String seq2 =
"{@type=korap:group, operation=operation:sequence, operands=[" +
"{@type=korap:span}," +
"{@type=korap:span}" +
@@ -212,7 +229,59 @@
"}";
aqlt = new AqlTree(query);
map = aqlt.getRequestMap().get("query").toString();
- assertEquals(dom2.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ assertEquals(seq2.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "node & node & #1 .2,3 #2";
+ String seq3 =
+ "{@type=korap:group, operation=operation:sequence, operands=[" +
+ "{@type=korap:span}," +
+ "{@type=korap:span}" +
+ "], distances=[" +
+ "{@type=korap:distance, key=w, min=2, max=3}" +
+ "], inOrder=true" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(seq3.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ }
+
+ @Test
+ public void testMultipleSequence() throws QueryException {
+ query = "tok=\"Sonne\" & tok=\"Mond\" & tok=\"Sterne\" & #1 .0,2 #2 .0,4 #3";
+ 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}" +
+ "],distances=[" +
+ "{@type=korap:distance, key=w, min=0, max=2}" +
+ "], inOrder=true" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(seq4.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ 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=[" +
+ "{@type=korap:distance, key=w, min=1, max=3}" +
+ "], inOrder=true}" +
+ "], inOrder=true" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(seq5.replaceAll(" ", ""), map.replaceAll(" ", ""));
}
@Test
@@ -228,48 +297,110 @@
map = aqlt.getRequestMap().get("query").toString();
assertEquals(pos1.replaceAll(" ", ""), map.replaceAll(" ", ""));
-// query = "node & node & #2 _i_ #1";
-// String pos2 =
-// "{@type=korap:group, operation=operation:position, operands=[" +
-// "{@type=korap:span}," +
-// "{@type=korap:span}" +
-// "], frame=frame:contains" +
-// "}";
-// aqlt = new AqlTree(query);
-// map = aqlt.getRequestMap().get("query").toString();
-// assertEquals(pos2.replaceAll(" ", ""), map.replaceAll(" ", ""));
-//
-// query = "node & node & #2 _l_ #1";
-// String pos3 =
-// "{@type=korap:group, operation=operation:position, operands=[" +
-// "{@type=korap:span}," +
-// "{@type=korap:span}" +
-// "], frame=frame:startswith" +
-// "}";
-// aqlt = new AqlTree(query);
-// map = aqlt.getRequestMap().get("query").toString();
-// assertEquals(pos3.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ query = "node & node & #2 _i_ #1";
+ String pos2 =
+ "{@type=korap:group, operation=operation:position, operands=[" +
+ "{@type=korap:span}," +
+ "{@type=korap:span}" +
+ "], frame=frame:contains" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(pos2.replaceAll(" ", ""), map.replaceAll(" ", ""));
- query = "node & \"Mann\" & #2 _r_ #1";
+ query = "node & node & #2 _l_ #1";
+ String pos3 =
+ "{@type=korap:group, operation=operation:position, operands=[" +
+ "{@type=korap:span}," +
+ "{@type=korap:span}" +
+ "], inOrder=false, frame=frame:startswith" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(pos3.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "node & \"Mann\" & #1 _r_ #2";
String pos4 =
- "{@type=korap:group, operation=operation:or, operands=[" +
"{@type=korap:group, operation=operation:position, operands=[" +
"{@type=korap:span}," +
- "{@type=korap:token, wrap={@type=korap:term, type=type:regex, key=Mann, match=match:eq}}" +
- "], frame=frame:endswith" +
- "}," +
- "{@type=korap:group, operation=operation:position, operands=[" +
- "{@type=korap:token, wrap={@type=korap:term, type=type:regex, key=Mann, match=match:eq}}," +
- "{@type=korap:span}" +
- "], frame=frame:endswith" +
- "}" +
- "]}";
+ "{@type=korap:token, wrap={@type=korap:term, key=Mann, match=match:eq}}" +
+ "], inOrder=false, frame=frame:endswith" +
+ "}";
aqlt = new AqlTree(query);
map = aqlt.getRequestMap().get("query").toString();
assertEquals(pos4.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ query = "node & \"Mann\" & #2 _r_ #1";
+ String pos5 =
+ "{@type=korap:group, operation=operation:position, operands=[" +
+ "{@type=korap:token, wrap={@type=korap:term, key=Mann, match=match:eq}}," +
+ "{@type=korap:span}" +
+ "], inOrder=false, frame=frame:endswith" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(pos5.replaceAll(" ", ""), map.replaceAll(" ", ""));
}
+ @Test
+ public void testMultiplePredications() throws QueryException {
+ // a noun before a verb before a noun
+ // XXX Warning incorrect serialisation! Need to ensure that the two Vs are identical!
+ // Embed in one sequence!
+ query = "pos=\"N\" & pos=\"V\" & pos=\"N\" & #1 . #2 & #2 . #3";
+ String mult1 =
+ "{@type=korap:group, operation=operation:sequence, operands=[" +
+ "{@type=korap:group, operation=operation:sequence, operands=[" +
+ "{@type=korap:token, wrap={@type=korap:term, layer=pos, key=N, match=match:eq}}," +
+ "{@type=korap:token, wrap={@type=korap:term, layer=pos, key=V, match=match:eq}}" +
+ "], inOrder=true}," +
+ "{@type=korap:group, operation=operation:sequence, operands=[" +
+ "{@type=korap:token, wrap={@type=korap:term, layer=pos, key=V, match=match:eq}}," +
+ "{@type=korap:token, wrap={@type=korap:term, layer=pos, key=N, match=match:eq}}" +
+ "], inOrder=true}" +
+ "], distances=[" +
+ "{@type=korap:distance, key=t, min=0, max=0}" +
+ "]}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(mult1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ }
-}
-
+ @Test
+ public void testUnaryRelations() throws QueryException {
+ query = "node & #1:tokenarity=2";
+ String unary1 =
+ "{@type=korap:span, attr={@type=korap:term, tokenarity={@type=korap:boundary,min=2,max=2}}}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(unary1.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "cnx/cat=\"NP\" & #1:tokenarity=2";
+ String unary2 =
+ "{@type=korap:span, foundry=cnx, layer=cat, key=NP, match=match:eq, attr={@type=korap:term, tokenarity={@type=korap:boundary,min=2,max=2}}}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(unary2.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "cnx/cat=\"NP\" & #1:root";
+ String unary3 =
+ "{@type=korap:span, foundry=cnx, layer=cat, key=NP, match=match:eq, attr={@type=korap:term, root=true}}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(unary3.replaceAll(" ", ""), map.replaceAll(" ", ""));
+
+ query = "cnx/cat=\"NP\" & node & #1>#2 & #1:tokenarity=2";
+ String unary4 =
+ "{@type=korap:group, operation=operation:relation, operands=[" +
+ "{@type=korap:span, foundry=cnx, layer=cat, key=NP, match=match:eq, attr={@type=korap:term, tokenarity={@type=korap:boundary,min=2,max=2}}}," +
+ "{@type=korap:span}" +
+ "], relation={@type=korap:treeRelation, reltype=dominance}" +
+ "}";
+ aqlt = new AqlTree(query);
+ map = aqlt.getRequestMap().get("query").toString();
+ assertEquals(unary4.replaceAll(" ", ""), map.replaceAll(" ", ""));
+ }
+
+ // TODO commonparent, commonancestor
+
+}
\ No newline at end of file