diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java
index 936d4ed..1f10342 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java
@@ -1486,6 +1486,11 @@ public final class DirectPostingsFormat extends PostingsFormat {
       }
       return docID();
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return postings.length;
+    }
   }
 
   // Docs + freqs:
@@ -1551,6 +1556,11 @@ public final class DirectPostingsFormat extends PostingsFormat {
       }
       return docID();
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return postings.length;
+    }
   }
 
   // Docs + freqs + positions/offets:
@@ -1632,6 +1642,11 @@ public final class DirectPostingsFormat extends PostingsFormat {
       }
       return docID();
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return postings.length;
+    }
   }
 
   private final static class LowFreqDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -1784,6 +1799,11 @@ public final class DirectPostingsFormat extends PostingsFormat {
         return null;
       }
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return postings.length;
+    }
   }
 
   // Docs + freqs:
@@ -1957,6 +1977,11 @@ public final class DirectPostingsFormat extends PostingsFormat {
         return docID = docIDs[upto];
       }
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return docIDs.length;
+    }
   }
 
   // TODO: specialize offsets and not
@@ -2190,5 +2215,10 @@ public final class DirectPostingsFormat extends PostingsFormat {
         return payload;
       }
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return docIDs.length;
+    }
   }
 }
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java
index 3dab4df..6948cc2 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java
@@ -431,6 +431,11 @@ public final class MemoryPostingsFormat extends PostingsFormat {
     public int freq() {
       return freq;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return numDocs;
+    }
   }
 
   private final static class FSTDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -618,6 +623,11 @@ public final class MemoryPostingsFormat extends PostingsFormat {
     public int freq() {
       return freq;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return numDocs;
+    }
   }
 
   private final static class FSTTermsEnum extends TermsEnum {
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/pulsing/PulsingPostingsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/pulsing/PulsingPostingsReader.java
index 76fa37a..74b3898 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/pulsing/PulsingPostingsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/pulsing/PulsingPostingsReader.java
@@ -260,6 +260,7 @@ public class PulsingPostingsReader extends PostingsReaderBase {
     private int accum;
     private int freq;
     private int payloadLength;
+    private long cost;
 
     public PulsingDocsEnum(FieldInfo fieldInfo) {
       indexOptions = fieldInfo.getIndexOptions();
@@ -283,6 +284,7 @@ public class PulsingPostingsReader extends PostingsReaderBase {
       docID = -1;
       accum = 0;
       freq = 1;
+      cost = termState.docFreq;
       payloadLength = 0;
       this.liveDocs = liveDocs;
       return this;
@@ -367,6 +369,11 @@ public class PulsingPostingsReader extends PostingsReaderBase {
       }
       return docID = NO_MORE_DOCS;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return cost;
+    }
   }
 
   private static class PulsingDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -390,6 +397,7 @@ public class PulsingPostingsReader extends PostingsReaderBase {
     private int offsetLength;
 
     private boolean payloadRetrieved;
+    private int cost;
 
     public PulsingDocsAndPositionsEnum(FieldInfo fieldInfo) {
       indexOptions = fieldInfo.getIndexOptions();
@@ -415,6 +423,7 @@ public class PulsingPostingsReader extends PostingsReaderBase {
       posPending = 0;
       docID = -1;
       accum = 0;
+      cost = termState.docFreq;
       startOffset = storeOffsets ? 0 : -1; // always return -1 if no offsets are stored
       offsetLength = 0;
       //System.out.println("PR d&p reset storesPayloads=" + storePayloads + " bytes=" + bytes.length + " this=" + this);
@@ -551,6 +560,11 @@ public class PulsingPostingsReader extends PostingsReaderBase {
         return null;
       }
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return cost;
+    }
   }
 
   @Override
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/sep/SepPostingsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/sep/SepPostingsReader.java
index 53f902b..a4ff375 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/sep/SepPostingsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/sep/SepPostingsReader.java
@@ -441,6 +441,11 @@ public class SepPostingsReader extends PostingsReaderBase {
 
       return doc;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return docFreq;
+    }
   }
 
   class SepDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -717,5 +722,10 @@ public class SepPostingsReader extends PostingsReaderBase {
       pendingPayloadBytes = 0;
       return payload;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return docFreq;
+    }
   }
 }
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java
index c56fd20..b8aa7ad 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java
@@ -209,7 +209,7 @@ class SimpleTextFieldsReader extends FieldsProducer {
       } else {
         docsAndPositionsEnum = new SimpleTextDocsAndPositionsEnum();
       } 
-      return docsAndPositionsEnum.reset(docsStart, liveDocs, indexOptions);
+      return docsAndPositionsEnum.reset(docsStart, liveDocs, indexOptions, docFreq);
     }
     
     @Override
@@ -309,6 +309,11 @@ class SimpleTextFieldsReader extends FieldsProducer {
       while(nextDoc() < target);
       return docID;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return 0;
+    }
   }
 
   private class SimpleTextDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -327,6 +332,7 @@ class SimpleTextFieldsReader extends FieldsProducer {
     private boolean readPositions;
     private int startOffset;
     private int endOffset;
+    private int cost;
 
     public SimpleTextDocsAndPositionsEnum() {
       this.inStart = SimpleTextFieldsReader.this.in;
@@ -337,7 +343,7 @@ class SimpleTextFieldsReader extends FieldsProducer {
       return in == inStart;
     }
 
-    public SimpleTextDocsAndPositionsEnum reset(long fp, Bits liveDocs, IndexOptions indexOptions) {
+    public SimpleTextDocsAndPositionsEnum reset(long fp, Bits liveDocs, IndexOptions indexOptions, int docFreq) {
       this.liveDocs = liveDocs;
       nextDocStart = fp;
       docID = -1;
@@ -347,6 +353,7 @@ class SimpleTextFieldsReader extends FieldsProducer {
         startOffset = -1;
         endOffset = -1;
       }
+      cost = docFreq;
       return this;
     }
 
@@ -464,6 +471,11 @@ class SimpleTextFieldsReader extends FieldsProducer {
     public BytesRef getPayload() {
       return payload;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return cost;
+    }
   }
 
   static class TermData {
diff --git a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java
index 2b105de..d609ad8 100644
--- a/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java
+++ b/lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java
@@ -444,6 +444,11 @@ public class SimpleTextTermVectorsReader extends TermVectorsReader {
       this.doc = -1;
       didNext = false;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return 1;
+    }
   }
   
   private static class SimpleTVDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -535,5 +540,10 @@ public class SimpleTextTermVectorsReader extends TermVectorsReader {
         return endOffsets[nextPos-1];
       }
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return 1;
+    }
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsAndPositionsEnum.java b/lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsAndPositionsEnum.java
index 30899b3..848b613 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsAndPositionsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsAndPositionsEnum.java
@@ -134,5 +134,14 @@ public final class MappingMultiDocsAndPositionsEnum extends DocsAndPositionsEnum
   public BytesRef getPayload() throws IOException {
     return current.getPayload();
   }
+
+  @Override
+  public long estimatedDocCount() {
+    long cost = 0;
+    for (EnumWithSlice enumWithSlice : subs) {
+      cost += enumWithSlice.docsAndPositionsEnum.estimatedDocCount();
+    }
+    return cost;
+  }
 }
 
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsEnum.java b/lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsEnum.java
index 0203750..9a138b2 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsEnum.java
@@ -114,5 +114,14 @@ public final class MappingMultiDocsEnum extends DocsEnum {
       }
     }
   }
+
+  @Override
+  public long estimatedDocCount() {
+    long cost = 0;
+    for (EnumWithSlice enumWithSlice : subs) {
+      cost += enumWithSlice.docsEnum.estimatedDocCount();
+    }
+    return cost;
+  }
 }
 
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsReader.java
index a3729e2..9c32a2c 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsReader.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsReader.java
@@ -513,6 +513,11 @@ public class Lucene40PostingsReader extends PostingsReaderBase {
       }
       return scanTo(target);
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return limit;
+    }
   }
   
   private final class AllDocsSegmentDocsEnum extends SegmentDocsEnumBase {
@@ -589,7 +594,7 @@ public class Lucene40PostingsReader extends PostingsReaderBase {
         return NO_MORE_DOCS;
       }
     }
-    
+
   }
   
   private final class LiveDocsSegmentDocsEnum extends SegmentDocsEnumBase {
@@ -886,6 +891,11 @@ public class Lucene40PostingsReader extends PostingsReaderBase {
     public BytesRef getPayload() throws IOException {
       return null;
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return limit;
+    }
   }
   
   // Decodes docs & positions & (payloads and/or offsets)
@@ -1179,5 +1189,10 @@ public class Lucene40PostingsReader extends PostingsReaderBase {
         return null;
       }
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return limit;
+    }
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40TermVectorsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40TermVectorsReader.java
index 714ec84..cc58c9d 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40TermVectorsReader.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40TermVectorsReader.java
@@ -619,6 +619,11 @@ public class Lucene40TermVectorsReader extends TermVectorsReader implements Clos
       this.doc = -1;
       didNext = false;
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return freq;
+    }
   }
 
   private static class TVDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -634,7 +639,7 @@ public class Lucene40TermVectorsReader extends TermVectorsReader implements Clos
     private byte[] payloadBytes;
 
     @Override
-    public int freq() throws IOException {
+    public int freq() {
       if (positions != null) {
         return positions.length;
       } else {
@@ -726,6 +731,11 @@ public class Lucene40TermVectorsReader extends TermVectorsReader implements Clos
         return endOffsets[nextPos-1];
       }
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return freq();
+    }
   }
 
   @Override
diff --git a/lucene/core/src/java/org/apache/lucene/codecs/lucene41/Lucene41PostingsReader.java b/lucene/core/src/java/org/apache/lucene/codecs/lucene41/Lucene41PostingsReader.java
index 02540ef..05ded42 100644
--- a/lucene/core/src/java/org/apache/lucene/codecs/lucene41/Lucene41PostingsReader.java
+++ b/lucene/core/src/java/org/apache/lucene/codecs/lucene41/Lucene41PostingsReader.java
@@ -599,6 +599,11 @@ public final class Lucene41PostingsReader extends PostingsReaderBase {
         return nextDoc();
       }
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return docFreq;
+    }
   }
 
 
@@ -1010,6 +1015,11 @@ public final class Lucene41PostingsReader extends PostingsReaderBase {
     public BytesRef getPayload() {
       return null;
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return docFreq;
+    }
   }
 
   // Also handles payloads + offsets
@@ -1588,5 +1598,10 @@ public final class Lucene41PostingsReader extends PostingsReaderBase {
         return payload;
       }
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return docFreq;
+    }
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java b/lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java
index 54edfc7..0e6f743 100644
--- a/lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java
+++ b/lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java
@@ -251,6 +251,11 @@ public class FilterAtomicReader extends AtomicReader {
     public AttributeSource attributes() {
       return in.attributes();
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return in.estimatedDocCount();
+    }
   }
 
   /** Base class for filtering {@link DocsAndPositionsEnum} implementations. */
@@ -310,6 +315,11 @@ public class FilterAtomicReader extends AtomicReader {
     public AttributeSource attributes() {
       return in.attributes();
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return in.estimatedDocCount();
+    }
   }
 
   /** The underlying AtomicReader. */
diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java
index 7942f62..c100083 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java
@@ -37,6 +37,7 @@ public final class MultiDocsAndPositionsEnum extends DocsAndPositionsEnum {
   DocsAndPositionsEnum current;
   int currentBase;
   int doc = -1;
+  int cost;
 
   /** Sole constructor. */
   public MultiDocsAndPositionsEnum(MultiTermsEnum parent, int subReaderCount) {
@@ -54,10 +55,14 @@ public final class MultiDocsAndPositionsEnum extends DocsAndPositionsEnum {
   public MultiDocsAndPositionsEnum reset(final EnumWithSlice[] subs, final int numSubs) {
     this.numSubs = numSubs;
     this.subs = new EnumWithSlice[subs.length];
+    this.cost = 0;
     for(int i=0;i<subs.length;i++) {
       this.subs[i] = new EnumWithSlice();
       this.subs[i].docsAndPositionsEnum = subs[i].docsAndPositionsEnum;
       this.subs[i].slice = subs[i].slice;
+      if (this.subs[i].docsAndPositionsEnum != null) {
+        cost += this.subs[i].docsAndPositionsEnum.estimatedDocCount();
+      }
     }
     upto = -1;
     doc = -1;
@@ -173,5 +178,10 @@ public final class MultiDocsAndPositionsEnum extends DocsAndPositionsEnum {
   public String toString() {
     return "MultiDocsAndPositionsEnum(" + Arrays.toString(getSubs()) + ")";
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return cost;
+  }
 }
 
diff --git a/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java b/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java
index 6049b82..4ae2347 100644
--- a/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java
+++ b/lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java
@@ -37,6 +37,7 @@ public final class MultiDocsEnum extends DocsEnum {
   DocsEnum current;
   int currentBase;
   int doc = -1;
+  long cost = 0;
 
   /** Sole constructor
    * @param parent The {@link MultiTermsEnum} that created us.
@@ -50,9 +51,13 @@ public final class MultiDocsEnum extends DocsEnum {
     this.numSubs = numSubs;
 
     this.subs = new EnumWithSlice[subs.length];
+    cost = 0;
     for(int i=0;i<subs.length;i++) {
       this.subs[i] = new EnumWithSlice();
       this.subs[i].docsEnum = subs[i].docsEnum;
+      if (subs[i].docsEnum != null) {
+        cost += subs[i].docsEnum.estimatedDocCount();
+      }
       this.subs[i].slice = subs[i].slice;
     }
     upto = -1;
@@ -154,5 +159,10 @@ public final class MultiDocsEnum extends DocsEnum {
   public String toString() {
     return "MultiDocsEnum(" + Arrays.toString(getSubs()) + ")";
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return cost;
+  }
 }
 
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
index d0b9b5c..c960b28 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
@@ -26,7 +26,6 @@ import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermsEnum;
 import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.search.ConjunctionTermScorer.DocsAndFreqs;
 import org.apache.lucene.search.TermQuery.TermWeight;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.search.similarities.Similarity.ExactSimScorer;
@@ -173,24 +172,18 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
     protected ArrayList<Weight> weights;
     protected int maxCoord;  // num optional + num required
     private final boolean disableCoord;
-    private final boolean termConjunction;
 
     public BooleanWeight(IndexSearcher searcher, boolean disableCoord)
       throws IOException {
       this.similarity = searcher.getSimilarity();
       this.disableCoord = disableCoord;
       weights = new ArrayList<Weight>(clauses.size());
-      boolean termConjunction = clauses.isEmpty() || minNrShouldMatch != 0 ? false : true;
       for (int i = 0 ; i < clauses.size(); i++) {
         BooleanClause c = clauses.get(i);
         Weight w = c.getQuery().createWeight(searcher);
-        if (!(c.isRequired() && (w instanceof TermWeight))) {
-          termConjunction = false;
-        }
         weights.add(w);
         if (!c.isProhibited()) maxCoord++;
       }
-      this.termConjunction = termConjunction;
     }
 
     @Override
@@ -309,10 +302,6 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
     public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
         boolean topScorer, Bits acceptDocs)
         throws IOException {
-      if (termConjunction) {
-        // specialized scorer for term conjunctions
-        return createConjunctionTermScorer(context, acceptDocs);
-      }
       List<Scorer> required = new ArrayList<Scorer>();
       List<Scorer> prohibited = new ArrayList<Scorer>();
       List<Scorer> optional = new ArrayList<Scorer>();
@@ -359,28 +348,6 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
       return new BooleanScorer2(this, disableCoord, minNrShouldMatch, required, prohibited, optional, maxCoord);
     }
 
-    private Scorer createConjunctionTermScorer(AtomicReaderContext context, Bits acceptDocs)
-        throws IOException {
-
-      // TODO: fix scorer API to specify "needsScores" up
-      // front, so we can do match-only if caller doesn't
-      // needs scores
-
-      final DocsAndFreqs[] docsAndFreqs = new DocsAndFreqs[weights.size()];
-      for (int i = 0; i < docsAndFreqs.length; i++) {
-        final TermWeight weight = (TermWeight) weights.get(i);
-        final Scorer scorer = weight.scorer(context, true, false, acceptDocs);
-        if (scorer == null) {
-          return null;
-        } else {
-          assert scorer instanceof TermScorer;
-          docsAndFreqs[i] = new DocsAndFreqs((TermScorer) scorer);
-        }
-      }
-      return new ConjunctionTermScorer(this, disableCoord ? 1.0f : coord(
-          docsAndFreqs.length, docsAndFreqs.length), docsAndFreqs);
-    }
-    
     @Override
     public boolean scoresDocsOutOfOrder() {
       for (BooleanClause c : clauses) {
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
index 6cba54f..b3d72cc 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
@@ -134,6 +134,9 @@ final class BooleanScorer extends Scorer {
     
     @Override
     public float score() { return (float)score; }
+
+    @Override
+    public long estimatedDocCount() { return 1; }
     
   }
 
@@ -203,6 +206,7 @@ final class BooleanScorer extends Scorer {
   private Bucket current;
   // Any time a prohibited clause matches we set bit 0:
   private static final int PROHIBITED_MASK = 1;
+  private long cost;
   
   BooleanScorer(BooleanWeight weight, boolean disableCoord, int minNrShouldMatch,
       List<Scorer> optionalScorers, List<Scorer> prohibitedScorers, int maxCoord) throws IOException {
@@ -212,6 +216,7 @@ final class BooleanScorer extends Scorer {
     if (optionalScorers != null && optionalScorers.size() > 0) {
       for (Scorer scorer : optionalScorers) {
         if (scorer.nextDoc() != NO_MORE_DOCS) {
+          cost += scorer.estimatedDocCount();
           scorers = new SubScorer(scorer, false, false, bucketTable.newCollector(0), scorers);
         }
       }
@@ -347,4 +352,9 @@ final class BooleanScorer extends Scorer {
   public Collection<ChildScorer> getChildren() {
     throw new UnsupportedOperationException();
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return cost;
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java
index db96728..717c83c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java
@@ -22,7 +22,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
-import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanQuery.BooleanWeight;
 import org.apache.lucene.search.similarities.Similarity;
 
@@ -147,6 +146,11 @@ class BooleanScorer2 extends Scorer {
     public int advance(int target) throws IOException {
       return scorer.advance(target);
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return 1;
+    }
   }
 
   private Scorer countingDisjunctionSumScorer(final List<Scorer> scorers,
@@ -171,7 +175,7 @@ class BooleanScorer2 extends Scorer {
     };
   }
 
-  private Scorer countingConjunctionSumScorer(boolean disableCoord,
+  private Scorer countingConjunctionSumScorer(boolean disableCoords,
                                               List<Scorer> requiredScorers) throws IOException {
     // each scorer from the list counted as a single matcher
     final int requiredNrMatchers = requiredScorers.size();
@@ -336,4 +340,9 @@ class BooleanScorer2 extends Scorer {
     }
     return children;
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return countingSumScorer.estimatedDocCount();
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java b/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
index deaafec..4e812c5 100644
--- a/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
+++ b/lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
@@ -89,6 +89,10 @@ public abstract class CachingCollector extends Collector {
     
     @Override
     public final int nextDoc() { throw new UnsupportedOperationException(); }
+
+    @Override
+    public long estimatedDocCount() {  return 0;  }
+    
     }
 
   // A CachingCollector which caches scores
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java b/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
index 24ade75..3bb6e28 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
@@ -28,6 +28,7 @@ class ConjunctionScorer extends Scorer {
   
   private final Scorer[] scorers;
   private int lastDoc = -1;
+  private final Scorer lead;
 
   public ConjunctionScorer(Weight weight, Collection<Scorer> scorers) throws IOException {
     this(weight, scorers.toArray(new Scorer[scorers.size()]));
@@ -36,79 +37,44 @@ class ConjunctionScorer extends Scorer {
   public ConjunctionScorer(Weight weight, Scorer... scorers) throws IOException {
     super(weight);
     this.scorers = scorers;
-    
-    for (int i = 0; i < scorers.length; i++) {
-      if (scorers[i].nextDoc() == NO_MORE_DOCS) {
-        // If even one of the sub-scorers does not have any documents, this
-        // scorer should not attempt to do any more work.
-        lastDoc = NO_MORE_DOCS;
-        return;
-      }
-    }
-
-    // Sort the array the first time...
-    // We don't need to sort the array in any future calls because we know
-    // it will already start off sorted (all scorers on same doc).
-    
-    // Note that this comparator is not consistent with equals!
-    // Also we use mergeSort here to be stable (so order of Scoreres that
-    // match on first document keeps preserved):
+    // Sort the array the first time to allow the least frequent Scorer to
+    // lead the matching.
     ArrayUtil.mergeSort(scorers, new Comparator<Scorer>() { // sort the array
       public int compare(Scorer o1, Scorer o2) {
-        return o1.docID() - o2.docID();
+        return Long.signum(o1.estimatedDocCount() - o2.estimatedDocCount());
       }
     });
-
-    // NOTE: doNext() must be called before the re-sorting of the array later on.
-    // The reason is this: assume there are 5 scorers, whose first docs are 1,
-    // 2, 3, 5, 5 respectively. Sorting (above) leaves the array as is. Calling
-    // doNext() here advances all the first scorers to 5 (or a larger doc ID
-    // they all agree on). 
-    // However, if we re-sort before doNext() is called, the order will be 5, 3,
-    // 2, 1, 5 and then doNext() will stop immediately, since the first scorer's
-    // docs equals the last one. So the invariant that after calling doNext() 
-    // all scorers are on the same doc ID is broken.
-    if (doNext() == NO_MORE_DOCS) {
-      // The scorers did not agree on any document.
-      lastDoc = NO_MORE_DOCS;
-      return;
-    }
-
-    // If first-time skip distance is any predictor of
-    // scorer sparseness, then we should always try to skip first on
-    // those scorers.
-    // Keep last scorer in it's last place (it will be the first
-    // to be skipped on), but reverse all of the others so that
-    // they will be skipped on in order of original high skip.
-    int end = scorers.length - 1;
-    int max = end >> 1;
-    for (int i = 0; i < max; i++) {
-      Scorer tmp = scorers[i];
-      int idx = end - i - 1;
-      scorers[i] = scorers[idx];
-      scorers[idx] = tmp;
-    }
+    lead = scorers[0]; // least frequent Scorer leads the intersection
   }
 
-  private int doNext() throws IOException {
-    int first = 0;
-    int doc = scorers[scorers.length - 1].docID();
-    Scorer firstScorer;
-    while ((firstScorer = scorers[first]).docID() < doc) {
-      doc = firstScorer.advance(doc);
-      first = first == scorers.length - 1 ? 0 : first + 1;
-    }
-    return doc;
+  private int doNext(int doc) throws IOException {
+    do {
+      if (lead.docID() == DocIdSetIterator.NO_MORE_DOCS) {
+        return NO_MORE_DOCS;
+      }
+      advanceHead: do {
+        for (int i = 1; i < scorers.length; i++) {
+          int currentDoc = scorers[i].docID();
+          if (currentDoc < doc) {
+            currentDoc = scorers[i].advance(doc);
+          }
+          if (currentDoc > doc) {
+            // DocsEnum beyond the current doc - break and advance lead
+            break advanceHead;
+          }
+        }
+        // success - all DocsEnums are on the same doc
+        return doc;
+      } while (true);
+      // advance head for next iteration
+      doc = lead.nextDoc();  
+    } while (true);
   }
   
   @Override
   public int advance(int target) throws IOException {
-    if (lastDoc == NO_MORE_DOCS) {
-      return lastDoc;
-    } else if (scorers[(scorers.length - 1)].docID() < target) {
-      scorers[(scorers.length - 1)].advance(target);
-    }
-    return lastDoc = doNext();
+    int doc = lead.advance(target);
+    return lastDoc = doNext(doc);
   }
 
   @Override
@@ -118,21 +84,16 @@ class ConjunctionScorer extends Scorer {
   
   @Override
   public int nextDoc() throws IOException {
-    if (lastDoc == NO_MORE_DOCS) {
-      return lastDoc;
-    } else if (lastDoc == -1) {
-      return lastDoc = scorers[scorers.length - 1].docID();
-    }
-    scorers[(scorers.length - 1)].nextDoc();
-    return lastDoc = doNext();
+    int doc = lead.nextDoc();
+    return lastDoc = doNext(doc);
   }
   
   @Override
   public float score() throws IOException {
     // TODO: sum into a double and cast to float if we ever send required clauses to BS1
     float sum = 0.0f;
-    for (int i = 0; i < scorers.length; i++) {
-      sum += scorers[i].score();
+    for (Scorer scorer : scorers) {
+      sum += scorer.score();
     }
     return sum;
   }
@@ -150,4 +111,9 @@ class ConjunctionScorer extends Scorer {
     }
     return children;
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return lead.estimatedDocCount() * scorers.length;
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConjunctionTermScorer.java b/lucene/core/src/java/org/apache/lucene/search/ConjunctionTermScorer.java
deleted file mode 100644
index 3d71e47..0000000
--- a/lucene/core/src/java/org/apache/lucene/search/ConjunctionTermScorer.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package org.apache.lucene.search;
-
-/*
- * 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.
- */
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-
-import org.apache.lucene.index.DocsEnum;
-import org.apache.lucene.util.ArrayUtil;
-
-/** Scorer for conjunctions, sets of terms, all of which are required. */
-class ConjunctionTermScorer extends Scorer {
-  protected final float coord;
-  protected int lastDoc = -1;
-  protected final DocsAndFreqs[] docsAndFreqs;
-  private final DocsAndFreqs lead;
-
-  ConjunctionTermScorer(Weight weight, float coord,
-      DocsAndFreqs[] docsAndFreqs) {
-    super(weight);
-    this.coord = coord;
-    this.docsAndFreqs = docsAndFreqs;
-    // Sort the array the first time to allow the least frequent DocsEnum to
-    // lead the matching.
-    ArrayUtil.mergeSort(docsAndFreqs, new Comparator<DocsAndFreqs>() {
-      public int compare(DocsAndFreqs o1, DocsAndFreqs o2) {
-        return o1.docFreq - o2.docFreq;
-      }
-    });
-
-    lead = docsAndFreqs[0]; // least frequent DocsEnum leads the intersection
-  }
-
-  private int doNext(int doc) throws IOException {
-    do {
-      if (lead.doc == DocIdSetIterator.NO_MORE_DOCS) {
-        return NO_MORE_DOCS;
-      }
-      advanceHead: do {
-        for (int i = 1; i < docsAndFreqs.length; i++) {
-          if (docsAndFreqs[i].doc < doc) {
-            docsAndFreqs[i].doc = docsAndFreqs[i].docs.advance(doc);
-          }
-          if (docsAndFreqs[i].doc > doc) {
-            // DocsEnum beyond the current doc - break and advance lead
-            break advanceHead;
-          }
-        }
-        // success - all DocsEnums are on the same doc
-        return doc;
-      } while (true);
-      // advance head for next iteration
-      doc = lead.doc = lead.docs.nextDoc();  
-    } while (true);
-  }
-
-  @Override
-  public int advance(int target) throws IOException {
-    lead.doc = lead.docs.advance(target);
-    return lastDoc = doNext(lead.doc);
-  }
-
-  @Override
-  public int docID() {
-    return lastDoc;
-  }
-
-  @Override
-  public int nextDoc() throws IOException {
-    lead.doc = lead.docs.nextDoc();
-    return lastDoc = doNext(lead.doc);
-  }
-
-  @Override
-  public float score() throws IOException {
-    // TODO: sum into a double and cast to float if we ever send required clauses to BS1
-    float sum = 0.0f;
-    for (DocsAndFreqs docs : docsAndFreqs) {
-      sum += docs.scorer.score();
-    }
-    return sum * coord;
-  }
-  
-  @Override
-  public int freq() {
-    return docsAndFreqs.length;
-  }
-
-  @Override
-  public Collection<ChildScorer> getChildren() {
-    ArrayList<ChildScorer> children = new ArrayList<ChildScorer>(docsAndFreqs.length);
-    for (DocsAndFreqs docs : docsAndFreqs) {
-      children.add(new ChildScorer(docs.scorer, "MUST"));
-    }
-    return children;
-  }
-
-  static final class DocsAndFreqs {
-    final DocsEnum docs;
-    final int docFreq;
-    final Scorer scorer;
-    int doc = -1;
-
-    DocsAndFreqs(TermScorer termScorer) {
-      this(termScorer, termScorer.getDocsEnum(), termScorer.getDocFreq());
-    }
-    
-    DocsAndFreqs(Scorer scorer, DocsEnum docs, int docFreq) {
-      this.docs = docs;
-      this.docFreq = docFreq;
-      this.scorer = scorer;
-    }
-  }
-}
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
index 71add6d..88aae48 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
@@ -124,6 +124,7 @@ public class ConstantScoreQuery extends Query {
     public Scorer scorer(AtomicReaderContext context, boolean scoreDocsInOrder,
         boolean topScorer, final Bits acceptDocs) throws IOException {
       final DocIdSetIterator disi;
+      final long cost;
       if (filter != null) {
         assert query == null;
         final DocIdSet dis = filter.getDocIdSet(context, acceptDocs);
@@ -131,15 +132,17 @@ public class ConstantScoreQuery extends Query {
           return null;
         }
         disi = dis.iterator();
+        cost = context.reader().maxDoc();
       } else {
         assert query != null && innerWeight != null;
         disi = innerWeight.scorer(context, scoreDocsInOrder, topScorer, acceptDocs);
+        cost = disi == null ? 0 : ((Scorer)disi).estimatedDocCount();
       }
 
       if (disi == null) {
         return null;
       }
-      return new ConstantScorer(disi, this, queryWeight);
+      return new ConstantScorer(disi, this, queryWeight, cost);
     }
     
     @Override
@@ -171,11 +174,13 @@ public class ConstantScoreQuery extends Query {
   protected class ConstantScorer extends Scorer {
     final DocIdSetIterator docIdSetIterator;
     final float theScore;
+    final long cost;
 
-    public ConstantScorer(DocIdSetIterator docIdSetIterator, Weight w, float theScore) {
+    public ConstantScorer(DocIdSetIterator docIdSetIterator, Weight w, float theScore, long cost) {
       super(w);
       this.theScore = theScore;
       this.docIdSetIterator = docIdSetIterator;
+      this.cost = cost;
     }
 
     @Override
@@ -209,7 +214,7 @@ public class ConstantScoreQuery extends Query {
         @Override
         public void setScorer(Scorer scorer) throws IOException {
           // we must wrap again here, but using the scorer passed in as parameter:
-          collector.setScorer(new ConstantScorer(scorer, ConstantScorer.this.weight, ConstantScorer.this.theScore));
+          collector.setScorer(new ConstantScorer(scorer, ConstantScorer.this.weight, ConstantScorer.this.theScore, ConstantScorer.this.cost));
         }
         
         @Override
@@ -248,6 +253,11 @@ public class ConstantScoreQuery extends Query {
         return super.score(collector, max, firstDocID);
       }
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return cost;
+    }
   }
 
   @Override
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
index 84bf866..061e2dd 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
@@ -105,4 +105,13 @@ abstract class DisjunctionScorer extends Scorer {
     }
     return children;
   }
+  
+  @Override
+  public long estimatedDocCount() {
+    long cost = 0;
+    for (int i = 0; i < numScorers; i++) {
+      cost += subScorers[i].estimatedDocCount();
+    }
+    return cost;
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java b/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java
index a326c8e..fcea23f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DocIdSet.java
@@ -36,6 +36,8 @@ public abstract class DocIdSet {
       public int docID() { return NO_MORE_DOCS; }
       @Override
       public int nextDoc() { return NO_MORE_DOCS; }
+      @Override
+      public long estimatedDocCount() { return 0; }
     };
     
     @Override
diff --git a/lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java b/lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java
index d5fc01b..3762c43 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java
@@ -92,5 +92,16 @@ public abstract class DocIdSetIterator {
    * @since 2.9
    */
   public abstract int advance(int target) throws IOException;
+  
+  /**
+   * Returns the estimated number of docs retured by this
+   * {@link DocIdSetIterator}.
+   * <p>
+   * Note: The returned number of doc IDs contained in this set might not be
+   * accurate implementations should retrun an upper bound if no exact number
+   * can be returned.
+   * </p>
+   */
+  public abstract long estimatedDocCount();
 
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java b/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
index ac4f0e3..d3f7e67 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
@@ -25,7 +25,7 @@ import org.apache.lucene.search.similarities.Similarity;
 
 final class ExactPhraseScorer extends Scorer {
   private final int endMinus1;
-
+  
   private final static int CHUNK = 4096;
 
   private int gen;
@@ -56,6 +56,7 @@ final class ExactPhraseScorer extends Scorer {
   private int freq;
 
   private final Similarity.ExactSimScorer docScorer;
+  private final long cost;
   
   ExactPhraseScorer(Weight weight, PhraseQuery.PostingsAndFreq[] postings,
                     Similarity.ExactSimScorer docScorer) throws IOException {
@@ -65,7 +66,11 @@ final class ExactPhraseScorer extends Scorer {
     chunkStates = new ChunkState[postings.length];
 
     endMinus1 = postings.length-1;
-
+    
+    // minimum docfreq * # of terms
+    // TODO: should we use totalTermFreq instead?
+    // and what about the optimization below?
+    cost = postings[0].docFreq * (long)postings.length;
     for(int i=0;i<postings.length;i++) {
 
       // Coarse optimization: advance(target) is fairly
@@ -315,4 +320,9 @@ final class ExactPhraseScorer extends Scorer {
 
     return freq;
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return cost;
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java b/lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java
index 11d88f3..67c7956 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java
@@ -107,6 +107,11 @@ public abstract class FieldCacheDocIdSet extends DocIdSet {
           }
           return doc = NO_MORE_DOCS;
         }
+        
+        @Override
+        public long estimatedDocCount() {
+          return maxDoc;
+        }
       };
     } else if (acceptDocs instanceof FixedBitSet || acceptDocs instanceof OpenBitSet) {
       // special case for FixedBitSet / OpenBitSet: use the iterator and filter it
@@ -116,6 +121,11 @@ public abstract class FieldCacheDocIdSet extends DocIdSet {
         protected boolean match(int doc) {
           return FieldCacheDocIdSet.this.matchDoc(doc);
         }
+
+        @Override
+        public long estimatedDocCount() {
+          return maxDoc;
+        }
       };
     } else {
       // Stupid consultation of acceptDocs and matchDoc()
@@ -147,6 +157,11 @@ public abstract class FieldCacheDocIdSet extends DocIdSet {
           }
           return doc = NO_MORE_DOCS;
         }
+
+        @Override
+        public long estimatedDocCount() {
+          return maxDoc;
+        }
       };
     }
   }
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSet.java b/lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSet.java
index 0d94ca9..9adcc15 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSet.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSet.java
@@ -93,6 +93,11 @@ public abstract class FilteredDocIdSet extends DocIdSet {
       protected boolean match(int docid) {
         return FilteredDocIdSet.this.match(docid);
       }
+
+      @Override
+      public long estimatedDocCount() {
+        return _innerIter.estimatedDocCount();
+      }
     };
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java b/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
index 2fc81d7..f47d32c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
@@ -145,7 +145,7 @@ public class FilteredQuery extends Query {
   private static final class QueryFirstScorer extends Scorer {
     private final Scorer scorer;
     private int scorerDoc = -1;
-    private Bits filterbits;
+    private final Bits filterbits;
 
     protected QueryFirstScorer(Weight weight, Bits filterBits, Scorer other) {
       super(weight);
@@ -210,6 +210,11 @@ public class FilteredQuery extends Query {
     public Collection<ChildScorer> getChildren() {
       return Collections.singleton(new ChildScorer(scorer, "FILTERED"));
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return scorer.estimatedDocCount();
+    }
   }
   
   /**
@@ -304,25 +309,10 @@ public class FilteredQuery extends Query {
     public final Collection<ChildScorer> getChildren() {
       return Collections.singleton(new ChildScorer(scorer, "FILTERED"));
     }
-  }
-  
-  // TODO once we have way to figure out if we use RA or LeapFrog we can remove this scorer
-  private static final class PrimaryAdvancedLeapFrogScorer extends LeapFrogScorer {
-    private final int firstFilteredDoc;
-
-    protected PrimaryAdvancedLeapFrogScorer(Weight weight, int firstFilteredDoc, DocIdSetIterator filterIter, Scorer other) {
-      super(weight, filterIter, other, other);
-      this.firstFilteredDoc = firstFilteredDoc;
-      this.primaryDoc = firstFilteredDoc; // initialize to prevent and advance call to move it further
-    }
 
     @Override
-    protected int primaryNext() throws IOException {
-      if (secondaryDoc != -1) {
-        return super.primaryNext();
-      } else {
-        return firstFilteredDoc;
-      }
+    public long estimatedDocCount() {
+      return primary.estimatedDocCount();
     }
   }
   
@@ -407,7 +397,7 @@ public class FilteredQuery extends Query {
    * A {@link FilterStrategy} that conditionally uses a random access filter if
    * the given {@link DocIdSet} supports random access (returns a non-null value
    * from {@link DocIdSet#bits()}) and
-   * {@link RandomAccessFilterStrategy#useRandomAccess(Bits, int)} returns
+   * {@link RandomAccessFilterStrategy#useRandomAccess(Bits, long, int)} returns
    * <code>true</code>. Otherwise this strategy falls back to a "zig-zag join" (
    * {@link FilteredQuery#LEAP_FROG_FILTER_FIRST_STRATEGY}) strategy.
    * 
@@ -491,7 +481,7 @@ public class FilteredQuery extends Query {
    * A {@link FilterStrategy} that conditionally uses a random access filter if
    * the given {@link DocIdSet} supports random access (returns a non-null value
    * from {@link DocIdSet#bits()}) and
-   * {@link RandomAccessFilterStrategy#useRandomAccess(Bits, int)} returns
+   * {@link RandomAccessFilterStrategy#useRandomAccess(Bits, long, int)} returns
    * <code>true</code>. Otherwise this strategy falls back to a "zig-zag join" (
    * {@link FilteredQuery#LEAP_FROG_FILTER_FIRST_STRATEGY}) strategy .
    */
@@ -505,24 +495,29 @@ public class FilteredQuery extends Query {
         return null;
       }  
 
-      final int firstFilterDoc = filterIter.nextDoc();
-      if (firstFilterDoc == DocIdSetIterator.NO_MORE_DOCS) {
-        return null;
-      }
-      
       final Bits filterAcceptDocs = docIdSet.bits();
         // force if RA is requested
-      final boolean useRandomAccess = (filterAcceptDocs != null && (useRandomAccess(filterAcceptDocs, firstFilterDoc)));
+      final boolean useRandomAccess = (filterAcceptDocs != null && (useRandomAccess(filterAcceptDocs, filterIter.estimatedDocCount(), context.reader().maxDoc())));
       if (useRandomAccess) {
         // if we are using random access, we return the inner scorer, just with other acceptDocs
         return weight.scorer(context, scoreDocsInOrder, topScorer, filterAcceptDocs);
       } else {
-        assert firstFilterDoc > -1;
         // we are gonna advance() this scorer, so we set inorder=true/toplevel=false
         // we pass null as acceptDocs, as our filter has already respected acceptDocs, no need to do twice
         final Scorer scorer = weight.scorer(context, true, false, null);
-        // TODO once we have way to figure out if we use RA or LeapFrog we can remove this scorer
-        return (scorer == null) ? null : new PrimaryAdvancedLeapFrogScorer(weight, firstFilterDoc, filterIter, scorer);
+        if (scorer == null) {
+          return null;
+        }
+        final DocIdSetIterator primary;
+        final DocIdSetIterator secondary;
+        if (scorer.estimatedDocCount() < filterIter.estimatedDocCount()) {
+          primary = scorer;
+          secondary = filterIter;
+        } else {
+          primary = filterIter;
+          secondary = scorer;
+        }
+        return new LeapFrogScorer(weight, primary, secondary, scorer);
       }
     }
     
@@ -533,14 +528,14 @@ public class FilteredQuery extends Query {
      * However, when the filter is very sparse, it can be faster to execute the query+filter
      * as a conjunction in some cases.
      * 
-     * The default implementation returns <code>true</code> if the first document accepted by the
-     * filter is < 100.
+     * The default implementation returns <code>true</code> if the estimated doc count &gt; 10% of the
+     * number of documents in the index.
      * 
      * @lucene.internal
      */
-    protected boolean useRandomAccess(Bits bits, int firstFilterDoc) {
-      //TODO once we have a cost API on filters and scorers we should rethink this heuristic
-      return firstFilterDoc < 100;
+    protected boolean useRandomAccess(Bits bits, long estimatedDocCount, int maxDoc) {
+      // nocommit - check what benchmarks say when to apply random access
+      return (estimatedDocCount / (double)maxDoc) > 0.2;
     }
   }
   
diff --git a/lucene/core/src/java/org/apache/lucene/search/FilteredScorer.java b/lucene/core/src/java/org/apache/lucene/search/FilteredScorer.java
new file mode 100644
index 0000000..59d5dda
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/FilteredScorer.java
@@ -0,0 +1,74 @@
+package org.apache.lucene.search;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.util.Collection;
+
+/**  A <code>FilteredScorer</code> contains another Scorer, which it
+ * uses as its basic source of data, possibly transforming the data along the
+ * way or providing additional functionality. The class
+ * <code>FilteredScorer</code> itself simply implements all abstract methods
+ * of <code>Scorer</code> with versions that pass all requests to the
+ * contained scorer. Subclasses of <code>FilteredScorer</code> may
+ * further override some of these methods and may also provide additional
+ * methods and fields.
+ */
+public abstract class FilteredScorer extends Scorer {
+  protected final Scorer in;
+  
+  public FilteredScorer(Scorer in) {
+    super(in.weight);
+    this.in = in;
+  }
+
+  @Override
+  public float score() throws IOException {
+    return in.score();
+  }
+
+  @Override
+  public int freq() throws IOException {
+    return in.freq();
+  }
+
+  @Override
+  public Collection<ChildScorer> getChildren() {
+    return in.getChildren();
+  }
+
+  @Override
+  public long estimatedDocCount() {
+    return in.estimatedDocCount();
+  }
+
+  @Override
+  public int docID() {
+    return in.docID();
+  }
+
+  @Override
+  public int nextDoc() throws IOException {
+    return in.nextDoc();
+  }
+
+  @Override
+  public int advance(int target) throws IOException {
+    return in.advance(target);
+  }
+}
\ No newline at end of file
diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
index 80af684..7c1a345 100644
--- a/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
+++ b/lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
@@ -771,6 +771,11 @@ public class IndexSearcher {
       public float score() {
         return score;
       }
+
+      @Override
+      public long estimatedDocCount() {
+        return 0;
+      }
     }
 
     private final FakeScorer fakeScorer = new FakeScorer();
diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
index 633586e..d8bceec 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
@@ -77,6 +77,11 @@ public class MatchAllDocsQuery extends Query {
       doc = target-1;
       return nextDoc();
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return maxDoc;
+    }
   }
 
   private class MatchAllDocsWeight extends Weight {
diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
index 4faf7d2..0a6d551 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
@@ -569,4 +569,10 @@ class UnionDocsAndPositionsEnum extends DocsAndPositionsEnum {
   public final int docID() {
     return _doc;
   }
+
+  @Override
+  public long estimatedDocCount() {
+    // this query is nuts - no idea how to estimate the cost here
+    return Integer.MAX_VALUE;
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java b/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
index 2b53aeb..2fab28b 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
@@ -111,7 +111,7 @@ class ReqExclScorer extends Scorer {
 
   @Override
   public Collection<ChildScorer> getChildren() {
-    return Collections.singleton(new ChildScorer(reqScorer, "FILTERED"));
+    return Collections.singleton(new ChildScorer(reqScorer, "MUST"));
   }
 
   @Override
@@ -128,4 +128,9 @@ class ReqExclScorer extends Scorer {
     }
     return doc = toNonExcluded();
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return reqScorer.estimatedDocCount();
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java b/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
index 8ed6e27..50f7141 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
@@ -99,5 +99,10 @@ class ReqOptSumScorer extends Scorer {
     children.add(new ChildScorer(optScorer, "SHOULD"));
     return children;
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return reqScorer.estimatedDocCount();
+  }
 }
 
diff --git a/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java b/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
index 4144237..9c40e40 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
@@ -32,28 +32,21 @@ import java.util.Collections;
  * several places, however all they have in hand is a {@link Scorer} object, and
  * might end up computing the score of a document more than once.
  */
-public class ScoreCachingWrappingScorer extends Scorer {
+public class ScoreCachingWrappingScorer extends FilteredScorer {
 
-  private final Scorer scorer;
   private int curDoc = -1;
   private float curScore;
   
   /** Creates a new instance by wrapping the given scorer. */
-  public ScoreCachingWrappingScorer(Scorer scorer) {
-    super(scorer.weight);
-    this.scorer = scorer;
+  public ScoreCachingWrappingScorer(Scorer in) {
+    super(in);
   }
 
   @Override
-  public boolean score(Collector collector, int max, int firstDocID) throws IOException {
-    return scorer.score(collector, max, firstDocID);
-  }
-  
-  @Override
   public float score() throws IOException {
-    int doc = scorer.docID();
+    int doc = in.docID();
     if (doc != curDoc) {
-      curScore = scorer.score();
+      curScore = in.score();
       curDoc = doc;
     }
     
@@ -62,31 +55,31 @@ public class ScoreCachingWrappingScorer extends Scorer {
 
   @Override
   public int freq() throws IOException {
-    return scorer.freq();
+    return in.freq();
   }
 
   @Override
   public int docID() {
-    return scorer.docID();
+    return in.docID();
   }
 
   @Override
   public int nextDoc() throws IOException {
-    return scorer.nextDoc();
+    return in.nextDoc();
   }
   
   @Override
   public void score(Collector collector) throws IOException {
-    scorer.score(collector);
+    in.score(collector);
   }
   
-  @Override
-  public int advance(int target) throws IOException {
-    return scorer.advance(target);
+  public boolean score(Collector collector, int max, int firstDocID)
+      throws IOException {
+    return in.score(collector, max, firstDocID);
   }
 
   @Override
   public Collection<ChildScorer> getChildren() {
-    return Collections.singleton(new ChildScorer(scorer, "CACHED"));
+    return Collections.singleton(new ChildScorer(in, "CACHED"));
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
index f810af2..8f92a44 100644
--- a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
@@ -48,6 +48,7 @@ final class SloppyPhraseScorer extends Scorer {
   private PhrasePositions[][] rptGroups; // in each group are PPs that repeats each other (i.e. same term), sorted by (query) offset 
   private PhrasePositions[] rptStack; // temporary stack for switching colliding repeating pps 
   
+  private final long cost;
   private int numMatches;
   
   SloppyPhraseScorer(Weight weight, PhraseQuery.PostingsAndFreq[] postings,
@@ -57,6 +58,9 @@ final class SloppyPhraseScorer extends Scorer {
     this.slop = slop;
     this.numPostings = postings==null ? 0 : postings.length;
     pq = new PhraseQueue(postings.length);
+    // minimum docfreq * # of terms
+    // TODO: should we use totalTermFreq instead?
+    cost = postings[0].docFreq * (long)postings.length;
     // convert tps to a list of phrase positions.
     // note: phrase-position differs from term-position in that its position
     // reflects the phrase offset: pp.pos = tp.pos - offset.
@@ -511,6 +515,10 @@ final class SloppyPhraseScorer extends Scorer {
   }
 
   @Override
+  public long estimatedDocCount() {
+    return cost;
+  }
+  
   public int freq() {
     return numMatches;
   }
diff --git a/lucene/core/src/java/org/apache/lucene/search/TermScorer.java b/lucene/core/src/java/org/apache/lucene/search/TermScorer.java
index 3ccc55e..534001b 100644
--- a/lucene/core/src/java/org/apache/lucene/search/TermScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/TermScorer.java
@@ -106,4 +106,9 @@ final class TermScorer extends Scorer {
   int getDocFreq() {
     return docFreq;
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return docFreq;
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
index cd59463..72b1b08 100644
--- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
@@ -107,4 +107,9 @@ public class SpanScorer extends Scorer {
   public float sloppyFreq() throws IOException {
     return freq;
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return Integer.MAX_VALUE; // ;)
+  }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java b/lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java
index 1470de2..7e9a694 100644
--- a/lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java
+++ b/lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java
@@ -95,5 +95,11 @@ public class DocIdBitSet extends DocIdSet implements Bits {
       docId = d == -1 ? NO_MORE_DOCS : d;
       return docId;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      // cardinality is too expensive - lets do an upper bound
+      return bitSet.length();
+    }
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/util/OpenBitSetIterator.java b/lucene/core/src/java/org/apache/lucene/util/OpenBitSetIterator.java
index cd0ce37..fe38be5 100644
--- a/lucene/core/src/java/org/apache/lucene/util/OpenBitSetIterator.java
+++ b/lucene/core/src/java/org/apache/lucene/util/OpenBitSetIterator.java
@@ -17,6 +17,8 @@
 
 package org.apache.lucene.util;
 
+import java.util.concurrent.TimeUnit;
+
 import org.apache.lucene.search.DocIdSetIterator;
 
 /** An iterator to iterate over set bits in an OpenBitSet.
@@ -189,5 +191,16 @@ public class OpenBitSetIterator extends DocIdSetIterator {
   public int docID() {
     return curDocId;
   }
-  
+
+  @Override
+  public long estimatedDocCount() {
+    /*
+     *  nocommit 
+     *  this is a true uppper bound - we could either calculate the real number of bits via:
+     *      BitUtil.pop_array(arr, 0, words);
+     *  or we estimate the number of bits here by using adaptive sampling. 
+     */
+
+    return Integer.MAX_VALUE;
+  }
 }
diff --git a/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java b/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
index 80d9c46..7c52083 100644
--- a/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
+++ b/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
@@ -87,6 +87,11 @@ final class JustCompileSearch {
     public int advance(int target) {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
+
+    @Override
+    public long estimatedDocCount() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
   }
   
   static final class JustCompileExtendedFieldCacheLongParser implements FieldCache.LongParser {
@@ -186,6 +191,11 @@ final class JustCompileSearch {
     protected boolean match(int doc) {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
+
+    @Override
+    public long estimatedDocCount() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
     
   }
 
@@ -233,6 +243,11 @@ final class JustCompileSearch {
     public int advance(int target) {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
+
+    @Override
+    public long estimatedDocCount() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
   }
   
   static final class JustCompileSimilarity extends Similarity {
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
index 89651ab..0d43be0 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
@@ -92,6 +92,7 @@ public class TestBooleanScorer extends LuceneTestCase
         return doc = target <= 3000 ? 3000 : NO_MORE_DOCS;
       }
       
+      @Override public long estimatedDocCount() { return 1; }
     }};
     
     BooleanScorer bs = new BooleanScorer(weight, false, 1, Arrays.asList(scorers), null, scorers.length);
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java b/lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java
index 23216e8..5f885f0 100755
--- a/lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java
@@ -47,6 +47,9 @@ public class TestCachingCollector extends LuceneTestCase {
     @Override
     public int advance(int target) throws IOException { return 0; }
     
+    @Override 
+    public long estimatedDocCount() { return 1; }
+    
   }
   
   private static class NoOpCollector extends Collector {
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestDocIdSet.java b/lucene/core/src/test/org/apache/lucene/search/TestDocIdSet.java
index 98fe6fb..3862384 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestDocIdSet.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestDocIdSet.java
@@ -59,6 +59,11 @@ public class TestDocIdSet extends LuceneTestCase {
               while (nextDoc() < target) {}
               return docid;
             }
+
+            @Override
+            public long estimatedDocCount() {
+              return maxdoc;
+            }
           };
         } 
       };
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java
index 55381cb..95258ec 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java
@@ -373,10 +373,13 @@ public class TestFilteredQuery extends LuceneTestCase {
   private static FilteredQuery.FilterStrategy randomFilterStrategy(Random random, final boolean useRandomAccess) {
     if (useRandomAccess) {
       return  new FilteredQuery.RandomAccessFilterStrategy() {
+
         @Override
-        protected boolean useRandomAccess(Bits bits, int firstFilterDoc) {
+        protected boolean useRandomAccess(Bits bits, long estimatedCost,
+            int maxDoc) {
           return useRandomAccess;
         }
+        
       };
     }
     return _TestUtil.randomFilterStrategy(random);
@@ -522,6 +525,11 @@ public class TestFilteredQuery extends LuceneTestCase {
                 advanceCalled = true;
                 return termDocsEnum.advance(target);
               }
+
+              @Override
+              public long estimatedDocCount() {
+                return termDocsEnum.estimatedDocCount();
+              }
             };
           }
           
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java b/lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
index ba92c0f..8547be8 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
@@ -50,6 +50,8 @@ public class TestPositiveScoresOnlyCollector extends LuceneTestCase {
       idx = target;
       return idx < scores.length ? idx : NO_MORE_DOCS;
     }
+    
+    @Override public long estimatedDocCount() { return 1; }
   }
 
   // The scores must have positive as well as negative values
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
index 4701901..092dd7f 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
@@ -59,6 +59,8 @@ public class TestScoreCachingWrappingScorer extends LuceneTestCase {
       return doc < scores.length ? doc : NO_MORE_DOCS;
     }
     
+    @Override public long estimatedDocCount() { return 1; }
+    
   }
   
   private static final class ScoreCachingCollector extends Collector {
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/util/ScoredDocIdsUtils.java b/lucene/facet/src/java/org/apache/lucene/facet/util/ScoredDocIdsUtils.java
index cd83769..eeac9c9 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/util/ScoredDocIdsUtils.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/util/ScoredDocIdsUtils.java
@@ -150,6 +150,11 @@ public class ScoredDocIdsUtils {
                 return docids[next];
               }
 
+              @Override
+              public long estimatedDocCount() {
+                return size;
+              }
+
             };
           }
         };
@@ -274,6 +279,11 @@ public class ScoredDocIdsUtils {
               return ++next < maxDoc ? next : NO_MORE_DOCS;
             }
 
+            @Override
+            public long estimatedDocCount() {
+              return maxDoc;
+            }
+
           };
         }
       };
@@ -364,6 +374,11 @@ public class ScoredDocIdsUtils {
               return next < maxDoc ? next : NO_MORE_DOCS;
             }
 
+            @Override
+            public long estimatedDocCount() {
+              return maxDoc;
+            }
+
           };
         }
       };
diff --git a/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java b/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
index 42934b5..f89f941 100644
--- a/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
+++ b/lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
@@ -117,6 +117,11 @@ public class BlockGroupingCollector extends Collector {
     public int nextDoc() {
       throw new UnsupportedOperationException();
     }
+    
+    @Override
+    public long estimatedDocCount() {
+      return 0;
+    }
   }
 
   private static final class OneGroup {
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
index 5e29374..3baa006 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
@@ -125,18 +125,19 @@ class TermsIncludingScoreQuery extends Query {
         if (terms == null) {
           return null;
         }
-
+        // what is the runtime...seems ok?
+        final long cost = context.reader().maxDoc() * terms.size();
         segmentTermsEnum = terms.iterator(segmentTermsEnum);
         if (scoreDocsInOrder) {
           if (multipleValuesPerDocument) {
-            return new MVInOrderScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc());
+            return new MVInOrderScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
           } else {
-            return new SVInOrderScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc());
+            return new SVInOrderScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
           }
         } else if (multipleValuesPerDocument) {
-          return new MVInnerScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc());
+          return new MVInnerScorer(this, acceptDocs, segmentTermsEnum, context.reader().maxDoc(), cost);
         } else {
-          return new SVInnerScorer(this, acceptDocs, segmentTermsEnum);
+          return new SVInnerScorer(this, acceptDocs, segmentTermsEnum, cost);
         }
       }
     };
@@ -153,11 +154,13 @@ class TermsIncludingScoreQuery extends Query {
     DocsEnum docsEnum;
     DocsEnum reuse;
     int scoreUpto;
+    final long cost;
 
-    SVInnerScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum) {
+    SVInnerScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum, long cost) {
       super(weight);
       this.acceptDocs = acceptDocs;
       this.termsEnum = termsEnum;
+      this.cost = cost;
     }
 
     public float score() throws IOException {
@@ -222,6 +225,11 @@ class TermsIncludingScoreQuery extends Query {
     public int freq() {
       return 1;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return cost;
+    }
   }
 
   // This impl that tracks whether a docid has already been emitted. This check makes sure that docs aren't emitted
@@ -231,8 +239,8 @@ class TermsIncludingScoreQuery extends Query {
 
     final FixedBitSet alreadyEmittedDocs;
 
-    MVInnerScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum, int maxDoc) {
-      super(weight, acceptDocs, termsEnum);
+    MVInnerScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum, int maxDoc, long cost) {
+      super(weight, acceptDocs, termsEnum, cost);
       alreadyEmittedDocs = new FixedBitSet(maxDoc);
     }
 
@@ -288,13 +296,15 @@ class TermsIncludingScoreQuery extends Query {
     final float[] scores;
 
     int currentDoc = -1;
+    final long cost;
 
-    SVInOrderScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum, int maxDoc) throws IOException {
+    SVInOrderScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum, int maxDoc, long cost) throws IOException {
       super(weight);
       FixedBitSet matchingDocs = new FixedBitSet(maxDoc);
       this.scores = new float[maxDoc];
       fillDocsAndScores(matchingDocs, acceptDocs, termsEnum);
       this.matchingDocsIterator = matchingDocs.iterator();
+      this.cost = cost;
     }
 
     protected void fillDocsAndScores(FixedBitSet matchingDocs, Bits acceptDocs, TermsEnum termsEnum) throws IOException {
@@ -333,13 +343,18 @@ class TermsIncludingScoreQuery extends Query {
     public int advance(int target) throws IOException {
       return currentDoc = matchingDocsIterator.advance(target);
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return cost;
+    }
   }
 
   // This scorer deals with the fact that a document can have more than one score from multiple related documents.
   class MVInOrderScorer extends SVInOrderScorer {
 
-    MVInOrderScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum, int maxDoc) throws IOException {
-      super(weight, acceptDocs, termsEnum, maxDoc);
+    MVInOrderScorer(Weight weight, Bits acceptDocs, TermsEnum termsEnum, int maxDoc, long cost) throws IOException {
+      super(weight, acceptDocs, termsEnum, maxDoc, cost);
     }
 
     @Override
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
index 7ca55c1..1275028 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
@@ -301,6 +301,11 @@ public class ToChildBlockJoinQuery extends Query {
       }
       return childDoc;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return parentScorer.estimatedDocCount();
+    }
   }
 
   @Override
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
index 583eee5..868a9c8 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
@@ -347,6 +347,11 @@ public class ToParentBlockJoinCollector extends Collector {
     public int nextDoc() {
       throw new UnsupportedOperationException();
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return 1;
+    }
   }
 
   private OneGroup[] sortedGroups;
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
index 8ce4779..4758b5b 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
@@ -409,6 +409,11 @@ public class ToParentBlockJoinQuery extends Query {
       );
     }
 
+    @Override
+    public long estimatedDocCount() {
+      return childScorer.estimatedDocCount();
+    }
+
   }
 
   @Override
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 cbcb82f..1307d9c 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
@@ -997,6 +997,11 @@ public class MemoryIndex {
       public int freq() throws IOException {
         return freq;
       }
+
+      @Override
+      public long estimatedDocCount() {
+        return 1;
+      }
     }
     
     private class MemoryDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -1077,6 +1082,11 @@ public class MemoryIndex {
       public BytesRef getPayload() {
         return null;
       }
+
+      @Override
+      public long estimatedDocCount() {
+        return 1;
+      }
     }
     
     @Override
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
index 744869b..2f47d27 100755
--- a/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
@@ -346,6 +346,11 @@ public class CustomScoreQuery extends Query {
       }
       return doc;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return subQueryScorer.estimatedDocCount();
+    }
   }
 
   @Override
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
index 0bee440..5da52a1 100755
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
@@ -188,6 +188,11 @@ public class BoostedQuery extends Query {
       res.addDetail(vals.explain(doc));
       return res;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return scorer.estimatedDocCount();
+    }
   }
 
 
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
index 35d951b..910506f 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
@@ -174,6 +174,11 @@ public class FunctionQuery extends Query {
       result.addDetail(new Explanation(weight.queryNorm,"queryNorm"));
       return result;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return maxDoc;
+    }
   }
 
 
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
index d1b843d..cbaa321 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
@@ -91,4 +91,9 @@ public class ValueSourceScorer extends Scorer {
   public int freq() throws IOException {
     return 1;
   }
+
+  @Override
+  public long estimatedDocCount() {
+    return maxDoc;
+  }
 }
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java
index f0e4a9c..7ef63ab 100755
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java
@@ -97,6 +97,11 @@ public class TFValueSource extends TermFreqValueSource {
             public int advance(int target) {
               return DocIdSetIterator.NO_MORE_DOCS;
             }
+
+            @Override
+            public long estimatedDocCount() {
+              return 0;
+            }
           };
         }
         atDoc = -1;
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java
index c2b0654..bfc088c 100755
--- a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java
@@ -90,6 +90,11 @@ public class TermFreqValueSource extends DocFreqValueSource {
             public int advance(int target) {
               return DocIdSetIterator.NO_MORE_DOCS;
             }
+
+            @Override
+            public long estimatedDocCount() {
+              return 0;
+            }
           };
         }
         atDoc = -1;
diff --git a/lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java b/lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java
index fddd4da..daac026 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java
@@ -432,6 +432,11 @@ public final class RAMOnlyPostingsFormat extends PostingsFormat {
     public int docID() {
       return current.docID;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return ramTerm.docs.size();
+    }
   }
 
   private static class RAMDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -504,6 +509,11 @@ public final class RAMOnlyPostingsFormat extends PostingsFormat {
         return null;
       }
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return ramTerm.docs.size();
+    }
   }
 
   // Holds all indexes created, keyed by the ID assigned in fieldsConsumer
diff --git a/lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java
index bb1f7c1..50a9e63 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java
@@ -279,6 +279,11 @@ public abstract class BasePostingsFormatTestCase extends LuceneTestCase {
       }
       return docID;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return docFreq;
+    }
   }
   
   private static class FieldAndTerm {
diff --git a/lucene/test-framework/src/java/org/apache/lucene/util/_TestUtil.java b/lucene/test-framework/src/java/org/apache/lucene/util/_TestUtil.java
index d2760ae..023c9cd 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/util/_TestUtil.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/util/_TestUtil.java
@@ -994,7 +994,8 @@ public class _TestUtil {
       case 4:
         return new FilteredQuery.RandomAccessFilterStrategy() {
           @Override
-          protected boolean useRandomAccess(Bits bits, int firstFilterDoc) {
+          protected boolean useRandomAccess(Bits bits, long estimatedCost,
+              int maxDoc) {
             return LuceneTestCase.random().nextBoolean();
           }
         };
diff --git a/solr/core/src/java/org/apache/solr/schema/LatLonType.java b/solr/core/src/java/org/apache/solr/schema/LatLonType.java
index d9fe739..7c96b73 100644
--- a/solr/core/src/java/org/apache/solr/schema/LatLonType.java
+++ b/solr/core/src/java/org/apache/solr/schema/LatLonType.java
@@ -504,6 +504,11 @@ class SpatialDistanceQuery extends ExtendedQueryBase implements PostFilter {
       result.addDetail(new Explanation(weight.queryNorm,"queryNorm"));
       return result;
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return maxDoc;
+    }
   }
 
   @Override
diff --git a/solr/core/src/java/org/apache/solr/search/BitDocSet.java b/solr/core/src/java/org/apache/solr/search/BitDocSet.java
index 2a8aecf..2697561 100644
--- a/solr/core/src/java/org/apache/solr/search/BitDocSet.java
+++ b/solr/core/src/java/org/apache/solr/search/BitDocSet.java
@@ -243,6 +243,7 @@ public class BitDocSet extends DocSetBase {
     final OpenBitSet bs = bits;
     // TODO: if cardinality isn't cached, do a quick measure of sparseness
     // and return null from bits() if too sparse.
+    final int cost = size();
 
     return new Filter() {
       @Override
@@ -258,7 +259,7 @@ public class BitDocSet extends DocSetBase {
         final int base = context.docBase;
         final int maxDoc = reader.maxDoc();
         final int max = base + maxDoc;   // one past the max doc in this segment.
-
+        
         return BitsFilteredDocIdSet.wrap(new DocIdSet() {
           @Override
           public DocIdSetIterator iterator() {
@@ -283,6 +284,11 @@ public class BitDocSet extends DocSetBase {
                 pos = bs.nextSetBit(target+base);
                 return adjustedDoc = (pos>=0 && pos<max) ? pos-base : NO_MORE_DOCS;
               }
+
+              @Override
+              public long estimatedDocCount() {
+                return cost;
+              }
             };
           }
 
diff --git a/solr/core/src/java/org/apache/solr/search/DocSetBase.java b/solr/core/src/java/org/apache/solr/search/DocSetBase.java
index c5b7518..f04bf07 100644
--- a/solr/core/src/java/org/apache/solr/search/DocSetBase.java
+++ b/solr/core/src/java/org/apache/solr/search/DocSetBase.java
@@ -154,7 +154,8 @@ abstract class DocSetBase implements DocSet {
         return BitsFilteredDocIdSet.wrap(new DocIdSet() {
           @Override
           public DocIdSetIterator iterator() {
-            return new DocIdSetIterator() {
+            return new DocIdSetIterator() {            
+              final long cost = bs.iterator().estimatedDocCount();
               int pos=base-1;
               int adjustedDoc=-1;
 
@@ -175,6 +176,11 @@ abstract class DocSetBase implements DocSet {
                 pos = bs.nextSetBit(target+base);
                 return adjustedDoc = (pos>=0 && pos<max) ? pos-base : NO_MORE_DOCS;
               }
+
+              @Override
+              public long estimatedDocCount() {
+                return cost;
+              }
             };
           }
 
diff --git a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
index 682268a..ef2a854 100644
--- a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
@@ -248,7 +248,7 @@ class JoinQuery extends Query {
       // Although this set only includes live docs, other filters can be pushed down to queries.
       DocIdSet readerSet = filter.getDocIdSet(context, acceptDocs);
       if (readerSet == null) readerSet=DocIdSet.EMPTY_DOCIDSET;
-      return new JoinScorer(this, readerSet.iterator(), getBoost());
+      return new JoinScorer(this, readerSet.iterator(), getBoost(), context.reader().maxDoc());
     }
 
 
@@ -510,10 +510,12 @@ class JoinQuery extends Query {
     final DocIdSetIterator iter;
     final float score;
     int doc = -1;
+    final long cost;
 
-    public JoinScorer(Weight w, DocIdSetIterator iter, float score) throws IOException {
+    public JoinScorer(Weight w, DocIdSetIterator iter, float score, long cost) throws IOException {
       super(w);
       this.score = score;
+      this.cost = cost;
       this.iter = iter==null ? DocIdSet.EMPTY_DOCIDSET.iterator() : iter;
     }
 
@@ -541,6 +543,11 @@ class JoinQuery extends Query {
     public int advance(int target) throws IOException {
       return iter.advance(target);
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return cost;
+    }
   }
 
 
diff --git a/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java b/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
index c081574..7f4188e 100755
--- a/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
@@ -153,6 +153,7 @@ public class SolrConstantScoreQuery extends ConstantScoreQuery implements Extend
     final DocIdSetIterator docIdSetIterator;
     final float theScore;
     final Bits acceptDocs;
+    final long cost;
     int doc = -1;
 
     public ConstantScorer(AtomicReaderContext context, ConstantWeight w, float theScore, Bits acceptDocs) throws IOException {
@@ -162,12 +163,15 @@ public class SolrConstantScoreQuery extends ConstantScoreQuery implements Extend
       DocIdSet docIdSet = filter instanceof SolrFilter ? ((SolrFilter)filter).getDocIdSet(w.context, context, acceptDocs) : filter.getDocIdSet(context, acceptDocs);
       if (docIdSet == null) {
         docIdSetIterator = DocIdSet.EMPTY_DOCIDSET.iterator();
+        cost = 0;
       } else {
         DocIdSetIterator iter = docIdSet.iterator();
         if (iter == null) {
           docIdSetIterator = DocIdSet.EMPTY_DOCIDSET.iterator();
+          cost = 0;
         } else {
           docIdSetIterator = iter;
+          cost = context.reader().maxDoc();
         }
       }
     }
@@ -196,6 +200,11 @@ public class SolrConstantScoreQuery extends ConstantScoreQuery implements Extend
     public int advance(int target) throws IOException {
       return docIdSetIterator.advance(target);
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return cost;
+    }
   }
 
   @Override
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 0116658..c167294 100644
--- a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
+++ b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
@@ -2252,6 +2252,11 @@ class FilterImpl extends Filter {
     public int advance(int target) throws IOException {
       return doNext(first.advance(target));
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return first.estimatedDocCount();
+    }
   }
 
   private static class DualFilterIterator extends DocIdSetIterator {
@@ -2289,6 +2294,11 @@ class FilterImpl extends Filter {
         if (other == doc) return doc;
       }
     }
+
+    @Override
+    public long estimatedDocCount() {
+      return Math.min(a.estimatedDocCount(), b.estimatedDocCount());
+    }
   }
 
 }
diff --git a/solr/core/src/java/org/apache/solr/search/SortedIntDocSet.java b/solr/core/src/java/org/apache/solr/search/SortedIntDocSet.java
index 754fd14..c3620de 100755
--- a/solr/core/src/java/org/apache/solr/search/SortedIntDocSet.java
+++ b/solr/core/src/java/org/apache/solr/search/SortedIntDocSet.java
@@ -745,6 +745,11 @@ public class SortedIntDocSet extends DocSetBase {
                 }
               }
 
+              @Override
+              public long estimatedDocCount() {
+                return endIdx - startIdx;
+              }
+
             };
           }
 
