Index: lucene/core/src/java/org/apache/lucene/search/FieldComparator.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FieldComparator.java	(revision 1557967)
+++ lucene/core/src/java/org/apache/lucene/search/FieldComparator.java	(working copy)
@@ -737,20 +737,48 @@
     public void copy(int slot, int doc) {
       throw new UnsupportedOperationException();
     }
+    
+    // this stuff caches the ordinal of the last 'value' from compareDocToValue
+    BytesRef lastValue = new BytesRef(); // sentinel
+    int lastOrd;
+    int lastReaderGen = -1;
+    boolean lastSameReader = false;
 
     @Override
     public int compareDocToValue(int doc, BytesRef value) {
-      int ord = termsIndex.getOrd(doc);
-      if (ord == -1) {
+      if (value != lastValue || currentReaderGen != lastReaderGen) {
+        // cache miss (typically once per segment)
+        // nocommit: move this out to another method?
+        lastReaderGen = currentReaderGen;
+        lastValue = value;
         if (value == null) {
-          return 0;
+          // -1 ord is null
+          lastOrd = -1;
+          lastSameReader = true;
+        } else {
+          final int index = termsIndex.lookupTerm(value);
+          if (index < 0) {
+            lastOrd = -index - 2;
+            lastSameReader = false;
+          } else {
+            lastOrd = index;
+            lastSameReader = true;
+          }
         }
+      }
+      
+      int ord = termsIndex.getOrd(doc);
+
+      if (lastSameReader) {
+        // ord is precisely comparable, even in the equal case
+        return ord - lastOrd;
+      } else if (ord <= lastOrd) {
+        // the equals case always means doc is < value
+        // (because we set lastOrd to the lower bound)
         return -1;
-      } else if (value == null) {
+      } else {
         return 1;
       }
-      termsIndex.lookupOrd(ord, tempBR);
-      return tempBR.compareTo(value);
     }
 
     /** Base class for specialized (per bit width of the
Index: solr/core/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java	(revision 1557967)
+++ solr/core/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java	(working copy)
@@ -223,19 +223,48 @@
       return values==null ? parent.NULL_VAL : values[slot];
     }
 
+    // this stuff caches the ordinal of the last 'value' from compareDocToValue
+    BytesRef lastValue = new BytesRef(); // sentinel
+    int lastOrd;
+    int lastReaderGen = -1;
+    boolean lastSameReader = false;
+
     @Override
     public int compareDocToValue(int doc, BytesRef value) {
-      int docOrd = termsIndex.getOrd(doc);
-      if (docOrd == -1) {
+      if (value != lastValue || currentReaderGen != lastReaderGen) {
+        // cache miss (typically once per segment)
+        // nocommit: move this out to another method?
+        lastReaderGen = currentReaderGen;
+        lastValue = value;
         if (value == null) {
-          return 0;
+          // -1 ord is null
+          lastOrd = NULL_ORD;
+          lastSameReader = true;
+        } else {
+          final int index = termsIndex.lookupTerm(value);
+          if (index < 0) {
+            lastOrd = -index - 2;
+            lastSameReader = false;
+          } else {
+            lastOrd = index;
+            lastSameReader = true;
+          }
         }
+      }
+      
+      int ord = termsIndex.getOrd(doc);
+      if (ord == -1) ord = NULL_ORD;
+
+      if (lastSameReader) {
+        // ord is precisely comparable, even in the equal case
+        return ord - lastOrd;
+      } else if (ord <= lastOrd) {
+        // the equals case always means doc is < value
+        // (because we set lastOrd to the lower bound)
+        return -1;
+      } else {
         return 1;
-      } else if (value == null) {
-        return -1;
       }
-      termsIndex.lookupOrd(docOrd, tempBR);
-      return tempBR.compareTo(value);
     }
   }
 
@@ -247,34 +276,18 @@
     @Override
     public int compareBottom(int doc) {
       assert bottomSlot != -1;
-      int order = termsIndex.getOrd(doc);
-      if (order == -1) order = NULL_ORD;
+      int docOrd = termsIndex.getOrd(doc);
+      if (docOrd == -1) docOrd = NULL_ORD;
       if (bottomSameReader) {
-        // ord is precisely comparable, even in the equal
-        // case
-        return bottomOrd - order;
+        // ord is precisely comparable, even in the equal case
+        return bottomOrd - docOrd;
+      } else if (bottomOrd >= docOrd) {
+        // the equals case always means bottom is > doc
+        // (because we set bottomOrd to the lower bound in
+        // setBottom):
+        return 1;
       } else {
-        // ord is only approx comparable: if they are not
-        // equal, we can use that; if they are equal, we
-        // must fallback to compare by value
-
-        final int cmp = bottomOrd - order;
-        if (cmp != 0) {
-          return cmp;
-        }
-
-        // take care of the case where both vals are null
-        if (order == NULL_ORD) {
-          return 0;
-        }
-
-        // and at this point we know that neither value is null, so safe to compare
-        if (order == NULL_ORD) {
-          return bottomValue.compareTo(parent.NULL_VAL);
-        } else {
-          termsIndex.lookupOrd(order, tempBR);
-          return bottomValue.compareTo(tempBR);
-        }
+        return -1;
       }
     }
 
