Implemented relation queries with regex.

Change-Id: I1dafc835f6034d90a350d9688acdcf54bac28124
diff --git a/src/main/java/de/ids_mannheim/korap/KrillQuery.java b/src/main/java/de/ids_mannheim/korap/KrillQuery.java
index 4f5901f..8e30b7a 100644
--- a/src/main/java/de/ids_mannheim/korap/KrillQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KrillQuery.java
@@ -5,6 +5,7 @@
 import java.util.List;
 import java.util.Iterator;
 
+import org.apache.lucene.search.RegexpQuery;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -13,6 +14,7 @@
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
+import de.ids_mannheim.korap.constants.RelationDirection;
 import de.ids_mannheim.korap.query.QueryBuilder;
 import de.ids_mannheim.korap.query.SpanWithinQuery;
 import de.ids_mannheim.korap.query.wrap.SpanAlterQueryWrapper;
@@ -323,7 +325,7 @@
                     return this._termFromJson(json);
 
                 // This is an ugly hack
-                return this._termFromJson(json.get("wrap"), "<>:");
+                return this._termFromJson(json.get("wrap"), true);
         };
 
         // Unknown query type
@@ -566,9 +568,12 @@
         SpanQueryWrapper operand1 = this._fromKoral(operands.get(0), true);
         SpanQueryWrapper operand2 = this._fromKoral(operands.get(1), true);
 
-        String direction = ">:";
+        RelationDirection direction;
         if (operand1.isEmpty() && !operand2.isEmpty()) {
-            direction = "<:";
+            direction = RelationDirection.LEFT; // "<:";
+        }
+        else{
+            direction = RelationDirection.RIGHT; // ">:"
         }
 
         if (!relation.has("@type")){
@@ -576,15 +581,20 @@
                     "JSON-LD group has no @type attribute");
         }
         
-        SpanRelationWrapper spanRelationWrapper;
         if (relation.get("@type").asText().equals("koral:relation")) {
+            SpanRelationWrapper spanRelationWrapper;
+            SpanQueryWrapper relationTermWrapper;
             if (!relation.has("wrap")) {
-                throw new QueryException(718, "Missing relation term");
+                throw new QueryException(718, "Missing relation term.");
+            }
+            else{
+                relationTermWrapper =
+                        _termFromJson(relation.get("wrap"), false, direction);
+                spanRelationWrapper = new SpanRelationWrapper(relationTermWrapper, operand1, operand2);
             }
             
-            SpanQueryWrapper relationWrapper =
-                    _termFromJson(relation.get("wrap"), direction);
-            return new SpanRelationWrapper(relationWrapper, operand1, operand2);
+            spanRelationWrapper.setDirection(direction);    
+            return spanRelationWrapper;
         }
         else {
             throw new QueryException(713, "Query type is not supported");
@@ -999,13 +1009,8 @@
         return sseqqw;
     };
 
-    private SpanQueryWrapper _segFromJson (JsonNode json)
-            throws QueryException {
-        return _segFromJson(json, null);
-    }
-    
     // Deserialize koral:token
-    private SpanQueryWrapper _segFromJson (JsonNode json, String direction)
+    private SpanQueryWrapper _segFromJson (JsonNode json)
             throws QueryException {
 
         if (!json.has("@type"))
@@ -1038,7 +1043,7 @@
                 //                return this.seg().without(ssqw);
                 //
                 //            case "match:eq":
-                return this._termFromJson(json, direction);
+                return this._termFromJson(json);
             //            };
             //
             //            throw new QueryException(741, "Match relation unknown");
@@ -1095,13 +1100,16 @@
 
     private SpanQueryWrapper _termFromJson (JsonNode json)
             throws QueryException {
-        return this._termFromJson(json, null);
+        return this._termFromJson(json, false, null);
     }
-
+    private SpanQueryWrapper _termFromJson (JsonNode json, boolean isSpan)
+            throws QueryException {
+        return this._termFromJson(json, isSpan, null);
+    }
 
     // Deserialize koral:term
     // TODO: Not optimal as it does not respect non-term
-    private SpanQueryWrapper _termFromJson (JsonNode json, String direction)
+    private SpanQueryWrapper _termFromJson (JsonNode json, boolean isSpan, RelationDirection direction)
             throws QueryException {
 
         if (!json.has("@type")) {
@@ -1124,10 +1132,9 @@
             }
         };
         
-        // Ugly direction hack
-        if (direction != null && direction.equals("<>:")) {
+        // Empty koral:span hack
+        if (isSpan) {
             isTerm = false;
-            direction = null;
         };
 
         // <legacy>
@@ -1154,7 +1161,7 @@
         StringBuilder value = new StringBuilder();
 
         if (direction != null)
-            value.append(direction);
+            value.append(direction.value());
 
         // expect orth? expect lemma? 
         // s:den | i:den | cnx/l:die | mate/m:mood:ind | cnx/syn:@PREMOD |
diff --git a/src/main/java/de/ids_mannheim/korap/constants/RelationDirection.java b/src/main/java/de/ids_mannheim/korap/constants/RelationDirection.java
new file mode 100644
index 0000000..e20055d
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/constants/RelationDirection.java
@@ -0,0 +1,15 @@
+package de.ids_mannheim.korap.constants;
+
+public enum RelationDirection {
+    LEFT("<:"), RIGHT(">:");
+
+    private String value;
+
+    RelationDirection (String value) {
+        this.value = value;
+    }
+
+    public String value () {
+        return value;
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanRelationMatchQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanRelationMatchQuery.java
index de77ef4..866dbc7 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanRelationMatchQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanRelationMatchQuery.java
@@ -10,6 +10,7 @@
 import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.util.Bits;
 
+import de.ids_mannheim.korap.constants.RelationDirection;
 import de.ids_mannheim.korap.query.spans.FocusSpans;
 
 /**
@@ -81,7 +82,7 @@
         SpanFocusQuery sq = null;
         SpanFocusQuery sq2 = null;
         // match source and then target
-        if (relationQuery.getDirection() == 0) {
+        if (relationQuery.getDirection().equals(RelationDirection.RIGHT)) {
             sq = new SpanFocusQuery(
                     new SpanSegmentQuery(relationQuery, operandQuery, true),
                     relation.getTempTargetNum());
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanRelationQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanRelationQuery.java
index ea71d63..fe9d3b3 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanRelationQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanRelationQuery.java
@@ -9,11 +9,11 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.search.spans.SpanQuery;
-import org.apache.lucene.search.spans.SpanTermQuery;
 import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.ToStringUtils;
 
+import de.ids_mannheim.korap.constants.RelationDirection;
 import de.ids_mannheim.korap.query.spans.RelationSpans;
 
 /**
@@ -54,7 +54,7 @@
  */
 public class SpanRelationQuery extends SimpleSpanQuery {
 
-    private int direction = 0; // >
+    private RelationDirection direction;
     private byte tempSourceNum = 1;
     private byte tempTargetNum = 2;
     private byte sourceClass;
@@ -75,29 +75,32 @@
      *            payloads are to be collected, otherwise
      *            <code>false</code>.
      */
-    public SpanRelationQuery (SpanQuery firstClause, boolean collectPayloads) {
+    public SpanRelationQuery (SpanQuery firstClause, boolean collectPayloads, 
+            RelationDirection direction) {
         super(firstClause, collectPayloads);
-        SpanTermQuery st = (SpanTermQuery) firstClause;
-        String direction = st.getTerm().text().substring(0, 1);
-        if (direction.equals("<")) {
-            this.direction = 1;
-        }
+        this.direction = direction;
+//        SpanTermQuery st = (SpanTermQuery) firstClause;
+//        String direction = st.getTerm().text().substring(0, 1);
+//        if (direction.equals("<")) {
+//            this.direction = 1;
+//        }
     }
 
 
-    public SpanRelationQuery (SpanQuery firstClause, List<Byte> classNumbers,
-                              boolean collectPayloads) {
-        this(firstClause, collectPayloads);
-        this.tempClassNumbers = classNumbers;
-        this.tempSourceNum = classNumbers.get(0);
-        this.tempTargetNum = classNumbers.get(1);
-    }
+//    public SpanRelationQuery (SpanQuery firstClause, List<Byte> classNumbers,
+//                              boolean collectPayloads) {
+//        this(firstClause, collectPayloads);
+//        this.tempClassNumbers = classNumbers;
+//        this.tempSourceNum = classNumbers.get(0);
+//        this.tempTargetNum = classNumbers.get(1);
+//    }
 
 
     @Override
     public SimpleSpanQuery clone () {
         SimpleSpanQuery sq = new SpanRelationQuery(
-                (SpanQuery) this.firstClause.clone(), this.collectPayloads);
+                (SpanQuery) this.firstClause.clone(), this.collectPayloads, 
+                this.direction);
         return sq;
     }
 
@@ -136,12 +139,12 @@
     }
 
 
-    public int getDirection () {
+    public RelationDirection getDirection () {
         return direction;
     }
 
 
-    public void setDirection (int direction) {
+    public void setDirection (RelationDirection direction) {
         this.direction = direction;
     }
 
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/FocusSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/FocusSpans.java
index 51bf64f..c5e9487 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/FocusSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/FocusSpans.java
@@ -14,6 +14,7 @@
 import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.util.Bits;
 
+import de.ids_mannheim.korap.constants.RelationDirection;
 import de.ids_mannheim.korap.query.SpanFocusQuery;
 
 
@@ -157,10 +158,10 @@
 
         if (firstSpans instanceof RelationSpans && classNumbers.size() == 1) {
             RelationSpans relationSpans = (RelationSpans) firstSpans;
-            int direction = relationSpans.getDirection();
+            RelationDirection direction = relationSpans.getDirection();
 
             if (classNumbers.get(0) == relationSpans.getTempSourceNum()) {
-                if (direction == 0) {
+                if (direction.equals(RelationDirection.RIGHT)) {
                     setSpanId(relationSpans.getLeftId());
                 }
                 else {
@@ -168,7 +169,7 @@
                 }
             }
             else if (classNumbers.get(0) == relationSpans.getTempTargetNum()) {
-                if (direction == 0) {
+                if (direction.equals(RelationDirection.RIGHT)) {
                     setSpanId(relationSpans.getRightId());
                 }
                 else {
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/RelationSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/RelationSpans.java
index ca467fc..a10e770 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/RelationSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/RelationSpans.java
@@ -10,11 +10,13 @@
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
+import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.search.spans.TermSpans;
 import org.apache.lucene.util.Bits;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import de.ids_mannheim.korap.constants.RelationDirection;
 import de.ids_mannheim.korap.query.SpanRelationQuery;
 
 /**
@@ -52,8 +54,8 @@
 public class RelationSpans extends RelationBaseSpans {
 
     private int currentDoc, currentPosition;
-    private int direction;
-    private TermSpans relationTermSpan;
+    private RelationDirection direction;
+    private Spans relationTermSpan;
 
     protected Logger logger = LoggerFactory.getLogger(RelationSpans.class);
     private List<CandidateSpan> candidateList;
@@ -96,7 +98,7 @@
         targetClass = relationSpanQuery.getTargetClass();
 
         candidateList = new ArrayList<>();
-        relationTermSpan = (TermSpans) firstSpans;
+        relationTermSpan = firstSpans;
         hasMoreSpans = relationTermSpan.next();
     }
 
@@ -138,8 +140,10 @@
             }
             else {
                 setCandidateList();
-                currentDoc = relationTermSpan.doc();
-                currentPosition = relationTermSpan.start();
+                if(hasMoreSpans){
+                    currentDoc = relationTermSpan.doc();
+                    currentPosition = relationTermSpan.start();
+                }
             }
         }
         return false;
@@ -155,6 +159,7 @@
      * @throws IOException
      */
     private void setCandidateList () throws IOException {
+        logger.debug("hasMoreSpans "+hasMoreSpans+" "+relationTermSpan.doc());
         while (hasMoreSpans && relationTermSpan.doc() == currentDoc
                 && relationTermSpan.start() == currentPosition) {
 
@@ -257,7 +262,7 @@
         if (relationTermSpan.isPayloadAvailable()) {
             payload.addAll(relationTermSpan.getPayload());
         }
-        if (direction == 0) {
+        if (direction.equals(RelationDirection.RIGHT)) {
             payload.add(createClassPayload(cs.getLeftStart(), cs.getLeftEnd(),
                     tempSourceNum, false));
             payload.add(createClassPayload(cs.getRightStart(), cs.getRightEnd(),
@@ -393,12 +398,12 @@
     }
 
 
-    public int getDirection () {
+    public RelationDirection getDirection () {
         return direction;
     }
 
 
-    public void setDirection (int direction) {
+    public void setDirection (RelationDirection direction) {
         this.direction = direction;
     }
 
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanRelationWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanRelationWrapper.java
index d7b0f6d..fe980c9 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanRelationWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanRelationWrapper.java
@@ -1,8 +1,10 @@
 package de.ids_mannheim.korap.query.wrap;
 
+import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper;
 import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.search.spans.SpanTermQuery;
 
+import de.ids_mannheim.korap.constants.RelationDirection;
 import de.ids_mannheim.korap.query.SpanFocusQuery;
 import de.ids_mannheim.korap.query.SpanRelationMatchQuery;
 import de.ids_mannheim.korap.query.SpanRelationQuery;
@@ -13,7 +15,7 @@
     private SpanQueryWrapper relationQuery;
     private SpanQueryWrapper subQuery1;
     private SpanQueryWrapper subQuery2;
-
+    private RelationDirection direction;
 
     public SpanRelationWrapper (SpanQueryWrapper relationWrapper,
                                 SpanQueryWrapper operand1,
@@ -43,12 +45,15 @@
             return null;
         }
 
-        SpanTermQuery relationTermQuery = (SpanTermQuery) relationQuery
+        SpanQuery relationTermQuery = relationQuery
                 .retrieveNode(this.retrieveNode).toFragmentQuery();
+        
+//        SpanTermQuery relationTermQuery = (SpanTermQuery) relationQuery
+//                .retrieveNode(this.retrieveNode).toFragmentQuery();
         if (relationTermQuery == null)
             return null;
 
-        SpanRelationQuery rq = new SpanRelationQuery(relationTermQuery, true);
+        SpanRelationQuery rq = new SpanRelationQuery(relationTermQuery, true, direction);
         SpanQuery subq1, subq2;
 
         if (subQuery1.isEmpty) {
@@ -94,4 +99,11 @@
         fq.setSorted(false);
         return fq;
     }
+    
+    public void setDirection (RelationDirection direction) {
+        this.direction = direction;
+    }
+    public RelationDirection getDirection () {
+        return direction;
+    }
 }