You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jp...@apache.org on 2016/10/24 08:52:10 UTC

[1/2] lucene-solr:master: LUCENE-7462: Give doc values APIs an `advanceExact` method.

Repository: lucene-solr
Updated Branches:
  refs/heads/master 9b49c72db -> 9aca4c9d5


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java
index 0cb86db..ff390b3 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/BM25Similarity.java
@@ -244,11 +244,7 @@ public class BM25Similarity extends Similarity {
       if (norms == null) {
         norm = k1;
       } else {
-        int normsDocID = norms.docID();
-        if (normsDocID < doc) {
-          normsDocID = norms.advance(doc);
-        }
-        if (normsDocID == doc) {
+        if (norms.advanceExact(doc)) {
           norm = cache[(byte)norms.longValue() & 0xFF];
         } else {
           norm = cache[0];
@@ -310,7 +306,7 @@ public class BM25Similarity extends Similarity {
           "tfNorm, computed from:", subs);
     } else {
       byte norm;
-      if (norms.advance(doc) == doc) {
+      if (norms.advanceExact(doc)) {
         norm = (byte) norms.longValue();
       } else {
         norm = 0;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java b/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java
index ed837c4..925dc59 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/SimilarityBase.java
@@ -279,11 +279,7 @@ public abstract class SimilarityBase extends Similarity {
       if (norms == null) {
         return 1F;
       }
-      int normsDocID = norms.docID();
-      if (normsDocID < doc) {
-        normsDocID = norms.advance(doc);
-      }
-      if (normsDocID == doc) {
+      if (norms.advanceExact(doc)) {
         return decodeNormValue((byte) norms.longValue());
       } else {
         return decodeNormValue((byte) 0);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java b/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java
index 6cd87b5..cd8acd6 100644
--- a/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java
+++ b/lucene/core/src/java/org/apache/lucene/search/similarities/TFIDFSimilarity.java
@@ -599,11 +599,7 @@ public abstract class TFIDFSimilarity extends Similarity {
         return raw;
       } else {
         long normValue;
-        int normsDocID = norms.docID();
-        if (normsDocID < doc) {
-          normsDocID = norms.advance(doc);
-        }
-        if (normsDocID == doc) {
+        if (norms.advanceExact(doc)) {
           normValue = norms.longValue();
         } else {
           normValue = 0;
@@ -649,7 +645,7 @@ public abstract class TFIDFSimilarity extends Similarity {
   private Explanation explainField(int doc, Explanation freq, IDFStats stats, NumericDocValues norms) throws IOException {
     Explanation tfExplanation = Explanation.match(tf(freq.getValue()), "tf(freq="+freq.getValue()+"), with freq of:", freq);
     float norm;
-    if (norms != null && norms.advance(doc) == doc) {
+    if (norms != null && norms.advanceExact(doc)) {
       norm = decodeNormValue(norms.longValue());
     } else {
       norm = 1f;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestIndexedDISI.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestIndexedDISI.java b/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestIndexedDISI.java
index 18b4590..64bfbd5 100644
--- a/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestIndexedDISI.java
+++ b/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestIndexedDISI.java
@@ -153,7 +153,7 @@ public class TestIndexedDISI extends LuceneTestCase {
 
   public void testRandom() throws IOException {
     try (Directory dir = newDirectory()) {
-      for (int i = 0; i < 100; ++i) {
+      for (int i = 0; i < 10; ++i) {
         doTestRandom(dir);
       }
     }
@@ -217,6 +217,32 @@ public class TestIndexedDISI extends LuceneTestCase {
       }
     }
 
+    for (int step : new int[] {10, 100, 1000, 10000, 100000}) {
+      try (IndexInput in = dir.openInput("foo", IOContext.DEFAULT)) {
+        IndexedDISI disi = new IndexedDISI(in, 0L, length, cardinality);
+        BitSetIterator disi2 = new BitSetIterator(set, cardinality);
+        int index = -1;
+        for (int target = 0; target < set.length(); ) {
+          target += TestUtil.nextInt(random(), 0, step);
+          int doc = disi2.docID();
+          while (doc < target) {
+            doc = disi2.nextDoc();
+            index++;
+          }
+
+          boolean exists = disi.advanceExact(target);
+          assertEquals(doc == target, exists);
+          if (exists) {
+            assertEquals(index, disi.index());
+          } else if (random().nextBoolean()) {
+            assertEquals(doc, disi.nextDoc());
+            assertEquals(index, disi.index());
+            target = doc;
+          }
+        }
+      }
+    }
+
     dir.deleteFile("foo");
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestLucene70DocValuesFormat.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestLucene70DocValuesFormat.java b/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestLucene70DocValuesFormat.java
index 5ad701e..8661298 100644
--- a/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestLucene70DocValuesFormat.java
+++ b/lucene/core/src/test/org/apache/lucene/codecs/lucene70/TestLucene70DocValuesFormat.java
@@ -104,7 +104,7 @@ public class TestLucene70DocValuesFormat extends BaseCompressingDocValuesFormatT
   public void testSortedVariableLengthBigVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestSortedVsStoredFields(atLeast(300), 1, 32766);
+      doTestSortedVsStoredFields(atLeast(300), 1d, 1, 32766);
     }
   }
   
@@ -112,7 +112,7 @@ public class TestLucene70DocValuesFormat extends BaseCompressingDocValuesFormatT
   public void testSortedVariableLengthManyVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestSortedVsStoredFields(TestUtil.nextInt(random(), 1024, 2049), 1, 500);
+      doTestSortedVsStoredFields(TestUtil.nextInt(random(), 1024, 2049), 1d, 1, 500);
     }
   }
   

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java
----------------------------------------------------------------------
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java b/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java
index 1219494..4fff6a6 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java
@@ -199,10 +199,7 @@ public class SortedSetDocValuesFacetCounts extends Facets {
           int doc;
           while ((doc = docs.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
             //System.out.println("    doc=" + doc);
-            if (doc > segValues.docID()) {
-              segValues.advance(doc);
-            }
-            if (doc == segValues.docID()) {
+            if (segValues.advanceExact(doc)) {
               int term = (int) segValues.nextOrd();
               while (term != SortedSetDocValues.NO_MORE_ORDS) {
                 //System.out.println("      segOrd=" + segOrd + " ord=" + term + " globalOrd=" + ordinalMap.getGlobalOrd(segOrd, term));
@@ -219,10 +216,7 @@ public class SortedSetDocValuesFacetCounts extends Facets {
           int doc;
           while ((doc = docs.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
             //System.out.println("    doc=" + doc);
-            if (doc > segValues.docID()) {
-              segValues.advance(doc);
-            }
-            if (doc == segValues.docID()) {
+            if (segValues.advanceExact(doc)) {
               int term = (int) segValues.nextOrd();
               while (term != SortedSetDocValues.NO_MORE_ORDS) {
                 //System.out.println("      ord=" + term);
@@ -246,10 +240,7 @@ public class SortedSetDocValuesFacetCounts extends Facets {
         // just aggregate directly into counts:
         int doc;
         while ((doc = docs.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-          if (doc > segValues.docID()) {
-            segValues.advance(doc);
-          }
-          if (doc == segValues.docID()) {
+          if (segValues.advanceExact(doc)) {
             int term = (int) segValues.nextOrd();
             while (term != SortedSetDocValues.NO_MORE_ORDS) {
               counts[term]++;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/join/src/java/org/apache/lucene/search/join/BlockJoinSelector.java
----------------------------------------------------------------------
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/BlockJoinSelector.java b/lucene/join/src/java/org/apache/lucene/search/join/BlockJoinSelector.java
index a2e0c55..359b3cb 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/BlockJoinSelector.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/BlockJoinSelector.java
@@ -102,7 +102,7 @@ public class BlockJoinSelector {
     }
     return new SortedDocValues() {
 
-      private int ord;
+      private int ord = -1;
       private int docID = -1;
 
       @Override
@@ -169,6 +169,60 @@ public class BlockJoinSelector {
       }
 
       @Override
+      public boolean advanceExact(int targetParentDocID) throws IOException {
+        if (targetParentDocID < docID) {
+          throw new IllegalArgumentException("target must be after the current document: current=" + docID + " target=" + targetParentDocID);
+        }
+        int previousDocId = docID;
+        docID = targetParentDocID;
+        if (targetParentDocID == previousDocId) {
+          return ord != -1;
+        }
+        docID = targetParentDocID;
+        ord = -1;
+        if (parents.get(targetParentDocID) == false) {
+          return false;
+        }
+        int prevParentDocId = docID == 0 ? -1 : parents.prevSetBit(docID - 1);
+        int childDoc = values.docID();
+        if (childDoc <= prevParentDocId) {
+          childDoc = values.advance(prevParentDocId + 1);
+        }
+        if (childDoc >= docID) {
+          return false;
+        }
+        
+        boolean hasValue = false;
+        for (int doc = values.docID(); doc < docID; doc = values.nextDoc()) {
+          if (children.get(doc)) {
+            ord = values.ordValue();
+            hasValue = true;
+            values.nextDoc();
+            break;
+          }
+        }
+        if (hasValue == false) {
+          return false;
+        }
+
+        for (int doc = values.docID(); doc < docID; doc = values.nextDoc()) {
+          if (children.get(doc)) {
+            switch (selection) {
+              case MIN:
+                ord = Math.min(ord, values.ordValue());
+                break;
+              case MAX:
+                ord = Math.max(ord, values.ordValue());
+                break;
+              default:
+                throw new AssertionError();
+            }
+          }
+        }
+        return true;
+      }
+
+      @Override
       public int ordValue() {
         return ord;
       }
@@ -288,6 +342,54 @@ public class BlockJoinSelector {
       }
 
       @Override
+      public boolean advanceExact(int targetParentDocID) throws IOException {
+        if (targetParentDocID <= parentDocID) {
+          throw new IllegalArgumentException("target must be after the current document: current=" + parentDocID + " target=" + targetParentDocID);
+        }
+        parentDocID = targetParentDocID;
+        if (parents.get(targetParentDocID) == false) {
+          return false;
+        }
+        int prevParentDocId = parentDocID == 0 ? -1 : parents.prevSetBit(parentDocID - 1);
+        int childDoc = values.docID();
+        if (childDoc <= prevParentDocId) {
+          childDoc = values.advance(prevParentDocId + 1);
+        }
+        if (childDoc >= parentDocID) {
+          return false;
+        }
+        
+        boolean hasValue = false;
+        for (int doc = values.docID(); doc < parentDocID; doc = values.nextDoc()) {
+          if (children.get(doc)) {
+            value = values.longValue();
+            hasValue = true;
+            values.nextDoc();
+            break;
+          }
+        }
+        if (hasValue == false) {
+          return false;
+        }
+
+        for (int doc = values.docID(); doc < parentDocID; doc = values.nextDoc()) {
+          if (children.get(doc)) {
+            switch (selection) {
+              case MIN:
+                value = Math.min(value, values.longValue());
+                break;
+              case MAX:
+                value = Math.max(value, values.longValue());
+                break;
+              default:
+                throw new AssertionError();
+            }
+          }
+        }
+        return true;
+      }
+
+      @Override
       public long longValue() {
         return value;
       }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/join/src/java/org/apache/lucene/search/join/GenericTermsCollector.java
----------------------------------------------------------------------
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/GenericTermsCollector.java b/lucene/join/src/java/org/apache/lucene/search/join/GenericTermsCollector.java
index 3ad0fe3..47b1b62 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/GenericTermsCollector.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/GenericTermsCollector.java
@@ -74,6 +74,13 @@ interface GenericTermsCollector extends Collector {
         }
 
         @Override
+        public boolean advanceExact(int dest) throws IOException {
+          boolean exists = target.advanceExact(dest);
+          out.println("\nadvanceExact(" + dest + ") -> exists# "+exists);
+          return exists;
+        }
+
+        @Override
         public long cost() {
           return target.cost();
         }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java
----------------------------------------------------------------------
diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java
index 41f994c..04cb771 100644
--- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java
+++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoinSelector.java
@@ -150,6 +150,12 @@ public class TestBlockJoinSelector extends LuceneTestCase {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      docID = target;
+      return ords[docID] != -1;
+    }
+
+    @Override
     public int ordValue() {
       assert ords[docID] != -1;
       return ords[docID];
@@ -257,6 +263,12 @@ public class TestBlockJoinSelector extends LuceneTestCase {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      docID = target;
+      return docsWithValue.get(docID);
+    }
+
+    @Override
     public long longValue() {
       return values[docID];
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
----------------------------------------------------------------------
diff --git a/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java b/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
index ccbbf24..218d26c 100644
--- a/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
+++ b/lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
@@ -970,6 +970,12 @@ public class MemoryIndex {
           }
 
           @Override
+          public boolean advanceExact(int target) throws IOException {
+            docID = target;
+            return docID == 0;
+          }
+
+          @Override
           public long cost() {
             return 1;
           }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java
----------------------------------------------------------------------
diff --git a/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java b/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java
index b64afc5..043141a 100644
--- a/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java
+++ b/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java
@@ -144,6 +144,11 @@ public class TestDiversifiedTopDocsCollector extends LuceneTestCase {
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          return sdv.advanceExact(target + context.docBase);
+        }
+
+        @Override
         public long cost() {
           return 0;
         }
@@ -187,6 +192,10 @@ public class TestDiversifiedTopDocsCollector extends LuceneTestCase {
           return vals.advance(target);
         }
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          return vals.advanceExact(target);
+        }
+        @Override
         public long cost() {
           return vals.cost();
         }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java
----------------------------------------------------------------------
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java b/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java
index 6686ec4..37c549e 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/AssertingLeafReader.java
@@ -398,6 +398,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     private final NumericDocValues in;
     private final int maxDoc;
     private int lastDocID = -1;
+    private boolean exists;
     
     public AssertingNumericDocValues(NumericDocValues in, int maxDoc) {
       this.in = in;
@@ -420,6 +421,7 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID == NO_MORE_DOCS || docID < maxDoc;
       assert docID == in.docID();
       lastDocID = docID;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
@@ -432,10 +434,23 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID >= target;
       assert docID == NO_MORE_DOCS || docID < maxDoc;
       lastDocID = docID;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      assertThread("Numeric doc values", creationThread);
+      assert target >= 0;
+      assert target >= in.docID();
+      assert target < maxDoc;
+      exists = in.advanceExact(target);
+      assert in.docID() == target;
+      lastDocID = target;
+      return exists;
+    }
+
+    @Override
     public long cost() {
       assertThread("Numeric doc values", creationThread);
       long cost = in.cost();
@@ -446,8 +461,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     @Override
     public long longValue() throws IOException {
       assertThread("Numeric doc values", creationThread);
-      assert in.docID() != -1;
-      assert in.docID() != NO_MORE_DOCS;
+      assert exists;
       return in.longValue();
     }    
 
@@ -463,6 +477,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     private final BinaryDocValues in;
     private final int maxDoc;
     private int lastDocID = -1;
+    private boolean exists;
     
     public AssertingBinaryDocValues(BinaryDocValues in, int maxDoc) {
       this.in = in;
@@ -485,6 +500,7 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID == NO_MORE_DOCS || docID < maxDoc;
       assert docID == in.docID();
       lastDocID = docID;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
@@ -497,10 +513,23 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID >= target;
       assert docID == NO_MORE_DOCS || docID < maxDoc;
       lastDocID = docID;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      assertThread("Numeric doc values", creationThread);
+      assert target >= 0;
+      assert target >= in.docID();
+      assert target < maxDoc;
+      exists = in.advanceExact(target);
+      assert in.docID() == target;
+      lastDocID = target;
+      return exists;
+    }
+
+    @Override
     public long cost() {
       assertThread("Binary doc values", creationThread);
       long cost = in.cost();
@@ -511,8 +540,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     @Override
     public BytesRef binaryValue() throws IOException {
       assertThread("Binary doc values", creationThread);
-      assert in.docID() != -1;
-      assert in.docID() != NO_MORE_DOCS;
+      assert exists;
       return in.binaryValue();
     }
 
@@ -529,6 +557,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     private final int maxDoc;
     private final int valueCount;
     private int lastDocID = -1;
+    private boolean exists;
     
     public AssertingSortedDocValues(SortedDocValues in, int maxDoc) {
       this.in = in;
@@ -551,6 +580,7 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID == NO_MORE_DOCS || docID < maxDoc;
       assert docID == in.docID();
       lastDocID = docID;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
@@ -563,10 +593,23 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID >= target;
       assert docID == NO_MORE_DOCS || docID < maxDoc;
       lastDocID = docID;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      assertThread("Numeric doc values", creationThread);
+      assert target >= 0;
+      assert target >= in.docID();
+      assert target < maxDoc;
+      exists = in.advanceExact(target);
+      assert in.docID() == target;
+      lastDocID = target;
+      return exists;
+    }
+
+    @Override
     public long cost() {
       assertThread("Sorted doc values", creationThread);
       long cost = in.cost();
@@ -577,6 +620,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     @Override
     public int ordValue() {
       assertThread("Sorted doc values", creationThread);
+      assert exists;
       int ord = in.ordValue();
       assert ord >= -1 && ord < valueCount;
       return ord;
@@ -625,6 +669,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     private final int maxDoc;
     private int lastDocID = -1;
     private int valueUpto;
+    private boolean exists;
     
     public AssertingSortedNumericDocValues(SortedNumericDocValues in, int maxDoc) {
       this.in = in;
@@ -645,6 +690,7 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID == in.docID();
       lastDocID = docID;
       valueUpto = 0;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
@@ -659,10 +705,24 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID == NO_MORE_DOCS || docID < maxDoc;
       lastDocID = docID;
       valueUpto = 0;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      assertThread("Numeric doc values", creationThread);
+      assert target >= 0;
+      assert target >= in.docID();
+      assert target < maxDoc;
+      exists = in.advanceExact(target);
+      assert in.docID() == target;
+      lastDocID = target;
+      valueUpto = 0;
+      return exists;
+    }
+
+    @Override
     public long cost() {
       assertThread("Sorted numeric doc values", creationThread);
       long cost = in.cost();
@@ -673,6 +733,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     @Override
     public long nextValue() throws IOException {
       assertThread("Sorted numeric doc values", creationThread);
+      assert exists;
       assert valueUpto < in.docValueCount(): "valueUpto=" + valueUpto + " in.docValueCount()=" + in.docValueCount();
       valueUpto++;
       return in.nextValue();
@@ -681,6 +742,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     @Override
     public int docValueCount() {
       assertThread("Sorted numeric doc values", creationThread);
+      assert exists;
       assert in.docValueCount() > 0;
       return in.docValueCount();
     } 
@@ -693,7 +755,8 @@ public class AssertingLeafReader extends FilterLeafReader {
     private final int maxDoc;
     private final long valueCount;
     private int lastDocID = -1;
-    long lastOrd = NO_MORE_ORDS;
+    private long lastOrd = NO_MORE_ORDS;
+    private boolean exists;
     
     public AssertingSortedSetDocValues(SortedSetDocValues in, int maxDoc) {
       this.in = in;
@@ -717,6 +780,7 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID == in.docID();
       lastDocID = docID;
       lastOrd = -2;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
@@ -731,10 +795,24 @@ public class AssertingLeafReader extends FilterLeafReader {
       assert docID == NO_MORE_DOCS || docID < maxDoc;
       lastDocID = docID;
       lastOrd = -2;
+      exists = docID != NO_MORE_DOCS;
       return docID;
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      assertThread("Numeric doc values", creationThread);
+      assert target >= 0;
+      assert target >= in.docID();
+      assert target < maxDoc;
+      exists = in.advanceExact(target);
+      assert in.docID() == target;
+      lastDocID = target;
+      lastOrd = -2;
+      return exists;
+    }
+
+    @Override
     public long cost() {
       assertThread("Sorted set doc values", creationThread);
       long cost = in.cost();
@@ -746,6 +824,7 @@ public class AssertingLeafReader extends FilterLeafReader {
     public long nextOrd() throws IOException {
       assertThread("Sorted set doc values", creationThread);
       assert lastOrd != NO_MORE_ORDS;
+      assert exists;
       long ord = in.nextOrd();
       assert ord < valueCount;
       assert ord == NO_MORE_ORDS || ord > lastOrd;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java
index b9bf745..d55f212 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/BaseDocValuesFormatTestCase.java
@@ -30,6 +30,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.CountDownLatch;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.MockAnalyzer;
@@ -556,7 +558,6 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     IndexReader ireader = DirectoryReader.open(directory); // read-only=true
     assert ireader.leaves().size() == 1;
     BinaryDocValues dv = ireader.leaves().get(0).reader().getBinaryDocValues("dv");
-    BytesRef scratch = new BytesRef();
     for(int i=0;i<2;i++) {
       Document doc2 = ireader.leaves().get(0).reader().document(i);
       String expected;
@@ -1185,20 +1186,7 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     dir.close();
   }
 
-  static abstract class LongProducer {
-    abstract long next();
-  }
-
-  private void doTestNumericsVsStoredFields(final long minValue, final long maxValue) throws Exception {
-    doTestNumericsVsStoredFields(new LongProducer() {
-      @Override
-      long next() {
-        return TestUtil.nextLong(random(), minValue, maxValue);
-      }
-    });
-  }
-
-  private void doTestNumericsVsStoredFields(LongProducer longs) throws Exception {
+  private void doTestNumericsVsStoredFields(double density, LongSupplier longs) throws Exception {
     Directory dir = newDirectory();
     IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random()));
     RandomIndexWriter writer = new RandomIndexWriter(random(), dir, conf);
@@ -1216,8 +1204,12 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     // for numbers of values <= 256, all storage layouts are tested
     assert numDocs > 256;
     for (int i = 0; i < numDocs; i++) {
+      if (random().nextDouble() > density) {
+        writer.addDocument(new Document());
+        continue;
+      }
       idField.setStringValue(Integer.toString(i));
-      long value = longs.next();
+      long value = longs.getAsLong();
       storedField.setStringValue(Long.toString(value));
       dvField.setLongValue(value);
       writer.addDocument(doc);
@@ -1241,20 +1233,28 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     
     // compare
     DirectoryReader ir = DirectoryReader.open(dir);
+    TestUtil.checkReader(ir);
     for (LeafReaderContext context : ir.leaves()) {
       LeafReader r = context.reader();
-      NumericDocValues docValues = r.getNumericDocValues("dv");
+      NumericDocValues docValues = DocValues.getNumeric(r, "dv");
+      docValues.nextDoc();
       for (int i = 0; i < r.maxDoc(); i++) {
-        long storedValue = Long.parseLong(r.document(i).get("stored"));
-        assertEquals(i, docValues.nextDoc());
-        assertEquals(storedValue, docValues.longValue());
+        String storedValue = r.document(i).get("stored");
+        if (storedValue == null) {
+          assertTrue(docValues.docID() > i);
+        } else {
+          assertEquals(i, docValues.docID());
+          assertEquals(Long.parseLong(storedValue), docValues.longValue());
+          docValues.nextDoc();
+        }
       }
+      assertEquals(DocIdSetIterator.NO_MORE_DOCS, docValues.docID());
     }
     ir.close();
     dir.close();
   }
   
-  private void doTestSortedNumericsVsStoredFields(LongProducer counts, LongProducer values) throws Exception {
+  private void doTestSortedNumericsVsStoredFields(LongSupplier counts, LongSupplier values) throws Exception {
     Directory dir = newDirectory();
     IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random()));
     RandomIndexWriter writer = new RandomIndexWriter(random(), dir, conf);
@@ -1268,10 +1268,10 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
       Document doc = new Document();
       doc.add(new StringField("id", Integer.toString(i), Field.Store.NO));
       
-      int valueCount = (int) counts.next();
+      int valueCount = (int) counts.getAsLong();
       long valueArray[] = new long[valueCount];
       for (int j = 0; j < valueCount; j++) {
-        long value = values.next();
+        long value = values.getAsLong();
         valueArray[j] = value;
         doc.add(new SortedNumericDocValuesField("dv", value));
       }
@@ -1300,6 +1300,7 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     
     // compare
     DirectoryReader ir = DirectoryReader.open(dir);
+    TestUtil.checkReader(ir);
     for (LeafReaderContext context : ir.leaves()) {
       LeafReader r = context.reader();
       SortedNumericDocValues docValues = DocValues.getSortedNumeric(r, "dv");
@@ -1326,39 +1327,74 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
   public void testBooleanNumericsVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestNumericsVsStoredFields(0, 1);
+      doTestNumericsVsStoredFields(1, () -> random().nextInt(2));
     }
   }
-  
+
+  public void testSparseBooleanNumericsVsStoredFields() throws Exception {
+    int numIterations = atLeast(1);
+    for (int i = 0; i < numIterations; i++) {
+      doTestNumericsVsStoredFields(random().nextDouble(), () -> random().nextInt(2));
+    }
+  }
+
   public void testByteNumericsVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestNumericsVsStoredFields(Byte.MIN_VALUE, Byte.MAX_VALUE);
+      doTestNumericsVsStoredFields(1, () -> TestUtil.nextInt(random(), Byte.MIN_VALUE, Byte.MAX_VALUE));
     }
   }
-  
+
+  public void testSparseByteNumericsVsStoredFields() throws Exception {
+    int numIterations = atLeast(1);
+    for (int i = 0; i < numIterations; i++) {
+      doTestNumericsVsStoredFields(random().nextDouble(), () -> TestUtil.nextInt(random(), Byte.MIN_VALUE, Byte.MAX_VALUE));
+    }
+  }
+
   public void testShortNumericsVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestNumericsVsStoredFields(Short.MIN_VALUE, Short.MAX_VALUE);
+      doTestNumericsVsStoredFields(1, () -> TestUtil.nextInt(random(), Short.MIN_VALUE, Short.MAX_VALUE));
     }
   }
-  
+
+  public void testSparseShortNumericsVsStoredFields() throws Exception {
+    int numIterations = atLeast(1);
+    for (int i = 0; i < numIterations; i++) {
+      doTestNumericsVsStoredFields(random().nextDouble(), () -> TestUtil.nextInt(random(), Short.MIN_VALUE, Short.MAX_VALUE));
+    }
+  }
+
   public void testIntNumericsVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestNumericsVsStoredFields(Integer.MIN_VALUE, Integer.MAX_VALUE);
+      doTestNumericsVsStoredFields(1, random()::nextInt);
+    }
+  }
+  
+  public void testSparseIntNumericsVsStoredFields() throws Exception {
+    int numIterations = atLeast(1);
+    for (int i = 0; i < numIterations; i++) {
+      doTestNumericsVsStoredFields(random().nextDouble(), random()::nextInt);
     }
   }
   
   public void testLongNumericsVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestNumericsVsStoredFields(Long.MIN_VALUE, Long.MAX_VALUE);
+      doTestNumericsVsStoredFields(1, random()::nextLong);
     }
   }
   
-  private void doTestBinaryVsStoredFields(int minLength, int maxLength) throws Exception {
+  public void testSparseLongNumericsVsStoredFields() throws Exception {
+    int numIterations = atLeast(1);
+    for (int i = 0; i < numIterations; i++) {
+      doTestNumericsVsStoredFields(random().nextDouble(), random()::nextLong);
+    }
+  }
+
+  private void doTestBinaryVsStoredFields(double density, Supplier<byte[]> bytes) throws Exception {
     Directory dir = newDirectory();
     IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random()));
     RandomIndexWriter writer = new RandomIndexWriter(random(), dir, conf);
@@ -1373,15 +1409,12 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     // index some docs
     int numDocs = atLeast(300);
     for (int i = 0; i < numDocs; i++) {
-      idField.setStringValue(Integer.toString(i));
-      final int length;
-      if (minLength == maxLength) {
-        length = minLength; // fixed length
-      } else {
-        length = TestUtil.nextInt(random(), minLength, maxLength);
+      if (random().nextDouble() > density) {
+        writer.addDocument(new Document());
+        continue;
       }
-      byte buffer[] = new byte[length];
-      random().nextBytes(buffer);
+      idField.setStringValue(Integer.toString(i));
+      byte[] buffer = bytes.get();
       storedField.setBytesValue(buffer);
       dvField.setBytesValue(buffer);
       writer.addDocument(doc);
@@ -1399,28 +1432,44 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     
     // compare
     DirectoryReader ir = writer.getReader();
+    TestUtil.checkReader(ir);
     for (LeafReaderContext context : ir.leaves()) {
       LeafReader r = context.reader();
-      BinaryDocValues docValues = r.getBinaryDocValues("dv");
+      BinaryDocValues docValues = DocValues.getBinary(r, "dv");
+      docValues.nextDoc();
       for (int i = 0; i < r.maxDoc(); i++) {
         BytesRef binaryValue = r.document(i).getBinaryValue("stored");
-        assertEquals(i, docValues.nextDoc());
-        assertEquals(binaryValue, docValues.binaryValue());
+        if (binaryValue == null) {
+          assertTrue(docValues.docID() > i);
+        } else {
+          assertEquals(i, docValues.docID());
+          assertEquals(binaryValue, docValues.binaryValue());
+          docValues.nextDoc();
+        }
       }
+      assertEquals(DocIdSetIterator.NO_MORE_DOCS, docValues.docID());
     }
     ir.close();
     
     // compare again
     writer.forceMerge(1);
     ir = writer.getReader();
+    TestUtil.checkReader(ir);
     for (LeafReaderContext context : ir.leaves()) {
       LeafReader r = context.reader();
-      BinaryDocValues docValues = r.getBinaryDocValues("dv");
+      BinaryDocValues docValues = DocValues.getBinary(r, "dv");
+      docValues.nextDoc();
       for (int i = 0; i < r.maxDoc(); i++) {
         BytesRef binaryValue = r.document(i).getBinaryValue("stored");
-        assertEquals(i, docValues.nextDoc());
-        assertEquals(binaryValue, docValues.binaryValue());
+        if (binaryValue == null) {
+          assertTrue(docValues.docID() > i);
+        } else {
+          assertEquals(i, docValues.docID());
+          assertEquals(binaryValue, docValues.binaryValue());
+          docValues.nextDoc();
+        }
       }
+      assertEquals(DocIdSetIterator.NO_MORE_DOCS, docValues.docID());
     }
     ir.close();
     writer.close();
@@ -1428,21 +1477,46 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
   }
   
   public void testBinaryFixedLengthVsStoredFields() throws Exception {
+    doTestBinaryFixedLengthVsStoredFields(1);
+  }
+
+  public void testSparseBinaryFixedLengthVsStoredFields() throws Exception {
+    doTestBinaryFixedLengthVsStoredFields(random().nextDouble());
+  }
+
+  private void doTestBinaryFixedLengthVsStoredFields(double density) throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
       int fixedLength = TestUtil.nextInt(random(), 0, 10);
-      doTestBinaryVsStoredFields(fixedLength, fixedLength);
+      doTestBinaryVsStoredFields(density, () -> {
+        byte buffer[] = new byte[fixedLength];
+        random().nextBytes(buffer);
+        return buffer;
+      });
     }
   }
-  
+
   public void testBinaryVariableLengthVsStoredFields() throws Exception {
+    doTestBinaryVariableLengthVsStoredFields(1);
+  }
+
+  public void testSparseBinaryVariableLengthVsStoredFields() throws Exception {
+    doTestBinaryVariableLengthVsStoredFields(random().nextDouble());
+  }
+
+  public void doTestBinaryVariableLengthVsStoredFields(double density) throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestBinaryVsStoredFields(0, 10);
+      doTestBinaryVsStoredFields(density, () -> {
+        final int length = random().nextInt(10);
+        byte buffer[] = new byte[length];
+        random().nextBytes(buffer);
+        return buffer;
+      });
     }
   }
   
-  protected void doTestSortedVsStoredFields(int numDocs, int minLength, int maxLength) throws Exception {
+  protected void doTestSortedVsStoredFields(int numDocs, double density, Supplier<byte[]> bytes) throws Exception {
     Directory dir = newFSDirectory(createTempDir("dvduel"));
     IndexWriterConfig conf = newIndexWriterConfig(new MockAnalyzer(random()));
     RandomIndexWriter writer = new RandomIndexWriter(random(), dir, conf);
@@ -1456,15 +1530,12 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     
     // index some docs
     for (int i = 0; i < numDocs; i++) {
-      idField.setStringValue(Integer.toString(i));
-      final int length;
-      if (minLength == maxLength) {
-        length = minLength; // fixed length
-      } else {
-        length = TestUtil.nextInt(random(), minLength, maxLength);
+      if (random().nextDouble() > density) {
+        writer.addDocument(new Document());
+        continue;
       }
-      byte buffer[] = new byte[length];
-      random().nextBytes(buffer);
+      idField.setStringValue(Integer.toString(i));
+      byte[] buffer = bytes.get();
       storedField.setBytesValue(buffer);
       dvField.setBytesValue(buffer);
       writer.addDocument(doc);
@@ -1482,28 +1553,44 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     
     // compare
     DirectoryReader ir = writer.getReader();
+    TestUtil.checkReader(ir);
     for (LeafReaderContext context : ir.leaves()) {
       LeafReader r = context.reader();
       BinaryDocValues docValues = DocValues.getBinary(r, "dv");
+      docValues.nextDoc();
       for (int i = 0; i < r.maxDoc(); i++) {
         BytesRef binaryValue = r.document(i).getBinaryValue("stored");
-        assertEquals(i, docValues.nextDoc());
-        assertEquals(binaryValue, docValues.binaryValue());
+        if (binaryValue == null) {
+          assertTrue(docValues.docID() > i);
+        } else {
+          assertEquals(i, docValues.docID());
+          assertEquals(binaryValue, docValues.binaryValue());
+          docValues.nextDoc();
+        }
       }
+      assertEquals(DocIdSetIterator.NO_MORE_DOCS, docValues.docID());
     }
     ir.close();
     writer.forceMerge(1);
     
     // compare again
     ir = writer.getReader();
+    TestUtil.checkReader(ir);
     for (LeafReaderContext context : ir.leaves()) {
       LeafReader r = context.reader();
       BinaryDocValues docValues = DocValues.getBinary(r, "dv");
+      docValues.nextDoc();
       for (int i = 0; i < r.maxDoc(); i++) {
         BytesRef binaryValue = r.document(i).getBinaryValue("stored");
-        assertEquals(i, docValues.nextDoc());
-        assertEquals(binaryValue, docValues.binaryValue());
+        if (binaryValue == null) {
+          assertTrue(docValues.docID() > i);
+        } else {
+          assertEquals(i, docValues.docID());
+          assertEquals(binaryValue, docValues.binaryValue());
+          docValues.nextDoc();
+        }
       }
+      assertEquals(DocIdSetIterator.NO_MORE_DOCS, docValues.docID());
     }
     ir.close();
     writer.close();
@@ -1514,17 +1601,41 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
       int fixedLength = TestUtil.nextInt(random(), 1, 10);
-      doTestSortedVsStoredFields(atLeast(300), fixedLength, fixedLength);
+      doTestSortedVsStoredFields(atLeast(300), 1, fixedLength, fixedLength);
     }
   }
   
-  public void testSortedVariableLengthVsStoredFields() throws Exception {
+  public void testSparseSortedFixedLengthVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestSortedVsStoredFields(atLeast(300), 1, 10);
+      int fixedLength = TestUtil.nextInt(random(), 1, 10);
+      doTestSortedVsStoredFields(atLeast(300), random().nextDouble(), fixedLength, fixedLength);
     }
   }
   
+  public void testSortedVariableLengthVsStoredFields() throws Exception {
+    int numIterations = atLeast(1);
+    for (int i = 0; i < numIterations; i++) {
+      doTestSortedVsStoredFields(atLeast(300), 1, 1, 10);
+    }
+  }
+
+  public void testSparseSortedVariableLengthVsStoredFields() throws Exception {
+    int numIterations = atLeast(1);
+    for (int i = 0; i < numIterations; i++) {
+      doTestSortedVsStoredFields(atLeast(300), random().nextDouble(), 1, 10);
+    }
+  }
+
+  protected void doTestSortedVsStoredFields(int numDocs, double density, int minLength, int maxLength) throws Exception {
+    doTestSortedVsStoredFields(numDocs, density, () -> {
+      int length = TestUtil.nextInt(random(), minLength, maxLength);
+      byte[] buffer = new byte[length];
+      random().nextBytes(buffer);
+      return buffer;
+    });
+  }
+
   public void testSortedSetOneValue() throws IOException {
     Directory directory = newDirectory();
     RandomIndexWriter iwriter = new RandomIndexWriter(random(), directory);
@@ -2001,6 +2112,7 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     
     // compare
     DirectoryReader ir = writer.getReader();
+    TestUtil.checkReader(ir);
     for (LeafReaderContext context : ir.leaves()) {
       LeafReader r = context.reader();
       SortedSetDocValues docValues = r.getSortedSetDocValues("dv");
@@ -2029,6 +2141,7 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     
     // compare again
     ir = writer.getReader();
+    TestUtil.checkReader(ir);
     for (LeafReaderContext context : ir.leaves()) {
       LeafReader r = context.reader();
       SortedSetDocValues docValues = r.getSortedSetDocValues("dv");
@@ -2067,18 +2180,8 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
       doTestSortedNumericsVsStoredFields(
-          new LongProducer() {
-            @Override
-            long next() {
-              return 1;
-            }
-          },
-          new LongProducer() {
-            @Override
-            long next() {
-              return TestUtil.nextLong(random(), Long.MIN_VALUE, Long.MAX_VALUE);
-            }
-          }
+          () -> 1,
+          random()::nextLong
       );
     }
   }
@@ -2087,18 +2190,8 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
       doTestSortedNumericsVsStoredFields(
-          new LongProducer() {
-            @Override
-            long next() {
-              return random().nextBoolean() ? 0 : 1;
-            }
-          },
-          new LongProducer() {
-            @Override
-            long next() {
-              return TestUtil.nextLong(random(), Long.MIN_VALUE, Long.MAX_VALUE);
-            }
-          }
+          () -> random().nextBoolean() ? 0 : 1,
+          random()::nextLong
       );
     }
   }
@@ -2107,18 +2200,8 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
       doTestSortedNumericsVsStoredFields(
-          new LongProducer() {
-            @Override
-            long next() {
-              return TestUtil.nextLong(random(), 0, 50);
-            }
-          },
-          new LongProducer() {
-            @Override
-            long next() {
-              return TestUtil.nextLong(random(), Long.MIN_VALUE, Long.MAX_VALUE);
-            }
-          }
+          () -> TestUtil.nextLong(random(), 0, 50),
+          random()::nextLong
       );
     }
   }
@@ -2131,18 +2214,8 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
       doTestSortedNumericsVsStoredFields(
-          new LongProducer() {
-            @Override
-            long next() {
-              return TestUtil.nextLong(random(), 0, 6);
-            }
-          },
-          new LongProducer() {
-            @Override
-            long next() {
-              return values[random().nextInt(values.length)];
-            }
-          }
+          () -> TestUtil.nextLong(random(), 0, 6),
+          () -> values[random().nextInt(values.length)]
       );
     }
   }
@@ -2198,22 +2271,31 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
   }
 
   public void testGCDCompression() throws Exception {
+    doTestGCDCompression(1);
+  }
+
+  public void testSparseGCDCompression() throws Exception {
+    doTestGCDCompression(random().nextDouble());
+  }
+
+  private void doTestGCDCompression(double density) throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
       final long min = - (((long) random().nextInt(1 << 30)) << 32);
       final long mul = random().nextInt() & 0xFFFFFFFFL;
-      final LongProducer longs = new LongProducer() {
-        @Override
-        long next() {
-          return min + mul * random().nextInt(1 << 20);
-        }
+      final LongSupplier longs = () -> {
+        return min + mul * random().nextInt(1 << 20);
       };
-      doTestNumericsVsStoredFields(longs);
+      doTestNumericsVsStoredFields(density, longs);
     }
   }
 
   public void testZeros() throws Exception {
-    doTestNumericsVsStoredFields(0, 0);
+    doTestNumericsVsStoredFields(1, () -> 0);
+  }
+
+  public void testSparseZeros() throws Exception {
+    doTestNumericsVsStoredFields(random().nextDouble(), () -> 0);
   }
 
   public void testZeroOrMin() throws Exception {
@@ -2221,13 +2303,10 @@ public abstract class BaseDocValuesFormatTestCase extends BaseIndexFileFormatTes
     // the GCD of 0 and MIN_VALUE is negative
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      final LongProducer longs = new LongProducer() {
-        @Override
-        long next() {
-          return random().nextBoolean() ? 0 : Long.MIN_VALUE;
-        }
+      final LongSupplier longs = () -> {
+        return random().nextBoolean() ? 0 : Long.MIN_VALUE;
       };
-      doTestNumericsVsStoredFields(longs);
+      doTestNumericsVsStoredFields(1, longs);
     }
   }
   

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/test-framework/src/java/org/apache/lucene/index/BaseIndexFileFormatTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/BaseIndexFileFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/BaseIndexFileFormatTestCase.java
index d56e6cb..7a7abc0 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/BaseIndexFileFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/BaseIndexFileFormatTestCase.java
@@ -362,6 +362,12 @@ abstract class BaseIndexFileFormatTestCase extends LuceneTestCase {
                                      }
 
                                      @Override
+                                    public boolean advanceExact(int target) throws IOException {
+                                      docID = target;
+                                      return target == 0;
+                                    }
+
+                                     @Override
                                      public long cost() {
                                        return 1;
                                      }
@@ -415,6 +421,12 @@ abstract class BaseIndexFileFormatTestCase extends LuceneTestCase {
                                      }
 
                                      @Override
+                                    public boolean advanceExact(int target) throws IOException {
+                                      docID = target;
+                                      return target == 0;
+                                    }
+
+                                     @Override
                                      public long cost() {
                                        return 1;
                                      }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/test-framework/src/java/org/apache/lucene/index/BaseNormsFormatTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/BaseNormsFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/BaseNormsFormatTestCase.java
index 64e99da..cd62218 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/BaseNormsFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/BaseNormsFormatTestCase.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
 import java.util.concurrent.CountDownLatch;
+import java.util.function.LongSupplier;
 
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.MockAnalyzer;
@@ -59,9 +60,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
         }
       });
@@ -73,9 +74,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
         }
       });
@@ -86,9 +87,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return TestUtil.nextLong(r, Short.MIN_VALUE, Short.MAX_VALUE);
         }
       });
@@ -100,9 +101,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return TestUtil.nextLong(r, Short.MIN_VALUE, Short.MAX_VALUE);
         }
       });
@@ -113,9 +114,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return TestUtil.nextLong(r, Long.MIN_VALUE, Long.MAX_VALUE);
         }
       });
@@ -127,9 +128,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return TestUtil.nextLong(r, Long.MIN_VALUE, Long.MAX_VALUE);
         }
       });
@@ -140,9 +141,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           int thingToDo = r.nextInt(3);
           switch (thingToDo) {
             case 0: return Long.MIN_VALUE;
@@ -159,9 +160,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           int thingToDo = r.nextInt(3);
           switch (thingToDo) {
             case 0: return Long.MIN_VALUE;
@@ -177,9 +178,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextBoolean() ? 20 : 3;
         }
       });
@@ -191,9 +192,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextBoolean() ? 20 : 3;
         }
       });
@@ -204,9 +205,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextBoolean() ? 1000000L : -5000;
         }
       });
@@ -218,9 +219,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextBoolean() ? 1000000L : -5000;
         }
       });
@@ -230,9 +231,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
   public void testAllZeros() throws Exception {
     int iterations = atLeast(1);
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return 0;
         }
       });
@@ -243,9 +244,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     assumeTrue("Requires sparse norms support", codecSupportsSparsity());
     int iterations = atLeast(1);
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return 0;
         }
       });
@@ -256,9 +257,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     int iterations = atLeast(1);
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextInt(100) == 0 ? TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE) : 0;
         }
       });
@@ -270,9 +271,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
       final long commonValue = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextInt(100) == 0 ? TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE) : commonValue;
         }
       });
@@ -285,9 +286,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     final Random r = random();
     for (int i = 0; i < iterations; i++) {
       final long commonValue = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextInt(100) == 0 ? TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE) : commonValue;
         }
       });
@@ -300,9 +301,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     for (int i = 0; i < iterations; i++) {
       final long commonValue = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
       final long uncommonValue = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
-      doTestNormsVersusDocValues(1, new LongProducer() {
+      doTestNormsVersusDocValues(1, new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextInt(100) == 0 ? uncommonValue : commonValue;
         }
       });
@@ -316,9 +317,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     for (int i = 0; i < iterations; i++) {
       final long commonValue = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
       final long uncommonValue = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
-      doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+      doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
         @Override
-        long next() {
+        public long getAsLong() {
           return r.nextInt(100) == 0 ? uncommonValue : commonValue;
         }
       });
@@ -337,9 +338,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     for (int j = 0; j < numOtherValues; ++j) {
       otherValues[j] = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
     }
-    doTestNormsVersusDocValues(1, new LongProducer() {
+    doTestNormsVersusDocValues(1, new LongSupplier() {
       @Override
-      long next() {
+      public long getAsLong() {
         return r.nextInt(100) == 0 ? otherValues[r.nextInt(numOtherValues - 1)] : commonValues[r.nextInt(N - 1)];
       }
     });
@@ -358,9 +359,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     for (int j = 0; j < numOtherValues; ++j) {
       otherValues[j] = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
     }
-    doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+    doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
       @Override
-      long next() {
+      public long getAsLong() {
         return r.nextInt(100) == 0 ? otherValues[r.nextInt(numOtherValues - 1)] : commonValues[r.nextInt(N - 1)];
       }
     });
@@ -386,9 +387,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
         for (int j = 0; j < numOtherValues; ++j) {
           otherValues[j] = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
         }
-        doTestNormsVersusDocValues(1, new LongProducer() {
+        doTestNormsVersusDocValues(1, new LongSupplier() {
           @Override
-          long next() {
+          public long getAsLong() {
             return r.nextInt(100) == 0 ? otherValues[r.nextInt(numOtherValues - 1)] : commonValues[r.nextInt(N - 1)];
           }
         });
@@ -417,9 +418,9 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
         for (int j = 0; j < numOtherValues; ++j) {
           otherValues[j] = TestUtil.nextLong(r, Byte.MIN_VALUE, Byte.MAX_VALUE);
         }
-        doTestNormsVersusDocValues(random().nextDouble(), new LongProducer() {
+        doTestNormsVersusDocValues(random().nextDouble(), new LongSupplier() {
           @Override
-          long next() {
+          public long getAsLong() {
             return r.nextInt(100) == 0 ? otherValues[r.nextInt(numOtherValues - 1)] : commonValues[r.nextInt(N - 1)];
           }
         });
@@ -427,7 +428,7 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     }
   }
 
-  private void doTestNormsVersusDocValues(double density, LongProducer longs) throws Exception {
+  private void doTestNormsVersusDocValues(double density, LongSupplier longs) throws Exception {
     int numDocs = atLeast(500);
     final FixedBitSet docsWithField = new FixedBitSet(numDocs);
     final int numDocsWithField = Math.max(1, (int) (density * numDocs));
@@ -445,7 +446,7 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     }
     long norms[] = new long[numDocsWithField];
     for (int i = 0; i < numDocsWithField; i++) {
-      norms[i] = longs.next();
+      norms[i] = longs.getAsLong();
     }
     
     Directory dir = newDirectory();
@@ -519,10 +520,6 @@ public abstract class BaseNormsFormatTestCase extends BaseIndexFileFormatTestCas
     }
   }
   
-  static abstract class LongProducer {
-    abstract long next();
-  }
-  
   static class CannedNormSimilarity extends Similarity {
     final long norms[];
     int index = 0;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/solr/core/src/java/org/apache/solr/request/DocValuesFacets.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/request/DocValuesFacets.java b/solr/core/src/java/org/apache/solr/request/DocValuesFacets.java
index 09ad836..0dab34b 100644
--- a/solr/core/src/java/org/apache/solr/request/DocValuesFacets.java
+++ b/solr/core/src/java/org/apache/solr/request/DocValuesFacets.java
@@ -272,11 +272,8 @@ public class DocValuesFacets {
     final LongValues ordmap = map == null ? null : map.getGlobalOrds(subIndex);
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > si.docID()) {
-        si.advance(doc);
-      }
       int term;
-      if (doc == si.docID()) {
+      if (si.advanceExact(doc)) {
         term = si.ordValue();
       } else {
         term = -1;
@@ -301,10 +298,7 @@ public class DocValuesFacets {
     
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > si.docID()) {
-        si.advance(doc);
-      }
-      if (doc == si.docID()) {
+      if (si.advanceExact(doc)) {
         segCounts[1+si.ordValue()]++;
       } else {
         segCounts[0]++;
@@ -334,10 +328,7 @@ public class DocValuesFacets {
     final LongValues ordMap = map == null ? null : map.getGlobalOrds(subIndex);
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > si.docID()) {
-        si.advance(doc);
-      }
-      if (doc == si.docID()) {
+      if (si.advanceExact(doc)) {
         // strange do-while to collect the missing count (first ord is NO_MORE_ORDS)
         int term = (int) si.nextOrd();
         do {
@@ -365,10 +356,7 @@ public class DocValuesFacets {
     
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > si.docID()) {
-        si.advance(doc);
-      }
-      if (doc == si.docID()) {
+      if (si.advanceExact(doc)) {
         int term = (int) si.nextOrd();
         do {
           segCounts[1+term]++;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/solr/core/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java b/solr/core/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java
index 0c88b80..3db7b4c 100644
--- a/solr/core/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java
+++ b/solr/core/src/java/org/apache/solr/request/PerSegmentSingleValuedFaceting.java
@@ -293,11 +293,8 @@ class PerSegmentSingleValuedFaceting {
         // specialized version when collecting counts for all terms
         int doc;
         while ((doc = iter.nextDoc()) < DocIdSetIterator.NO_MORE_DOCS) {
-          if (doc > si.docID()) {
-            si.advance(doc);
-          }
           int t;
-          if (doc == si.docID()) {
+          if (si.advanceExact(doc)) {
             t = 1+si.ordValue();
           } else {
             t = 0;
@@ -309,11 +306,8 @@ class PerSegmentSingleValuedFaceting {
         // version that adjusts term numbers because we aren't collecting the full range
         int doc;
         while ((doc = iter.nextDoc()) < DocIdSetIterator.NO_MORE_DOCS) {
-          if (doc > si.docID()) {
-            si.advance(doc);
-          }
           int term;
-          if (doc == si.docID()) {
+          if (si.advanceExact(doc)) {
             term = si.ordValue();
           } else {
             term = -1;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
index 933477b..5ac1975 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
@@ -795,7 +795,7 @@ public class SolrIndexSearcher extends IndexSearcher implements Closeable, SolrI
               continue;
             }
             Long val;
-            if (ndv.advance(localId) == localId) {
+            if (ndv.advanceExact(localId)) {
               val = ndv.longValue();
             } else {
               continue;
@@ -820,7 +820,7 @@ public class SolrIndexSearcher extends IndexSearcher implements Closeable, SolrI
               continue;
             }
             BytesRef value;
-            if (bdv.advance(localId) == localId) {
+            if (bdv.advanceExact(localId)) {
               value = BytesRef.deepCopyOf(bdv.binaryValue());
             } else {
               continue;
@@ -832,7 +832,7 @@ public class SolrIndexSearcher extends IndexSearcher implements Closeable, SolrI
             if (sdv == null) {
               continue;
             }
-            if (sdv.advance(localId) == localId) {
+            if (sdv.advanceExact(localId)) {
               final BytesRef bRef = sdv.binaryValue();
               // Special handling for Boolean fields since they're stored as 'T' and 'F'.
               if (schemaField.getType() instanceof BoolField) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/solr/core/src/java/org/apache/solr/search/facet/FacetFieldProcessorByArrayDV.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/facet/FacetFieldProcessorByArrayDV.java b/solr/core/src/java/org/apache/solr/search/facet/FacetFieldProcessorByArrayDV.java
index fb60945..88adf67 100644
--- a/solr/core/src/java/org/apache/solr/search/facet/FacetFieldProcessorByArrayDV.java
+++ b/solr/core/src/java/org/apache/solr/search/facet/FacetFieldProcessorByArrayDV.java
@@ -186,10 +186,7 @@ class FacetFieldProcessorByArrayDV extends FacetFieldProcessorByArray {
 
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > singleDv.docID()) {
-        singleDv.advance(doc);
-      }
-      if (doc == singleDv.docID()) {
+      if (singleDv.advanceExact(doc)) {
         counts[ singleDv.ordValue() + 1 ]++;
       } else {
         counts[ 0 ]++;
@@ -211,10 +208,7 @@ class FacetFieldProcessorByArrayDV extends FacetFieldProcessorByArray {
 
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > multiDv.docID()) {
-        multiDv.advance(doc);
-      }
-      if (doc == multiDv.docID()) {
+      if (multiDv.advanceExact(doc)) {
         for(;;) {
           int segOrd = (int)multiDv.nextOrd();
           if (segOrd < 0) break;
@@ -247,10 +241,7 @@ class FacetFieldProcessorByArrayDV extends FacetFieldProcessorByArray {
   private void collectDocs(SortedDocValues singleDv, DocIdSetIterator disi, LongValues toGlobal) throws IOException {
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > singleDv.docID()) {
-        singleDv.advance(doc);
-      }
-      if (doc == singleDv.docID()) {
+      if (singleDv.advanceExact(doc)) {
         int segOrd = singleDv.ordValue();
         collect(doc, segOrd, toGlobal);
       }
@@ -260,10 +251,7 @@ class FacetFieldProcessorByArrayDV extends FacetFieldProcessorByArray {
   private void collectCounts(SortedDocValues singleDv, DocIdSetIterator disi, LongValues toGlobal) throws IOException {
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > singleDv.docID()) {
-        singleDv.advance(doc);
-      }
-      if (doc == singleDv.docID()) {
+      if (singleDv.advanceExact(doc)) {
         int segOrd = singleDv.ordValue();
         int ord = (int)toGlobal.get(segOrd);
         countAcc.incrementCount(ord, 1);
@@ -274,10 +262,7 @@ class FacetFieldProcessorByArrayDV extends FacetFieldProcessorByArray {
   private void collectDocs(SortedSetDocValues multiDv, DocIdSetIterator disi, LongValues toGlobal) throws IOException {
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > multiDv.docID()) {
-        multiDv.advance(doc);
-      }
-      if (doc == multiDv.docID()) {
+      if (multiDv.advanceExact(doc)) {
         for(;;) {
           int segOrd = (int)multiDv.nextOrd();
           if (segOrd < 0) break;
@@ -290,10 +275,7 @@ class FacetFieldProcessorByArrayDV extends FacetFieldProcessorByArray {
   private void collectCounts(SortedSetDocValues multiDv, DocIdSetIterator disi, LongValues toGlobal) throws IOException {
     int doc;
     while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (doc > multiDv.docID()) {
-        multiDv.advance(doc);
-      }
-      if (doc == multiDv.docID()) {
+      if (multiDv.advanceExact(doc)) {
         for(;;) {
           int segOrd = (int)multiDv.nextOrd();
           if (segOrd < 0) break;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/solr/core/src/java/org/apache/solr/uninverting/FieldCacheImpl.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/uninverting/FieldCacheImpl.java b/solr/core/src/java/org/apache/solr/uninverting/FieldCacheImpl.java
index b63e5e9..2224010 100644
--- a/solr/core/src/java/org/apache/solr/uninverting/FieldCacheImpl.java
+++ b/solr/core/src/java/org/apache/solr/uninverting/FieldCacheImpl.java
@@ -692,6 +692,12 @@ class FieldCacheImpl implements FieldCache {
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          docID = target;
+          return docsWithField.get(docID);
+        }
+
+        @Override
         public long cost() {
           return values.size();
         }
@@ -821,6 +827,12 @@ class FieldCacheImpl implements FieldCache {
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          docID = target;
+          return docToTermOrd.get(docID) != 0;
+        }
+
+        @Override
         public long cost() {
           return 0;
         }
@@ -1022,6 +1034,12 @@ class FieldCacheImpl implements FieldCache {
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          docID = target;
+          return docsWithField.get(docID);
+        }
+
+        @Override
         public long cost() {
           return 0;
         }


[2/2] lucene-solr:master: LUCENE-7462: Give doc values APIs an `advanceExact` method.

Posted by jp...@apache.org.
LUCENE-7462: Give doc values APIs an `advanceExact` method.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/9aca4c9d
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/9aca4c9d
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/9aca4c9d

Branch: refs/heads/master
Commit: 9aca4c9d56089a9ac89df5fd93be76a4fe822448
Parents: 9b49c72
Author: Adrien Grand <jp...@gmail.com>
Authored: Thu Oct 20 14:07:10 2016 +0200
Committer: Adrien Grand <jp...@gmail.com>
Committed: Mon Oct 24 10:51:23 2016 +0200

----------------------------------------------------------------------
 .../codecs/lucene53/Lucene53NormsProducer.java  |   6 +
 .../lucene54/Lucene54DocValuesProducer.java     |  65 +++-
 .../lucene54/TestLucene54DocValuesFormat.java   |   5 +-
 .../simpletext/SimpleTextDocValuesReader.java   |  95 +++++-
 .../simpletext/SimpleTextDocValuesWriter.java   |   9 +
 .../apache/lucene/codecs/DocValuesConsumer.java |  25 ++
 .../org/apache/lucene/codecs/NormsConsumer.java |   5 +
 .../lucene/codecs/lucene70/IndexedDISI.java     |  88 +++--
 .../lucene70/Lucene70DocValuesProducer.java     |  62 ++++
 .../codecs/lucene70/Lucene70NormsProducer.java  |  11 +
 .../apache/lucene/index/BinaryDocValues.java    |   5 +-
 .../lucene/index/BinaryDocValuesWriter.java     |   5 +
 .../org/apache/lucene/index/CheckIndex.java     |  81 ++++-
 .../java/org/apache/lucene/index/DocValues.java |  95 +++---
 .../apache/lucene/index/DocValuesIterator.java  |  33 ++
 .../lucene/index/FilterBinaryDocValues.java     |   5 +
 .../lucene/index/FilterNumericDocValues.java    |   5 +
 .../index/LegacyBinaryDocValuesWrapper.java     |   8 +
 .../index/LegacyNumericDocValuesWrapper.java    |   9 +
 .../index/LegacySortedDocValuesWrapper.java     |   9 +
 .../LegacySortedNumericDocValuesWrapper.java    |   9 +
 .../index/LegacySortedSetDocValuesWrapper.java  |  10 +
 .../org/apache/lucene/index/MultiDocValues.java | 125 +++++++
 .../apache/lucene/index/NormValuesWriter.java   |   5 +
 .../apache/lucene/index/NumericDocValues.java   |   7 +-
 .../lucene/index/NumericDocValuesWriter.java    |   5 +
 .../apache/lucene/index/ReadersAndUpdates.java  |  10 +
 .../index/SingletonSortedNumericDocValues.java  |  24 +-
 .../index/SingletonSortedSetDocValues.java      |  18 +-
 .../apache/lucene/index/SortedDocValues.java    |   3 +
 .../lucene/index/SortedDocValuesWriter.java     |   5 +
 .../lucene/index/SortedNumericDocValues.java    |   6 +-
 .../index/SortedNumericDocValuesWriter.java     |   5 +
 .../apache/lucene/index/SortedSetDocValues.java |   5 +-
 .../lucene/index/SortedSetDocValuesWriter.java  |   5 +
 .../apache/lucene/index/SortingLeafReader.java  |  32 ++
 .../apache/lucene/search/FieldComparator.java   |  40 +--
 .../lucene/search/SortedNumericSelector.java    |  18 +
 .../apache/lucene/search/SortedSetSelector.java |  36 ++
 .../search/similarities/BM25Similarity.java     |   8 +-
 .../search/similarities/SimilarityBase.java     |   6 +-
 .../search/similarities/TFIDFSimilarity.java    |   8 +-
 .../lucene/codecs/lucene70/TestIndexedDISI.java |  28 +-
 .../lucene70/TestLucene70DocValuesFormat.java   |   4 +-
 .../SortedSetDocValuesFacetCounts.java          |  15 +-
 .../lucene/search/join/BlockJoinSelector.java   | 104 +++++-
 .../search/join/GenericTermsCollector.java      |   7 +
 .../search/join/TestBlockJoinSelector.java      |  12 +
 .../apache/lucene/index/memory/MemoryIndex.java |   6 +
 .../search/TestDiversifiedTopDocsCollector.java |   9 +
 .../lucene/index/AssertingLeafReader.java       |  89 ++++-
 .../index/BaseDocValuesFormatTestCase.java      | 331 ++++++++++++-------
 .../index/BaseIndexFileFormatTestCase.java      |  12 +
 .../lucene/index/BaseNormsFormatTestCase.java   | 101 +++---
 .../apache/solr/request/DocValuesFacets.java    |  20 +-
 .../request/PerSegmentSingleValuedFaceting.java |  10 +-
 .../apache/solr/search/SolrIndexSearcher.java   |   6 +-
 .../facet/FacetFieldProcessorByArrayDV.java     |  30 +-
 .../apache/solr/uninverting/FieldCacheImpl.java |  18 +
 59 files changed, 1405 insertions(+), 413 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene53/Lucene53NormsProducer.java
----------------------------------------------------------------------
diff --git a/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene53/Lucene53NormsProducer.java b/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene53/Lucene53NormsProducer.java
index a97cb5a..718fcd6 100644
--- a/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene53/Lucene53NormsProducer.java
+++ b/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene53/Lucene53NormsProducer.java
@@ -222,6 +222,12 @@ class Lucene53NormsProducer extends NormsProducer {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      docID = target;
+      return true;
+    }
+
+    @Override
     public long cost() {
       // TODO
       return 0;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene54/Lucene54DocValuesProducer.java
----------------------------------------------------------------------
diff --git a/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene54/Lucene54DocValuesProducer.java b/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene54/Lucene54DocValuesProducer.java
index 1f785fe..f1c169c 100644
--- a/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene54/Lucene54DocValuesProducer.java
+++ b/lucene/backward-codecs/src/java/org/apache/lucene/codecs/lucene54/Lucene54DocValuesProducer.java
@@ -477,6 +477,12 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
           }
 
           @Override
+          public boolean advanceExact(int target) throws IOException {
+            docID = target;
+            return true;
+          }
+
+          @Override
           public long cost() {
             // TODO
             return 0;
@@ -524,6 +530,13 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
       }
 
       @Override
+      public boolean advanceExact(int target) throws IOException {
+        doc = target;
+        value = values.get(doc);
+        return value != 0 || docsWithField.get(doc);
+      }
+
+      @Override
       public long cost() {
         return maxDoc;
       }
@@ -696,6 +709,16 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      if (advance(target) == target) {
+        return true;
+      }
+      --index;
+      doc = target;
+      return false;
+    }
+
+    @Override
     public long longValue() {
       assert index >= 0;
       assert index < docIDsLength;
@@ -891,6 +914,11 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          return sparseValues.advanceExact(target);
+        }
+
+        @Override
         public long cost() {
           return sparseValues.cost();
         }
@@ -933,7 +961,14 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
           return nextDoc();
         }
       }
-          
+
+      @Override
+      public boolean advanceExact(int target) throws IOException {
+        docID = target;
+        ord = (int) ordinals.get(target);
+        return ord != -1;
+      }
+
       @Override
       public int ordValue() {
         return ord;
@@ -1017,6 +1052,11 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
           }
 
           @Override
+          public boolean advanceExact(int target) throws IOException {
+            return sparseValues.advanceExact(target);
+          }
+
+          @Override
           public long cost() {
             return sparseValues.cost();
           }
@@ -1061,6 +1101,12 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          docID = target;
+          return docsWithField.get(docID);
+        }
+
+        @Override
         public long cost() {
           // TODO
           return 0;
@@ -1122,6 +1168,14 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
         }
         
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          docID = target;
+          startOffset = ordIndex.get(docID);
+          endOffset = ordIndex.get(docID+1L);
+          return endOffset > startOffset;
+        }
+        
+        @Override
         public long cost() {
           // TODO
           return 0;
@@ -1185,6 +1239,15 @@ final class Lucene54DocValuesProducer extends DocValuesProducer implements Close
         }
         
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          docID = target;
+          int ord = (int) ordinals.get(docID);
+          startOffset = offsets[ord];
+          endOffset = offsets[ord+1];
+          return endOffset > startOffset;
+        }
+        
+        @Override
         public long cost() {
           // TODO
           return 0;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/backward-codecs/src/test/org/apache/lucene/codecs/lucene54/TestLucene54DocValuesFormat.java
----------------------------------------------------------------------
diff --git a/lucene/backward-codecs/src/test/org/apache/lucene/codecs/lucene54/TestLucene54DocValuesFormat.java b/lucene/backward-codecs/src/test/org/apache/lucene/codecs/lucene54/TestLucene54DocValuesFormat.java
index c6ca201..b231716 100644
--- a/lucene/backward-codecs/src/test/org/apache/lucene/codecs/lucene54/TestLucene54DocValuesFormat.java
+++ b/lucene/backward-codecs/src/test/org/apache/lucene/codecs/lucene54/TestLucene54DocValuesFormat.java
@@ -106,7 +106,7 @@ public class TestLucene54DocValuesFormat extends BaseCompressingDocValuesFormatT
   public void testSortedVariableLengthBigVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestSortedVsStoredFields(atLeast(300), 1, 32766);
+      doTestSortedVsStoredFields(atLeast(300), 1d, 1, 32766);
     }
   }
   
@@ -114,7 +114,7 @@ public class TestLucene54DocValuesFormat extends BaseCompressingDocValuesFormatT
   public void testSortedVariableLengthManyVsStoredFields() throws Exception {
     int numIterations = atLeast(1);
     for (int i = 0; i < numIterations; i++) {
-      doTestSortedVsStoredFields(TestUtil.nextInt(random(), 1024, 2049), 1, 500);
+      doTestSortedVsStoredFields(TestUtil.nextInt(random(), 1024, 2049), 1d, 1, 500);
     }
   }
   
@@ -201,6 +201,7 @@ public class TestLucene54DocValuesFormat extends BaseCompressingDocValuesFormatT
     }
 
     final IndexReader indexReader = writer.getReader();
+    TestUtil.checkReader(indexReader);
     writer.close();
 
     for (LeafReaderContext context : indexReader.leaves()) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesReader.java
----------------------------------------------------------------------
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesReader.java
index adf5e42..09f97ab 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesReader.java
@@ -144,7 +144,7 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
     if (values == null) {
       return null;
     } else {
-      DocIdSetIterator docsWithField = getNumericDocsWithField(fieldInfo);
+      DocValuesIterator docsWithField = getNumericDocsWithField(fieldInfo);
       return new NumericDocValues() {
         
         @Override
@@ -168,6 +168,11 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
         }
         
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          return docsWithField.advanceExact(target);
+        }
+        
+        @Override
         public long longValue() throws IOException {
           return values.apply(docsWithField.docID());
         }
@@ -214,12 +219,16 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
       }
     };
   }
-  
-  private DocIdSetIterator getNumericDocsWithField(FieldInfo fieldInfo) throws IOException {
+
+  private static abstract class DocValuesIterator extends DocIdSetIterator {
+    abstract boolean advanceExact(int target) throws IOException;
+  }
+
+  private DocValuesIterator getNumericDocsWithField(FieldInfo fieldInfo) throws IOException {
     final OneField field = fields.get(fieldInfo.name);
     final IndexInput in = data.clone();
     final BytesRefBuilder scratch = new BytesRefBuilder();
-    return new DocIdSetIterator() {
+    return new DocValuesIterator() {
       
       int doc = -1;
       
@@ -250,6 +259,15 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
         }
         return doc = NO_MORE_DOCS;
       }
+
+      @Override
+      boolean advanceExact(int target) throws IOException {
+        this.doc = target;
+        in.seek(field.dataStartFilePointer + (1+field.pattern.length()+2)*target);
+        SimpleTextUtil.readLine(in, scratch); // data
+        SimpleTextUtil.readLine(in, scratch); // 'T' or 'F'
+        return scratch.byteAt(0) == (byte) 'T';
+      }
     };
   }
   
@@ -265,7 +283,7 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
     final BytesRefBuilder scratch = new BytesRefBuilder();
     final DecimalFormat decoder = new DecimalFormat(field.pattern, new DecimalFormatSymbols(Locale.ROOT));
 
-    DocIdSetIterator docsWithField = getBinaryDocsWithField(fieldInfo);
+    DocValuesIterator docsWithField = getBinaryDocsWithField(fieldInfo);
     
     IntFunction<BytesRef> values = new IntFunction<BytesRef>() {
       final BytesRefBuilder term = new BytesRefBuilder();
@@ -317,19 +335,24 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
       }
       
       @Override
+      public boolean advanceExact(int target) throws IOException {
+        return docsWithField.advanceExact(target);
+      }
+      
+      @Override
       public BytesRef binaryValue() throws IOException {
         return values.apply(docsWithField.docID());
       }
     };
   }
 
-  private DocIdSetIterator getBinaryDocsWithField(FieldInfo fieldInfo) throws IOException {
+  private DocValuesIterator getBinaryDocsWithField(FieldInfo fieldInfo) throws IOException {
     final OneField field = fields.get(fieldInfo.name);
     final IndexInput in = data.clone();
     final BytesRefBuilder scratch = new BytesRefBuilder();
     final DecimalFormat decoder = new DecimalFormat(field.pattern, new DecimalFormatSymbols(Locale.ROOT));
 
-    return new DocIdSetIterator() {
+    return new DocValuesIterator() {
       
       int doc = -1;
       
@@ -371,6 +394,26 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
         }
         return doc = NO_MORE_DOCS;
       }
+
+      @Override
+      boolean advanceExact(int target) throws IOException {
+        this.doc = target;
+        in.seek(field.dataStartFilePointer + (9+field.pattern.length() + field.maxLength+2)*target);
+        SimpleTextUtil.readLine(in, scratch);
+        assert StringHelper.startsWith(scratch.get(), LENGTH);
+        int len;
+        try {
+          len = decoder.parse(new String(scratch.bytes(), LENGTH.length, scratch.length() - LENGTH.length, StandardCharsets.UTF_8)).intValue();
+        } catch (ParseException pe) {
+          throw new CorruptIndexException("failed to parse int length", in, pe);
+        }
+        // skip past bytes
+        byte bytes[] = new byte[len];
+        in.readBytes(bytes, 0, len);
+        SimpleTextUtil.readLine(in, scratch); // newline
+        SimpleTextUtil.readLine(in, scratch); // 'T' or 'F'
+        return scratch.byteAt(0) == (byte) 'T';
+      }
     };
   }
 
@@ -424,7 +467,20 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
         }
         return doc = NO_MORE_DOCS;
       }
-      
+
+      @Override
+      public boolean advanceExact(int target) throws IOException {
+        this.doc = target;
+        in.seek(field.dataStartFilePointer + field.numValues * (9 + field.pattern.length() + field.maxLength) + target * (1 + field.ordPattern.length()));
+        SimpleTextUtil.readLine(in, scratch);
+        try {
+          ord = (int) ordDecoder.parse(scratch.get().utf8ToString()).longValue()-1;
+        } catch (ParseException pe) {
+          throw new CorruptIndexException("failed to parse ord", in, pe);
+        }
+        return ord >= 0;
+      }
+
       @Override
       public int ordValue() {
         return ord;
@@ -488,6 +544,15 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
         return doc;
       }
       
+      @Override
+      public boolean advanceExact(int target) throws IOException {
+        if (binary.advanceExact(target)) {
+          setCurrentDoc();
+          return true;
+        }
+        return false;
+      }
+      
       long values[];
       int index;
       
@@ -570,6 +635,20 @@ class SimpleTextDocValuesReader extends DocValuesProducer {
       }
       
       @Override
+      public boolean advanceExact(int target) throws IOException {
+        in.seek(field.dataStartFilePointer + field.numValues * (9 + field.pattern.length() + field.maxLength) + target * (1 + field.ordPattern.length()));
+        SimpleTextUtil.readLine(in, scratch);
+        String ordList = scratch.get().utf8ToString().trim();
+        doc = target;
+        if (ordList.isEmpty() == false) {
+          currentOrds = ordList.split(",");
+          currentIndex = 0;
+          return true;
+        }
+        return false;
+      }
+      
+      @Override
       public long nextOrd() throws IOException {
         if (currentIndex == currentOrds.length) {
           return NO_MORE_ORDS;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesWriter.java
----------------------------------------------------------------------
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesWriter.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesWriter.java
index 2649ae6..8c6bdde 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesWriter.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextDocValuesWriter.java
@@ -342,6 +342,15 @@ class SimpleTextDocValuesWriter extends DocValuesConsumer {
             return doc;
           }
 
+          @Override
+          public boolean advanceExact(int target) throws IOException {
+            if (values.advanceExact(target)) {
+              setCurrentDoc();
+              return true;
+            }
+            return false;
+          }
+          
           final StringBuilder builder = new StringBuilder();
           BytesRef binaryValue;
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java b/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java
index e04d5b9..e61724f 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/DocValuesConsumer.java
@@ -228,6 +228,11 @@ public abstract class DocValuesConsumer implements Closeable {
                           }
 
                           @Override
+                          public boolean advanceExact(int target) throws IOException {
+                            throw new UnsupportedOperationException();
+                          }
+
+                          @Override
                           public long cost() {
                             return finalCost;
                           }
@@ -320,6 +325,11 @@ public abstract class DocValuesConsumer implements Closeable {
                          }
 
                          @Override
+                         public boolean advanceExact(int target) throws IOException {
+                           throw new UnsupportedOperationException();
+                         }
+
+                         @Override
                          public long cost() {
                            return finalCost;
                          }
@@ -417,6 +427,11 @@ public abstract class DocValuesConsumer implements Closeable {
                                 }
 
                                 @Override
+                                public boolean advanceExact(int target) throws IOException {
+                                  throw new UnsupportedOperationException();
+                                }
+
+                                @Override
                                 public int docValueCount() {
                                   return currentSub.values.docValueCount();
                                 }
@@ -575,6 +590,11 @@ public abstract class DocValuesConsumer implements Closeable {
                          }
 
                          @Override
+                         public boolean advanceExact(int target) throws IOException {
+                           throw new UnsupportedOperationException();
+                         }
+
+                         @Override
                          public long cost() {
                            return finalCost;
                          }
@@ -732,6 +752,11 @@ public abstract class DocValuesConsumer implements Closeable {
                             }
 
                             @Override
+                            public boolean advanceExact(int target) throws IOException {
+                              throw new UnsupportedOperationException();
+                            }
+
+                            @Override
                             public long nextOrd() throws IOException {
                               long subOrd = currentSub.values.nextOrd();
                               if (subOrd == NO_MORE_ORDS) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/codecs/NormsConsumer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/NormsConsumer.java b/lucene/core/src/java/org/apache/lucene/codecs/NormsConsumer.java
index 3a6ce22..51abb69 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/NormsConsumer.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/NormsConsumer.java
@@ -158,6 +158,11 @@ public abstract class NormsConsumer implements Closeable {
                           }
 
                           @Override
+                          public boolean advanceExact(int target) throws IOException {
+                            throw new UnsupportedOperationException();
+                          }
+
+                          @Override
                           public long cost() {
                             return 0;
                           }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/codecs/lucene70/IndexedDISI.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene70/IndexedDISI.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene70/IndexedDISI.java
index 3ea3141..24eaf7a 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene70/IndexedDISI.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene70/IndexedDISI.java
@@ -112,6 +112,9 @@ final class IndexedDISI extends DocIdSetIterator {
   private int doc = -1;
   private int index = -1;
 
+  // SPARSE variables
+  boolean exists;
+
   // DENSE variables
   private long word;
   private int wordIndex = -1;
@@ -129,7 +132,7 @@ final class IndexedDISI extends DocIdSetIterator {
   @Override
   public int advance(int target) throws IOException {
     final int targetBlock = target & 0xFFFF0000;
-    if (block != targetBlock) {
+    if (block < targetBlock) {
       advanceBlock(targetBlock);
     }
     if (block == targetBlock) {
@@ -138,7 +141,19 @@ final class IndexedDISI extends DocIdSetIterator {
       }
       readBlockHeader();
     }
-    return doc = method.readFirstDoc(this);
+    boolean found = method.advanceWithinBlock(this, block);
+    assert found;
+    return doc;
+  }
+
+  public boolean advanceExact(int target) throws IOException {
+    final int targetBlock = target & 0xFFFF0000;
+    if (block < targetBlock) {
+      advanceBlock(targetBlock);
+    }
+    boolean found = block == targetBlock && method.advanceExactWithinBlock(this, target);
+    this.doc = target;
+    return found;
   }
 
   private void advanceBlock(int targetBlock) throws IOException {
@@ -186,11 +201,6 @@ final class IndexedDISI extends DocIdSetIterator {
   enum Method {
     SPARSE {
       @Override
-      int readFirstDoc(IndexedDISI disi) throws IOException {
-        disi.index++;
-        return disi.block | Short.toUnsignedInt(disi.slice.readShort());
-      }
-      @Override
       boolean advanceWithinBlock(IndexedDISI disi, int target) throws IOException {
         final int targetInBlock = target & 0xFFFF;
         // TODO: binary search
@@ -199,23 +209,37 @@ final class IndexedDISI extends DocIdSetIterator {
           disi.index++;
           if (doc >= targetInBlock) {
             disi.doc = disi.block | doc;
+            disi.exists = true;
             return true;
           }
         }
         return false;
       }
-    },
-    DENSE {
       @Override
-      int readFirstDoc(IndexedDISI disi) throws IOException {
-        do {
-          disi.word = disi.slice.readLong();
-          disi.wordIndex++;
-        } while (disi.word == 0L);
-        disi.index = disi.numberOfOnes;
-        disi.numberOfOnes += Long.bitCount(disi.word);
-        return disi.block | (disi.wordIndex << 6) | Long.numberOfTrailingZeros(disi.word);
+      boolean advanceExactWithinBlock(IndexedDISI disi, int target) throws IOException {
+        final int targetInBlock = target & 0xFFFF;
+        // TODO: binary search
+        if (target == disi.doc) {
+          return disi.exists;
+        }
+        for (; disi.index < disi.nextBlockIndex;) {
+          int doc = Short.toUnsignedInt(disi.slice.readShort());
+          disi.index++;
+          if (doc >= targetInBlock) {
+            if (doc != targetInBlock) {
+              disi.index--;
+              disi.slice.seek(disi.slice.getFilePointer() - Short.BYTES);
+              break;
+            }
+            disi.exists = true;
+            return true;
+          }
+        }
+        disi.exists = false;
+        return false;
       }
+    },
+    DENSE {
       @Override
       boolean advanceWithinBlock(IndexedDISI disi, int target) throws IOException {
         final int targetInBlock = target & 0xFFFF;
@@ -244,26 +268,42 @@ final class IndexedDISI extends DocIdSetIterator {
         }
         return false;
       }
-    },
-    ALL {
       @Override
-      int readFirstDoc(IndexedDISI disi) {
-        return disi.block;
+      boolean advanceExactWithinBlock(IndexedDISI disi, int target) throws IOException {
+        final int targetInBlock = target & 0xFFFF;
+        final int targetWordIndex = targetInBlock >>> 6;
+        for (int i = disi.wordIndex + 1; i <= targetWordIndex; ++i) {
+          disi.word = disi.slice.readLong();
+          disi.numberOfOnes += Long.bitCount(disi.word);
+        }
+        disi.wordIndex = targetWordIndex;
+
+        long leftBits = disi.word >>> target;
+        disi.index = disi.numberOfOnes - Long.bitCount(leftBits);
+        return (leftBits & 1L) != 0;
       }
+    },
+    ALL {
       @Override
       boolean advanceWithinBlock(IndexedDISI disi, int target) throws IOException {
         disi.doc = target;
         disi.index = target - disi.gap;
         return true;
       }
+      @Override
+      boolean advanceExactWithinBlock(IndexedDISI disi, int target) throws IOException {
+        disi.index = target - disi.gap;
+        return true;
+      }
     };
 
-    /** Read the first document of the current block. */
-    abstract int readFirstDoc(IndexedDISI disi) throws IOException;
-
     /** Advance to the first doc from the block that is equal to or greater than {@code target}.
      *  Return true if there is such a doc and false otherwise. */
     abstract boolean advanceWithinBlock(IndexedDISI disi, int target) throws IOException;
+
+    /** Advance the iterator exactly to the position corresponding to the given {@code target}
+     * and return whether this document exists. */
+    abstract boolean advanceExactWithinBlock(IndexedDISI disi, int target) throws IOException;
   }
 
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesProducer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesProducer.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesProducer.java
index 637c8ee..19815ba 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesProducer.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70DocValuesProducer.java
@@ -375,6 +375,12 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) {
+          doc = target;
+          return true;
+        }
+
+        @Override
         public long cost() {
           return maxDoc;
         }
@@ -392,6 +398,11 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          return disi.advanceExact(target);
+        }
+
+        @Override
         public int nextDoc() throws IOException {
           return disi.nextDoc();
         }
@@ -521,6 +532,12 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          doc = target;
+          return true;
+        }
+
+        @Override
         public BytesRef binaryValue() throws IOException {
           return bytesRefs.get(doc);
         }
@@ -551,6 +568,11 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          return disi.advanceExact(target);
+        }
+
+        @Override
         public BytesRef binaryValue() throws IOException {
           return bytesRefs.get(disi.index());
         }
@@ -616,6 +638,12 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) {
+          doc = target;
+          return true;
+        }
+
+        @Override
         public int ordValue() {
           return (int) ords.get(doc);
         }
@@ -646,6 +674,11 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          return disi.advanceExact(target);
+        }
+
+        @Override
         public int ordValue() {
           return (int) ords.get(disi.index());
         }
@@ -960,6 +993,15 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          start = addresses.get(target);
+          end = addresses.get(target + 1L);
+          count = (int) (end - start);
+          doc = target;
+          return true;
+        }
+
+        @Override
         public long nextValue() throws IOException {
           return values.get(start++);
         }
@@ -1001,6 +1043,12 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          set = false;
+          return disi.advanceExact(target);
+        }
+
+        @Override
         public long nextValue() throws IOException {
           set();
           return values.get(start++);
@@ -1073,6 +1121,14 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          start = addresses.get(target);
+          end = addresses.get(target + 1L);
+          doc = target;
+          return true;
+        }
+
+        @Override
         public long nextOrd() throws IOException {
           if (start == end) {
             return NO_MORE_ORDS;
@@ -1113,6 +1169,12 @@ final class Lucene70DocValuesProducer extends DocValuesProducer implements Close
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          set = false;
+          return disi.advanceExact(target);
+        }
+
+        @Override
         public long nextOrd() throws IOException {
           if (set == false) {
             final int index = disi.index();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70NormsProducer.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70NormsProducer.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70NormsProducer.java
index e3f6f79..c97f1c3 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70NormsProducer.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene70/Lucene70NormsProducer.java
@@ -160,6 +160,12 @@ final class Lucene70NormsProducer extends NormsProducer {
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          this.doc = target;
+          return true;
+        }
+
+        @Override
         public long cost() {
           return maxDoc;
         }
@@ -177,6 +183,11 @@ final class Lucene70NormsProducer extends NormsProducer {
         }
 
         @Override
+        public boolean advanceExact(int target) throws IOException {
+          return disi.advanceExact(target);
+        }
+
+        @Override
         public int nextDoc() throws IOException {
           return disi.nextDoc();
         }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/BinaryDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/BinaryDocValues.java b/lucene/core/src/java/org/apache/lucene/index/BinaryDocValues.java
index 66397e4..6d23cf1 100644
--- a/lucene/core/src/java/org/apache/lucene/index/BinaryDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/BinaryDocValues.java
@@ -19,13 +19,12 @@ package org.apache.lucene.index;
 
 import java.io.IOException;
 
-import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.util.BytesRef;
 
 /**
  * A per-document numeric value.
  */
-public abstract class BinaryDocValues extends DocIdSetIterator {
+public abstract class BinaryDocValues extends DocValuesIterator {
   
   /** Sole constructor. (For invocation by subclass 
    *  constructors, typically implicit.) */
@@ -33,6 +32,8 @@ public abstract class BinaryDocValues extends DocIdSetIterator {
 
   /**
    * Returns the binary value for the current document ID.
+   * It is illegal to call this method after {@link #advanceExact(int)}
+   * returned {@code false}.
    * @return binary value
    */
   public abstract BytesRef binaryValue() throws IOException;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/BinaryDocValuesWriter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/BinaryDocValuesWriter.java b/lucene/core/src/java/org/apache/lucene/index/BinaryDocValuesWriter.java
index ff2e67c..9611a03 100644
--- a/lucene/core/src/java/org/apache/lucene/index/BinaryDocValuesWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/BinaryDocValuesWriter.java
@@ -154,6 +154,11 @@ class BinaryDocValuesWriter extends DocValuesWriter {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public long cost() {
       return docsWithField.cost();
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java b/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
index aec7537..f3d3562 100644
--- a/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
+++ b/lucene/core/src/java/org/apache/lucene/index/CheckIndex.java
@@ -2062,13 +2062,83 @@ public final class CheckIndex implements Closeable {
     return status;
   }
 
+  @FunctionalInterface
+  private static interface DocValuesIteratorSupplier {
+    DocValuesIterator get(FieldInfo fi) throws IOException;
+  }
+
+  private static void checkDVIterator(FieldInfo fi, int maxDoc, DocValuesIteratorSupplier producer) throws IOException {
+    String field = fi.name;
+
+    // Check advance
+    DocValuesIterator it1 = producer.get(fi);
+    DocValuesIterator it2 = producer.get(fi);
+    int i = 0;
+    for (int doc = it1.nextDoc(); ; doc = it1.nextDoc()) {
+
+      if (i++ % 10 == 1) {
+        int doc2 = it2.advance(doc - 1);
+        if (doc2 < doc - 1) {
+          throw new RuntimeException("dv iterator field=" + field + ": doc=" + (doc-1) + " went backwords (got: " + doc2 + ")");
+        }
+        if (doc2 == doc - 1) {
+          doc2 = it2.nextDoc();
+        }
+        if (doc2 != doc) {
+          throw new RuntimeException("dv iterator field=" + field + ": doc=" + doc + " was not found through advance() (got: " + doc2 + ")");
+        }
+        if (it2.docID() != doc) {
+          throw new RuntimeException("dv iterator field=" + field + ": doc=" + doc + " reports wrong doc ID (got: " + it2.docID() + ")");
+        }
+      }
+
+      if (doc == NO_MORE_DOCS) {
+        break;
+      }
+    }
+
+    // Check advanceExact
+    it1 = producer.get(fi);
+    it2 = producer.get(fi);
+    i = 0;
+    int lastDoc = -1;
+    for (int doc = it1.nextDoc(); doc != NO_MORE_DOCS ; doc = it1.nextDoc()) {
+
+      if (i++ % 13 == 1) {
+        boolean found = it2.advanceExact(doc - 1);
+        if ((doc - 1 == lastDoc) != found) {
+          throw new RuntimeException("dv iterator field=" + field + ": doc=" + (doc-1) + " disagrees about whether document exists (got: " + found + ")");
+        }
+        if (it2.docID() != doc - 1) {
+          throw new RuntimeException("dv iterator field=" + field + ": doc=" + (doc-1) + " reports wrong doc ID (got: " + it2.docID() + ")");
+        }
+        
+        boolean found2 = it2.advanceExact(doc - 1);
+        if (found != found2) {
+          throw new RuntimeException("dv iterator field=" + field + ": doc=" + (doc-1) + " has unstable advanceExact");
+        }
+
+        if (i % 1 == 0) {
+          int doc2 = it2.nextDoc();
+          if (doc != doc2) {
+            throw new RuntimeException("dv iterator field=" + field + ": doc=" + doc + " was not found through advance() (got: " + doc2 + ")");
+          }
+          if (it2.docID() != doc) {
+            throw new RuntimeException("dv iterator field=" + field + ": doc=" + doc + " reports wrong doc ID (got: " + it2.docID() + ")");
+          }
+        }
+      }
+
+      lastDoc = doc;
+    }
+  }
+
   private static void checkBinaryDocValues(String fieldName, int maxDoc, BinaryDocValues bdv) throws IOException {
     int doc;
     if (bdv.docID() != -1) {
       throw new RuntimeException("binary dv iterator for field: " + fieldName + " should start at docID=-1, but got " + bdv.docID());
     }
     // TODO: we could add stats to DVs, e.g. total doc count w/ a value for this field
-    // TODO: check advance too
     while ((doc = bdv.nextDoc()) != NO_MORE_DOCS) {
       BytesRef value = bdv.binaryValue();
       value.isValid();
@@ -2083,7 +2153,6 @@ public final class CheckIndex implements Closeable {
     FixedBitSet seenOrds = new FixedBitSet(dv.getValueCount());
     int maxOrd2 = -1;
     int docID;
-    // TODO: check advance too
     while ((docID = dv.nextDoc()) != NO_MORE_DOCS) {
       int ord = dv.ordValue();
       if (ord == -1) {
@@ -2119,7 +2188,6 @@ public final class CheckIndex implements Closeable {
     LongBitSet seenOrds = new LongBitSet(dv.getValueCount());
     long maxOrd2 = -1;
     int docID;
-    // TODO: check advance too
     while ((docID = dv.nextDoc()) != NO_MORE_DOCS) {
       long lastOrd = -1;
       long ord;
@@ -2164,7 +2232,6 @@ public final class CheckIndex implements Closeable {
     if (ndv.docID() != -1) {
       throw new RuntimeException("dv iterator for field: " + fieldName + " should start at docID=-1, but got " + ndv.docID());
     }
-    // TODO: check advance too
     while (true) {
       int docID = ndv.nextDoc();
       if (docID == NO_MORE_DOCS) {
@@ -2191,7 +2258,6 @@ public final class CheckIndex implements Closeable {
       throw new RuntimeException("dv iterator for field: " + fieldName + " should start at docID=-1, but got " + ndv.docID());
     }
     // TODO: we could add stats to DVs, e.g. total doc count w/ a value for this field
-    // TODO: check advance too
     while ((doc = ndv.nextDoc()) != NO_MORE_DOCS) {
       ndv.longValue();
     }
@@ -2201,23 +2267,28 @@ public final class CheckIndex implements Closeable {
     switch(fi.getDocValuesType()) {
       case SORTED:
         status.totalSortedFields++;
+        checkDVIterator(fi, maxDoc, dvReader::getSorted);
         checkBinaryDocValues(fi.name, maxDoc, dvReader.getSorted(fi));
         checkSortedDocValues(fi.name, maxDoc, dvReader.getSorted(fi));
         break;
       case SORTED_NUMERIC:
         status.totalSortedNumericFields++;
+        checkDVIterator(fi, maxDoc, dvReader::getSortedNumeric);
         checkSortedNumericDocValues(fi.name, maxDoc, dvReader.getSortedNumeric(fi));
         break;
       case SORTED_SET:
         status.totalSortedSetFields++;
+        checkDVIterator(fi, maxDoc, dvReader::getSortedSet);
         checkSortedSetDocValues(fi.name, maxDoc, dvReader.getSortedSet(fi));
         break;
       case BINARY:
         status.totalBinaryFields++;
+        checkDVIterator(fi, maxDoc, dvReader::getBinary);
         checkBinaryDocValues(fi.name, maxDoc, dvReader.getBinary(fi));
         break;
       case NUMERIC:
         status.totalNumericFields++;
+        checkDVIterator(fi, maxDoc, dvReader::getNumeric);
         checkNumericDocValues(fi.name, dvReader.getNumeric(fi));
         break;
       default:

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/DocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/DocValues.java b/lucene/core/src/java/org/apache/lucene/index/DocValues.java
index 3377f45..b25d484 100644
--- a/lucene/core/src/java/org/apache/lucene/index/DocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/DocValues.java
@@ -35,26 +35,27 @@ public final class DocValues {
    */
   public static final BinaryDocValues emptyBinary() {
     return new BinaryDocValues() {
-      private boolean exhausted = false;
+      private int doc = -1;
       
       @Override
       public int advance(int target) {
-        assert exhausted == false;
-        assert target >= 0;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
+      }
+      
+      @Override
+      public boolean advanceExact(int target) throws IOException {
+        doc = target;
+        return true;
       }
       
       @Override
       public int docID() {
-        return exhausted ? NO_MORE_DOCS : -1;
+        return doc;
       }
       
       @Override
       public int nextDoc() {
-        assert exhausted == false;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
       }
       
       @Override
@@ -75,26 +76,27 @@ public final class DocValues {
    */
   public static final NumericDocValues emptyNumeric() {
     return new NumericDocValues() {
-      private boolean exhausted = false;
+      private int doc = -1;
       
       @Override
       public int advance(int target) {
-        assert exhausted == false;
-        assert target >= 0;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
+      }
+      
+      @Override
+      public boolean advanceExact(int target) throws IOException {
+        doc = target;
+        return false;
       }
       
       @Override
       public int docID() {
-        return exhausted ? NO_MORE_DOCS : -1;
+        return doc;
       }
       
       @Override
       public int nextDoc() {
-        assert exhausted == false;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
       }
       
       @Override
@@ -140,26 +142,27 @@ public final class DocValues {
     final BytesRef empty = new BytesRef();
     return new SortedDocValues() {
       
-      private boolean exhausted = false;
+      private int doc = -1;
       
       @Override
       public int advance(int target) {
-        assert exhausted == false;
-        assert target >= 0;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
+      }
+      
+      @Override
+      public boolean advanceExact(int target) throws IOException {
+        doc = target;
+        return false;
       }
       
       @Override
       public int docID() {
-        return exhausted ? NO_MORE_DOCS : -1;
+        return doc;
       }
       
       @Override
       public int nextDoc() {
-        assert exhausted == false;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
       }
       
       @Override
@@ -191,26 +194,27 @@ public final class DocValues {
   public static final SortedNumericDocValues emptySortedNumeric(int maxDoc) {
     return new SortedNumericDocValues() {
       
-      private boolean exhausted = false;
+      private int doc = -1;
       
       @Override
       public int advance(int target) {
-        assert exhausted == false;
-        assert target >= 0;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
+      }
+      
+      @Override
+      public boolean advanceExact(int target) throws IOException {
+        doc = target;
+        return false;
       }
       
       @Override
       public int docID() {
-        return exhausted ? NO_MORE_DOCS : -1;
+        return doc;
       }
       
       @Override
       public int nextDoc() {
-        assert exhausted == false;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
       }
       
       @Override
@@ -237,26 +241,27 @@ public final class DocValues {
     final BytesRef empty = new BytesRef();
     return new SortedSetDocValues() {
       
-      private boolean exhausted = false;
+      private int doc = -1;
       
       @Override
       public int advance(int target) {
-        assert exhausted == false;
-        assert target >= 0;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
+      }
+      
+      @Override
+      public boolean advanceExact(int target) throws IOException {
+        doc = target;
+        return false;
       }
       
       @Override
       public int docID() {
-        return exhausted ? NO_MORE_DOCS : -1;
+        return doc;
       }
       
       @Override
       public int nextDoc() {
-        assert exhausted == false;
-        exhausted = true;
-        return NO_MORE_DOCS;
+        return doc = NO_MORE_DOCS;
       }
       
       @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/DocValuesIterator.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/DocValuesIterator.java b/lucene/core/src/java/org/apache/lucene/index/DocValuesIterator.java
new file mode 100644
index 0000000..d53e26a
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/index/DocValuesIterator.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.index;
+
+import java.io.IOException;
+
+import org.apache.lucene.search.DocIdSetIterator;
+
+abstract class DocValuesIterator extends DocIdSetIterator {
+
+  /** Advance the iterator to exactly {@code target} and return whether
+   *  {@code target} has a value.
+   *  {@code target} must be greater than or equal to the current
+   *  {@link #docID() doc ID} and must be a valid doc ID, ie. &ge; 0 and
+   *  &lt; {@code maxDoc}.
+   *  After this method returns, {@link #docID()} retuns {@code target}. */
+  public abstract boolean advanceExact(int target) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/FilterBinaryDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/FilterBinaryDocValues.java b/lucene/core/src/java/org/apache/lucene/index/FilterBinaryDocValues.java
index 66c4323..650ad04 100644
--- a/lucene/core/src/java/org/apache/lucene/index/FilterBinaryDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/FilterBinaryDocValues.java
@@ -50,6 +50,11 @@ public abstract class FilterBinaryDocValues extends BinaryDocValues {
   }
   
   @Override
+  public boolean advanceExact(int target) throws IOException {
+    return in.advanceExact(target);
+  }
+  
+  @Override
   public long cost() {
     return in.cost();
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/FilterNumericDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/FilterNumericDocValues.java b/lucene/core/src/java/org/apache/lucene/index/FilterNumericDocValues.java
index 0058fa6..bd00cf2 100644
--- a/lucene/core/src/java/org/apache/lucene/index/FilterNumericDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/FilterNumericDocValues.java
@@ -48,6 +48,11 @@ public abstract class FilterNumericDocValues extends NumericDocValues {
   }
   
   @Override
+  public boolean advanceExact(int target) throws IOException {
+    return in.advanceExact(target);
+  }
+  
+  @Override
   public long cost() {
     return in.cost();
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/LegacyBinaryDocValuesWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/LegacyBinaryDocValuesWrapper.java b/lucene/core/src/java/org/apache/lucene/index/LegacyBinaryDocValuesWrapper.java
index 13bc207..919b1ff 100644
--- a/lucene/core/src/java/org/apache/lucene/index/LegacyBinaryDocValuesWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/index/LegacyBinaryDocValuesWrapper.java
@@ -17,6 +17,8 @@
 
 package org.apache.lucene.index;
 
+import java.io.IOException;
+
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.BytesRef;
 
@@ -71,6 +73,12 @@ public final class LegacyBinaryDocValuesWrapper extends BinaryDocValues {
   }
 
   @Override
+  public boolean advanceExact(int target) throws IOException {
+    docID = target;
+    return docsWithField.get(target);
+  }
+
+  @Override
   public long cost() {
     return 0;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/LegacyNumericDocValuesWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/LegacyNumericDocValuesWrapper.java b/lucene/core/src/java/org/apache/lucene/index/LegacyNumericDocValuesWrapper.java
index a72efe8..aaccc05 100644
--- a/lucene/core/src/java/org/apache/lucene/index/LegacyNumericDocValuesWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/index/LegacyNumericDocValuesWrapper.java
@@ -17,6 +17,8 @@
 
 package org.apache.lucene.index;
 
+import java.io.IOException;
+
 import org.apache.lucene.util.Bits;
 
 /**
@@ -70,6 +72,13 @@ public final class LegacyNumericDocValuesWrapper extends NumericDocValues {
   }
 
   @Override
+  public boolean advanceExact(int target) throws IOException {
+    docID = target;
+    value = values.get(docID);
+    return value != 0 || docsWithField.get(docID);
+  }
+
+  @Override
   public long cost() {
     // TODO
     return 0;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/LegacySortedDocValuesWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/LegacySortedDocValuesWrapper.java b/lucene/core/src/java/org/apache/lucene/index/LegacySortedDocValuesWrapper.java
index d8ef2f4..ecc114b 100644
--- a/lucene/core/src/java/org/apache/lucene/index/LegacySortedDocValuesWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/index/LegacySortedDocValuesWrapper.java
@@ -17,6 +17,8 @@
 
 package org.apache.lucene.index;
 
+import java.io.IOException;
+
 import org.apache.lucene.util.BytesRef;
 
 /**
@@ -71,6 +73,13 @@ public final class LegacySortedDocValuesWrapper extends SortedDocValues {
   }
 
   @Override
+  public boolean advanceExact(int target) throws IOException {
+    docID = target;
+    ord = values.getOrd(docID);
+    return ord != -1;
+  }
+
+  @Override
   public long cost() {
     return 0;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/LegacySortedNumericDocValuesWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/LegacySortedNumericDocValuesWrapper.java b/lucene/core/src/java/org/apache/lucene/index/LegacySortedNumericDocValuesWrapper.java
index bf3c6cd..cfb61e3 100644
--- a/lucene/core/src/java/org/apache/lucene/index/LegacySortedNumericDocValuesWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/index/LegacySortedNumericDocValuesWrapper.java
@@ -17,6 +17,8 @@
 
 package org.apache.lucene.index;
 
+import java.io.IOException;
+
 /**
  * Wraps a {@link LegacySortedNumericDocValues} into a {@link SortedNumericDocValues}.
  *
@@ -72,6 +74,13 @@ public final class LegacySortedNumericDocValuesWrapper extends SortedNumericDocV
   }
 
   @Override
+  public boolean advanceExact(int target) throws IOException {
+    docID = target;
+    values.setDocument(docID);
+    return values.count() != 0;
+  }
+
+  @Override
   public long cost() {
     return 0;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/LegacySortedSetDocValuesWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/LegacySortedSetDocValuesWrapper.java b/lucene/core/src/java/org/apache/lucene/index/LegacySortedSetDocValuesWrapper.java
index 45d12d2..0e96e02 100644
--- a/lucene/core/src/java/org/apache/lucene/index/LegacySortedSetDocValuesWrapper.java
+++ b/lucene/core/src/java/org/apache/lucene/index/LegacySortedSetDocValuesWrapper.java
@@ -17,6 +17,8 @@
 
 package org.apache.lucene.index;
 
+import java.io.IOException;
+
 import org.apache.lucene.util.BytesRef;
 
 /**
@@ -72,6 +74,14 @@ public final class LegacySortedSetDocValuesWrapper extends SortedSetDocValues {
   }
 
   @Override
+  public boolean advanceExact(int target) throws IOException {
+    docID = target;
+    values.setDocument(docID);
+    ord = values.nextOrd();
+    return ord != NO_MORE_ORDS;
+  }
+
+  @Override
   public long cost() {
     return 0;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/MultiDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiDocValues.java b/lucene/core/src/java/org/apache/lucene/index/MultiDocValues.java
index aeb49c5..51d684d 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MultiDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MultiDocValues.java
@@ -139,6 +139,27 @@ public class MultiDocValues {
       }
 
       @Override
+      public boolean advanceExact(int targetDocID) throws IOException {
+        if (targetDocID <= docID) {
+          throw new IllegalArgumentException("can only advance beyond current document: on docID=" + docID + " but targetDocID=" + targetDocID);
+        }
+        int readerIndex = ReaderUtil.subIndex(targetDocID, leaves);
+        if (readerIndex >= nextLeaf) {
+          if (readerIndex == leaves.size()) {
+            throw new IllegalArgumentException("Out of range: " + targetDocID);
+          }
+          currentLeaf = leaves.get(readerIndex);
+          currentValues = currentLeaf.reader().getNormValues(field);
+          nextLeaf = readerIndex+1;
+        }
+        docID = targetDocID;
+        if (currentValues == null) {
+          return false;
+        }
+        return currentValues.advanceExact(targetDocID - currentLeaf.docBase);
+      }
+
+      @Override
       public long longValue() throws IOException {
         return currentValues.longValue();
       }
@@ -244,6 +265,26 @@ public class MultiDocValues {
       }
 
       @Override
+      public boolean advanceExact(int targetDocID) throws IOException {
+        if (targetDocID <= docID) {
+          throw new IllegalArgumentException("can only advance beyond current document: on docID=" + docID + " but targetDocID=" + targetDocID);
+        }
+        int readerIndex = ReaderUtil.subIndex(targetDocID, leaves);
+        if (readerIndex >= nextLeaf) {
+          if (readerIndex == leaves.size()) {
+            throw new IllegalArgumentException("Out of range: " + targetDocID);
+          }
+          currentLeaf = leaves.get(readerIndex);
+          currentValues = currentLeaf.reader().getNumericDocValues(field);
+          nextLeaf = readerIndex+1;
+        }
+        docID = targetDocID;
+        if (currentValues == null) {
+          return false;
+        }
+        return currentValues.advanceExact(targetDocID - currentLeaf.docBase);
+      }
+      @Override
       public long longValue() throws IOException {
         return currentValues.longValue();
       }
@@ -348,6 +389,27 @@ public class MultiDocValues {
       }
 
       @Override
+      public boolean advanceExact(int targetDocID) throws IOException {
+        if (targetDocID <= docID) {
+          throw new IllegalArgumentException("can only advance beyond current document: on docID=" + docID + " but targetDocID=" + targetDocID);
+        }
+        int readerIndex = ReaderUtil.subIndex(targetDocID, leaves);
+        if (readerIndex >= nextLeaf) {
+          if (readerIndex == leaves.size()) {
+            throw new IllegalArgumentException("Out of range: " + targetDocID);
+          }
+          currentLeaf = leaves.get(readerIndex);
+          currentValues = currentLeaf.reader().getBinaryDocValues(field);
+          nextLeaf = readerIndex+1;
+        }
+        docID = targetDocID;
+        if (currentValues == null) {
+          return false;
+        }
+        return currentValues.advanceExact(targetDocID - currentLeaf.docBase);
+      }
+
+      @Override
       public BytesRef binaryValue() throws IOException {
         return currentValues.binaryValue();
       }
@@ -462,6 +524,27 @@ public class MultiDocValues {
       }
 
       @Override
+      public boolean advanceExact(int targetDocID) throws IOException {
+        if (targetDocID <= docID) {
+          throw new IllegalArgumentException("can only advance beyond current document: on docID=" + docID + " but targetDocID=" + targetDocID);
+        }
+        int readerIndex = ReaderUtil.subIndex(targetDocID, leaves);
+        if (readerIndex >= nextLeaf) {
+          if (readerIndex == leaves.size()) {
+            throw new IllegalArgumentException("Out of range: " + targetDocID);
+          }
+          currentLeaf = leaves.get(readerIndex);
+          currentValues = values[readerIndex];
+          nextLeaf = readerIndex+1;
+        }
+        docID = targetDocID;
+        if (currentValues == null) {
+          return false;
+        }
+        return currentValues.advanceExact(targetDocID - currentLeaf.docBase);
+      }
+
+      @Override
       public long cost() {
         return finalTotalCost;
       }
@@ -923,6 +1006,27 @@ public class MultiDocValues {
     }
     
     @Override
+    public boolean advanceExact(int targetDocID) throws IOException {
+      if (targetDocID <= docID) {
+        throw new IllegalArgumentException("can only advance beyond current document: on docID=" + docID + " but targetDocID=" + targetDocID);
+      }
+      int readerIndex = ReaderUtil.subIndex(targetDocID, docStarts);
+      if (readerIndex >= nextLeaf) {
+        if (readerIndex == values.length) {
+          throw new IllegalArgumentException("Out of range: " + targetDocID);
+        }
+        currentDocStart = docStarts[readerIndex];
+        currentValues = values[readerIndex];
+        nextLeaf = readerIndex+1;
+      }
+      docID = targetDocID;
+      if (currentValues == null) {
+        return false;
+      }
+      return currentValues.advanceExact(targetDocID - currentDocStart);
+    }
+    
+    @Override
     public int ordValue() {
       return (int) mapping.getGlobalOrds(nextLeaf-1).get(currentValues.ordValue());
     }
@@ -1029,6 +1133,27 @@ public class MultiDocValues {
     }
 
     @Override
+    public boolean advanceExact(int targetDocID) throws IOException {
+      if (targetDocID < docID) {
+        throw new IllegalArgumentException("can only advance beyond current document: on docID=" + docID + " but targetDocID=" + targetDocID);
+      }
+      int readerIndex = ReaderUtil.subIndex(targetDocID, docStarts);
+      if (readerIndex >= nextLeaf) {
+        if (readerIndex == values.length) {
+          throw new IllegalArgumentException("Out of range: " + targetDocID);
+        }
+        currentDocStart = docStarts[readerIndex];
+        currentValues = values[readerIndex];
+        nextLeaf = readerIndex+1;
+      }
+      docID = targetDocID;
+      if (currentValues == null) {
+        return false;
+      }
+      return currentValues.advanceExact(targetDocID - currentDocStart);
+    }
+
+    @Override
     public long nextOrd() throws IOException {
       long segmentOrd = currentValues.nextOrd();
       if (segmentOrd == NO_MORE_ORDS) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/NormValuesWriter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/NormValuesWriter.java b/lucene/core/src/java/org/apache/lucene/index/NormValuesWriter.java
index 46b8c1c..b0d05e4 100644
--- a/lucene/core/src/java/org/apache/lucene/index/NormValuesWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/NormValuesWriter.java
@@ -133,6 +133,11 @@ class NormValuesWriter {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public long cost() {
       return docsWithField.cost();
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/NumericDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/NumericDocValues.java b/lucene/core/src/java/org/apache/lucene/index/NumericDocValues.java
index 5ae2e47..29b9918 100644
--- a/lucene/core/src/java/org/apache/lucene/index/NumericDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/NumericDocValues.java
@@ -19,12 +19,10 @@ package org.apache.lucene.index;
 
 import java.io.IOException;
 
-import org.apache.lucene.search.DocIdSetIterator;
-
 /**
  * A per-document numeric value.
  */
-public abstract class NumericDocValues extends DocIdSetIterator {
+public abstract class NumericDocValues extends DocValuesIterator {
   
   /** Sole constructor. (For invocation by subclass 
    *  constructors, typically implicit.) */
@@ -32,7 +30,10 @@ public abstract class NumericDocValues extends DocIdSetIterator {
 
   /**
    * Returns the numeric value for the current document ID.
+   * It is illegal to call this method after {@link #advanceExact(int)}
+   * returned {@code false}.
    * @return numeric value
    */
   public abstract long longValue() throws IOException;
+
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/NumericDocValuesWriter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/NumericDocValuesWriter.java b/lucene/core/src/java/org/apache/lucene/index/NumericDocValuesWriter.java
index adfa706..24a7010 100644
--- a/lucene/core/src/java/org/apache/lucene/index/NumericDocValuesWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/NumericDocValuesWriter.java
@@ -119,6 +119,11 @@ class NumericDocValuesWriter extends DocValuesWriter {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public long cost() {
       return docsWithField.cost();
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/ReadersAndUpdates.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/ReadersAndUpdates.java b/lucene/core/src/java/org/apache/lucene/index/ReadersAndUpdates.java
index 894c81a..3cd465c 100644
--- a/lucene/core/src/java/org/apache/lucene/index/ReadersAndUpdates.java
+++ b/lucene/core/src/java/org/apache/lucene/index/ReadersAndUpdates.java
@@ -360,6 +360,11 @@ class ReadersAndUpdates {
                 }
 
                 @Override
+                public boolean advanceExact(int target) throws IOException {
+                  throw new UnsupportedOperationException();
+                }
+
+                @Override
                 public long cost() {
                   // TODO
                   return 0;
@@ -462,6 +467,11 @@ class ReadersAndUpdates {
                 }
 
                 @Override
+                public boolean advanceExact(int target) throws IOException {
+                  throw new UnsupportedOperationException();
+                }
+
+                @Override
                 public long cost() {
                   return currentValues.cost();
                 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SingletonSortedNumericDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SingletonSortedNumericDocValues.java b/lucene/core/src/java/org/apache/lucene/index/SingletonSortedNumericDocValues.java
index d95f0c0..5dbdec8 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SingletonSortedNumericDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SingletonSortedNumericDocValues.java
@@ -27,7 +27,6 @@ import java.io.IOException;
  */
 final class SingletonSortedNumericDocValues extends SortedNumericDocValues {
   private final NumericDocValues in;
-  private long value;
   
   public SingletonSortedNumericDocValues(NumericDocValues in) {
     if (in.docID() != -1) {
@@ -51,30 +50,27 @@ final class SingletonSortedNumericDocValues extends SortedNumericDocValues {
 
   @Override
   public int nextDoc() throws IOException {
-    int docID = in.nextDoc();
-    if (docID != NO_MORE_DOCS) {
-      value = in.longValue();
-    }
-    return docID;
+    return in.nextDoc();
   }
   
   @Override
   public int advance(int target) throws IOException {
-    int docID = in.advance(target);
-    if (docID != NO_MORE_DOCS) {
-      value = in.longValue();
-    }
-    return docID;
+    return in.advance(target);
+  }
+
+  @Override
+  public boolean advanceExact(int target) throws IOException {
+    return in.advanceExact(target);
   }
-      
+
   @Override
   public long cost() {
     return in.cost();
   }
   
   @Override
-  public long nextValue() {
-    return value;
+  public long nextValue() throws IOException {
+    return in.longValue();
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SingletonSortedSetDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SingletonSortedSetDocValues.java b/lucene/core/src/java/org/apache/lucene/index/SingletonSortedSetDocValues.java
index cc7360e..f16cdf1 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SingletonSortedSetDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SingletonSortedSetDocValues.java
@@ -29,7 +29,6 @@ import org.apache.lucene.util.BytesRef;
  */
 final class SingletonSortedSetDocValues extends SortedSetDocValues {
   private final SortedDocValues in;
-  private long currentOrd;
   private long ord;
   
   /** Creates a multi-valued view over the provided SortedDocValues */
@@ -55,8 +54,8 @@ final class SingletonSortedSetDocValues extends SortedSetDocValues {
 
   @Override
   public long nextOrd() {
-    long v = currentOrd;
-    currentOrd = NO_MORE_ORDS;
+    long v = ord;
+    ord = NO_MORE_ORDS;
     return v;
   }
 
@@ -64,7 +63,7 @@ final class SingletonSortedSetDocValues extends SortedSetDocValues {
   public int nextDoc() throws IOException {
     int docID = in.nextDoc();
     if (docID != NO_MORE_DOCS) {
-      currentOrd = ord = in.ordValue();
+      ord = in.ordValue();
     }
     return docID;
   }
@@ -73,12 +72,21 @@ final class SingletonSortedSetDocValues extends SortedSetDocValues {
   public int advance(int target) throws IOException {
     int docID = in.advance(target);
     if (docID != NO_MORE_DOCS) {
-      currentOrd = ord = in.ordValue();
+      ord = in.ordValue();
     }
     return docID;
   }
 
   @Override
+  public boolean advanceExact(int target) throws IOException {
+    if (in.advanceExact(target)) {
+      ord = in.ordValue();
+      return true;
+    }
+    return false;
+  }
+
+  @Override
   public BytesRef lookupOrd(long ord) throws IOException {
     // cast is ok: single-valued cannot exceed Integer.MAX_VALUE
     return in.lookupOrd((int) ord);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SortedDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortedDocValues.java b/lucene/core/src/java/org/apache/lucene/index/SortedDocValues.java
index 7ff084f..e2d7dfd 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortedDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortedDocValues.java
@@ -40,6 +40,8 @@ public abstract class SortedDocValues extends BinaryDocValues {
 
   /**
    * Returns the ordinal for the current docID.
+   * It is illegal to call this method after {@link #advanceExact(int)}
+   * returned {@code false}.
    * @return ordinal for the document: this is dense, starts at 0, then
    *         increments by 1 for the next value in sorted order.
    */
@@ -107,4 +109,5 @@ public abstract class SortedDocValues extends BinaryDocValues {
   public TermsEnum termsEnum() throws IOException {
     return new SortedDocValuesTermsEnum(this);
   }
+
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesWriter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesWriter.java b/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesWriter.java
index 885ee89..7e43e49 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortedDocValuesWriter.java
@@ -166,6 +166,11 @@ class SortedDocValuesWriter extends DocValuesWriter {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public long cost() {
       return docsWithField.cost();
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValues.java b/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValues.java
index 8c11495..a76b46d 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValues.java
@@ -18,14 +18,12 @@ package org.apache.lucene.index;
 
 import java.io.IOException;
 
-import org.apache.lucene.search.DocIdSetIterator;
-
 
 /**
  * A list of per-document numeric values, sorted 
  * according to {@link Long#compare(long, long)}.
  */
-public abstract class SortedNumericDocValues extends DocIdSetIterator {
+public abstract class SortedNumericDocValues extends DocValuesIterator {
   
   /** Sole constructor. (For invocation by subclass 
    *  constructors, typically implicit.) */
@@ -40,6 +38,8 @@ public abstract class SortedNumericDocValues extends DocIdSetIterator {
   /** 
    * Retrieves the number of values for the current document.  This must always
    * be greater than zero.
+   * It is illegal to call this method after {@link #advanceExact(int)}
+   * returned {@code false}.
    */
   public abstract int docValueCount();
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValuesWriter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValuesWriter.java b/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValuesWriter.java
index e154547..3f50623 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValuesWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortedNumericDocValuesWriter.java
@@ -155,6 +155,11 @@ class SortedNumericDocValuesWriter extends DocValuesWriter {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public int docValueCount() {
       return valueCount;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValues.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValues.java b/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValues.java
index 439843b..6d02c25 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValues.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValues.java
@@ -19,7 +19,6 @@ package org.apache.lucene.index;
 
 import java.io.IOException;
 
-import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.util.BytesRef;
 
 /**
@@ -30,7 +29,7 @@ import org.apache.lucene.util.BytesRef;
  * dictionary value (ordinal) can be retrieved for each document. Ordinals
  * are dense and in increasing sorted order.
  */
-public abstract class SortedSetDocValues extends DocIdSetIterator {
+public abstract class SortedSetDocValues extends DocValuesIterator {
   
   /** Sole constructor. (For invocation by subclass 
    * constructors, typically implicit.) */
@@ -43,6 +42,8 @@ public abstract class SortedSetDocValues extends DocIdSetIterator {
 
   /** 
    * Returns the next ordinal for the current document.
+   * It is illegal to call this method after {@link #advanceExact(int)}
+   * returned {@code false}.
    * @return next ordinal for the document, or {@link #NO_MORE_ORDS}. 
    *         ordinals are dense, start at 0, then increment by 1 for 
    *         the next value in sorted order. 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesWriter.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesWriter.java b/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesWriter.java
index e7d915f..35157d4 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesWriter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortedSetDocValuesWriter.java
@@ -226,6 +226,11 @@ class SortedSetDocValuesWriter extends DocValuesWriter {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
     public long getValueCount() {
       return ordMap.length;
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/index/SortingLeafReader.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/index/SortingLeafReader.java b/lucene/core/src/java/org/apache/lucene/index/SortingLeafReader.java
index 8139ed1..4fb5027 100644
--- a/lucene/core/src/java/org/apache/lucene/index/SortingLeafReader.java
+++ b/lucene/core/src/java/org/apache/lucene/index/SortingLeafReader.java
@@ -186,6 +186,12 @@ class SortingLeafReader extends FilterLeafReader {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      docID = target;
+      return dvs.docsWithField.get(target);
+    }
+
+    @Override
     public BytesRef binaryValue() {
       return dvs.values[docID];
     }
@@ -255,6 +261,12 @@ class SortingLeafReader extends FilterLeafReader {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      docID = target;
+      return dvs.docsWithField.get(target);
+    }
+
+    @Override
     public long longValue() {
       return dvs.values[docID];
     }
@@ -395,6 +407,12 @@ class SortingLeafReader extends FilterLeafReader {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      docID = target;
+      return ords[target] != -1;
+    }
+
+    @Override
     public int ordValue() {
       return ords[docID];
     }
@@ -468,6 +486,13 @@ class SortingLeafReader extends FilterLeafReader {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      docID = target;
+      ordUpto = 0;
+      return ords[docID] != null;
+    }
+
+    @Override
     public long nextOrd() {
       if (ordUpto == ords[docID].length) {
         return NO_MORE_ORDS;
@@ -539,6 +564,13 @@ class SortingLeafReader extends FilterLeafReader {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      docID = target;
+      upto = 0;
+      return values[docID] != null;
+    }
+
+    @Override
     public long nextValue() {
       if (upto == values[docID].length) {
         throw new AssertionError();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java b/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java
index b6c17c0..8216201 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FieldComparator.java
@@ -178,11 +178,7 @@ public abstract class FieldComparator<T> {
     }
 
     private double getValueForDoc(int doc) throws IOException {
-      int curDocID = currentReaderValues.docID();
-      if (doc > curDocID) {
-        curDocID = currentReaderValues.advance(doc);
-      }
-      if (doc == curDocID) {
+      if (currentReaderValues.advanceExact(doc)) {
         return Double.longBitsToDouble(currentReaderValues.longValue());
       } else {
         return missingValue;
@@ -242,11 +238,7 @@ public abstract class FieldComparator<T> {
     }
     
     private float getValueForDoc(int doc) throws IOException {
-      int curDocID = currentReaderValues.docID();
-      if (doc > curDocID) {
-        curDocID = currentReaderValues.advance(doc);
-      }
-      if (doc == curDocID) {
+      if (currentReaderValues.advanceExact(doc)) {
         return Float.intBitsToFloat((int) currentReaderValues.longValue());
       } else {
         return missingValue;
@@ -308,11 +300,7 @@ public abstract class FieldComparator<T> {
     }
 
     private int getValueForDoc(int doc) throws IOException {
-      int curDocID = currentReaderValues.docID();
-      if (doc > curDocID) {
-        curDocID = currentReaderValues.advance(doc);
-      }
-      if (doc == curDocID) {
+      if (currentReaderValues.advanceExact(doc)) {
         return (int) currentReaderValues.longValue();
       } else {
         return missingValue;
@@ -372,11 +360,7 @@ public abstract class FieldComparator<T> {
     }
 
     private long getValueForDoc(int doc) throws IOException {
-      int curDocID = currentReaderValues.docID();
-      if (doc > curDocID) {
-        curDocID = currentReaderValues.advance(doc);
-      }
-      if (doc == curDocID) {
+      if (currentReaderValues.advanceExact(doc)) {
         return currentReaderValues.longValue();
       } else {
         return missingValue;
@@ -656,15 +640,11 @@ public abstract class FieldComparator<T> {
     }
 
     private int getOrdForDoc(int doc) throws IOException {
-      int curDocID = termsIndex.docID();
-      if (doc > curDocID) {
-        if (termsIndex.advance(doc) == doc) {
-          return termsIndex.ordValue();
-        }
-      } else if (doc == curDocID) {
+      if (termsIndex.advanceExact(doc)) {
         return termsIndex.ordValue();
+      } else {
+        return -1;
       }
-      return -1;
     }
 
     @Override
@@ -864,11 +844,7 @@ public abstract class FieldComparator<T> {
     }
 
     private BytesRef getValueForDoc(int doc) throws IOException {
-      int curDocID = docTerms.docID();
-      if (doc > curDocID) {
-        curDocID = docTerms.advance(doc);
-      }
-      if (doc == curDocID) {
+      if (docTerms.advanceExact(doc)) {
         return docTerms.binaryValue();
       } else {
         return null;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/search/SortedNumericSelector.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/SortedNumericSelector.java b/lucene/core/src/java/org/apache/lucene/search/SortedNumericSelector.java
index 43e97e7..705454e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/SortedNumericSelector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/SortedNumericSelector.java
@@ -132,6 +132,15 @@ public class SortedNumericSelector {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      if (in.advanceExact(target)) {
+        value = in.nextValue();
+        return true;
+      }
+      return false;
+    }
+
+    @Override
     public long cost() {
       return in.cost();
     }
@@ -182,6 +191,15 @@ public class SortedNumericSelector {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      if (in.advanceExact(target)) {
+        setValue();
+        return true;
+      }
+      return false;
+    }
+
+    @Override
     public long cost() {
       return in.cost();
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9aca4c9d/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java b/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java
index f10dbf7..2d6c351 100644
--- a/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java
@@ -118,6 +118,15 @@ public class SortedSetSelector {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      if (in.advanceExact(target)) {
+        setOrd();
+        return true;
+      }
+      return false;
+    }
+
+    @Override
     public long cost() {
       return in.cost();
     }
@@ -180,6 +189,15 @@ public class SortedSetSelector {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      if (in.advanceExact(target)) {
+        setOrd();
+        return true;
+      }
+      return false;
+    }
+
+    @Override
     public long cost() {
       return in.cost();
     }
@@ -249,6 +267,15 @@ public class SortedSetSelector {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      if (in.advanceExact(target)) {
+        setOrd();
+        return true;
+      }
+      return false;
+    }
+
+    @Override
     public long cost() {
       return in.cost();
     }
@@ -330,6 +357,15 @@ public class SortedSetSelector {
     }
 
     @Override
+    public boolean advanceExact(int target) throws IOException {
+      if (in.advanceExact(target)) {
+        setOrd();
+        return true;
+      }
+      return false;
+    }
+
+    @Override
     public long cost() {
       return in.cost();
     }