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 d77c73de48d..0436cde7c4b 100644
--- a/solr/core/src/java/org/apache/solr/request/DocValuesFacets.java
+++ b/solr/core/src/java/org/apache/solr/request/DocValuesFacets.java
@@ -24,6 +24,7 @@ import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.MultiDocValues.MultiSortedSetDocValues;
 import org.apache.lucene.index.MultiDocValues;
+import org.apache.lucene.index.OrdStatsCollector;
 import org.apache.lucene.index.OrdinalMap;
 import org.apache.lucene.index.SortedDocValues;
 import org.apache.lucene.index.SortedSetDocValues;
@@ -276,21 +277,26 @@ public class DocValuesFacets {
   
   /** accumulates per-segment single-valued facet counts, mapping to global ordinal space on-the-fly */
   static void accumSingleGeneric(int counts[], int startTermIndex, SortedDocValues si, DocIdSetIterator disi, int subIndex, OrdinalMap map) throws IOException {
-    final LongValues ordmap = map == null ? null : map.getGlobalOrds(subIndex);
-    int doc;
-    while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      int term;
-      if (si.advanceExact(doc)) {
-        term = si.ordValue();
-      } else {
-        term = -1;
+    final LongValues ordMap = map == null ? null : map.getGlobalOrds(subIndex);
+
+    si.forEach(disi, new OrdStatsCollector() {
+      @Override
+      public void collectOrd(long ord) {
+        int term = (int) ord;
+        if (map != null) {
+          term = (int) ordMap.get(term);
+        }
+        int arrIdx = term-startTermIndex;
+        if (arrIdx>=0 && arrIdx<counts.length) counts[arrIdx]++;
       }
-      if (map != null && term >= 0) {
-        term = (int) ordmap.get(term);
+
+      @Override
+      public void collectMissing(int count) {
+        if (startTermIndex == -1) {
+          counts[0] += count;
+        }
       }
-      int arrIdx = term-startTermIndex;
-      if (arrIdx>=0 && arrIdx<counts.length) counts[arrIdx]++;
-    }
+    });
   }
   
   /** "typical" single-valued faceting: not too many unique values, no prefixing. maps to global ordinals as a separate step */
@@ -302,16 +308,19 @@ public class DocValuesFacets {
     } else {
       segCounts = new int[1+si.getValueCount()];
     }
-    
-    int doc;
-    while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (si.advanceExact(doc)) {
-        segCounts[1+si.ordValue()]++;
-      } else {
-        segCounts[0]++;
+
+    si.forEach(disi, new OrdStatsCollector() {
+      @Override
+      public void collectOrd(long ord) {
+        segCounts[(int) (1 + ord)]++;
       }
-    }
-    
+
+      @Override
+      public void collectMissing(int count) {
+        segCounts[0] += count;
+      }
+    });
+
     // migrate to global ords (if necessary)
     if (map != null) {
       migrateGlobal(counts, segCounts, subIndex, map);
@@ -333,22 +342,25 @@ public class DocValuesFacets {
   /** accumulates per-segment multi-valued facet counts, mapping to global ordinal space on-the-fly */
   static void accumMultiGeneric(int counts[], int startTermIndex, SortedSetDocValues si, DocIdSetIterator disi, int subIndex, OrdinalMap map) throws IOException {
     final LongValues ordMap = map == null ? null : map.getGlobalOrds(subIndex);
-    int doc;
-    while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (si.advanceExact(doc)) {
-        // strange do-while to collect the missing count (first ord is NO_MORE_ORDS)
-        int term = (int) si.nextOrd();
-        do {
-          if (map != null) {
-            term = (int) ordMap.get(term);
-          }
-          int arrIdx = term-startTermIndex;
-          if (arrIdx>=0 && arrIdx<counts.length) counts[arrIdx]++;
-        } while ((term = (int) si.nextOrd()) >= 0);
-      } else if (startTermIndex == -1) {
-        counts[0]++; // missing count
+
+    si.forEach(disi, new OrdStatsCollector() {
+      @Override
+      public void collectOrd(long ord) {
+        int term = (int) ord;
+        if (map != null) {
+          term = (int) ordMap.get(term);
+        }
+        int arrIdx = term-startTermIndex;
+        if (arrIdx>=0 && arrIdx<counts.length) counts[arrIdx]++;
       }
-    }
+
+      @Override
+      public void collectMissing(int count) {
+        if (startTermIndex == -1) {
+          counts[0] += count; // missing count
+        }
+      }
+    });
   }
   
   /** "typical" multi-valued faceting: not too many unique values, no prefixing. maps to global ordinals as a separate step */
@@ -360,19 +372,20 @@ public class DocValuesFacets {
     } else {
       segCounts = new int[1+(int)si.getValueCount()];
     }
-    
-    int doc;
-    while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
-      if (si.advanceExact(doc)) {
-        int term = (int) si.nextOrd();
-        do {
-          segCounts[1+term]++;
-        } while ((term = (int)si.nextOrd()) >= 0);
-      } else {
-        counts[0]++; // missing
+
+    si.forEach(disi, new OrdStatsCollector() {
+      @Override
+      public void collectOrd(long ord) {
+        segCounts[(int) (1 + ord)]++;
       }
-    }
-    
+
+      @Override
+      public void collectMissing(int count) {
+        counts[0] += count;
+      }
+    });
+
+
     // migrate to global ords (if necessary)
     if (map != null) {
       migrateGlobal(counts, segCounts, subIndex, map);
