Merge branch 'master' of ssh://korap.ids-mannheim.de:29418/KorAP/Krill
diff --git a/.gitignore b/.gitignore
index d7c1747..13c3b07 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,5 +13,6 @@
 
 # /src/main/resources/
 /src/main/resources/server.properties
+/src/main/resources/krill.properties
 /src/main/resources/korap.conf
 /bin
diff --git a/Changes b/Changes
index a6f30b2..7410203 100644
--- a/Changes
+++ b/Changes
@@ -1,3 +1,6 @@
+0.52 2015-06-10
+        - [bugfix] Fixed payload filtering in FocusSpans (margaretha)
+
 0.51 2015-03-17
         - This is a major version (prepared for the GitHub release)
         - [cleanup] Changed groupID to "de.ids_mannheim.korap",
diff --git a/misc/krill.png b/misc/krill.png
new file mode 100644
index 0000000..1adab86
--- /dev/null
+++ b/misc/krill.png
Binary files differ
diff --git a/pom.xml b/pom.xml
index 821b2f1..1757cd9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,7 +24,7 @@
 
   <groupId>de.ids_mannheim.korap</groupId>
   <artifactId>Krill</artifactId>
-  <version>0.51</version>
+  <version>0.52</version>
   <packaging>jar</packaging>
 
   <name>Krill</name>
diff --git a/src/main/java/de/ids_mannheim/korap/KrillQuery.java b/src/main/java/de/ids_mannheim/korap/KrillQuery.java
index c966cc0..5a05bd5 100644
--- a/src/main/java/de/ids_mannheim/korap/KrillQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KrillQuery.java
@@ -9,6 +9,8 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import de.ids_mannheim.korap.query.QueryBuilder;
 import de.ids_mannheim.korap.query.SpanWithinQuery;
@@ -17,6 +19,7 @@
 import de.ids_mannheim.korap.query.wrap.SpanClassQueryWrapper;
 import de.ids_mannheim.korap.query.wrap.SpanFocusQueryWrapper;
 import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
+import de.ids_mannheim.korap.query.wrap.SpanReferenceQueryWrapper;
 import de.ids_mannheim.korap.query.wrap.SpanRegexQueryWrapper;
 import de.ids_mannheim.korap.query.wrap.SpanRelationWrapper;
 import de.ids_mannheim.korap.query.wrap.SpanRepetitionQueryWrapper;
@@ -85,10 +88,6 @@
 
     private static final int MAX_CLASS_NUM = 255; // 127;
 
-    // Variables used for relation queries
-    private String direction;
-    private byte[] classNumbers;
-
     // Private class for koral:boundary objects
     private class Boundary {
         public int min, max;
@@ -121,7 +120,6 @@
         };
     };
 
-
     /**
      * Constructs a new object for query deserialization
      * and building. Expects the name of an index field
@@ -133,10 +131,6 @@
      */
     public KrillQuery (String field) {
         this.field = field;
-        this.direction = ">:";
-        this.classNumbers = new byte[2];
-        this.classNumbers[0] = (byte) 1;
-        this.classNumbers[1] = (byte) 2;
     };
 
 
@@ -229,7 +223,6 @@
                     throw new QueryException(766,
                             "Peripheral references are currently not supported");
                 }
-                ;
 
                 JsonNode operands = json.get("operands");
 
@@ -257,9 +250,6 @@
                     if (number > MAX_CLASS_NUM)
                         throw new QueryException(709,
                                 "Valid class numbers exceeded");
-
-                    this.classNumbers = null;
-
                 }
 
                 // Reference based on spans
@@ -303,12 +293,6 @@
                 // Get wrapped token
                 return this._segFromJson(json.get("wrap"));
 
-            case "koral:relation":
-                if (!json.has("wrap")) {
-                    throw new QueryException(718, "Missing relation term");
-                }
-                return this._termFromJson(json.get("wrap"), direction);
-
             case "koral:span":
                 return this._termFromJson(json);
         };
@@ -396,6 +380,11 @@
         if (DEBUG)
             log.trace("Operands are {}", operands);
 
+        SpanQueryWrapper spanReferenceQueryWrapper = _operationReferenceFromJSON(json, operands);
+        if (spanReferenceQueryWrapper != null) {
+            return spanReferenceQueryWrapper;
+        }
+
         // Branch on operation
         switch (operation) {
             case "operation:junction":
@@ -435,6 +424,87 @@
         throw new QueryException(711, "Unknown group operation");
     };
 
+    private SpanQueryWrapper _operationReferenceFromJSON(JsonNode node, JsonNode operands)
+            throws QueryException {
+        boolean isReference = false;
+        int classNum = -1;
+        int refOperandNum = -1;
+        JsonNode childNode;
+
+        for (int i = 0; i < operands.size(); i++) {
+            childNode = operands.get(i);
+            if (childNode.has("@type")
+                    && childNode.get("@type").asText()
+                            .equals("koral:reference")
+                    && childNode.has("operation")
+                    && childNode.get("operation").asText()
+                            .equals("operation:focus")
+                    && !childNode.has("operands")) {
+
+                if (childNode.has("classRef")) {
+                    classNum = childNode.get("classRef").get(0).asInt();
+                    refOperandNum = i;
+                    isReference = true;
+                    break;
+                }
+            }
+        }
+
+        if (isReference) {
+            JsonNode resolvedNode = _resolveReference(node, operands,
+                    refOperandNum, classNum);
+            return new SpanReferenceQueryWrapper(fromJson(resolvedNode),
+                    (byte) classNum);
+        }
+
+        return null;
+    }
+
+    private JsonNode _resolveReference(JsonNode node, JsonNode operands,
+            int refOperandNum, int classNum) throws QueryException {
+        JsonNode referent = null;
+        ObjectMapper m = new ObjectMapper();
+        ArrayNode newOperands = m.createArrayNode();
+        boolean isReferentFound = false;
+        for (int i = 0; i < operands.size(); i++) {
+            if (i != refOperandNum) {
+                if (!isReferentFound) {
+                    referent = _extractReferentClass(operands.get(i), classNum);
+                    if (referent != null) isReferentFound = true;
+                }
+                newOperands.insert(i, operands.get(i));
+            }
+        }
+
+        if (isReferentFound) {
+            newOperands.insert(refOperandNum, referent);
+            ((ObjectNode) node).set("operands", newOperands);
+            return node;
+        }
+        else
+            throw new QueryException("Referent node is not found");
+
+    }
+
+    private JsonNode _extractReferentClass(JsonNode node, int classNum) {
+        JsonNode referent;
+        if (node.has("classOut") && node.get("classOut").asInt() == classNum) {
+            // System.out.println("found: " + node.toString());
+            return node;
+        }
+        else {
+            if (node.has("operands") && node.get("operands").isArray()) {
+                for (JsonNode childOperand : node.get("operands")) {
+                    referent = _extractReferentClass(childOperand, classNum);
+                    if (referent != null) {
+                        return referent;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
 
     private SpanQueryWrapper _operationRelationFromJson (JsonNode operands,
             JsonNode relation) throws QueryException {
@@ -446,16 +516,28 @@
 
         SpanQueryWrapper operand1 = fromJson(operands.get(0));
         SpanQueryWrapper operand2 = fromJson(operands.get(1));
-
-        if (operand1.isEmpty()) {
+        
+        String direction = ">:";
+        if (operand1.isEmpty() && !operand2.isEmpty()) {
             direction = "<:";
         }
 
-        SpanQueryWrapper relationWrapper = fromJson(relation);
-
-        return new SpanRelationWrapper(relationWrapper, operand1, operand2,
-                classNumbers);
-
+        if (!relation.has("@type"))
+            throw new QueryException(701,
+                    "JSON-LD group has no @type attribute");
+        
+        if (relation.get("@type").asText().equals("koral:relation")) {
+            if (!relation.has("wrap")) {
+                throw new QueryException(718, "Missing relation term");
+            }
+            SpanQueryWrapper relationWrapper = _termFromJson(
+                    relation.get("wrap"),
+                    direction);
+            return new SpanRelationWrapper(relationWrapper, operand1, operand2);
+        }
+        else {
+            throw new QueryException(713, "Query type is not supported");
+        }
     }
 
 
diff --git a/src/main/java/de/ids_mannheim/korap/query/SimpleSpanQuery.java b/src/main/java/de/ids_mannheim/korap/query/SimpleSpanQuery.java
index ff47287..de1e4c8 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SimpleSpanQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SimpleSpanQuery.java
@@ -72,9 +72,10 @@
 
     protected SpanQuery firstClause = null, secondClause = null;
     protected List<SpanQuery> clauseList = null;
-    private String field;
+    protected String field;
     protected boolean collectPayloads;
 
+    public SimpleSpanQuery () {}
 
     /**
      * Constructs a new SimpleSpanQuery using the specified
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanClassFilterQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanClassFilterQuery.java
new file mode 100644
index 0000000..48790bc
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanClassFilterQuery.java
@@ -0,0 +1,86 @@
+package de.ids_mannheim.korap.query;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.AtomicReaderContext;
+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.Spans;
+import org.apache.lucene.util.Bits;
+
+import de.ids_mannheim.korap.query.spans.ClassFilteredSpans;
+
+public class SpanClassFilterQuery extends SimpleSpanQuery {
+
+    public enum ClassOperation {
+        DISJOINT, INTERSECT, INCLUDE, EQUAL, DIFFER
+    }
+
+    private ClassOperation operation;
+    private byte classNum1, classNum2;
+
+    public SpanClassFilterQuery (SpanQuery sq, ClassOperation type,
+            int classNum1, int classNum2,
+            boolean collectPayloads) {
+        super(sq, collectPayloads);
+        this.operation = type;
+        this.classNum1 = (byte) classNum1;
+        this.classNum2 = (byte) classNum2;
+    }
+
+    @Override
+    public SimpleSpanQuery clone() {
+        return new SpanClassFilterQuery((SpanQuery) firstClause.clone(),
+                operation,
+                classNum1, classNum2, collectPayloads);
+    }
+
+    @Override
+    public Spans getSpans(AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        return new ClassFilteredSpans(this, context, acceptDocs,
+                termContexts);
+    }
+
+    @Override
+    public String toString(String field) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("spanClassFilter(");
+        sb.append(firstClause.toString());
+        sb.append(",");
+        sb.append(operation);
+        sb.append(",");
+        sb.append(classNum1);
+        sb.append(",");
+        sb.append(classNum2);
+        sb.append(")");
+        return sb.toString();
+    }
+
+    public ClassOperation getOperation() {
+        return operation;
+    }
+
+    public void setOperation(ClassOperation operation) {
+        this.operation = operation;
+    }
+
+    public byte getClassNum1() {
+        return classNum1;
+    }
+
+    public void setClassNum1(byte classNum1) {
+        this.classNum1 = classNum1;
+    }
+
+    public byte getClassNum2() {
+        return classNum2;
+    }
+
+    public void setClassNum2(byte classNum2) {
+        this.classNum2 = classNum2;
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanFocusQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanFocusQuery.java
index 305e42c..c144a58 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanFocusQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanFocusQuery.java
@@ -21,16 +21,13 @@
  * Modify the span of a match to the boundaries of a certain class.
  * 
  * In case multiple classes are found with the very same number, the
- * span is
- * maximized to start on the first occurrence from the left and end on
- * the last
- * occurrence on the right.
+ * span is maximized to start on the first occurrence from the left
+ * and end on the last occurrence on the right.
  * 
  * In case the class to modify on is not found in the subquery, the
- * match is
- * ignored.
+ * match is ignored.
  * 
- * @author diewald
+ * @author diewald, margaretha
  * 
  * @see FocusSpans
  */
@@ -38,7 +35,8 @@
 
     private List<Byte> classNumbers = new ArrayList<Byte>();
     private boolean isSorted = true;
-
+    private boolean matchTemporaryClass = false;
+    private boolean removeTemporaryClasses = false;
 
     /**
      * Construct a new SpanFocusQuery.
@@ -59,7 +57,6 @@
     public SpanFocusQuery (SpanQuery sq, List<Byte> classNumbers) {
         super(sq, true);
         this.classNumbers = classNumbers;
-        isSorted = false;
     };
 
 
@@ -83,6 +80,9 @@
     public String toString (String field) {
         StringBuffer buffer = new StringBuffer();
         buffer.append("focus(");
+        if (matchTemporaryClass){
+            buffer.append("#");
+        }
         if (classNumbers.size() > 1) {
             buffer.append("[");
             for (int i = 0; i < classNumbers.size(); i++) {
@@ -133,6 +133,9 @@
         SpanFocusQuery spanFocusQuery = new SpanFocusQuery(
                 (SpanQuery) this.firstClause.clone(), this.getClassNumbers());
         spanFocusQuery.setBoost(getBoost());
+        spanFocusQuery.setMatchTemporaryClass(this.matchTemporaryClass);
+        spanFocusQuery.setSorted(this.isSorted);
+        spanFocusQuery.setRemoveTemporaryClasses(this.removeTemporaryClasses);
         return spanFocusQuery;
     };
 
@@ -185,4 +188,20 @@
         this.isSorted = isSorted;
     }
 
+    public boolean matchTemporaryClass() {
+        return matchTemporaryClass;
+    }
+
+    public void setMatchTemporaryClass(boolean matchTemporaryClass) {
+        this.matchTemporaryClass = matchTemporaryClass;
+    }
+
+    public boolean removeTemporaryClasses() {
+        return removeTemporaryClasses;
+    }
+
+    public void setRemoveTemporaryClasses(boolean rem) {
+        this.removeTemporaryClasses = rem;
+    }
+
 };
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanReferenceQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanReferenceQuery.java
new file mode 100644
index 0000000..9c2484d
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanReferenceQuery.java
@@ -0,0 +1,61 @@
+package de.ids_mannheim.korap.query;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.AtomicReaderContext;
+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.Spans;
+import org.apache.lucene.util.Bits;
+
+import de.ids_mannheim.korap.query.spans.ReferenceSpans;
+
+public class SpanReferenceQuery extends SimpleSpanQuery {
+
+    private byte classNum;
+
+    public SpanReferenceQuery (SpanQuery firstClause, byte classNum,
+            boolean collectPayloads) {
+        super(firstClause, collectPayloads);
+        this.classNum = classNum;
+    }
+
+
+    @Override
+    public SimpleSpanQuery clone () {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+
+    @Override
+    public Spans getSpans (AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        // TODO Auto-generated method stub
+        return new ReferenceSpans(this, context, acceptDocs, termContexts);
+    }
+
+
+    @Override
+    public String toString (String field) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("spanReference(");
+        sb.append(firstClause.toString());
+        sb.append(", ");
+        sb.append(classNum);
+        sb.append(")");
+        return sb.toString();
+    }
+
+
+    public byte getClassNum() {
+        return classNum;
+    }
+
+    public void setClassNum(byte classNum) {
+        this.classNum = classNum;
+    }
+
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanRelationMatchQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanRelationMatchQuery.java
new file mode 100644
index 0000000..5bc962a
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanRelationMatchQuery.java
@@ -0,0 +1,125 @@
+package de.ids_mannheim.korap.query;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.AtomicReaderContext;
+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.Spans;
+import org.apache.lucene.util.Bits;
+
+import de.ids_mannheim.korap.query.spans.FocusSpans;
+
+public class SpanRelationMatchQuery extends SimpleSpanQuery{
+
+    private SpanQuery operandQuery;
+    private SpanQuery operand2Query;
+    private SpanRelationQuery relationQuery;
+
+    public SpanRelationMatchQuery (SpanRelationQuery relation, SpanQuery operand,
+            boolean collectPayloads) {
+
+        checkVariables(relation, operand);
+        SpanFocusQuery sq = new SpanFocusQuery(new SpanSegmentQuery(
+                relationQuery, operandQuery, true),
+                relation.getTempClassNumbers());
+        sq.setMatchTemporaryClass(true);
+        sq.setRemoveTemporaryClasses(true);
+        sq.setSorted(false); // which operand to focus might be
+                             // different from that to match
+
+        this.setFirstClause(sq);
+        this.collectPayloads = collectPayloads;
+    }
+
+    public SpanRelationMatchQuery (SpanRelationQuery relation, SpanQuery source,
+            SpanQuery target, boolean collectPayloads) {
+
+        checkVariables(relation, source, target);
+        SpanFocusQuery sq = null;
+        // match source
+        if (relationQuery.getDirection() == 0) {
+            sq = new SpanFocusQuery(new SpanSegmentQuery(relationQuery,
+                    operandQuery, true), relation.getTempTargetNum());
+        }
+        // match target
+        else {
+            sq = new SpanFocusQuery(new SpanSegmentQuery(relationQuery,
+                    operandQuery, true), relation.getTempSourceNum());
+        }
+        sq.setSorted(false);
+        sq.setMatchTemporaryClass(true);
+
+        SpanFocusQuery sq2 = new SpanFocusQuery(new SpanSegmentQuery(sq,
+                operand2Query, true), relation.getTempClassNumbers());
+        sq2.setMatchTemporaryClass(true);
+        sq2.setRemoveTemporaryClasses(true);
+        sq2.setSorted(false);
+
+        this.setFirstClause(sq2);
+        this.collectPayloads = collectPayloads;
+
+    }
+
+    public void checkVariables(SpanRelationQuery relation, SpanQuery operand) {
+        if (relation == null) {
+            throw new IllegalArgumentException(
+                    "The relation query cannot be null.");
+        }
+        if (operand == null) {
+            throw new IllegalArgumentException(
+                    "The operand query cannot be null.");
+        }
+        this.field = relation.getField();
+        if (!operand.getField().equals(field)) {
+            throw new IllegalArgumentException(
+                    "Clauses must have the same field.");
+        }
+        this.relationQuery = relation;
+        this.operandQuery = operand;
+    }
+    
+    public void checkVariables(SpanRelationQuery relation, SpanQuery operand, SpanQuery target) {
+        checkVariables(relation, operand);
+        if (target == null) {
+            if (operand == null) {
+                throw new IllegalArgumentException(
+                        "The target query cannot be null.");
+            }
+        }
+        if (!target.getField().equals(field)) {
+            throw new IllegalArgumentException(
+                    "Clauses must have the same field.");
+        }
+        this.operand2Query = target;
+    }
+
+    @Override
+    public SimpleSpanQuery clone() {
+        if (operand2Query != null) {
+            return new SpanRelationMatchQuery(
+                    (SpanRelationQuery) relationQuery.clone(),
+                    (SpanQuery) operandQuery.clone(),
+                    (SpanQuery) operand2Query.clone(), collectPayloads);
+        }
+
+        return new SpanRelationMatchQuery(
+                (SpanRelationQuery) relationQuery.clone(),
+                (SpanQuery) operandQuery.clone(), collectPayloads);
+    }
+
+    @Override
+    public Spans getSpans(AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+
+        return new FocusSpans((SpanFocusQuery) firstClause, context,
+                acceptDocs, termContexts);
+    }
+
+    @Override
+    public String toString(String field) {
+        return getFirstClause().toString();
+    }
+}
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 8df29b3..8d06a7e 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanRelationQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanRelationQuery.java
@@ -1,6 +1,8 @@
 package de.ids_mannheim.korap.query;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.lucene.index.AtomicReaderContext;
@@ -55,7 +57,12 @@
 public class SpanRelationQuery extends SimpleSpanQuery {
 
     private int direction = 0;
+    private byte tempSourceNum = 1;
+    private byte tempTargetNum = 2;
+    private byte sourceClass;
+    private byte targetClass;
 
+    private List<Byte> tempClassNumbers = Arrays.asList(tempSourceNum, tempTargetNum);
     /**
      * Constructs a SpanRelationQuery based on the given span query.
      * 
@@ -76,6 +83,13 @@
         }
     }
 
+    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 () {
@@ -95,9 +109,25 @@
     @Override
     public String toString (String field) {
         StringBuilder sb = new StringBuilder();
+        if (sourceClass > 0) {
+            sb.append("{");
+            sb.append(sourceClass);
+            sb.append(": source:");
+        }
+        if (targetClass > 0) {
+            sb.append("{");
+            sb.append(targetClass);
+            sb.append(": target:");
+        }
         sb.append("spanRelation(");
         sb.append(firstClause.toString(field));
         sb.append(")");
+        if (sourceClass > 0) {
+            sb.append("}");
+        }
+        if (targetClass > 0) {
+            sb.append("}");
+        }
         sb.append(ToStringUtils.boost(getBoost()));
         return sb.toString();
     }
@@ -110,4 +140,54 @@
         this.direction = direction;
     }
 
+    public List<Byte> getTempClassNumbers() {
+        return tempClassNumbers;
+    }
+
+    public void setTempClassNumbers(List<Byte> classNumbers) {
+        this.tempClassNumbers = classNumbers;
+    }
+
+    public byte getTempSourceNum() {
+        return tempSourceNum;
+    }
+
+    public void setTempSourceNum(byte sourceNum) {
+        this.tempSourceNum = sourceNum;
+    }
+
+    public byte getTempTargetNum() {
+        return tempTargetNum;
+    }
+
+    public void setTempTargetNum(byte targetNum) {
+        this.tempTargetNum = targetNum;
+    }
+
+    public byte getSourceClass() {
+        return sourceClass;
+    }
+
+    public void setSourceClass(byte sourceClass)
+            throws IllegalArgumentException {
+        if (sourceClass < 1) {
+            throw new IllegalArgumentException(
+                    "Class number must be bigger than 0.");
+        }
+
+        this.sourceClass = sourceClass;
+    }
+
+    public byte getTargetClass() {
+        return targetClass;
+    }
+
+    public void setTargetClass(byte targetClass)
+            throws IllegalArgumentException {
+        if (targetClass < 1) {
+            throw new IllegalArgumentException(
+                    "Class number must be bigger than 0.");
+        }
+        this.targetClass = targetClass;
+    }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanSegmentQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanSegmentQuery.java
index bb69d4c..7e75543 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanSegmentQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanSegmentQuery.java
@@ -46,7 +46,6 @@
         this(firstClause, secondClause, true);
     }
 
-
     /**
      * Constructs a SpanSegmentQuery from the two given SpanQueries.
      * 
@@ -61,16 +60,11 @@
      *            <code>false</code>.
      */
     public SpanSegmentQuery (SpanQuery firstClause, SpanQuery secondClause,
-                             boolean collectPayloads) {
+            boolean collectPayloads) {
         super(firstClause, secondClause, collectPayloads);
-    }
-
-
-    public SpanSegmentQuery (SpanRelationQuery firstClause,
-                             SimpleSpanQuery secondClause,
-                             boolean collectPayloads) {
-        super(firstClause, secondClause, true);
-        isRelation = true;
+        if (firstClause instanceof SpanRelationQuery) {
+            isRelation = true;
+        }
     }
 
 
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/ClassFilteredSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/ClassFilteredSpans.java
new file mode 100644
index 0000000..bfd5f11
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/ClassFilteredSpans.java
@@ -0,0 +1,132 @@
+package de.ids_mannheim.korap.query.spans;
+
+import static de.ids_mannheim.korap.util.KrillByte.byte2int;
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.Map;
+
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermContext;
+import org.apache.lucene.util.Bits;
+
+import de.ids_mannheim.korap.query.SpanClassFilterQuery;
+import de.ids_mannheim.korap.query.SpanClassFilterQuery.ClassOperation;
+
+public class ClassFilteredSpans extends SimpleSpans {
+
+    private BitSet bitset1, bitset2;
+    private ClassOperation operation;
+    private byte classNum1, classNum2;
+
+    public ClassFilteredSpans (SpanClassFilterQuery query,
+            AtomicReaderContext context, Bits acceptDocs,
+            Map<Term, TermContext> termContexts) throws IOException {
+        super(query, context, acceptDocs, termContexts);
+        this.operation = query.getOperation();
+        this.classNum1 = query.getClassNum1();
+        this.classNum2 = query.getClassNum2();
+        hasMoreSpans = firstSpans.next();
+    }
+
+    @Override
+    public boolean next() throws IOException {
+        while (hasMoreSpans) {
+            matchPayload.clear();
+            bitset1 = null;
+            bitset2 = null;
+            if (isClassOperationValid()) {
+                this.matchStartPosition = firstSpans.start();
+                this.matchEndPosition = firstSpans.end();
+                this.matchDocNumber = firstSpans.doc();
+                this.matchPayload.addAll(firstSpans.getPayload());
+                hasMoreSpans = firstSpans.next();
+                return true;
+            }
+            hasMoreSpans = firstSpans.next();
+        }
+        return false;
+    }
+
+    private boolean isClassOperationValid() throws IOException {
+        setBitsets();
+        int cardinality = Math.max(bitset1.cardinality(), bitset2.cardinality());
+        bitset1.and(bitset2);
+        // System.out.println("cardinality:" + cardinality);
+        switch (operation) {
+            case DISJOINT:
+                if (bitset1.cardinality() == 0) return true;
+                break;
+            case EQUAL:
+                if (cardinality == bitset1.cardinality()) return true;
+                break;
+            case DIFFER:
+                if (cardinality == 0 || cardinality != bitset1.cardinality())
+                    return true;
+                break;
+            case INCLUDE:
+                if (bitset1.cardinality() == bitset2.cardinality()) {
+                    return true;
+                }
+                break;
+            case INTERSECT:
+                if (bitset1.cardinality() > 0) return true;
+                break;
+        }
+
+        return false;
+    }
+
+    private void setBitsets() throws IOException {
+        BitSet bs = new BitSet();
+        int start, end;
+        // System.out.println("------------------------");
+        for (byte[] payload : firstSpans.getPayload()) {
+            if (payload.length == 9){
+                start = byte2int(payload, 0) + 1;
+                end = byte2int(payload, 4) + 1;
+                if (payload[8] == classNum1) {
+                    // System.out.println("bitset1 " + start + " " +
+                    // end);
+                    if (bitset1 == null) {
+                        bitset1 = new BitSet();
+                        bitset1.set(start, end);
+                    }
+                    else {
+                        bs.set(start, end);
+                        bitset1.or(bs);
+                    }
+                    // System.out.println(bitset1);
+                }
+                else if (payload[8] == classNum2) {
+                    // System.out.println("#bitset2 " + start + " " +
+                    // end);
+                    if (bitset2 == null) {
+                        bitset2 = new BitSet();
+                        bitset2.set(start, end);
+                    }
+                    else {
+                        bs.set(start, end);
+                        bitset2.or(bs);
+                        // System.out.println("OR #2");
+                    }
+                    // System.out.println(bitset2);
+                }
+            }
+        }
+
+    }
+
+    @Override
+    public boolean skipTo(int target) throws IOException {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public long cost() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+}
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/ClassSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/ClassSpans.java
index 4e97dbc..ac83c4d 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/ClassSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/ClassSpans.java
@@ -26,16 +26,16 @@
  * @author diewald
  */
 
-public class ClassSpans extends Spans {
-    private List<byte[]> classedPayload;
-    private final Spans spans;
-    private byte number;
-    private SpanQuery operand;
-    private Boolean hasmorespans = false;
+public class ClassSpans extends SimpleSpans {
+    protected List<byte[]> classedPayload;
+    protected Spans spans;
+    protected byte number;
+    protected SpanQuery operand;
+    protected Boolean hasmorespans = false;
 
-    private ByteBuffer bb = ByteBuffer.allocate(9);
+    protected ByteBuffer bb;
 
-    private final static Logger log = LoggerFactory.getLogger(ClassSpans.class);
+    private final Logger log = LoggerFactory.getLogger(ClassSpans.class);
 
     // This advices the java compiler to ignore all loggings
     public static final boolean DEBUG = false;
@@ -69,6 +69,8 @@
 
         // The highlighted payload
         this.classedPayload = new ArrayList<byte[]>(3);
+
+        this.bb = ByteBuffer.allocate(9);
     };
 
 
@@ -126,7 +128,7 @@
     };
 
 
-    private boolean addClassPayload () throws IOException {
+    protected boolean addClassPayload() throws IOException {
         hasmorespans = true;
 
         classedPayload.clear();
@@ -149,6 +151,12 @@
 
         // Add highlight information as byte array
         classedPayload.add(bb.array());
+        
+        if (spans instanceof SimpleSpans) {
+            SimpleSpans ss = (SimpleSpans) spans;
+            this.hasSpanId = ss.hasSpanId;
+            this.spanId = ss.spanId;
+        }
         return true;
     };
 
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/ElementDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/ElementDistanceSpans.java
index 5060b31..ac8fe24 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/ElementDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/ElementDistanceSpans.java
@@ -14,16 +14,12 @@
 
 /**
  * Span enumeration of element-based distance span matches. Each match
- * consists
- * of two child spans. The element-distance between the child spans is
- * the
- * difference between the element position numbers where the child
- * spans are.
- * The element-distance unit can be a sentence or a paragraph. All
- * other child
- * spans' occurrences which are not in a sentence or a paragraph (with
- * respect
- * to the element distance type currently used), are ignored.
+ * consists of two child spans. The element-distance between the child
+ * spans is the difference between the element position numbers where
+ * the child spans are. The element-distance unit can be a sentence or
+ * a paragraph. All other child spans' occurrences which are not in a
+ * sentence or a paragraph (with respect to the element distance type
+ * currently used), are ignored.
  * 
  * Note: elements cannot overlap with each other.
  * 
@@ -105,8 +101,7 @@
 
     /**
      * Add new possible (candidate) firstspans. Candidate firstspans
-     * must be in
-     * an element and not too far from the secondspan.
+     * must be in an element and not too far from the secondspan.
      * 
      * @throws IOException
      */
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 2da1558..9598ac0 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
@@ -52,12 +52,10 @@
     // This advices the java compiler to ignore all loggings
     public static final boolean DEBUG = false;
 
-    // private SimpleSpans originalSpans;
-    private boolean isSorted;
+    private boolean isSorted, matchTemporaryClass, removeTemporaryClasses;
     private List<CandidateSpan> candidateSpans;
     private int windowSize = 10;
     private int currentDoc;
-    private byte number;
 
 
     /**
@@ -86,13 +84,16 @@
         }
         classNumbers = query.getClassNumbers();
         isSorted = query.isSorted();
+        matchTemporaryClass = query.matchTemporaryClass();
+        removeTemporaryClasses = query.removeTemporaryClasses();
         candidateSpans = new ArrayList<CandidateSpan>();
         hasMoreSpans = firstSpans.next();
         currentDoc = firstSpans.doc();
 
-        // matchPayload = new ArrayList<byte[]>(6);
         this.query = query;
-        hasSpanId = true;
+        if (getSpanId() > 0) {
+            hasSpanId = true;
+        }
     }
 
 
@@ -164,7 +165,8 @@
         for (byte[] payload : firstSpans.getPayload()) {
             // No class payload - ignore
             // this may be problematic for other calculated payloads!
-            if (payload.length == 9) {
+            if ((!matchTemporaryClass && payload.length == 9)
+                    || (matchTemporaryClass && payload.length == 10)) {
                 if (classNumbers.contains(payload[8])) {
                     isClassFound = true;
                     classStart = byte2int(payload, 0);
@@ -178,9 +180,13 @@
                         maxPos = classEnd;
                     }
                 }
-                candidateSpan.getPayloads().add(payload.clone());
             }
 
+            if (payload.length == 8
+                    || (removeTemporaryClasses && payload.length == 10)) {
+                continue;
+            }
+            candidateSpan.getPayloads().add(payload.clone());
         }
 
         if (isClassFound) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/ReferenceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/ReferenceSpans.java
new file mode 100644
index 0000000..a860afd
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/ReferenceSpans.java
@@ -0,0 +1,93 @@
+package de.ids_mannheim.korap.query.spans;
+
+import static de.ids_mannheim.korap.util.KrillByte.byte2int;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.AtomicReaderContext;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermContext;
+import org.apache.lucene.util.Bits;
+
+import de.ids_mannheim.korap.query.SpanReferenceQuery;
+
+public class ReferenceSpans extends SimpleSpans {
+
+    private byte classNum;
+
+    public ReferenceSpans (SpanReferenceQuery query,
+                           AtomicReaderContext context, Bits acceptDocs,
+                           Map<Term, TermContext> termContexts)
+            throws IOException {
+        super(query, context, acceptDocs, termContexts);
+        this.classNum = query.getClassNum();
+        hasMoreSpans = firstSpans.next();
+    }
+
+
+    @Override
+    public boolean next () throws IOException {
+        while (hasMoreSpans) {
+            if (hasSameClassPosition()) {
+                matchStartPosition = firstSpans.start();
+                matchEndPosition = firstSpans.end();
+                matchDocNumber = firstSpans.doc();
+                hasMoreSpans = firstSpans.next();
+                return true;
+            }
+            hasMoreSpans = firstSpans.next();
+        }
+        return false;
+    }
+
+
+    private boolean hasSameClassPosition () throws IOException {
+        int start=0, end=0;
+        boolean isFound = false;
+        boolean match = false;
+
+        matchPayload.clear();
+
+        for (byte[] payload : firstSpans.getPayload()) {
+            if (payload.length == 9 && payload[8] == classNum) {
+                if (isFound) {
+                    if (start == byte2int(payload, 0) && end == byte2int(payload, 4)){
+                        match = true;
+                        continue;
+                    }
+                    match = false;
+                    break;
+                }
+
+                start = byte2int(payload, 0);
+                end = byte2int(payload, 4);
+                isFound = true;
+                matchPayload.add(payload);
+            }
+            else {
+                matchPayload.add(payload);
+            }
+        }
+        return match;
+    }
+
+
+    @Override
+    public boolean skipTo (int target) throws IOException {
+        if (hasMoreSpans && (firstSpans.doc() < target)) {
+            if (!firstSpans.skipTo(target)) {
+                hasMoreSpans = false;
+                return false;
+            }
+        }
+        return next();
+    }
+
+
+    @Override
+    public long cost() {
+        return firstSpans.cost();
+    }
+
+}
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 f17043d..66afdbe 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
@@ -58,7 +58,8 @@
 
     protected Logger logger = LoggerFactory.getLogger(RelationSpans.class);
     private List<CandidateRelationSpan> candidateList;
-
+    private byte tempSourceNum, tempTargetNum;
+    private byte sourceClass, targetClass;
 
     /**
      * Constructs RelationSpans from the given
@@ -77,6 +78,11 @@
             throws IOException {
         super(relationSpanQuery, context, acceptDocs, termContexts);
         direction = relationSpanQuery.getDirection();
+        tempSourceNum = relationSpanQuery.getTempSourceNum();
+        tempTargetNum = relationSpanQuery.getTempTargetNum();
+        sourceClass = relationSpanQuery.getSourceClass();
+        targetClass = relationSpanQuery.getTargetClass();
+
         candidateList = new ArrayList<>();
         relationTermSpan = (TermSpans) firstSpans;
         hasMoreSpans = relationTermSpan.next();
@@ -213,22 +219,47 @@
         }
         if (direction == 0) {
             payload.add(createClassPayload(cs.getLeftStart(), cs.getLeftEnd(),
-                    (byte) 1));
+                    tempSourceNum, false));
             payload.add(createClassPayload(cs.getRightStart(),
-                    cs.getRightEnd(), (byte) 2));
+                    cs.getRightEnd(), tempTargetNum, false));
+
+            if (sourceClass > 0) {
+                payload.add(createClassPayload(cs.getLeftStart(),
+                        cs.getLeftEnd(), sourceClass, true));
+            }
+            if (targetClass > 0) {
+                payload.add(createClassPayload(cs.getRightStart(),
+                        cs.getRightEnd(), targetClass, true));
+            }
+
         }
         else {
             payload.add(createClassPayload(cs.getRightStart(),
-                    cs.getRightEnd(), (byte) 1));
+                    cs.getRightEnd(), tempSourceNum, false));
             payload.add(createClassPayload(cs.getLeftStart(), cs.getLeftEnd(),
-                    (byte) 2));
+                    tempTargetNum, false));
+
+            if (sourceClass > 0) {
+                payload.add(createClassPayload(cs.getRightStart(),
+                        cs.getRightEnd(), sourceClass, true));
+            }
+            if (targetClass > 0) {
+                payload.add(createClassPayload(cs.getLeftStart(),
+                        cs.getLeftEnd(), targetClass, true));
+            }
         }
         cs.setPayloads(payload);
     }
 
-
-    private byte[] createClassPayload (int start, int end, byte classNumber) {
-        ByteBuffer buffer = ByteBuffer.allocate(9);
+    private byte[] createClassPayload(int start, int end, byte classNumber,
+            boolean keep) {
+        ByteBuffer buffer = null;
+        if (keep) {
+            buffer = ByteBuffer.allocate(9);
+        }
+        else {
+            buffer = ByteBuffer.allocate(10);
+        }
         buffer.putInt(start);
         buffer.putInt(end);
         buffer.put(classNumber);
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/SegmentSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/SegmentSpans.java
index 8ac53b8..9d40d31 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/SegmentSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/SegmentSpans.java
@@ -94,6 +94,7 @@
     protected int findMatch () {
         RelationSpans s1;
         SimpleSpans s2;
+
         if (firstSpans.start() == secondSpans.start()
                 && firstSpans.end() == secondSpans.end()) {
 
@@ -101,13 +102,18 @@
                 s1 = (RelationSpans) firstSpans;
                 s2 = (SimpleSpans) secondSpans;
 
-                //System.out.println("segment: " + s1.getRightStart() + " "
-                // + s1.getRightEnd());
-                if (s1.getLeftId() == s2.getSpanId()) {
+                if (s2.hasSpanId) {
+                    if (s1.getLeftId() == s2.getSpanId()) {
+                        setSpanId(s2.getSpanId());
+                        setMatch();
+                        return 0;
+                    }
+                }
+                else {
                     setMatch();
-                    setSpanId(s2.getSpanId());
                     return 0;
                 }
+
             }
             else {
                 setMatch();
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedDistanceSpans.java
index 2b93ece..60e2a8a 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedDistanceSpans.java
@@ -100,12 +100,10 @@
 
     /**
      * Sets the list of matches for the span having the smallest
-     * position (i.e.
-     * between the first and the second spans), and its candidates
-     * (i.e. its
-     * counterparts). The candidates also must have smaller positions.
-     * Simply
-     * remove the span if it does not have any candidates.
+     * position (i.e. between the first and the second spans), and its
+     * candidates (i.e. its counterparts). The candidates also must
+     * have smaller positions. Simply remove the span if it does not
+     * have any candidates.
      * 
      * @throws IOException
      */
@@ -117,13 +115,13 @@
                 hasMoreSecondSpans, firstSpanList);
         // System.out.println("--------------------");
         // System.out.println("firstSpanList:");
-        // for (CandidateSpan cs: firstSpanList) {
-        // System.out.println(cs.getStart() +" "+ cs.getEnd());
+        // for (CandidateSpan cs : firstSpanList) {
+        // System.out.println(cs.getStart() + " " + cs.getEnd());
         // }
         //
         // System.out.println("secondSpanList:");
-        // for (CandidateSpan cs: secondSpanList) {
-        // System.out.println(cs.getStart() +" "+ cs.getEnd());
+        // for (CandidateSpan cs : secondSpanList) {
+        // System.out.println(cs.getStart() + " " + cs.getEnd());
         // }
 
         CandidateSpan currentFirstSpan, currentSecondSpan;
@@ -132,7 +130,7 @@
             currentFirstSpan = firstSpanList.get(0);
             currentSecondSpan = secondSpanList.get(0);
 
-            if (currentFirstSpan.getEnd() < currentSecondSpan.getEnd()
+            if (currentFirstSpan.getStart() < currentSecondSpan.getStart()
                     || isLastCandidateSmaller(currentFirstSpan,
                             currentSecondSpan)) {
                 // log.trace("current target: "
@@ -180,20 +178,16 @@
 
     /**
      * Tells if the last candidate from the secondSpanList has a
-     * smaller end
-     * position than the end position of the the last candidate from
-     * the
-     * firstSpanList.
+     * smaller end position than the end position of the the last
+     * candidate from the firstSpanList.
      * 
      * @param currentFirstSpan
      *            the current firstspan
      * @param currentSecondSpan
      *            the current secondspan
      * @return <code>true</code> if the end position of the last
-     *         candidate from
-     *         the secondSpanList is smaller than that from the
-     *         firstSpanList,
-     *         <code>false</code> otherwise.
+     *         candidate from the secondSpanList is smaller than that
+     *         from the firstSpanList, <code>false</code> otherwise.
      */
     private boolean isLastCandidateSmaller (CandidateSpan currentFirstSpan,
             CandidateSpan currentSecondSpan) {
@@ -285,7 +279,6 @@
                 payloads.addAll(cs.getPayloads());
             }
         }
-
         CandidateSpan match = new CandidateSpan(start, end, doc, cost, payloads);
         match.setChildSpan(cs);
         return match;
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedTokenDistanceSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedTokenDistanceSpans.java
index 3e50e76..0b8b055 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedTokenDistanceSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/UnorderedTokenDistanceSpans.java
@@ -137,14 +137,16 @@
             }
 
             // left candidate
-            if (cs.getEnd() < target.getStart())
+            if (cs.getEnd() < target.getStart()) {
                 actualDistance = target.getStart() - cs.getEnd() + 1;
-            else
+            }
+            else {
                 // right candidate
                 actualDistance = cs.getStart() - target.getEnd() + 1;
-
-            if (minDistance <= actualDistance && actualDistance <= maxDistance)
+            }
+            if (minDistance <= actualDistance && actualDistance <= maxDistance) {
                 matches.add(createMatchCandidate(target, cs, false));
+            }
         }
         return matches;
     }
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanReferenceQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanReferenceQueryWrapper.java
new file mode 100644
index 0000000..02a734f
--- /dev/null
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanReferenceQueryWrapper.java
@@ -0,0 +1,47 @@
+package de.ids_mannheim.korap.query.wrap;
+
+import org.apache.lucene.search.spans.SpanQuery;
+
+import de.ids_mannheim.korap.query.SpanReferenceQuery;
+import de.ids_mannheim.korap.util.QueryException;
+
+public class SpanReferenceQueryWrapper extends SpanQueryWrapper {
+
+    private SpanQueryWrapper subQuery;
+    private byte classNum;
+
+    public SpanReferenceQueryWrapper (SpanQueryWrapper subQueryWrapper,
+            byte classNum) {
+        this.subQuery = subQueryWrapper;
+        if (subQuery != null) {
+            this.isNull = false;
+        }
+        else
+            return;
+
+        if (subQuery.isEmpty) {
+            this.isEmpty = true;
+            return;
+        }
+
+        if (classNum < 0) {
+            throw new IllegalArgumentException(
+                    "Class number must be bigger than 0.");
+        }
+        this.classNum = classNum;
+    }
+    
+    @Override
+    public SpanQuery toQuery() throws QueryException {
+
+        if (this.isNull() || this.isEmpty()) {
+            return null;
+        }
+
+        SpanQuery sq = subQuery.retrieveNode(this.retrieveNode).toQuery();
+        if (sq == null) return null;
+        
+        return new SpanReferenceQuery(sq, classNum, true);
+    }
+
+}
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 17afa22..2535ce9 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,11 +1,11 @@
 package de.ids_mannheim.korap.query.wrap;
 
-import java.util.ArrayList;
-
 import org.apache.lucene.search.spans.SpanQuery;
+import org.apache.lucene.search.spans.SpanTermQuery;
 
 import de.ids_mannheim.korap.query.SpanFocusQuery;
-import de.ids_mannheim.korap.query.SpanSegmentQuery;
+import de.ids_mannheim.korap.query.SpanRelationMatchQuery;
+import de.ids_mannheim.korap.query.SpanRelationQuery;
 import de.ids_mannheim.korap.util.QueryException;
 
 public class SpanRelationWrapper extends SpanQueryWrapper {
@@ -13,12 +13,9 @@
     private SpanQueryWrapper relationQuery;
     private SpanQueryWrapper subQuery1;
     private SpanQueryWrapper subQuery2;
-    private byte[] classNumbers;
-
 
     public SpanRelationWrapper (SpanQueryWrapper relationWrapper,
-                                SpanQueryWrapper operand1,
-                                SpanQueryWrapper operand2, byte[] classNumbers) {
+            SpanQueryWrapper operand1, SpanQueryWrapper operand2) {
 
         this.relationQuery = relationWrapper;
         if (relationQuery != null) {
@@ -34,70 +31,63 @@
 
         this.subQuery1 = operand1;
         this.subQuery2 = operand2;
-        this.classNumbers = classNumbers;
     }
 
 
     @Override
-    public SpanQuery toQuery () throws QueryException {
+    public SpanQuery toQuery() throws QueryException {
 
         if (this.isNull() || this.isEmpty()) {
             return null;
         }
 
-        SpanQuery sq = relationQuery.retrieveNode(this.retrieveNode).toQuery();
-        if (sq == null)
+        SpanTermQuery relationTermQuery = (SpanTermQuery) relationQuery.retrieveNode(
+                this.retrieveNode).toQuery();
+        if (relationTermQuery == null)
             return null;
-
+        
+        SpanRelationQuery rq = new SpanRelationQuery(relationTermQuery, true);
         SpanQuery subq1, subq2;
+
         if (subQuery1.isEmpty) {
             if (!subQuery2.isEmpty) {
                 // match target
                 subq2 = subQuery2.retrieveNode(this.retrieveNode).toQuery();
-                if (subq2 != null) {
-                    return createQuery(new SpanSegmentQuery(sq, subq2, true));
+                if (subQuery1.hasClass) {
+                    rq.setSourceClass(subQuery1.getClassNumber());
                 }
+
+                return new SpanRelationMatchQuery(rq, subq2, true);
             }
         }
         else if (subQuery2.isEmpty) {
             if (!subQuery1.isEmpty) {
                 // match source
                 subq1 = subQuery1.retrieveNode(this.retrieveNode).toQuery();
-                if (subq1 != null) {
-                    return createQuery(new SpanSegmentQuery(sq, subq1, true));
+                if (subQuery2.hasClass) {
+                    rq.setTargetClass(subQuery2.getClassNumber());
                 }
+                return new SpanRelationMatchQuery(rq, subq1, true);
             }
         }
         else {
             // match both
             subq1 = subQuery1.retrieveNode(this.retrieveNode).toQuery();
-            if (subq1 != null) {
-                SpanFocusQuery fq = new SpanFocusQuery(new SpanSegmentQuery(sq,
-                        subq1, true), (byte) 2);
-                fq.setSorted(false);
-                sq = fq;
-            }
-
             subq2 = subQuery2.retrieveNode(this.retrieveNode).toQuery();
-            if (subq2 != null) {
-                return createQuery(new SpanSegmentQuery(sq, subq2, true));
-            }
+            return new SpanRelationMatchQuery(rq, subq1, subq2, true);
         }
 
-        return createQuery(sq);
-    }
-
-    private SpanQuery createQuery(SpanQuery sq) {
-        ArrayList<Byte> classNumbers = new ArrayList<Byte>();
-        if (this.classNumbers != null) {
-            for (byte c : this.classNumbers) {
-                if (c > 0) {
-                    classNumbers.add(c);
-                }
-            }
-            return new SpanFocusQuery(sq, classNumbers);
+        // both empty
+        if (subQuery1.hasClass) {
+            rq.setSourceClass(subQuery1.getClassNumber());
         }
-        return sq;
-
+        if (subQuery2.hasClass) {
+            rq.setTargetClass(subQuery2.getClassNumber());
+        }
+        SpanFocusQuery fq = new SpanFocusQuery(rq, rq.getTempClassNumbers());
+        fq.setMatchTemporaryClass(true);
+        fq.setRemoveTemporaryClasses(true);
+        fq.setSorted(false);
+        return fq;
     }
 }
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestClassFilterIndex.java b/src/test/java/de/ids_mannheim/korap/index/TestClassFilterIndex.java
new file mode 100644
index 0000000..77ef45d
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/index/TestClassFilterIndex.java
@@ -0,0 +1,297 @@
+package de.ids_mannheim.korap.index;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import de.ids_mannheim.korap.KrillIndex;
+import de.ids_mannheim.korap.query.DistanceConstraint;
+import de.ids_mannheim.korap.query.SpanClassFilterQuery;
+import de.ids_mannheim.korap.query.SpanClassFilterQuery.ClassOperation;
+import de.ids_mannheim.korap.query.SpanClassQuery;
+import de.ids_mannheim.korap.query.SpanDistanceQuery;
+import de.ids_mannheim.korap.query.SpanElementQuery;
+import de.ids_mannheim.korap.response.Result;
+
+public class TestClassFilterIndex {
+
+    private KrillIndex ki;
+    private Result kr;
+
+    @Test
+    public void testInclude() throws IOException {
+        ki = new KrillIndex();
+        ki.addDoc(TestReferenceIndex.createFieldDoc0());
+        ki.commit();
+
+        SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+        SpanElementQuery seq2 = new SpanElementQuery("tokens", "vp");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+        SpanDistanceQuery sdq = new SpanDistanceQuery(scq1, scq2,
+                new DistanceConstraint(0, 1, false, false), true);
+
+        SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq,
+                ClassOperation.INCLUDE, 2, 1, true);
+
+        assertEquals(
+                sq.toString(),
+                "spanClassFilter(spanDistance({1: <tokens:np />}, {2: <tokens:vp />}, "
+                        + "[(w[0:1], notOrdered, notExcluded)]),INCLUDE,2,1)");
+
+        kr = ki.search(sq, (short) 10);
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+        assertEquals(7, kr.getTotalResults());
+        assertEquals(1, kr.getMatch(0).getStartPos());
+        assertEquals(5, kr.getMatch(0).getEndPos());
+        assertEquals(
+                "Frankenstein, [{2:treat {1:my daughter} well}]. She is the one that saved ...",
+                kr.getMatch(0).getSnippetBrackets());
+        assertEquals(6, kr.getMatch(1).getStartPos());
+        assertEquals(18, kr.getMatch(1).getEndPos());
+        assertEquals(
+                "Frankenstein, treat my daughter well. She [{2:is {1:the one} that saved "
+                        + "your master who you hold so dear}].", kr.getMatch(1)
+                        .getSnippetBrackets());
+        assertEquals(
+                "Frankenstein, treat my daughter well. She [{2:is {1:the one that "
+                        + "saved your master who you hold so dear}}].", kr
+                        .getMatch(2).getSnippetBrackets());
+        assertEquals("Frankenstein, treat my daughter well. She [{2:is the one that "
+                        + "saved {1:your master} who you hold so dear}].", kr
+                        .getMatch(3).getSnippetBrackets());
+        assertEquals(
+                "Frankenstein, treat my daughter well. She [{2:is the one that saved your master who {1:you} hold so dear}].",
+                kr.getMatch(4).getSnippetBrackets());
+
+    }
+
+    @Test
+    public void testDisjoint() throws IOException {
+        ki = new KrillIndex();
+        ki.addDoc(TestReferenceIndex.createFieldDoc0());
+        ki.commit();
+
+        SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+        SpanElementQuery seq2 = new SpanElementQuery("tokens", "vp");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+        SpanDistanceQuery sdq = new SpanDistanceQuery(scq1, scq2,
+                new DistanceConstraint(0, 1, false, false), true);
+
+        // kr = ki.search(sdq, (short) 10);
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+
+        SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq,
+                ClassOperation.DISJOINT, 2, 1, true);
+        
+        kr = ki.search(sq, (short) 10);
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+        assertEquals(0, kr.getMatch(0).getStartPos());
+        assertEquals(5, kr.getMatch(0).getEndPos());
+        assertEquals(
+                "[{1:Frankenstein}, {2:treat my daughter well}]. She is the one that saved ...",
+                kr.getMatch(0).getSnippetBrackets());
+        assertEquals(1, kr.getMatch(1).getStartPos());
+        assertEquals(6, kr.getMatch(1).getEndPos());
+        assertEquals(
+                "Frankenstein, [{2:treat my daughter well}. {1:She}] is the one that saved your ...",
+                kr.getMatch(1).getSnippetBrackets());
+
+        assertEquals(5, kr.getMatch(2).getStartPos());
+        assertEquals(18, kr.getMatch(2).getEndPos());
+        assertEquals(
+                "Frankenstein, treat my daughter well. [{1:She} {2:is the one that saved your master who you hold so dear}].",
+                kr.getMatch(2).getSnippetBrackets());
+    }
+
+    // Problem with SpanDistanceQuery - unordered distance spans,
+    // -> unsorted
+    @Test
+    public void testEqual() throws IOException {
+        ki = new KrillIndex();
+        ki.addDoc(TestReferenceIndex.createFieldDoc0());
+        ki.commit();
+
+        SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+        SpanElementQuery seq2 = new SpanElementQuery("tokens", "prp");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+        SpanDistanceQuery sdq = new SpanDistanceQuery(scq1, scq2,
+                new DistanceConstraint(0, 1, false, false), true);
+
+        kr = ki.search(sdq, (short) 10);
+        assertEquals(6, kr.getTotalResults());
+
+        kr = ki.search(scq2, (short) 10);
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+
+        SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq,
+                ClassOperation.EQUAL, 2, 1, true);
+
+        kr = ki.search(sq, (short) 10);
+        assertEquals(5, kr.getMatch(0).getStartPos());
+        assertEquals(6, kr.getMatch(0).getEndPos());
+        assertEquals(14, kr.getMatch(1).getStartPos());
+        assertEquals(15, kr.getMatch(1).getEndPos());
+
+    }
+
+    @Test
+    public void testDiffer() throws IOException {
+        ki = new KrillIndex();
+        ki.addDoc(TestReferenceIndex.createFieldDoc0());
+        ki.commit();
+
+        SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+        SpanElementQuery seq2 = new SpanElementQuery("tokens", "prp");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+        SpanDistanceQuery sdq = new SpanDistanceQuery(scq1, scq2,
+                new DistanceConstraint(0, 2, false, false), true);
+
+        SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq,
+                ClassOperation.DIFFER, 1, 2, true);
+        kr = ki.search(sq, (short) 20);
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+
+        assertEquals(9, kr.getTotalResults());
+        assertEquals(0, kr.getMatch(0).getStartPos());
+        assertEquals(3, kr.getMatch(0).getEndPos());
+        assertEquals(
+                "[{1:Frankenstein}, treat {2:my}] daughter well. She is the one ...",
+                kr.getMatch(0).getSnippetBrackets());
+
+        assertEquals(5, kr.getMatch(3).getStartPos());
+        assertEquals(9, kr.getMatch(3).getEndPos());
+        assertEquals(
+                "Frankenstein, treat my daughter well. [{2:She} is {1:the one}] that saved your master who you ...",
+                kr.getMatch(3).getSnippetBrackets());
+        // she is both prp and np
+    }
+
+
+    @Test
+    public void testIntersect() throws IOException {
+        ki = new KrillIndex();
+        ki.addDoc(TestReferenceIndex.createFieldDoc0());
+        ki.commit();
+
+        SpanElementQuery seq1 = new SpanElementQuery("tokens", "np");
+        SpanElementQuery seq2 = new SpanElementQuery("tokens", "vb");
+        SpanClassQuery scq = new SpanClassQuery(seq2, (byte) 3);
+        SpanDistanceQuery sdq = new SpanDistanceQuery(seq1, scq,
+                new DistanceConstraint(0, 1, false, false), true);
+        SpanClassQuery scq1 = new SpanClassQuery(sdq, (byte) 1);
+
+        SpanElementQuery seq3 = new SpanElementQuery("tokens", "prp");
+        SpanDistanceQuery sdq2 = new SpanDistanceQuery(seq3, seq2,
+                new DistanceConstraint(0, 1, false, false), true);
+        SpanClassQuery scq2 = new SpanClassQuery(sdq2, (byte) 2);
+
+        SpanDistanceQuery sdq3 = new SpanDistanceQuery(scq1, scq2,
+                new DistanceConstraint(0, 1, false, false), true);
+
+        SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq3,
+                ClassOperation.INTERSECT, 1, 2, true);
+
+        assertEquals("spanClassFilter(spanDistance({1: spanDistance(<tokens:np />, " +
+        		"{3: <tokens:vb />}, [(w[0:1], notOrdered, notExcluded)])}, " +
+        		"{2: spanDistance(<tokens:prp />, <tokens:vb />, [(w[0:1], " +
+        		"notOrdered, notExcluded)])}, [(w[0:1], notOrdered, notExcluded)]),INTERSECT,1,2)",
+                sq.toString());
+        
+        
+        kr = ki.search(sq, (short) 20);
+
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+
+        assertEquals(13, kr.getTotalResults());
+        assertEquals(0, kr.getMatch(0).getStartPos());
+        assertEquals(3, kr.getMatch(0).getEndPos());
+        assertEquals(
+                "[{1:Frankenstein, {2:{3:treat}}}{2: my}] daughter well. She is the one ...",
+                kr.getMatch(0).getSnippetBrackets());
+        assertEquals(1, kr.getMatch(1).getStartPos());
+        assertEquals(4, kr.getMatch(1).getEndPos());
+        assertEquals(
+                "Frankenstein, [{1:{2:{3:treat} my} daughter}] well. She is the one that ...",
+                kr.getMatch(1).getSnippetBrackets());
+    }
+
+
+    @Test
+    public void testMultipleSameClasses() throws IOException {
+
+        ki = new KrillIndex();
+        ki.addDoc(TestReferenceIndex.createFieldDoc0());
+        ki.commit();
+
+        SpanElementQuery seq1 = new SpanElementQuery("tokens", "nn");
+        SpanElementQuery seq = new SpanElementQuery("tokens", "prp");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanClassQuery scq = new SpanClassQuery(seq, (byte) 1);
+
+        SpanDistanceQuery sdq = new SpanDistanceQuery(scq, scq1,
+                new DistanceConstraint(3, 5, false, false), true);
+
+        SpanElementQuery seq2 = new SpanElementQuery("tokens", "vp");
+        SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+
+        SpanDistanceQuery sdq2 = new SpanDistanceQuery(sdq, scq2,
+                new DistanceConstraint(0, 1, false, false), true);
+
+        SpanClassFilterQuery sq = new SpanClassFilterQuery(sdq2,
+                ClassOperation.INCLUDE, 2, 1, true);
+        
+        kr = ki.search(sdq2, (short) 20);
+        assertEquals(6, kr.getTotalResults());
+        
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+
+        kr = ki.search(sq, (short) 20);
+
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+
+        assertEquals(6, kr.getMatch(0).getStartPos());
+        assertEquals(18, kr.getMatch(0).getEndPos());
+        assertEquals(
+                "Frankenstein, treat my daughter well. She [{2:is the {1:one} that saved {1:your} master who you hold so dear}].",
+                kr.getMatch(0).getSnippetBrackets());
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestReferenceIndex.java b/src/test/java/de/ids_mannheim/korap/index/TestReferenceIndex.java
new file mode 100644
index 0000000..4a56a04
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/index/TestReferenceIndex.java
@@ -0,0 +1,324 @@
+package de.ids_mannheim.korap.index;
+
+import static de.ids_mannheim.korap.TestSimple.getJSONQuery;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.spans.SpanQuery;
+import org.apache.lucene.search.spans.SpanTermQuery;
+import org.junit.Test;
+
+import de.ids_mannheim.korap.KrillIndex;
+import de.ids_mannheim.korap.query.DistanceConstraint;
+import de.ids_mannheim.korap.query.SpanClassQuery;
+import de.ids_mannheim.korap.query.SpanDistanceQuery;
+import de.ids_mannheim.korap.query.SpanElementQuery;
+import de.ids_mannheim.korap.query.SpanFocusQuery;
+import de.ids_mannheim.korap.query.SpanNextQuery;
+import de.ids_mannheim.korap.query.SpanReferenceQuery;
+import de.ids_mannheim.korap.query.SpanRelationMatchQuery;
+import de.ids_mannheim.korap.query.SpanRelationQuery;
+import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
+import de.ids_mannheim.korap.response.Result;
+import de.ids_mannheim.korap.util.QueryException;
+
+public class TestReferenceIndex {
+    private KrillIndex ki;
+    private Result kr;
+
+    @Test
+    public void testCase1 () throws IOException {
+        ki = new KrillIndex();
+        ki.addDoc(TestRelationIndex.createFieldDoc2());
+        ki.commit();
+
+        SpanTermQuery seq1 = new SpanTermQuery(new Term("base", "pos:V"));
+        SpanElementQuery seq2 = new SpanElementQuery("base", "np");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+        SpanNextQuery snq1 = new SpanNextQuery(scq1, scq2);
+
+        SpanFocusQuery sfq1 = new SpanFocusQuery(snq1, (byte) 2);
+
+        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
+                new Term("base", "<:child-of")), true);
+        // SpanSegmentQuery ssq = new SpanSegmentQuery(srq, sfq1,
+        // true);
+        // SpanFocusQuery sfq2 = new SpanFocusQuery(ssq, (byte) 1);
+        // sfq2.setSorted(false);
+        // sfq2.setMatchTemporaryClass(false);
+
+        SpanElementQuery seq3 = new SpanElementQuery("base", "pp");
+        SpanClassQuery scq3 = new SpanClassQuery(seq3, (byte) 3);
+        // SpanSegmentQuery ssq2 = new SpanSegmentQuery(sfq2, scq3,
+        // true);
+
+        SpanRelationMatchQuery rq = new SpanRelationMatchQuery(srq, sfq1, scq3, true);
+
+        // System.out.println(rq.toString());
+        SpanFocusQuery sfq3 = new SpanFocusQuery(rq, (byte) 1);
+
+        DistanceConstraint constraint = new DistanceConstraint(3, 3, true,
+                false);
+        SpanDistanceQuery sdq = new SpanDistanceQuery(sfq3, scq3, constraint,
+                true);
+
+        SpanReferenceQuery ref = new SpanReferenceQuery(sdq, (byte) 3, true);
+        // System.out.println(ref.toString());
+
+        kr = ki.search(ref, (short) 10);
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+        assertEquals(
+                "spanReference(spanDistance(focus(1: focus(#[1,2]spanSegment("
+                        + "focus(#1: spanSegment(spanRelation(base:<:child-of), focus(2: spanNext("
+                        + "{1: base:pos:V}, {2: <base:np />})))), {3: <base:pp />}))), "
+                        + "{3: <base:pp />}, [(w[3:3], ordered, notExcluded)]), 3)",
+                ref.toString());
+
+        assertEquals(1, kr.getMatch(0).getStartPos());
+        assertEquals(7, kr.getMatch(0).getEndPos());
+    }
+
+    @Test
+    public void testCase2() throws IOException, QueryException {
+
+        String filepath = getClass().getResource(
+                "/queries/reference/distance-reference.jsonld").getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+
+        // cat="vb" & cat="prp" & cat="nn" & #1 .notordered #2 & #1
+        // .{0,2} #3 & #3 -> #2
+
+        assertEquals(
+            "spanReference(focus(#[1,2]spanSegment(focus(#2: "
+                    + "spanSegment(spanRelation(tokens:>:stanford/d:tag), "
+                    + "focus(3: spanDistance(focus(1: spanDistance({1: <tokens:vb />}, "
+                    + "{2: <tokens:prp />}, [(w[0:1], notOrdered, notExcluded)])), "
+                    + "{3: <tokens:nn />}, [(w[0:2], notOrdered, notExcluded)])))), "
+                    + "{2: <tokens:prp />})), 2)",
+            sq.toString());
+
+        SpanElementQuery seq1 = new SpanElementQuery("tokens", "vb");
+        // new SpanTermQuery(new Term("tokens", "c:vb"));
+        SpanElementQuery seq2 = new SpanElementQuery("tokens", "prp");
+        // new SpanTermQuery(new Term("tokens", "c:prp"));
+        SpanElementQuery seq3 = new SpanElementQuery("tokens", "nn");
+        // new SpanTermQuery(new Term("tokens", "c:nn"));
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+        SpanClassQuery scq3 = new SpanClassQuery(seq3, (byte) 3);
+
+        // vb .{0,1} prp
+        SpanDistanceQuery sdq1 = new SpanDistanceQuery(scq1, scq2,
+                new DistanceConstraint(0, 1, false, false), true);
+        SpanFocusQuery sfq1 = new SpanFocusQuery(sdq1, (byte) 1);
+
+        // vb .{0,2} nn
+        SpanDistanceQuery sdq2 = new SpanDistanceQuery(sfq1, scq3,
+                new DistanceConstraint(0, 2, false, false), true);
+        SpanFocusQuery sfq2 = new SpanFocusQuery(sdq2, (byte) 3);
+
+        // nn -> prp
+        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
+                new Term("tokens", ">:stanford/d:tag")), true);
+        SpanRelationMatchQuery rq = new SpanRelationMatchQuery(srq, sfq2, scq2,
+                true);
+
+        SpanReferenceQuery ref = new SpanReferenceQuery(rq, (byte) 2, true);
+
+        ki = new KrillIndex();
+        ki.addDoc(createFieldDoc0());
+        ki.commit();
+
+        kr = ki.search(ref, (short) 10);
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+
+        assertEquals(sq.toString(), ref.toString());
+        assertEquals(2, kr.getMatch(0).getStartPos());
+        assertEquals(4, kr.getMatch(0).getEndPos());
+        assertEquals(5, kr.getMatch(1).getStartPos());
+        assertEquals(9, kr.getMatch(1).getEndPos());
+        assertEquals(11, kr.getMatch(2).getStartPos());
+        assertEquals(13, kr.getMatch(2).getEndPos());
+
+        // multiple references
+
+        SpanFocusQuery sfq3 = new SpanFocusQuery(ref, (byte) 1);
+        // vp -> nn
+        SpanRelationMatchQuery rq2 = new SpanRelationMatchQuery(srq, sfq3,
+                scq3, true);
+
+        SpanReferenceQuery ref2 = new SpanReferenceQuery(rq2, (byte) 3, true);
+
+        kr = ki.search(ref2, (short) 10);
+        assertEquals(1, kr.getMatch(0).getStartPos());
+        assertEquals(4, kr.getMatch(0).getEndPos());
+        assertEquals(10, kr.getMatch(1).getStartPos());
+        assertEquals(13, kr.getMatch(1).getEndPos());
+
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+    }
+
+    // multiple references
+    @Test
+    public void testCase3() throws IOException, QueryException {
+        ki = new KrillIndex();
+        ki.addDoc(createFieldDoc0());
+        ki.commit();
+
+        String filepath = getClass().getResource(
+                "/queries/reference/distance-multiple-references.jsonld")
+                .getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+
+        // 'cat="vb" & cat="prp" & cat="nn" & #1 .{0,1} #2 & #1 .{0,2}
+        // #3 & #3 ->stanford/d #2 & #1 ->stanford #3' annis
+        // without layer=c and + relation key
+        assertEquals(
+                "spanReference(focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:stanford/d:tag), "
+                        + "focus(1: spanReference(focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:stanford/d:tag), "
+                        + "focus(3: spanDistance(focus(1: spanDistance({1: <tokens:vb />}, {2: <tokens:prp />}, "
+                        + "[(w[0:1], notOrdered, notExcluded)])), {3: <tokens:nn />}, [(w[0:2], notOrdered, notExcluded)])))), "
+                        + "{2: <tokens:prp />})), 2)))), {3: <tokens:nn />})), 3)",
+                sq.toString());
+        kr = ki.search(sq, (short) 10);
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+        assertEquals(1, kr.getMatch(0).getStartPos());
+        assertEquals(4, kr.getMatch(0).getEndPos());
+        assertEquals(10, kr.getMatch(1).getStartPos());
+        assertEquals(13, kr.getMatch(1).getEndPos());
+    }
+
+    // multiple document
+    @Test
+    public void testCase4() throws Exception {
+        ki = new KrillIndex();
+        ki.addDoc(createFieldDoc0());
+        ki.addDoc(createFieldDoc1());
+        ki.commit();
+
+        String filepath = getClass().getResource(
+                "/queries/reference/distance-reference.jsonld")
+                .getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+
+        kr = ki.search(sq, (short) 10);
+
+        assertEquals(4, kr.getTotalResults());
+        assertEquals("doc-1", kr.getMatch(3).getDocID());
+        assertEquals(2, kr.getMatch(3).getStartPos());
+        assertEquals(4, kr.getMatch(3).getEndPos());
+    }
+
+    public static FieldDocument createFieldDoc1() {
+        FieldDocument fd = new FieldDocument();
+        fd.addString("ID", "doc-1");
+        fd.addTV(
+                "tokens",
+                "Frankenstein, treat my daughter well. She is the one that saved your master who you hold so dear.",
+
+                "[(0-12)s:Frankenstein|_0#0-12|<>:nn#0-12$<i>1<s>18|<>:s#0-37$<i>4<s>1|"
+                        + "<>:np#0-13$<i>1<s>2|"
+                        + "<:stanford/d:tag$<i>1<s>1<s>18<s>19]"
+
+                        + "[(14-19)s:treat|_1#14-19|<>:vb#14-19$<i>2<s>19|<>:vp#14-36$<i>4<s>3|"
+                        + ">:stanford/d:tag$<i>0<s>2<s>19<s>18|"
+                        + ">:stanford/d:tag$<i>3<s>3<s>19<s>21|"
+                        + ">:stanford/d:tag$<i>4<s>4<s>19<s>22]"
+
+                        + "[(20-22)s:my|_2#20-22|<>:prp#20-22$<i>3<s>20|<>:np#20-31$<i>3<s>4]"
+
+                        + "[(23-31)s:daughter|_3#23-31|<>:nn#23-31$<i>4<s>21|"
+                        + ">:stanford/d:tag$<i>2<s>5<s>21<s>20]"
+
+                        + "[(32-36)s:well|_4#32-36|<>:rb#32-36$<i>5<s>22|<>:advp#32-36$<i>4<s>5]");
+        return fd;
+    }
+
+    public static FieldDocument createFieldDoc0 () {
+        FieldDocument fd = new FieldDocument();
+        fd.addString("ID", "doc-0");
+        fd.addTV(
+                "tokens",
+                "Frankenstein, treat my daughter well. She is the one that saved your master who you hold so dear.",
+                
+                "[(0-12)s:Frankenstein|_0#0-12|<>:nn#0-12$<i>1<s>18|<>:s#0-37$<i>5<s>1|"
+                        + "<>:np#0-13$<i>1<s>2|"
+                        + "<:stanford/d:tag$<i>1<s>1<s>18<s>19]"
+
+                + "[(14-19)s:treat|_1#14-19|<>:vb#14-19$<i>2<s>19|<>:vp#14-36$<i>5<s>3|"
+                        + ">:stanford/d:tag$<i>0<s>2<s>19<s>18|"
+                        + ">:stanford/d:tag$<i>3<s>3<s>19<s>21|"
+                        + ">:stanford/d:tag$<i>4<s>4<s>19<s>22]"
+
+                + "[(20-22)s:my|_2#20-22|<>:prp#20-22$<i>3<s>20|<>:np#20-31$<i>4<s>4]"
+
+                + "[(23-31)s:daughter|_3#23-31|<>:nn#23-31$<i>4<s>21|"
+                        + ">:stanford/d:tag$<i>2<s>5<s>21<s>20]"
+
+                + "[(32-36)s:well|_4#32-36|<>:rb#32-36$<i>5<s>22|<>:advp#32-36$<i>5<s>5]"
+
+                + "[(38-41)s:She|_5#38-41|<>:prp#38-41$<i>6<s>23|<>:np#38-41$<i>6<s>36|<>:s#38-97$<i>18<s>6]"
+
+                + "[(42-44)s:is|_6#42-44|<>:vb#42-44$<i>7<s>24|<>:vp#42-96$<i>18<s>7]"
+
+                + "[(45-48)s:the|_7#45-48|<>:dt#45-48$<i>8<s>25|<>:np#45-52$<i>9<s>8|<>:np#45-96$<i>18<s>9]"
+
+                + "[(49-52)s:one|_8#49-52|<>:nn#49-52$<i>9<s>26|"
+                        + ">:stanford/d:tag$<i>5<s>6<s>26<s>23|"
+                        + ">:stanford/d:tag$<i>6<s>7<s>26<s>24|"
+                        + ">:stanford/d:tag$<i>7<s>8<s>26<s>25|"
+                        + ">:stanford/d:tag$<i>10<s>9<s>26<s>28]"
+                
+                + "[(53-57)s:that|_9#53-57|<>:rp#53-57$<i>10<s>27|<>:sb#53-96$<i>18<s>10]"
+                                              
+                + "[(58-63)s:saved|_10#58-63|<>:vb#58-63$<i>11<s>28|<>:s#58-96$<i>18<s>11|"
+                        + "<>:vp#58-96$<i>18<s>12|"
+                        + ">:stanford/d:tag$<i>9<s>10<s>28<s>27|"
+                        + ">:stanford/d:tag$<i>12<s>11<s>28<s>30|"
+                        + ">:stanford/d:tag$<i>15<s>12<s>28<s>33]"
+
+                + "[(64-68)s:your|_11#64-68|<>:prp#64-68$<i>12<s>29|<>:np#64-75$<i>13<s>13]"
+
+                + "[(69-75)s:master|_12#69-75|<>:nn#69-75$<i>13<s>30|"
+                        + ">:stanford/d:tag$<i>11<s>13<s>30<s>29]"
+
+                + "[(76-79)s:who|_13#76-79|<>:rp#76-79$<i>14<s>31|<>:sb#76-96$<i>18<s>14]"
+
+                + "[(80-83)s:you|_14#80-83|<>:prp#80-83$<i>15<s>32|<>:np#80-83$<i>15<s>37|<>:s#80-96$<i>18<s>15]"
+
+                + "[(84-88)s:hold|_15#84-88|<>:vb#84-88$<i>16<s>33|<>:vp#84-96$<i>18<s>16|"
+                        + ">:stanford/d:tag$<i>13<s>14<s>33<s>31|"
+                        + ">:stanford/d:tag$<i>14<s>15<s>33<s>32|"
+                        + ">:stanford/d:tag$<i>17<s>16<s>33<s>35]"
+
+                + "[(89-91)s:so|_16#89-91|<>:rb#89-91$<i>17<s>341|<>:adjp#89-96$<i>18<s>17]"
+
+                + "[(92-96)s:dear|_17#92-96|<>:jj#92-96$<i>18<s>35|"
+                        + ">:stanford/d:tag$<i>16<s>17<s>35<s>34]"
+                 );
+                        
+        return fd;
+    }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestRelationIndex.java b/src/test/java/de/ids_mannheim/korap/index/TestRelationIndex.java
index e0d6598..63d2a64 100644
--- a/src/test/java/de/ids_mannheim/korap/index/TestRelationIndex.java
+++ b/src/test/java/de/ids_mannheim/korap/index/TestRelationIndex.java
@@ -3,7 +3,6 @@
 import static org.junit.Assert.assertEquals;
 
 import java.io.IOException;
-import java.util.ArrayList;
 
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.spans.SpanQuery;
@@ -12,10 +11,11 @@
 
 import de.ids_mannheim.korap.KrillIndex;
 import de.ids_mannheim.korap.query.SpanAttributeQuery;
+import de.ids_mannheim.korap.query.SpanClassQuery;
 import de.ids_mannheim.korap.query.SpanElementQuery;
 import de.ids_mannheim.korap.query.SpanFocusQuery;
+import de.ids_mannheim.korap.query.SpanRelationMatchQuery;
 import de.ids_mannheim.korap.query.SpanRelationQuery;
-import de.ids_mannheim.korap.query.SpanSegmentQuery;
 import de.ids_mannheim.korap.query.SpanTermWithIdQuery;
 import de.ids_mannheim.korap.query.SpanWithAttributeQuery;
 import de.ids_mannheim.korap.response.Result;
@@ -128,7 +128,7 @@
                         + "<:child-of$<i>0<s>3<s>3<s>1|"
                         + "<:child-of$<i>7<i>0<i>1<s>4<s>2<s>3|"
                         + "<:child-of$<i>7<i>1<i>7<s>5<s>2<s>2|"
-                        + "<:dep$<i>1<s>2<s>1<s>1|"
+                        + "<:dep$<i>0<s>2<s>1<s>1|"
                         + "r@:func=sbj$<i>0<i>7<s>1]"
                         +
 
@@ -149,14 +149,15 @@
                         + "<:child-of$<i>4<b>0<i>3<s>14<s>3<s>1|"
                         + "<:child-of$<i>7<i>2<i>4<s>15<s>4<s>3|"
                         + "<:child-of$<i>7<i>4<i>7<s>16<s>4<s>2|"
-                        + "<:dep$<i>3<s>2<s>1<s>1]" +
+                        + ">:parent-of$<i>7<i>4<i>7<s>17<s>4<s>2|"
+                        + "<:dep$<i>3<s>3<s>1<s>1]" +
 
                         "[(3-4)s:Blümen|_3#14-20|pos:NN$<s>1|"
                         + ">:child-of$<i>2<i>4<s>17<s>1<s>3|"
                         + "<:dep$<i>1<s>2<s>1<s>1|" + ">:dep$<i>2<s>3<s>1<s>1|"
                         + ">:dep$<i>4<s>4<s>1<s>1|"
-                        + "r@:func=head$<i>2<i>4<s>2|"
-                        + "r@:func=obj$<i>1<i>4<s>2]" +
+                        + "r@:func=head$<i>2<i>4<s>3|"
+                        + "r@:func=obj$<i>2<i>4<s>3]" +
 
                         "[(4-5)s:für|_4#21-24|pos:PREP$<s>1|<>:pp#21-38$<i>7<s>2|"
                         + ">:child-of$<i>4<i>7<s>18<s>1<s>2|"
@@ -261,9 +262,7 @@
 
 
     /**
-     * Relations with attributes
-     * need focusMulti on span relation query before
-     * SpanWithAttributeQuery
+     * Relations only
      * */
     @Test
     public void testCase3 () throws IOException {
@@ -288,32 +287,11 @@
         assertEquals(3, kr.getMatch(4).getEndPos());
         assertEquals(2, kr.getMatch(5).getStartPos());
         assertEquals(4, kr.getMatch(5).getEndPos());
-
-        ArrayList<Byte> classNumbers = new ArrayList<Byte>();
-        classNumbers.add((byte) 1);
-        classNumbers.add((byte) 2);
-
-        SpanFocusQuery fq = new SpanFocusQuery(srq, classNumbers);
-        kr = ki.search(fq, (short) 20);
-        /*
-         * for (Match km : kr.getMatches()) {
-         * System.out.println(km.getStartPos() + "," + km.getEndPos()
-         * + " " + km.getSnippetBrackets()); }
-         */
-        assertEquals((long) 13, kr.getTotalResults());
-        assertEquals(0, kr.getMatch(0).getStartPos());
-        assertEquals(1, kr.getMatch(0).getEndPos());
-        assertEquals(0, kr.getMatch(1).getStartPos());
-        assertEquals(7, kr.getMatch(1).getEndPos());
-        assertEquals(0, kr.getMatch(2).getStartPos());
-        assertEquals(7, kr.getMatch(2).getEndPos());
-        assertEquals(1, kr.getMatch(3).getStartPos());
-        assertEquals(7, kr.getMatch(3).getEndPos());
-        assertEquals(1, kr.getMatch(4).getStartPos());
-        assertEquals(7, kr.getMatch(4).getEndPos());
     }
 
-
+    /**
+     * Relations only with/out attribute
+     * */
     @Test
     public void testCase4 () throws IOException {
         ki.addDoc(createFieldDoc2());
@@ -322,12 +300,14 @@
         SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
                 new Term("base", ">:child-of")), true);
 
-        ArrayList<Byte> classNumbers = new ArrayList<Byte>();
-        classNumbers.add((byte) 1);
-        classNumbers.add((byte) 2);
+        SpanFocusQuery fq = new SpanFocusQuery(srq, srq.getTempClassNumbers());
+        fq.setMatchTemporaryClass(true);
+        fq.setRemoveTemporaryClasses(true);
+        fq.setSorted(false);
 
-        SpanWithAttributeQuery wq = new SpanWithAttributeQuery(
-                new SpanFocusQuery(srq, classNumbers), new SpanAttributeQuery(
+        // child-of with attr func=sbj
+        SpanWithAttributeQuery wq = new SpanWithAttributeQuery(fq,
+                new SpanAttributeQuery(
                         new SpanTermQuery(new Term("base", "r@:func=sbj")),
                         true), true);
 
@@ -337,7 +317,7 @@
         assertEquals(7, kr.getMatch(0).getEndPos());
 
         // child-of without attr func=sbj
-        wq = new SpanWithAttributeQuery(new SpanFocusQuery(srq, classNumbers),
+        wq = new SpanWithAttributeQuery(fq,
                 new SpanAttributeQuery(new SpanTermQuery(new Term("base",
                         "r@:func=sbj")), true, true), true);
         kr = ki.search(wq, (short) 20);
@@ -345,87 +325,9 @@
     }
 
 
-    @Test
-    public void testCase5 () throws IOException {
-
-        ki.addDoc(createFieldDoc2());
-        ki.commit();
-
-        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
-                new Term("base", "<:dep")), true);
-        kr = ki.search(srq, (short) 10);
-
-        ArrayList<Byte> classNumbers = new ArrayList<Byte>();
-        classNumbers.add((byte) 1);
-        classNumbers.add((byte) 2);
-
-        SpanFocusQuery fq = new SpanFocusQuery(srq, classNumbers);
-        kr = ki.search(fq, (short) 10);
-        // for (Match km : kr.getMatches()) {
-        // System.out.println(km.getStartPos() + "," + km.getEndPos()
-        // + " "
-        // + km.getSnippetBrackets());
-        // }
-
-        SpanAttributeQuery saq = new SpanAttributeQuery(new SpanTermQuery(
-                new Term("base", "r@:func=obj")), true);
-        kr = ki.search(saq, (short) 10);
-
-        // child-of with attr func-obj
-        SpanWithAttributeQuery wq = new SpanWithAttributeQuery(
-                new SpanFocusQuery(srq, classNumbers), new SpanAttributeQuery(
-                        new SpanTermQuery(new Term("base", "r@:func=obj")),
-                        true), true);
-
-        kr = ki.search(wq, (short) 10);
-        assertEquals((long) 1, kr.getTotalResults());
-        assertEquals(1, kr.getMatch(0).getStartPos()); // element
-        assertEquals(4, kr.getMatch(0).getEndPos());
-    }
-
-
-    @Test
-    public void testCase10 () throws IOException {
-        ki.addDoc(createFieldDoc2());
-        ki.commit();
-        // target of a dependency relation
-        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
-                new Term("base", "<:dep")), true);
-        kr = ki.search(srq, (short) 10);
-        assertEquals((long) 6, kr.getTotalResults());
-
-        ArrayList<Byte> classNumbers = new ArrayList<Byte>();
-        classNumbers.add((byte) 1);
-        classNumbers.add((byte) 2);
-
-        SpanFocusQuery fq = new SpanFocusQuery(srq, classNumbers);
-        kr = ki.search(fq, (short) 10);
-        assertEquals((long) 6, kr.getTotalResults());
-
-        SpanAttributeQuery aq = new SpanAttributeQuery(new SpanTermQuery(
-                new Term("base", "r@:func=head")), true);
-        kr = ki.search(aq, (short) 10);
-
-        // dependency relation, which is also a head
-        SpanWithAttributeQuery wq = new SpanWithAttributeQuery(
-                new SpanFocusQuery(srq, classNumbers), new SpanAttributeQuery(
-                        new SpanTermQuery(new Term("base", "r@:func=head")),
-                        true), true);
-
-        kr = ki.search(wq, (short) 20);
-
-        assertEquals((long) 2, kr.getTotalResults());
-        assertEquals(2, kr.getMatch(0).getStartPos());
-        assertEquals(4, kr.getMatch(0).getEndPos());
-        assertEquals(5, kr.getMatch(1).getStartPos());
-        assertEquals(7, kr.getMatch(1).getEndPos());
-
-    }
-
-
     /**
-     * Match left return left
-     * Match right return right
+     * Relation directions <br/>
+     * Relation with specific sources, return the sources
      * */
     @Test
     public void testCase6 () throws IOException {
@@ -433,9 +335,19 @@
         ki.commit();
 
         // return all children that are NP
-        SpanQuery rv = new SpanSegmentQuery(new SpanRelationQuery(
-                new SpanTermQuery(new Term("base", ">:child-of")), true),
-                new SpanElementQuery("base", "np"), true);
+        SpanElementQuery seq1 = new SpanElementQuery("base", "np");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+
+        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
+                new Term("base", ">:child-of")), true);
+
+        SpanRelationMatchQuery rm = new SpanRelationMatchQuery(srq, scq1, true);
+        SpanFocusQuery rv = new SpanFocusQuery(rm, (byte) 1);
+        rv.setSorted(false);
+
+        assertEquals(
+                "focus(1: focus(#[1,2]spanSegment(spanRelation(base:>:child-of), {1: <base:np />})))",
+                rv.toString());
 
         kr = ki.search(rv, (short) 10);
 
@@ -450,10 +362,10 @@
         assertEquals(7, kr.getMatch(3).getEndPos());
 
         // return all parents that are NP
-        rv = new SpanSegmentQuery(new SpanRelationQuery(new SpanTermQuery(
-                new Term("base", "<:child-of")), true), new SpanElementQuery(
-                "base", "np"), true);
-
+        srq = new SpanRelationQuery(new SpanTermQuery(new Term("base",
+                "<:child-of")), true);
+        rm = new SpanRelationMatchQuery(srq, scq1, true);
+        rv = new SpanFocusQuery(rm, (byte) 1);
         kr = ki.search(rv, (short) 10);
 
         assertEquals(7, kr.getTotalResults());
@@ -473,24 +385,78 @@
         assertEquals(7, kr.getMatch(6).getEndPos());
     }
 
+    /**
+     * Dependency relations with attribute
+     * */
+    @Test
+    public void testCase5() throws IOException {
+        ki.addDoc(createFieldDoc2());
+        ki.commit();
+
+        // target of a dependency relation
+        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
+                new Term("base", "<:dep")), true);
+        kr = ki.search(srq, (short) 10);
+        assertEquals((long) 6, kr.getTotalResults());
+
+        SpanFocusQuery fq = new SpanFocusQuery(srq, srq.getTempClassNumbers());
+        fq.setMatchTemporaryClass(true);
+        fq.setRemoveTemporaryClasses(true);
+        // fq.setSorted(false);
+
+        kr = ki.search(fq, (short) 10);
+        assertEquals((long) 6, kr.getTotalResults());
+        // for (Match km : kr.getMatches()) {
+        // System.out.println(km.getStartPos() + "," + km.getEndPos()
+        // + " "
+        // + km.getSnippetBrackets());
+        // }
+        SpanAttributeQuery aq = new SpanAttributeQuery(new SpanTermQuery(
+                new Term("base", "r@:func=head")), true);
+        kr = ki.search(aq, (short) 10);
+
+        // dependency relation, which is also a head
+        SpanWithAttributeQuery wq = new SpanWithAttributeQuery(fq,
+                new SpanAttributeQuery(new SpanTermQuery(new Term("base",
+                        "r@:func=head")), true), true);
+
+        kr = ki.search(wq, (short) 20);
+
+        assertEquals((long) 2, kr.getTotalResults());
+        assertEquals(2, kr.getMatch(0).getStartPos());
+        assertEquals(4, kr.getMatch(0).getEndPos());
+        assertEquals(5, kr.getMatch(1).getStartPos());
+        assertEquals(7, kr.getMatch(1).getEndPos());
+
+    }
 
     /**
-     * Match left, return right
-     * sort by left, then sort by right
+     * Relation with specific sources, return any targets
      * */
     @Test
     public void testCase7 () throws IOException {
         ki.addDoc(createFieldDoc2());
         ki.commit();
 
-        // return all children that are NP
-        SpanQuery rv = new SpanSegmentQuery(new SpanRelationQuery(
-                new SpanTermQuery(new Term("base", ">:child-of")), true),
-                new SpanElementQuery("base", "np"), true);
+        // match all children that are NP
+        SpanElementQuery seq1 = new SpanElementQuery("base", "np");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+
+        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
+                new Term("base", ">:child-of")), true);
+        srq.setTargetClass((byte) 2);
+
+        SpanRelationMatchQuery rm = new SpanRelationMatchQuery(srq, scq1, true);
+        // SpanQuery rv = new SpanFocusQuery(rm, (byte) 1);
 
         //return all parents of np
-        SpanFocusQuery rv2 = new SpanFocusQuery(rv, (byte) 2);
+        SpanFocusQuery rv2 = new SpanFocusQuery(rm, (byte) 2);
         rv2.setSorted(false);
+
+        assertEquals(
+                "focus(2: focus(#[1,2]spanSegment({2: target:spanRelation(base:>:child-of)}, {1: <base:np />})))",
+                rv2.toString());
+
         kr = ki.search(rv2, (short) 10);
 
         assertEquals((long) 4, kr.getTotalResults());
@@ -519,26 +485,32 @@
         ki.commit();
 
         //return source of dep relations to pos:NN
-        SpanQuery rv = new SpanFocusQuery(new SpanSegmentQuery(
-                new SpanRelationQuery(new SpanTermQuery(new Term("base",
-                        "<:dep")), true), new SpanTermWithIdQuery(new Term(
-                        "base", "pos:NN"), true), true), (byte) 1);
+        
+        SpanTermWithIdQuery tq = new SpanTermWithIdQuery(new Term("base",
+                "pos:NN"), true);
+        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(new Term("base",
+                "<:dep")), true);
+        srq.setSourceClass((byte) 1);
+        SpanRelationMatchQuery rm = new SpanRelationMatchQuery(srq, tq, true);
+        SpanQuery rv = new SpanFocusQuery(rm, (byte) 1);
 
         kr = ki.search(rv, (short) 10);
         assertEquals((long) 3, kr.getTotalResults());
-        assertEquals(1, kr.getMatch(0).getStartPos());
-        assertEquals(2, kr.getMatch(0).getEndPos());
+        assertEquals(0, kr.getMatch(0).getStartPos());
+        assertEquals(1, kr.getMatch(0).getEndPos());
         assertEquals(1, kr.getMatch(1).getStartPos());
         assertEquals(2, kr.getMatch(1).getEndPos());
         assertEquals(4, kr.getMatch(2).getStartPos());
         assertEquals(5, kr.getMatch(2).getEndPos());
 
-        //return target of dep relations from pos:NN
-        rv = new SpanFocusQuery(
-                new SpanSegmentQuery(new SpanRelationQuery(new SpanTermQuery(
-                        new Term("base", ">:dep")), true),
-                        new SpanTermWithIdQuery(new Term("base", "pos:NN"),
-                                true), true), (byte) 2);
+        // return target of dep relations from pos:NN
+        srq = new SpanRelationQuery(
+                new SpanTermQuery(
+                new Term("base", ">:dep")), true);
+        srq.setTargetClass((byte) 1);
+        rm = new SpanRelationMatchQuery(srq, tq, true);
+        rv = new SpanFocusQuery(rm, (byte) 1);
+
         kr = ki.search(rv, (short) 10);
         assertEquals((long) 3, kr.getTotalResults());
         assertEquals(2, kr.getMatch(0).getStartPos());
@@ -552,8 +524,9 @@
 
 
     /**
-     * Relation with variable match right, return left sort by right,
-     * then sort by left
+     * Relation with specific sources and return any targets <br/>
+     * Relation with specific sources and targets, return the targets <br/>
+     * Relation with specific sources and targets, return the sources
      * 
      * @throws IOException
      * */
@@ -563,12 +536,19 @@
         ki.commit();
 
         // return all children of np
-        SpanFocusQuery rv = new SpanFocusQuery(new SpanSegmentQuery(
-                new SpanRelationQuery(new SpanTermQuery(new Term("base",
-                        "<:child-of")), true), new SpanElementQuery("base",
-                        "np"), true), (byte) 1);
+        SpanElementQuery seq1 = new SpanElementQuery("base", "np");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
+                new Term("base", "<:child-of")), true);
+        srq.setSourceClass((byte) 2);
+        SpanRelationMatchQuery rm = new SpanRelationMatchQuery(srq, scq1, true);
+        SpanFocusQuery rv = new SpanFocusQuery(rm, (byte) 2);
         rv.setSorted(false);
 
+        assertEquals(
+                "focus(2: focus(#[1,2]spanSegment({2: source:spanRelation(base:<:child-of)}, {1: <base:np />})))",
+                rv.toString());
+
         kr = ki.search(rv, (short) 10);
 
         assertEquals((long) 7, kr.getTotalResults());
@@ -586,12 +566,23 @@
         assertEquals(6, kr.getMatch(5).getEndPos());
         assertEquals(6, kr.getMatch(6).getStartPos());
         assertEquals(7, kr.getMatch(6).getEndPos());
-        // sorting left problem (solved)
 
         // return all children of np that are articles
-        SpanSegmentQuery rv2 = new SpanSegmentQuery(rv, new SpanTermQuery(
-                new Term("base", "pos:ART")));
-        kr = ki.search(rv2, (short) 10);
+
+        SpanTermWithIdQuery tiq = new SpanTermWithIdQuery(new Term("base",
+                "pos:ART"), true);
+        SpanClassQuery scq2 = new SpanClassQuery(tiq, (byte) 2);
+
+        srq = new SpanRelationQuery(new SpanTermQuery(new Term("base",
+                ">:child-of")), true);
+        rm = new SpanRelationMatchQuery(srq, scq2, scq1, true);
+        rv = new SpanFocusQuery(rm, (byte) 2);
+
+        assertEquals(
+                "focus(2: focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(base:>:child-of), {2: spanTermWithId(base:pos:ART)})), {1: <base:np />})))",
+                rv.toString());
+
+        kr = ki.search(rv, (short) 10);
 
         assertEquals((long) 2, kr.getTotalResults());
         assertEquals(2, kr.getMatch(0).getStartPos());
@@ -600,20 +591,45 @@
         assertEquals(6, kr.getMatch(1).getEndPos());
 
         // return all nps whose children are articles
-        SpanSegmentQuery rv3 = new SpanSegmentQuery(rv,
-                new SpanTermWithIdQuery(new Term("base", "pos:ART"), true));
-        
-        
-        SpanFocusQuery sf = new SpanFocusQuery(rv3, (byte) 1);
+        SpanFocusQuery sf = new SpanFocusQuery(rm, (byte) 1);
+        assertEquals(
+                "focus(1: focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(base:>:child-of), {2: spanTermWithId(base:pos:ART)})), {1: <base:np />})))",
+                sf.toString());
+
         kr = ki.search(sf, (short) 10);
 
         assertEquals((long) 2, kr.getTotalResults());
-
         assertEquals(2, kr.getMatch(0).getStartPos());
-        assertEquals(3, kr.getMatch(0).getEndPos());
+        assertEquals(4, kr.getMatch(0).getEndPos());
         assertEquals(5, kr.getMatch(1).getStartPos());
-        assertEquals(6, kr.getMatch(1).getEndPos());
+        assertEquals(7, kr.getMatch(1).getEndPos());
 
     }
 
+    @Test
+    public void testCase10() throws IOException {
+        ki.addDoc(createFieldDoc2());
+        ki.commit();
+        SpanElementQuery seq1 = new SpanElementQuery("base", "np");
+        SpanElementQuery seq2 = new SpanElementQuery("base", "np");
+        SpanClassQuery scq1 = new SpanClassQuery(seq1, (byte) 1);
+        SpanClassQuery scq2 = new SpanClassQuery(seq2, (byte) 2);
+
+        SpanRelationQuery srq = new SpanRelationQuery(new SpanTermQuery(
+                new Term("base", ">:child-of")), true);
+        SpanRelationMatchQuery rq = new SpanRelationMatchQuery(srq, scq2, true);
+
+        kr = ki.search(rq, (short) 10);
+
+        assertEquals((long) 4, kr.getTotalResults());
+        assertEquals(0, kr.getMatch(0).getStartPos());
+        assertEquals(7, kr.getMatch(0).getEndPos());
+        assertEquals(1, kr.getMatch(1).getStartPos());
+        assertEquals(7, kr.getMatch(1).getEndPos());
+        assertEquals(2, kr.getMatch(2).getStartPos());
+        assertEquals(7, kr.getMatch(2).getEndPos());
+        assertEquals(4, kr.getMatch(3).getStartPos());
+        assertEquals(7, kr.getMatch(3).getEndPos());
+    }
+
 }
\ No newline at end of file
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanReferenceQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanReferenceQueryJSON.java
new file mode 100644
index 0000000..b8a21b8
--- /dev/null
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanReferenceQueryJSON.java
@@ -0,0 +1,84 @@
+package de.ids_mannheim.korap.query;
+
+import static de.ids_mannheim.korap.TestSimple.getJSONQuery;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+
+import org.apache.lucene.search.spans.SpanQuery;
+import org.junit.Test;
+
+import de.ids_mannheim.korap.query.wrap.SpanQueryWrapper;
+import de.ids_mannheim.korap.util.QueryException;
+
+public class TestSpanReferenceQueryJSON {
+
+    @Test
+    public void testFirstOperandRef() throws IOException, QueryException {
+
+        String filepath = getClass().getResource(
+                "/queries/reference/first-operand-reference.jsonld").getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+
+        // 'cat="V" & cat="NP" & cat="PP" & #2 . #1 & #1 ->dep #3 &
+        // #3 . #2
+        assertEquals(
+                "spanReference(spanNext({3: tokens:p:P}, focus(2: focus(#[1,2]spanSegment(focus(#2: "
+                        + "spanSegment(spanRelation(tokens:>:mate/d:HEAD), focus(1: spanNext("
+                        + "{2: tokens:p:V}, {1: <tokens:c:NP />})))), {3: tokens:p:P})))), 3)",
+                sq.toString());
+    }
+
+    @Test
+    public void testSecondOperandRef() throws QueryException {
+
+        String filepath = getClass().getResource(
+                "/queries/reference/second-operand-reference.jsonld").getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+
+        // 'cat="V" & cat="NP" & cat="PP" & #2 . #1 & #1 ->dep #3 &
+        // #2 . #3
+        assertEquals(
+                "spanReference(spanNext(focus(2: focus(#[1,2]spanSegment(focus(#2: "
+                        + "spanSegment(spanRelation(tokens:>:mate/d:HEAD), focus(1: spanNext("
+                        + "{2: tokens:p:V}, {1: <tokens:c:NP />})))), {3: tokens:p:P}))), "
+                        + "{3: tokens:p:P}), 3)", sq.toString());
+    }
+
+    @Test
+    public void testMultipleReferences() throws QueryException {
+        String filepath = getClass().getResource(
+                "/queries/reference/multiple-references.jsonld").getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+
+        // 'cat="VP" & cat="NP" & cat="PP" & #1 . #2 & #2 . #3 & #1 .
+        // #3 & #2 ->dep #1'
+        assertEquals(
+                "spanReference(focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:mate/d:HEAD), "
+                        + "focus(2: spanReference(spanNext(focus(1: spanNext(focus(2: spanNext({1: <tokens:c:VP />}, "
+                        + "{2: <tokens:c:NP />})), {3: <tokens:c:PP />})), {3: <tokens:c:PP />}), 3)))), "
+                        + "{1: <tokens:c:VP />})), 1)",
+                sq.toString());
+    }
+
+    @Test
+    public void testDistanceReferences() throws QueryException {
+        String filepath = getClass().getResource(
+        // "/queries/reference/distance-reference.jsonld").getFile();
+                "/queries/reference/bug-multiple-distance.jsonld").getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+
+        // 'cat="VP" & cat="NP" & cat="PP" & #1 . #2 & #2 . #3 & #1 .
+        // #3 & #2 ->dep #1'
+        assertEquals(
+                "spanReference(focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:stanford/d:tag), "
+                        + "focus(2: spanDistance(focus(1: spanDistance(<tokens:c:vb />, {1: <tokens:c:prp />}, "
+                        + "[(w[1:1], notOrdered, notExcluded)])), {2: <tokens:c:nn />}, [(w[0:2], ordered, notExcluded)])))), "
+                        + "{1: <tokens:c:prp />})), 1)",
+                sq.toString());
+    }
+}
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestSpanRelationQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestSpanRelationQueryJSON.java
index 2512d1d..fccd34a 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestSpanRelationQueryJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestSpanRelationQueryJSON.java
@@ -19,7 +19,7 @@
         SpanQueryWrapper sqwi = getJSONQuery(filepath);
         SpanQuery sq = sqwi.toQuery();
         assertEquals(
-                "focus([1,2]spanSegment(tokens:>:mate/d:HEAD, <tokens:c:s />))",
+                "focus(#[1,2]spanSegment(spanRelation(tokens:>:mate/d:HEAD), <tokens:c:s />))",
                 sq.toString());
     }
 
@@ -32,7 +32,7 @@
         SpanQueryWrapper sqwi = getJSONQuery(filepath);
         SpanQuery sq = sqwi.toQuery();
         assertEquals(
-                "focus([1,2]spanSegment(tokens:<:mate/d:HEAD, <tokens:c:vp />))",
+                "focus(#[1,2]spanSegment(spanRelation(tokens:<:mate/d:HEAD), <tokens:c:vp />))",
                 sq.toString());
     }
 
@@ -45,7 +45,7 @@
         SpanQueryWrapper sqwi = getJSONQuery(filepath);
         SpanQuery sq = sqwi.toQuery();
         assertEquals(
-                "focus([1,2]spanSegment(focus(2: spanSegment(tokens:>:mate/d:HEAD, <tokens:c:s />)), <tokens:c:vp />))",
+                "focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:mate/d:HEAD), <tokens:c:s />)), <tokens:c:vp />))",
                 sq.toString());
     }
 
@@ -58,7 +58,7 @@
         SpanQueryWrapper sqwi = getJSONQuery(filepath);
         SpanQuery sq = sqwi.toQuery();
         assertEquals(
-                "focus([1,2]spanSegment(focus(2: spanSegment(tokens:>:mate/d:HEAD, "
+                "focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:mate/d:HEAD), "
                         + "spanElementWithAttribute(<tokens:c:s />, spanAttribute(tokens:@root)))), <tokens:c:vp />))",
                 sq.toString());
     }
@@ -72,7 +72,7 @@
         SpanQueryWrapper sqwi = getJSONQuery(filepath);
         SpanQuery sq = sqwi.toQuery();
         assertEquals(
-                "focus([1,2]spanSegment(focus(2: spanSegment(tokens:>:mate/d:HEAD, "
+                "focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation(tokens:>:mate/d:HEAD), "
                         + "spanElementWithAttribute(<tokens:c:s />, spanAttribute(tokens:type:top)))), <tokens:c:vp />))",
                 sq.toString());
     }
@@ -85,7 +85,8 @@
                 "/queries/relation/relation-only.json").getFile();
         SpanQueryWrapper sqwi = getJSONQuery(filepath);
         SpanQuery sq = sqwi.toQuery();
-        assertEquals("focus([1,2]tokens:<:mate/d:HEAD)", sq.toString());
+        assertEquals("focus(#[1,2]spanRelation(tokens:>:mate/d:HEAD))",
+                sq.toString());
     }
 
     @Test
@@ -96,18 +97,41 @@
         SpanQueryWrapper sqwi = getJSONQuery(filepath);
         SpanQuery sq = sqwi.toQuery();
         assertEquals(
-                "focus(1: spanSegment(tokens:<:mate/d:HEAD, <tokens:np />))",
+                "focus(1: focus(#[1,2]spanSegment(spanRelation(tokens:<:mate/d:HEAD), {1: <tokens:c:np />})))",
                 sq.toString());
     }
 
     @Test
-    public void testFocusTarget () throws QueryException {
+    public void testFocusTarget() throws QueryException {
         String filepath = getClass().getResource(
                 "/queries/relation/focus-target.json").getFile();
         SpanQueryWrapper sqwi = getJSONQuery(filepath);
         SpanQuery sq = sqwi.toQuery();
         assertEquals(
-                "focus(2: spanSegment(tokens:>:mate/d:HEAD, <tokens:s />))",
+                "focus(2: focus(#[1,2]spanSegment(focus(#2: spanSegment(spanRelation("
+                        + "tokens:>:mate/d:HEAD), {1: <tokens:c:s />})), {2: <tokens:c:np />})))",
+                sq.toString());
+    }
+
+    @Test
+    public void testFocusEmptyTarget() throws QueryException {
+        String filepath = getClass().getResource(
+                "/queries/relation/focus-empty-target.json").getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+        assertEquals(
+                "focus(2: focus(#[1,2]spanSegment({2: target:spanRelation(tokens:>:mate/d:HEAD)}, {1: <tokens:c:s />})))",
+                sq.toString());
+    }
+
+    @Test
+    public void testFocusEmptyBoth() throws QueryException {
+        String filepath = getClass().getResource(
+                "/queries/relation/focus-empty-both.json").getFile();
+        SpanQueryWrapper sqwi = getJSONQuery(filepath);
+        SpanQuery sq = sqwi.toQuery();
+        assertEquals(
+                "focus(2: focus(#[1,2]{1: source:{2: target:spanRelation(tokens:>:mate/d:HEAD)}}))",
                 sq.toString());
     }
 }
diff --git a/src/test/java/de/ids_mannheim/korap/search/TestKrill.java b/src/test/java/de/ids_mannheim/korap/search/TestKrill.java
index 622a80b..973516e 100644
--- a/src/test/java/de/ids_mannheim/korap/search/TestKrill.java
+++ b/src/test/java/de/ids_mannheim/korap/search/TestKrill.java
@@ -1,35 +1,30 @@
 package de.ids_mannheim.korap.search;
 
-import java.util.*;
-import java.io.*;
+import static de.ids_mannheim.korap.TestSimple.getString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 
-import static de.ids_mannheim.korap.TestSimple.*;
+import java.io.IOException;
+import java.util.HashMap;
 
-import de.ids_mannheim.korap.Krill;
-import de.ids_mannheim.korap.KrillMeta;
-import de.ids_mannheim.korap.KrillCollection;
-import de.ids_mannheim.korap.KrillQuery;
-import de.ids_mannheim.korap.KrillIndex;
-import de.ids_mannheim.korap.query.QueryBuilder;
-import de.ids_mannheim.korap.index.FieldDocument;
-import de.ids_mannheim.korap.collection.CollectionBuilder;
-import de.ids_mannheim.korap.response.SearchContext;
-import de.ids_mannheim.korap.response.Result;
-import java.nio.file.Files;
-import java.nio.file.FileSystem;
-import java.nio.file.Path;
-import java.nio.charset.StandardCharsets;
-import java.nio.ByteBuffer;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.JsonNode;
-
-import static org.junit.Assert.*;
 import org.junit.Test;
-import org.junit.Ignore;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import de.ids_mannheim.korap.Krill;
+import de.ids_mannheim.korap.KrillCollection;
+import de.ids_mannheim.korap.KrillIndex;
+import de.ids_mannheim.korap.KrillMeta;
+import de.ids_mannheim.korap.collection.CollectionBuilder;
+import de.ids_mannheim.korap.index.FieldDocument;
+import de.ids_mannheim.korap.query.QueryBuilder;
+import de.ids_mannheim.korap.response.Result;
+import de.ids_mannheim.korap.response.SearchContext;
+
 @RunWith(JUnit4.class)
 public class TestKrill {
     @Test
@@ -635,6 +630,9 @@
         Result kr = ks.apply(ki);
         assertEquals(kr.getSerialQuery(),
                 "focus(1: spanContain(<tokens:base/s:s />, {1: tokens:s:Leben}))");
+        assertEquals(40, kr.getMatch(0).getStartPos());
+        assertEquals(41, kr.getMatch(0).getEndPos());
+
         assertEquals(
                 kr.getMatch(0).getSnippetBrackets(),
                 "... Initiative\" eine neue politische Gruppierung ins "
diff --git a/src/test/resources/queries/reference/bug-multiple-distance.jsonld b/src/test/resources/queries/reference/bug-multiple-distance.jsonld
new file mode 100644
index 0000000..4d59d1f
--- /dev/null
+++ b/src/test/resources/queries/reference/bug-multiple-distance.jsonld
@@ -0,0 +1,97 @@
+{
+    "query": {
+        "@type": "koral:group",
+        "operation": "operation:relation",
+        "operands": [
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [2],
+                "operands": [{
+                    "@type": "koral:group",
+                    "operation": "operation:sequence",
+                    "operands": [
+                        {
+                            "@type": "koral:reference",
+                            "operation": "operation:focus",
+                            "classRef": [1],
+                            "operands": [{
+                                "@type": "koral:group",
+                                "operation": "operation:sequence",
+                                "operands": [
+                                    {
+                                        "@type": "koral:span",
+                                        "layer": "c",
+                                        "key": "vb",
+                                        "match": "match:eq"
+                                    },
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 1,
+                                        "operands": [{
+                                            "@type": "koral:span",
+                                            "layer": "c",
+                                            "key": "prp",
+                                            "match": "match:eq"
+                                        }]
+                                    }
+                                ],
+                                "inOrder": false,
+                                "distances": [{
+                                    "@type": "koral:distance",
+                                    "key": "w",
+                                    "boundary": {
+                                        "@type": "koral:boundary",
+                                        "min": 1,
+                                        "max": 1
+                                    },
+                                    "min": 1,
+                                    "max": 1
+                                }]                                
+                            }]
+                        },
+                        {
+                            "@type": "koral:group",
+                            "operation": "operation:class",
+                            "classOut": 2,
+                            "operands": [{
+                                "@type": "koral:span",
+                                "layer": "c",
+                                "key": "nn",
+                                "match": "match:eq"
+                            }]
+                        }
+                    ],
+                    "distances": [{
+                        "@type": "koral:distance",
+                        "key": "w",
+                        "boundary": {
+                            "@type": "koral:boundary",
+                            "min": 0,
+                            "max": 2
+                        },
+                        "min": 0,
+                        "max": 2
+                    }],
+                    "inOrder": true
+                }]
+            },
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [1]
+            }
+        ],
+        "relation": {
+            "@type": "koral:relation",
+            "wrap": {
+                "@type": "koral:term",
+                "foundry": "stanford",
+                "layer": "d",
+                "key":"tag"
+            }
+        }
+    },
+    "meta": {}
+}
diff --git a/src/test/resources/queries/reference/distance-multiple-references.jsonld b/src/test/resources/queries/reference/distance-multiple-references.jsonld
new file mode 100644
index 0000000..31ef098
--- /dev/null
+++ b/src/test/resources/queries/reference/distance-multiple-references.jsonld
@@ -0,0 +1,124 @@
+{
+    "query": {
+        "@type": "koral:group",
+        "operation": "operation:relation",
+        "operands": [
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [1],
+                "operands": [{
+                    "@type": "koral:group",
+                    "operation": "operation:relation",
+                    "operands": [
+                        {
+                            "@type": "koral:reference",
+                            "operation": "operation:focus",
+                            "classRef": [3],
+                            "operands": [{
+                                "@type": "koral:group",
+                                "operation": "operation:sequence",
+                                "operands": [
+                                    {
+                                        "@type": "koral:reference",
+                                        "operation": "operation:focus",
+                                        "classRef": [1],
+                                        "operands": [{
+                                            "@type": "koral:group",
+                                            "operation": "operation:sequence",
+                                            "operands": [
+                                                {
+                                                    "@type": "koral:group",
+                                                    "operation": "operation:class",
+                                                    "classOut": 1,
+                                                    "operands": [{
+                                                        "@type": "koral:span",
+                                                        "key": "vb",
+                                                        "match": "match:eq"
+                                                    }]
+                                                },
+                                                {
+                                                    "@type": "koral:group",
+                                                    "operation": "operation:class",
+                                                    "classOut": 2,
+                                                    "operands": [{
+                                                        "@type": "koral:span",
+                                                        "key": "prp",
+                                                        "match": "match:eq"
+                                                    }]
+                                                }
+                                            ],
+                                            "distances": [{
+                                                "@type": "koral:distance",
+                                                "key": "w",
+                                                "boundary": {
+                                                    "@type": "koral:boundary",
+                                                    "min": 0,
+                                                    "max": 1
+                                                },
+                                                "min": 0,
+                                                "max": 1
+                                            }],
+                                            "inOrder": false
+                                        }]
+                                    },
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 3,
+                                        "operands": [{
+                                            "@type": "koral:span",
+                                            "key": "nn",
+                                            "match": "match:eq"
+                                        }]
+                                    }
+                                ],
+                                "distances": [{
+                                    "@type": "koral:distance",
+                                    "key": "w",
+                                    "boundary": {
+                                        "@type": "koral:boundary",
+                                        "min": 0,
+                                        "max": 2
+                                    },
+                                    "min": 0,
+                                    "max": 2
+                                }],
+                                "inOrder": false
+                            }]
+                        },
+                        {
+                            "@type": "koral:reference",
+                            "operation": "operation:focus",
+                            "classRef": [2]
+                        }
+                    ],
+                    "relation": {
+                        "@type": "koral:relation",
+                        "wrap": {
+                            "@type": "koral:term",
+                            "foundry": "stanford",
+                            "layer": "d",
+                            "key":"tag"
+                        }
+                    }
+                }]
+            },
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [3]
+            }
+        ],
+        "relation": {
+            "@type": "koral:relation",
+            "wrap": {
+                "@type": "koral:term",
+                "foundry": "stanford",
+                "layer": "d",
+                "key":"tag"
+            }
+        }
+    },
+    "meta": {}
+}
diff --git a/src/test/resources/queries/reference/distance-reference.jsonld b/src/test/resources/queries/reference/distance-reference.jsonld
new file mode 100644
index 0000000..cf23596
--- /dev/null
+++ b/src/test/resources/queries/reference/distance-reference.jsonld
@@ -0,0 +1,99 @@
+{
+    "query": {
+        "@type": "koral:group",
+        "operation": "operation:relation",
+        "operands": [
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [3],
+                "operands": [{
+                    "@type": "koral:group",
+                    "operation": "operation:sequence",
+                    "operands": [
+                        {
+                            "@type": "koral:reference",
+                            "operation": "operation:focus",
+                            "classRef": [1],
+                            "operands": [{
+                                "@type": "koral:group",
+                                "operation": "operation:sequence",
+                                "operands": [
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 1,
+                                        "operands": [{
+                                            "@type": "koral:span",
+                                            "key": "vb",
+                                            "match": "match:eq"
+                                        }]
+                                    },
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 2,
+                                        "operands": [{
+                                            "@type": "koral:span",
+                                            "key": "prp",
+                                            "match": "match:eq"
+                                        }]
+                                    }
+                                ],
+                                "distances": [{
+                                    "@type": "koral:distance",
+                                    "key": "w",
+                                    "boundary": {
+                                        "@type": "koral:boundary",
+                                        "min": 0,
+                                        "max": 1
+                                    },
+                                    "min": 0,
+                                    "max": 1
+                                }],
+                                "inOrder": false
+                            }]
+                        },
+                        {
+                            "@type": "koral:group",
+                            "operation": "operation:class",
+                            "classOut": 3,
+                            "operands": [{
+                                "@type": "koral:span",
+                                "key": "nn",
+                                "match": "match:eq"
+                            }]
+                        }
+                    ],
+                    "distances": [{
+                        "@type": "koral:distance",
+                        "key": "w",
+                        "boundary": {
+                            "@type": "koral:boundary",
+                            "min": 0,
+                            "max": 2
+                        },
+                        "min": 0,
+                        "max": 2
+                    }],
+                    "inOrder": false
+                }]
+            },
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [2]
+            }
+        ],
+        "relation": {
+            "@type": "koral:relation",
+            "wrap": {
+                "@type": "koral:term",
+                "foundry": "stanford",
+                "layer": "d",
+                "key":"tag"
+            }
+        }
+    },
+    "meta": {}
+}
diff --git a/src/test/resources/queries/reference/first-operand-reference.jsonld b/src/test/resources/queries/reference/first-operand-reference.jsonld
new file mode 100644
index 0000000..44792db
--- /dev/null
+++ b/src/test/resources/queries/reference/first-operand-reference.jsonld
@@ -0,0 +1,86 @@
+{
+    "query": {
+        "@type": "koral:group",
+        "operation": "operation:sequence",
+        "operands": [
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [3]
+            },
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [2],
+                "operands": [{
+                    "@type": "koral:group",
+                    "operation": "operation:relation",
+                    "operands": [
+                        {
+                            "@type": "koral:reference",
+                            "operation": "operation:focus",
+                            "classRef": [1],
+                            "operands": [{
+                                "@type": "koral:group",
+                                "operation": "operation:sequence",
+                                "operands": [
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 2,
+                                        "operands": [{
+                                            "@type": "koral:token",
+                                            "wrap": {
+                                                "@type": "koral:term",
+                                                "layer": "p",
+                                                "key": "V",
+                                                "match": "match:eq"
+                                            }
+                                        }]
+                                    },
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 1,
+                                        "operands": [{
+                                            "@type": "koral:span",
+                                            "layer": "c",
+                                            "key": "NP",
+                                            "match": "match:eq"
+                                        }]
+                                    }                                    
+                                ],
+                                "inOrder": true
+                            }]
+                        },
+                        {
+                            "@type": "koral:group",
+                            "operation": "operation:class",
+                            "classOut": 3,
+                            "operands": [{
+                                "@type": "koral:token",
+                                "wrap": {
+                                    "@type": "koral:term",
+                                    "layer": "p",
+                                    "key": "P",
+                                    "match": "match:eq"
+                                }
+                            }]
+                        }
+                    ],
+                    "relation": {
+                        "@type": "koral:relation",
+                        "wrap": {
+                            "@type": "koral:term",
+                            "foundry": "mate",
+                            "layer": "d",
+                            "key" : "HEAD"
+                        }
+                    }
+                }]
+            }         
+        ],
+        "inOrder": true
+    },
+    "meta": {}
+}
diff --git a/src/test/resources/queries/reference/multiple-references.jsonld b/src/test/resources/queries/reference/multiple-references.jsonld
new file mode 100644
index 0000000..f94cfb5
--- /dev/null
+++ b/src/test/resources/queries/reference/multiple-references.jsonld
@@ -0,0 +1,97 @@
+{
+    "query": {
+        "@type": "koral:group",
+        "operation": "operation:relation",
+        "operands": [
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [2],
+                "operands": [{
+                    "@type": "koral:group",
+                    "operation": "operation:sequence",
+                    "operands": [
+                        {
+                            "@type": "koral:reference",
+                            "operation": "operation:focus",
+                            "classRef": [1],
+                            "operands": [{
+                                "@type": "koral:group",
+                                "operation": "operation:sequence",
+                                "operands": [
+                                    {
+                                        "@type": "koral:reference",
+                                        "operation": "operation:focus",
+                                        "classRef": [2],
+                                        "operands": [{
+                                            "@type": "koral:group",
+                                            "operation": "operation:sequence",
+                                            "operands": [
+                                                {
+                                                    "@type": "koral:group",
+                                                    "operation": "operation:class",
+                                                    "classOut": 1,
+                                                    "operands": [{
+                                                        "@type": "koral:span",
+                                                        "layer": "c",
+                                                        "key": "VP",
+                                                        "match": "match:eq"
+                                                    }]
+                                                },
+                                                {
+                                                    "@type": "koral:group",
+                                                    "operation": "operation:class",
+                                                    "classOut": 2,
+                                                    "operands": [{
+                                                        "@type": "koral:span",
+                                                        "layer": "c",
+                                                        "key": "NP",
+                                                        "match": "match:eq"
+                                                    }]
+                                                }
+                                            ],
+                                            "inOrder": true
+                                        }]
+                                    },
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 3,
+                                        "operands": [{
+                                            "@type": "koral:span",
+                                            "layer": "c",
+                                            "key": "PP",
+                                            "match": "match:eq"
+                                        }]
+                                    }
+                                ],
+                                "inOrder": true
+                            }]
+                        },
+                        {
+                            "@type": "koral:reference",
+                            "operation": "operation:focus",
+                            "classRef": [3]
+                        }
+                    ],
+                    "inOrder": true
+                }]
+            },
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [1]
+            }
+        ],
+        "relation": {
+            "@type": "koral:relation",
+            "wrap": {
+                "@type": "koral:term",
+                "foundry" : "mate",
+                "layer": "d",
+                "key" : "HEAD"
+            }
+        }
+    },
+    "meta": {}
+}
diff --git a/src/test/resources/queries/reference/second-operand-reference.jsonld b/src/test/resources/queries/reference/second-operand-reference.jsonld
new file mode 100644
index 0000000..47388cc
--- /dev/null
+++ b/src/test/resources/queries/reference/second-operand-reference.jsonld
@@ -0,0 +1,86 @@
+{
+    "query": {
+        "@type": "koral:group",
+        "operation": "operation:sequence",
+        "operands": [
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [2],
+                "operands": [{
+                    "@type": "koral:group",
+                    "operation": "operation:relation",
+                    "operands": [
+                        {
+                            "@type": "koral:reference",
+                            "operation": "operation:focus",
+                            "classRef": [1],
+                            "operands": [{
+                                "@type": "koral:group",
+                                "operation": "operation:sequence",
+                                "operands": [
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 2,
+                                        "operands": [{
+                                            "@type": "koral:token",
+                                            "wrap": {
+                                                "@type": "koral:term",
+                                                "layer": "p",
+                                                "key": "V",
+                                                "match": "match:eq"
+                                            }
+                                        }]
+                                    },
+                                    {
+                                        "@type": "koral:group",
+                                        "operation": "operation:class",
+                                        "classOut": 1,
+                                        "operands": [{
+                                            "@type": "koral:span",
+                                            "layer": "c",
+                                            "key": "NP",
+                                            "match": "match:eq"
+                                        }]
+                                    }                                    
+                                ],
+                                "inOrder": true
+                            }]
+                        },
+                        {
+                            "@type": "koral:group",
+                            "operation": "operation:class",
+                            "classOut": 3,
+                            "operands": [{
+                                "@type": "koral:token",
+                                "wrap": {
+                                    "@type": "koral:term",
+                                    "layer": "p",
+                                    "key": "P",
+                                    "match": "match:eq"
+                                }
+                            }]
+                        }
+                    ],
+                    "relation": {
+                        "@type": "koral:relation",
+                        "wrap": {
+                            "@type": "koral:term",
+                            "foundry": "mate",
+                            "layer": "d",
+                            "key" : "HEAD"
+                        }
+                    }
+                }]
+            },
+            {
+                "@type": "koral:reference",
+                "operation": "operation:focus",
+                "classRef": [3]
+            }
+        ],
+        "inOrder": true
+    },
+    "meta": {}
+}
diff --git a/src/test/resources/queries/relation/focus-empty-both.json b/src/test/resources/queries/relation/focus-empty-both.json
new file mode 100644
index 0000000..1e9fa16
--- /dev/null
+++ b/src/test/resources/queries/relation/focus-empty-both.json
@@ -0,0 +1,39 @@
+{
+    "query": {
+        "@type": "koral:reference",
+        "operation": "operation:focus",
+        "classRef": [2],
+        "operands": [{
+            "@type": "koral:group",
+            "operation": "operation:relation",
+            "operands": [
+                {
+                   "@type": "koral:group",
+                    "operation": "operation:class",
+                    "classOut": 1,
+                    "operands": [{
+                        "@type": "koral:token"
+                    }]
+                },
+                {
+                    "@type": "koral:group",
+                    "operation": "operation:class",
+                    "classOut": 2,
+                    "operands": [{
+                        "@type": "koral:token"
+                    }]
+                }
+            ],
+            "relation": {
+                "@type": "koral:relation",
+                "wrap": {
+                    "@type": "koral:term",
+                    "foundry": "mate",
+                    "layer": "d",
+                    "key": "HEAD"
+                }
+            }
+        }]
+    },
+    "meta": {}
+}
diff --git a/src/test/resources/queries/relation/focus-empty-target.json b/src/test/resources/queries/relation/focus-empty-target.json
new file mode 100644
index 0000000..78ac125
--- /dev/null
+++ b/src/test/resources/queries/relation/focus-empty-target.json
@@ -0,0 +1,42 @@
+{
+    "query": {
+        "@type": "koral:reference",
+        "operation": "operation:focus",
+        "classRef": [2],
+        "operands": [{
+            "@type": "koral:group",
+            "operation": "operation:relation",
+            "operands": [
+                {
+                   "@type": "koral:group",
+                    "operation": "operation:class",
+                    "classOut": 1,
+                    "operands": [{
+                        "@type": "koral:span",
+                        "layer": "c",
+                        "key": "s",
+                        "match": "match:eq"
+                    }]
+                },
+                {
+                    "@type": "koral:group",
+                    "operation": "operation:class",
+                    "classOut": 2,
+                    "operands": [{
+                        "@type": "koral:token"
+                    }]
+                }
+            ],
+            "relation": {
+                "@type": "koral:relation",
+                "wrap": {
+                    "@type": "koral:term",
+                    "foundry": "mate",
+                    "layer": "d",
+                    "key": "HEAD"
+                }
+            }
+        }]
+    },
+    "meta": {}
+}
diff --git a/src/test/resources/queries/relation/focus-source.json b/src/test/resources/queries/relation/focus-source.json
index d934a3b..68d5ce4 100644
--- a/src/test/resources/queries/relation/focus-source.json
+++ b/src/test/resources/queries/relation/focus-source.json
@@ -11,8 +11,15 @@
                     "@type": "koral:token"
                 },
                 {
-                    "@type": "koral:span",
-                    "key": "np"
+                   "@type": "koral:group",
+                    "operation": "operation:class",
+                    "classOut": 1,
+                    "operands": [{
+                        "@type": "koral:span",
+                        "layer": "c",
+                        "key": "np",
+                        "match": "match:eq"
+                    }]
                 }
             ],
             "relation": {
diff --git a/src/test/resources/queries/relation/focus-target.json b/src/test/resources/queries/relation/focus-target.json
index daec972..46282a8 100644
--- a/src/test/resources/queries/relation/focus-target.json
+++ b/src/test/resources/queries/relation/focus-target.json
@@ -8,11 +8,26 @@
             "operation": "operation:relation",
             "operands": [
                 {
-                    "@type": "koral:span",
-                    "key": "s"
+                   "@type": "koral:group",
+                    "operation": "operation:class",
+                    "classOut": 1,
+                    "operands": [{
+                        "@type": "koral:span",
+                        "layer": "c",
+                        "key": "s",
+                        "match": "match:eq"
+                    }]
                 },
                 {
-                    "@type": "koral:token"
+                    "@type": "koral:group",
+                    "operation": "operation:class",
+                    "classOut": 2,
+                    "operands": [{
+                        "@type": "koral:span",
+                        "layer": "c",
+                        "key": "np",
+                        "match": "match:eq"
+                    }]
                 }
             ],
             "relation": {