Index: lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
===================================================================
--- lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java	(revision 1455706)
+++ lucene/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java	(working copy)
@@ -117,6 +117,11 @@
     public int nextDoc() {
       throw new UnsupportedOperationException();
     }
+
+    @Override
+    public long cost() {
+      return 1;
+    }
   }
 
   private static final class OneGroup {
Index: lucene/codecs/src/java/org/apache/lucene/codecs/pulsing/PulsingPostingsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/pulsing/PulsingPostingsReader.java	(revision 1455706)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/pulsing/PulsingPostingsReader.java	(working copy)
@@ -260,6 +260,7 @@
     private int accum;
     private int freq;
     private int payloadLength;
+    private int cost;
 
     public PulsingDocsEnum(FieldInfo fieldInfo) {
       indexOptions = fieldInfo.getIndexOptions();
@@ -283,6 +284,7 @@
       docID = -1;
       accum = 0;
       freq = 1;
+      cost = termState.docFreq;
       payloadLength = 0;
       this.liveDocs = liveDocs;
       return this;
@@ -367,6 +369,11 @@
       }
       return docID = NO_MORE_DOCS;
     }
+    
+    @Override
+    public long cost() {
+      return cost;
+    }
   }
 
   private static class PulsingDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -390,6 +397,7 @@
     private int offsetLength;
 
     private boolean payloadRetrieved;
+    private int cost;
 
     public PulsingDocsAndPositionsEnum(FieldInfo fieldInfo) {
       indexOptions = fieldInfo.getIndexOptions();
@@ -415,6 +423,7 @@
       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 @@
         return null;
       }
     }
+    
+    @Override
+    public long cost() {
+      return cost;
+    }
   }
 
   @Override
Index: lucene/codecs/src/java/org/apache/lucene/codecs/sep/SepPostingsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/sep/SepPostingsReader.java	(revision 1455706)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/sep/SepPostingsReader.java	(working copy)
@@ -441,6 +441,11 @@
 
       return doc;
     }
+    
+    @Override
+    public long cost() {
+      return docFreq;
+    }
   }
 
   class SepDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -717,5 +722,10 @@
       pendingPayloadBytes = 0;
       return payload;
     }
+    
+    @Override
+    public long cost() {
+      return docFreq;
+    }
   }
 }
Index: lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java	(revision 1455706)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextFieldsReader.java	(working copy)
@@ -199,7 +199,7 @@
       } else {
         docsEnum = new SimpleTextDocsEnum();
       }
-      return docsEnum.reset(docsStart, liveDocs, indexOptions == IndexOptions.DOCS_ONLY);
+      return docsEnum.reset(docsStart, liveDocs, indexOptions == IndexOptions.DOCS_ONLY, docFreq);
     }
 
     @Override
@@ -216,7 +216,7 @@
       } else {
         docsAndPositionsEnum = new SimpleTextDocsAndPositionsEnum();
       } 
-      return docsAndPositionsEnum.reset(docsStart, liveDocs, indexOptions);
+      return docsAndPositionsEnum.reset(docsStart, liveDocs, indexOptions, docFreq);
     }
     
     @Override
@@ -234,6 +234,7 @@
     private Bits liveDocs;
     private final BytesRef scratch = new BytesRef(10);
     private final CharsRef scratchUTF16 = new CharsRef(10);
+    private int cost;
     
     public SimpleTextDocsEnum() {
       this.inStart = SimpleTextFieldsReader.this.in;
@@ -244,12 +245,13 @@
       return in == inStart;
     }
 
-    public SimpleTextDocsEnum reset(long fp, Bits liveDocs, boolean omitTF) throws IOException {
+    public SimpleTextDocsEnum reset(long fp, Bits liveDocs, boolean omitTF, int docFreq) throws IOException {
       this.liveDocs = liveDocs;
       in.seek(fp);
       this.omitTF = omitTF;
       docID = -1;
       tf = 1;
+      cost = docFreq;
       return this;
     }
 
@@ -316,6 +318,11 @@
       while(nextDoc() < target);
       return docID;
     }
+    
+    @Override
+    public long cost() {
+      return cost;
+    }
   }
 
   private class SimpleTextDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -334,6 +341,7 @@
     private boolean readPositions;
     private int startOffset;
     private int endOffset;
+    private int cost;
 
     public SimpleTextDocsAndPositionsEnum() {
       this.inStart = SimpleTextFieldsReader.this.in;
@@ -344,7 +352,7 @@
       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;
@@ -354,6 +362,7 @@
         startOffset = -1;
         endOffset = -1;
       }
+      cost = docFreq;
       return this;
     }
 
@@ -471,6 +480,11 @@
     public BytesRef getPayload() {
       return payload;
     }
+    
+    @Override
+    public long cost() {
+      return cost;
+    }
   }
 
   static class TermData {
Index: lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java	(revision 1455706)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/simpletext/SimpleTextTermVectorsReader.java	(working copy)
@@ -444,6 +444,11 @@
       this.doc = -1;
       didNext = false;
     }
+    
+    @Override
+    public long cost() {
+      return 1;
+    }
   }
   
   private static class SimpleTVDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -535,5 +540,10 @@
         return endOffsets[nextPos-1];
       }
     }
+    
+    @Override
+    public long cost() {
+      return 1;
+    }
   }
 }
Index: lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java	(revision 1455706)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/memory/MemoryPostingsFormat.java	(working copy)
@@ -431,6 +431,11 @@
     public int freq() {
       return freq;
     }
+    
+    @Override
+    public long cost() {
+      return numDocs;
+    }
   }
 
   private final static class FSTDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -618,6 +623,11 @@
     public int freq() {
       return freq;
     }
+    
+    @Override
+    public long cost() {
+      return numDocs;
+    }
   }
 
   private final static class FSTTermsEnum extends TermsEnum {
Index: lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java
===================================================================
--- lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java	(revision 1455706)
+++ lucene/codecs/src/java/org/apache/lucene/codecs/memory/DirectPostingsFormat.java	(working copy)
@@ -1488,6 +1488,11 @@
       }
       return docID();
     }
+    
+    @Override
+    public long cost() {
+      return postings.length;
+    }
   }
 
   // Docs + freqs:
@@ -1553,6 +1558,11 @@
       }
       return docID();
     }
+    
+    @Override
+    public long cost() {
+      return postings.length / 2;
+    }
   }
 
   // Docs + freqs + positions/offets:
@@ -1634,6 +1644,12 @@
       }
       return docID();
     }
+    
+    @Override
+    public long cost() {
+      // TODO: could do a better estimate
+      return postings.length / 2;
+    }
   }
 
   private final static class LowFreqDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -1786,6 +1802,12 @@
         return null;
       }
     }
+    
+    @Override
+    public long cost() {
+      // TODO: could do a better estimate
+      return postings.length / 2;
+    }
   }
 
   // Docs + freqs:
@@ -1959,6 +1981,11 @@
         return docID = docIDs[upto];
       }
     }
+    
+    @Override
+    public long cost() {
+      return docIDs.length;
+    }
   }
 
   // TODO: specialize offsets and not
@@ -2192,5 +2219,10 @@
         return payload;
       }
     }
+    
+    @Override
+    public long cost() {
+      return docIDs.length;
+    }
   }
 }
Index: lucene/facet/src/java/org/apache/lucene/facet/util/ScoredDocIdsUtils.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/util/ScoredDocIdsUtils.java	(revision 1455706)
+++ lucene/facet/src/java/org/apache/lucene/facet/util/ScoredDocIdsUtils.java	(working copy)
@@ -162,6 +162,10 @@
                 return docids[next];
               }
 
+              @Override
+              public long cost() {
+                return size;
+              }
             };
           }
         };
@@ -298,6 +302,10 @@
               return ++next < maxDoc ? next : NO_MORE_DOCS;
             }
 
+            @Override
+            public long cost() {
+              return maxDoc;
+            }
           };
         }
       };
@@ -394,6 +402,10 @@
               return next < maxDoc ? next : NO_MORE_DOCS;
             }
 
+            @Override
+            public long cost() {
+              return maxDoc;
+            }
           };
         }
       };
Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillSidewaysScorer.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/search/DrillSidewaysScorer.java	(revision 1455706)
+++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillSidewaysScorer.java	(working copy)
@@ -615,6 +615,11 @@
   }
 
   @Override
+  public long cost() {
+    return baseScorer.cost();
+  }
+
+  @Override
   public Collection<ChildScorer> getChildren() {
     return Collections.singletonList(new ChildScorer(baseScorer, "MUST"));
   }
Index: lucene/facet/src/java/org/apache/lucene/facet/search/MatchingDocsAsScoredDocIDs.java
===================================================================
--- lucene/facet/src/java/org/apache/lucene/facet/search/MatchingDocsAsScoredDocIDs.java	(revision 1455706)
+++ lucene/facet/src/java/org/apache/lucene/facet/search/MatchingDocsAsScoredDocIDs.java	(working copy)
@@ -153,6 +153,11 @@
           }
           
           @Override
+          public long cost() {
+            return size;
+          }
+
+          @Override
           public int advance(int target) throws IOException {
             throw new UnsupportedOperationException("not supported");
           }
Index: lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java
===================================================================
--- lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java	(revision 1455706)
+++ lucene/memory/src/java/org/apache/lucene/index/memory/MemoryIndex.java	(working copy)
@@ -1020,6 +1020,11 @@
       public int freq() throws IOException {
         return freq;
       }
+
+      @Override
+      public long cost() {
+        return 1;
+      }
     }
     
     private class MemoryDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -1100,6 +1105,11 @@
       public BytesRef getPayload() {
         return null;
       }
+      
+      @Override
+      public long cost() {
+        return 1;
+      }
     }
     
     @Override
Index: lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java
===================================================================
--- lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java	(revision 1455706)
+++ lucene/highlighter/src/java/org/apache/lucene/search/postingshighlight/PostingsHighlighter.java	(working copy)
@@ -492,6 +492,9 @@
 
     @Override
     public int advance(int target) throws IOException { return NO_MORE_DOCS; }
+    
+    @Override
+    public long cost() { return 0; }
   };
   
   /** 
Index: lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java	(revision 1455706)
+++ lucene/test-framework/src/java/org/apache/lucene/index/BasePostingsFormatTestCase.java	(working copy)
@@ -280,6 +280,11 @@
       }
       return docID;
     }
+    
+    @Override
+    public long cost() {
+      return docFreq;
+    } 
   }
   
   private static class FieldAndTerm {
Index: lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java
===================================================================
--- lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java	(revision 1455706)
+++ lucene/test-framework/src/java/org/apache/lucene/codecs/ramonly/RAMOnlyPostingsFormat.java	(working copy)
@@ -433,6 +433,11 @@
     public int docID() {
       return current.docID;
     }
+    
+    @Override
+    public long cost() {
+      return ramTerm.docs.size();
+    } 
   }
 
   private static class RAMDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -505,6 +510,11 @@
         return null;
       }
     }
+    
+    @Override
+    public long cost() {
+      return ramTerm.docs.size();
+    } 
   }
 
   // Holds all indexes created, keyed by the ID assigned in fieldsConsumer
Index: lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java	(revision 1455706)
+++ lucene/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java	(working copy)
@@ -348,6 +348,11 @@
       }
       return doc;
     }
+
+    @Override
+    public long cost() {
+      return subQueryScorer.cost();
+    }
   }
 
   @Override
Index: lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java	(revision 1455706)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java	(working copy)
@@ -159,6 +159,11 @@
     }
 
     @Override
+    public long cost() {
+      return maxDoc;
+    }
+
+    @Override
     public int freq() throws IOException {
       return 1;
     }
Index: lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java	(revision 1455706)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java	(working copy)
@@ -91,4 +91,9 @@
   public int freq() throws IOException {
     return 1;
   }
+
+  @Override
+  public long cost() {
+    return maxDoc;
+  }
 }
Index: lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java	(revision 1455706)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TFValueSource.java	(working copy)
@@ -97,6 +97,11 @@
             public int advance(int target) {
               return DocIdSetIterator.NO_MORE_DOCS;
             }
+
+            @Override
+            public long cost() {
+              return 0;
+            }
           };
         }
         atDoc = -1;
Index: lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java	(revision 1455706)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/TermFreqValueSource.java	(working copy)
@@ -90,6 +90,11 @@
             public int advance(int target) {
               return DocIdSetIterator.NO_MORE_DOCS;
             }
+
+            @Override
+            public long cost() {
+              return 0;
+            }
           };
         }
         atDoc = -1;
Index: lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
===================================================================
--- lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java	(revision 1455706)
+++ lucene/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java	(working copy)
@@ -188,6 +188,11 @@
       res.addDetail(vals.explain(doc));
       return res;
     }
+
+    @Override
+    public long cost() {
+      return scorer.cost();
+    }
   }
 
 
Index: lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java	(working copy)
@@ -75,7 +75,11 @@
     public boolean isPayloadAvailable() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
-    
+
+    @Override
+    public long cost() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
   }
 
   static final class JustCompileSpanQuery extends SpanQuery {
@@ -133,6 +137,11 @@
     public int start() {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
+
+    @Override
+    public long cost() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
     
   }
   
Index: lucene/core/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/spans/MultiSpansWrapper.java	(working copy)
@@ -167,4 +167,9 @@
     return current.isPayloadAvailable();
   }
 
+  @Override
+  public long cost() {
+    return Integer.MAX_VALUE; // just for tests
+  }
+
 }
Index: lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java	(working copy)
@@ -92,6 +92,10 @@
         return doc = target <= 3000 ? 3000 : NO_MORE_DOCS;
       }
       
+      @Override
+      public long cost() {
+        return 1;
+      }
     }};
     
     BooleanScorer bs = new BooleanScorer(weight, false, 1, Arrays.asList(scorers), null, scorers.length);
Index: lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/TestCachingCollector.java	(working copy)
@@ -47,6 +47,10 @@
     @Override
     public int advance(int target) throws IOException { return 0; }
     
+    @Override
+    public long cost() {
+      return 1;
+    } 
   }
   
   private static class NoOpCollector extends Collector {
Index: lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java	(working copy)
@@ -50,6 +50,11 @@
       idx = target;
       return idx < scores.length ? idx : NO_MORE_DOCS;
     }
+    
+    @Override
+    public long cost() {
+      return scores.length;
+    } 
   }
 
   // The scores must have positive as well as negative values
Index: lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java	(working copy)
@@ -88,6 +88,11 @@
     public int advance(int target) {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
+    
+    @Override
+    public long cost() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
   }
   
   static final class JustCompileExtendedFieldCacheLongParser implements FieldCache.LongParser {
@@ -200,6 +205,10 @@
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
     
+    @Override
+    public long cost() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
   }
 
   static final class JustCompileQuery extends Query {
@@ -246,6 +255,11 @@
     public int advance(int target) {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
+    
+    @Override
+    public long cost() {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
   }
   
   static final class JustCompileSimilarity extends Similarity {
Index: lucene/core/src/test/org/apache/lucene/search/TestDocIdSet.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestDocIdSet.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/TestDocIdSet.java	(working copy)
@@ -59,6 +59,11 @@
               while (nextDoc() < target) {}
               return docid;
             }
+            
+            @Override
+            public long cost() {
+              return 1;
+            } 
           };
         } 
       };
Index: lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java	(working copy)
@@ -59,6 +59,10 @@
       return doc < scores.length ? doc : NO_MORE_DOCS;
     }
     
+    @Override
+    public long cost() {
+      return scores.length;
+    }
   }
   
   private static final class ScoreCachingCollector extends Collector {
Index: lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java
===================================================================
--- lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java	(revision 1455706)
+++ lucene/core/src/test/org/apache/lucene/search/TestFilteredQuery.java	(working copy)
@@ -536,6 +536,11 @@
                 advanceCalled = true;
                 return termDocsEnum.advance(target);
               }
+              
+              @Override
+              public long cost() {
+                return termDocsEnum.cost();
+              } 
             };
           }
           
Index: lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/codecs/compressing/CompressingTermVectorsReader.java	(working copy)
@@ -1037,6 +1037,10 @@
       }
     }
 
+    @Override
+    public long cost() {
+      return 1;
+    }
   }
 
   private static int sum(int[] arr) {
Index: lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsAndPositionsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsAndPositionsEnum.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsAndPositionsEnum.java	(working copy)
@@ -134,5 +134,14 @@
   public BytesRef getPayload() throws IOException {
     return current.getPayload();
   }
+
+  @Override
+  public long cost() {
+    long cost = 0;
+    for (EnumWithSlice enumWithSlice : subs) {
+      cost += enumWithSlice.docsAndPositionsEnum.cost();
+    }
+    return cost;
+  }
 }
 
Index: lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsEnum.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/codecs/MappingMultiDocsEnum.java	(working copy)
@@ -114,5 +114,14 @@
       }
     }
   }
+  
+  @Override
+  public long cost() {
+    long cost = 0;
+    for (EnumWithSlice enumWithSlice : subs) {
+      cost += enumWithSlice.docsEnum.cost();
+    }
+    return cost;
+  }
 }
 
Index: lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40TermVectorsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40TermVectorsReader.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40TermVectorsReader.java	(working copy)
@@ -619,6 +619,11 @@
       this.doc = -1;
       didNext = false;
     }
+    
+    @Override
+    public long cost() {
+      return 1;
+    }
   }
 
   private static class TVDocsAndPositionsEnum extends DocsAndPositionsEnum {
@@ -726,6 +731,11 @@
         return endOffsets[nextPos-1];
       }
     }
+    
+    @Override
+    public long cost() {
+      return 1;
+    }
   }
 
   @Override
Index: lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsReader.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/codecs/lucene40/Lucene40PostingsReader.java	(working copy)
@@ -513,6 +513,11 @@
       }
       return scanTo(target);
     }
+    
+    @Override
+    public long cost() {
+      return limit;
+    }
   }
   
   private final class AllDocsSegmentDocsEnum extends SegmentDocsEnumBase {
@@ -886,6 +891,11 @@
     public BytesRef getPayload() throws IOException {
       return null;
     }
+    
+    @Override
+    public long cost() {
+      return limit;
+    }
   }
   
   // Decodes docs & positions & (payloads and/or offsets)
@@ -1179,5 +1189,10 @@
         return null;
       }
     }
+    
+    @Override
+    public long cost() {
+      return limit;
+    }
   }
 }
Index: lucene/core/src/java/org/apache/lucene/codecs/lucene41/Lucene41PostingsReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/codecs/lucene41/Lucene41PostingsReader.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/codecs/lucene41/Lucene41PostingsReader.java	(working copy)
@@ -599,6 +599,11 @@
         return nextDoc();
       }
     }
+    
+    @Override
+    public long cost() {
+      return docFreq;
+    }
   }
 
 
@@ -1010,6 +1015,11 @@
     public BytesRef getPayload() {
       return null;
     }
+    
+    @Override
+    public long cost() {
+      return docFreq;
+    }
   }
 
   // Also handles payloads + offsets
@@ -1588,5 +1598,10 @@
         return payload;
       }
     }
+    
+    @Override
+    public long cost() {
+      return docFreq;
+    }
   }
 }
Index: lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java	(working copy)
@@ -135,6 +135,9 @@
     @Override
     public float score() { return (float)score; }
     
+    @Override
+    public long cost() { return 1; }
+
   }
 
   static final class Bucket {
@@ -327,6 +330,11 @@
   }
 
   @Override
+  public long cost() {
+    return Integer.MAX_VALUE;
+  }
+
+  @Override
   public void score(Collector collector) throws IOException {
     score(collector, Integer.MAX_VALUE, -1);
   }
Index: lucene/core/src/java/org/apache/lucene/search/TermQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TermQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/TermQuery.java	(working copy)
@@ -32,7 +32,6 @@
 import org.apache.lucene.search.similarities.Similarity.ExactSimScorer;
 import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.util.Bits;
-import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.ToStringUtils;
 
 /** A Query that matches documents containing a term.
@@ -85,7 +84,7 @@
       }
       DocsEnum docs = termsEnum.docs(acceptDocs, null);
       assert docs != null;
-      return new TermScorer(this, docs, similarity.exactSimScorer(stats, context), termsEnum.docFreq());
+      return new TermScorer(this, docs, similarity.exactSimScorer(stats, context));
     }
     
     /**
Index: lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/SloppyPhraseScorer.java	(working copy)
@@ -49,6 +49,7 @@
   private PhrasePositions[] rptStack; // temporary stack for switching colliding repeating pps 
   
   private int numMatches;
+  private final long cost;
   
   SloppyPhraseScorer(Weight weight, PhraseQuery.PostingsAndFreq[] postings,
       int slop, Similarity.SloppySimScorer docScorer) {
@@ -57,6 +58,8 @@
     this.slop = slop;
     this.numPostings = postings==null ? 0 : postings.length;
     pq = new PhraseQueue(postings.length);
+    // min(cost)
+    cost = postings[0].postings.cost();
     // 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.
@@ -590,5 +593,10 @@
   }
   
   @Override
+  public long cost() {
+    return cost;
+  }
+
+  @Override
   public String toString() { return "scorer(" + weight + ")"; }
 }
Index: lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/IndexSearcher.java	(working copy)
@@ -781,6 +781,11 @@
       public float score() {
         return score;
       }
+
+      @Override
+      public long cost() {
+        return 1;
+      }
     }
 
     private final FakeScorer fakeScorer = new FakeScorer();
Index: lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java	(working copy)
@@ -21,15 +21,10 @@
 import java.util.*;
 
 import org.apache.lucene.index.AtomicReaderContext;
-import org.apache.lucene.index.DocsEnum;
 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;
 import org.apache.lucene.util.Bits;
 import org.apache.lucene.util.ToStringUtils;
 
@@ -174,24 +169,18 @@
     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
@@ -310,10 +299,6 @@
     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>();
@@ -356,31 +341,14 @@
         return null;
       }
       
+      if (optional.size() == 0 && prohibited.size() == 0) {
+        float coord = disableCoord ? 1.0f : coord(required.size(), maxCoord);
+        return new ConjunctionScorer(this, required.toArray(new Scorer[required.size()]), coord);
+      }
+      
       // Return a BooleanScorer2
       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() {
Index: lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java	(working copy)
@@ -77,6 +77,11 @@
       doc = target-1;
       return nextDoc();
     }
+
+    @Override
+    public long cost() {
+      return maxDoc;
+    }
   }
 
   private class MatchAllDocsWeight extends Weight {
Index: lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/FieldCacheDocIdSet.java	(working copy)
@@ -111,6 +111,11 @@
           }
           return doc = NO_MORE_DOCS;
         }
+
+        @Override
+        public long cost() {
+          return maxDoc;
+        }
       };
     } else if (acceptDocs instanceof FixedBitSet || acceptDocs instanceof OpenBitSet) {
       // special case for FixedBitSet / OpenBitSet: use the iterator and filter it
@@ -151,6 +156,11 @@
           }
           return doc = NO_MORE_DOCS;
         }
+
+        @Override
+        public long cost() {
+          return maxDoc;
+        }
       };
     }
   }
Index: lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java	(working copy)
+++ lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java	(working copy)
@@ -22,27 +22,32 @@
 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;
+/** Scorer for conjunctions, sets of queries, all of which are required. */
+class ConjunctionScorer extends Scorer {
   protected int lastDoc = -1;
   protected final DocsAndFreqs[] docsAndFreqs;
   private final DocsAndFreqs lead;
+  private final float coord;
 
-  ConjunctionTermScorer(Weight weight, float coord,
-      DocsAndFreqs[] docsAndFreqs) {
+  ConjunctionScorer(Weight weight, Scorer[] scorers) {
+    this(weight, scorers, 1f);
+  }
+  
+  ConjunctionScorer(Weight weight, Scorer[] scorers, float coord) {
     super(weight);
     this.coord = coord;
-    this.docsAndFreqs = docsAndFreqs;
+    this.docsAndFreqs = new DocsAndFreqs[scorers.length];
+    for (int i = 0; i < scorers.length; i++) {
+      docsAndFreqs[i] = new DocsAndFreqs(scorers[i]);
+    }
     // Sort the array the first time to allow the least frequent DocsEnum to
     // lead the matching.
     ArrayUtil.mergeSort(docsAndFreqs, new Comparator<DocsAndFreqs>() {
       @Override
       public int compare(DocsAndFreqs o1, DocsAndFreqs o2) {
-        return o1.docFreq - o2.docFreq;
+        return Long.signum(o1.cost - o2.cost);
       }
     });
 
@@ -61,7 +66,7 @@
           // docsAndFreqs[i].doc may already be equal to doc if we "broke advanceHead"
           // on the previous iteration and the advance on the lead scorer exactly matched.
           if (docsAndFreqs[i].doc < doc) {
-            docsAndFreqs[i].doc = docsAndFreqs[i].docs.advance(doc);
+            docsAndFreqs[i].doc = docsAndFreqs[i].scorer.advance(doc);
 
             if (docsAndFreqs[i].doc > doc) {
               // DocsEnum beyond the current doc - break and advance lead to the new highest doc.
@@ -74,13 +79,13 @@
         return doc;
       }
       // advance head for next iteration
-      doc = lead.doc = lead.docs.advance(doc);
+      doc = lead.doc = lead.scorer.advance(doc);
     }
   }
 
   @Override
   public int advance(int target) throws IOException {
-    lead.doc = lead.docs.advance(target);
+    lead.doc = lead.scorer.advance(target);
     return lastDoc = doNext(lead.doc);
   }
 
@@ -91,7 +96,7 @@
 
   @Override
   public int nextDoc() throws IOException {
-    lead.doc = lead.docs.nextDoc();
+    lead.doc = lead.scorer.nextDoc();
     return lastDoc = doNext(lead.doc);
   }
 
@@ -111,6 +116,11 @@
   }
 
   @Override
+  public long cost() {
+    return lead.scorer.cost();
+  }
+
+  @Override
   public Collection<ChildScorer> getChildren() {
     ArrayList<ChildScorer> children = new ArrayList<ChildScorer>(docsAndFreqs.length);
     for (DocsAndFreqs docs : docsAndFreqs) {
@@ -120,19 +130,13 @@
   }
 
   static final class DocsAndFreqs {
-    final DocsEnum docs;
-    final int docFreq;
+    final long cost;
     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;
+   
+    DocsAndFreqs(Scorer scorer) {
       this.scorer = scorer;
+      this.cost = scorer.cost();
     }
   }
 }

Property changes on: lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
___________________________________________________________________
Deleted: cvs2svn:cvs-rev
## -1 +0,0 ##
-1.7
\ No newline at end of property
Deleted: svn:keywords
## -1 +0,0 ##
-Author Date Id Revision
\ No newline at end of property
Index: lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java	(working copy)
@@ -89,4 +89,9 @@
   public Collection<ChildScorer> getChildren() {
     return Collections.singleton(new ChildScorer(scorer, "CACHED"));
   }
+
+  @Override
+  public long cost() {
+    return scorer.cost();
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/CachingCollector.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/CachingCollector.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/CachingCollector.java	(working copy)
@@ -89,6 +89,9 @@
     
     @Override
     public final int nextDoc() { throw new UnsupportedOperationException(); }
+    
+    @Override
+    public long cost() { return 1; }
     }
 
   // A CachingCollector which caches scores
Index: lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/ReqOptSumScorer.java	(working copy)
@@ -99,5 +99,10 @@
     children.add(new ChildScorer(optScorer, "SHOULD"));
     return children;
   }
+
+  @Override
+  public long cost() {
+    return reqScorer.cost();
+  }
 }
 
Index: lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/BooleanScorer2.java	(working copy)
@@ -147,6 +147,11 @@
     public int advance(int target) throws IOException {
       return scorer.advance(target);
     }
+
+    @Override
+    public long cost() {
+      return scorer.cost();
+    }
   }
 
   private Scorer countingDisjunctionSumScorer(final List<Scorer> scorers,
@@ -175,7 +180,7 @@
                                               List<Scorer> requiredScorers) throws IOException {
     // each scorer from the list counted as a single matcher
     final int requiredNrMatchers = requiredScorers.size();
-    return new ConjunctionScorer(weight, requiredScorers) {
+    return new ConjunctionScorer(weight, requiredScorers.toArray(new Scorer[requiredScorers.size()])) {
       private int lastScoredDoc = -1;
       // Save the score of lastScoredDoc, so that we don't compute it more than
       // once in score().
@@ -200,7 +205,7 @@
 
   private Scorer dualConjunctionSumScorer(boolean disableCoord,
                                                 Scorer req1, Scorer req2) throws IOException { // non counting.
-    return new ConjunctionScorer(weight, req1, req2);
+    return new ConjunctionScorer(weight, new Scorer[] { req1, req2 });
     // All scorers match, so defaultSimilarity always has 1 as
     // the coordination factor.
     // Therefore the sum of the scores of two scorers
@@ -321,6 +326,11 @@
   public int advance(int target) throws IOException {
     return doc = countingSumScorer.advance(target);
   }
+  
+  @Override
+  public long cost() {
+    return countingSumScorer.cost();
+  }
 
   @Override
   public Collection<ChildScorer> getChildren() {
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java	(working copy)
@@ -181,6 +181,11 @@
     }
 
     @Override
+    public long cost() {
+      return spans.cost();
+    }
+
+    @Override
     public String toString() {
         return "spans(" + SpanPositionCheckQuery.this.toString() + ")";
       }
Index: lucene/core/src/java/org/apache/lucene/search/spans/Spans.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/Spans.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/spans/Spans.java	(working copy)
@@ -84,4 +84,12 @@
    */
   public abstract boolean isPayloadAvailable() throws IOException;
   
+  /**
+   * Returns the estimated cost of this spans.
+   * <p>
+   * This is generally an upper bound of the number of documents this iterator
+   * might match, but may be a rough heuristic, hardcoded value, or otherwise
+   * completely inaccurate.
+   */
+  public abstract long cost();
 }
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java	(working copy)
@@ -172,12 +172,14 @@
 
     return new Spans() {
         private SpanQueue queue = null;
+        private long cost;
 
         private boolean initSpanQueue(int target) throws IOException {
           queue = new SpanQueue(clauses.size());
           Iterator<SpanQuery> i = clauses.iterator();
           while (i.hasNext()) {
             Spans spans = i.next().getSpans(context, acceptDocs, termContexts);
+            cost += spans.cost();
             if (   ((target == -1) && spans.next())
                 || ((target != -1) && spans.skipTo(target))) {
               queue.add(spans);
@@ -259,6 +261,11 @@
              :(queue.size()>0?(doc()+":"+start()+"-"+end()):"END"));
         }
 
+      @Override
+      public long cost() {
+        return cost;
+      }
+      
       };
   }
 
Index: lucene/core/src/java/org/apache/lucene/search/spans/NearSpansOrdered.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/NearSpansOrdered.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/spans/NearSpansOrdered.java	(working copy)
@@ -134,6 +134,15 @@
     return matchPayload.isEmpty() == false;
   }
 
+  @Override
+  public long cost() {
+    long minCost = Long.MAX_VALUE;
+    for (int i = 0; i < subSpans.length; i++) {
+      minCost = Math.min(minCost, subSpans[i].cost());
+    }
+    return minCost;
+  }
+
   // inherit javadocs
   @Override
   public boolean next() throws IOException {
Index: lucene/core/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/spans/NearSpansUnordered.java	(working copy)
@@ -131,6 +131,11 @@
     }
 
     @Override
+    public long cost() {
+      return spans.cost();
+    }
+
+    @Override
     public String toString() { return spans.toString() + "#" + index; }
   }
 
@@ -267,6 +272,15 @@
 
     return false;
   }
+  
+  @Override
+  public long cost() {
+    long minCost = Long.MAX_VALUE;
+    for (int i = 0; i < subSpans.length; i++) {
+      minCost = Math.min(minCost, subSpans[i].cost());
+    }
+    return minCost;
+  }
 
   @Override
   public String toString() {
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java	(working copy)
@@ -162,6 +162,11 @@
       }
 
       @Override
+      public long cost() {
+        return includeSpans.cost();
+      }
+
+      @Override
       public String toString() {
           return "spans(" + SpanNotQuery.this.toString() + ")";
         }
Index: lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/spans/TermSpans.java	(working copy)
@@ -99,6 +99,11 @@
     return position + 1;
   }
 
+  @Override
+  public long cost() {
+    return postings.cost();
+  }
+
   // TODO: Remove warning after API has been finalized
   @Override
   public Collection<byte[]> getPayload() throws IOException {
@@ -166,6 +171,11 @@
     public boolean isPayloadAvailable() {
       return false;
     }
+
+    @Override
+    public long cost() {
+      return 0;
+    }
   }
 
   public static final TermSpans EMPTY_TERM_SPANS = new EmptyTermSpans();
Index: lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/spans/SpanScorer.java	(working copy)
@@ -107,4 +107,9 @@
   public float sloppyFreq() throws IOException {
     return freq;
   }
+  
+  @Override
+  public long cost() {
+    return spans.cost();
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/DocIdSetIterator.java	(working copy)
@@ -93,4 +93,12 @@
    */
   public abstract int advance(int target) throws IOException;
 
+  /**
+   * Returns the estimated cost of this {@link DocIdSetIterator}.
+   * <p>
+   * This is generally an upper bound of the number of documents this iterator
+   * might match, but may be a rough heuristic, hardcoded value, or otherwise
+   * completely inaccurate.
+   */
+  public abstract long cost();
 }
Index: lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/ReqExclScorer.java	(working copy)
@@ -128,4 +128,9 @@
     }
     return doc = toNonExcluded();
   }
+
+  @Override
+  public long cost() {
+    return reqScorer.cost();
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java	(working copy)
@@ -472,6 +472,7 @@
   private int _freq;
   private DocsQueue _queue;
   private IntQueue _posList;
+  private long cost;
 
   public UnionDocsAndPositionsEnum(Bits liveDocs, AtomicReaderContext context, Term[] terms, Map<Term,TermContext> termContexts, TermsEnum termsEnum) throws IOException {
     List<DocsAndPositionsEnum> docsEnums = new LinkedList<DocsAndPositionsEnum>();
@@ -488,6 +489,7 @@
         // term does exist, but has no positions
         throw new IllegalStateException("field \"" + term.field() + "\" was indexed without position data; cannot run PhraseQuery (term=" + term.text() + ")");
       }
+      cost += postings.cost();
       docsEnums.add(postings);
     }
 
@@ -570,4 +572,9 @@
   public final int docID() {
     return _doc;
   }
+
+  @Override
+  public long cost() {
+    return cost;
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/ExactPhraseScorer.java	(working copy)
@@ -33,6 +33,7 @@
   private final int[] gens = new int[CHUNK];
 
   boolean noDocs;
+  private final long cost;
 
   private final static class ChunkState {
     final DocsAndPositionsEnum posEnum;
@@ -65,6 +66,9 @@
     chunkStates = new ChunkState[postings.length];
 
     endMinus1 = postings.length-1;
+    
+    // min(cost)
+    cost = postings[0].postings.cost();
 
     for(int i=0;i<postings.length;i++) {
 
@@ -315,4 +319,9 @@
 
     return freq;
   }
+
+  @Override
+  public long cost() {
+    return cost;
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSetIterator.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSetIterator.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/FilteredDocIdSetIterator.java	(working copy)
@@ -81,5 +81,9 @@
     }
     return doc;
   }
-  
+
+  @Override
+  public long cost() {
+    return _innerIter.cost();
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/DocIdSet.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DocIdSet.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/DocIdSet.java	(working copy)
@@ -36,6 +36,8 @@
       public int docID() { return NO_MORE_DOCS; }
       @Override
       public int nextDoc() { return NO_MORE_DOCS; }
+      @Override
+      public long cost() { return 0; }
     };
     
     @Override
Index: lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/DisjunctionScorer.java	(working copy)
@@ -105,4 +105,13 @@
     }
     return children;
   }
+
+  @Override
+  public long cost() {
+    long sum = 0;
+    for (int i = 0; i < numScorers; i++) {
+      sum += subScorers[i].cost();
+    }
+    return sum;
+  } 
 }
Index: lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/FilteredQuery.java	(working copy)
@@ -210,6 +210,11 @@
     public Collection<ChildScorer> getChildren() {
       return Collections.singleton(new ChildScorer(scorer, "FILTERED"));
     }
+
+    @Override
+    public long cost() {
+      return scorer.cost();
+    }
   }
   
   /**
@@ -304,6 +309,11 @@
     public final Collection<ChildScorer> getChildren() {
       return Collections.singleton(new ChildScorer(scorer, "FILTERED"));
     }
+
+    @Override
+    public long cost() {
+      return Math.min(primary.cost(), secondary.cost());
+    }
   }
   
   // TODO once we have way to figure out if we use RA or LeapFrog we can remove this scorer
Index: lucene/core/src/java/org/apache/lucene/search/TermScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/TermScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/TermScorer.java	(working copy)
@@ -27,7 +27,6 @@
 final class TermScorer extends Scorer {
   private final DocsEnum docsEnum;
   private final Similarity.ExactSimScorer docScorer;
-  private final int docFreq;
   
   /**
    * Construct a <code>TermScorer</code>.
@@ -39,14 +38,11 @@
    * @param docScorer
    *          The </code>Similarity.ExactSimScorer</code> implementation 
    *          to be used for score computations.
-   * @param docFreq
-   *          per-segment docFreq of this term
    */
-  TermScorer(Weight weight, DocsEnum td, Similarity.ExactSimScorer docScorer, int docFreq) {
+  TermScorer(Weight weight, DocsEnum td, Similarity.ExactSimScorer docScorer) {
     super(weight);
     this.docScorer = docScorer;
     this.docsEnum = td;
-    this.docFreq = docFreq;
   }
 
   @Override
@@ -88,22 +84,13 @@
   public int advance(int target) throws IOException {
     return docsEnum.advance(target);
   }
+  
+  @Override
+  public long cost() {
+    return docsEnum.cost();
+  }
 
   /** Returns a string representation of this <code>TermScorer</code>. */
   @Override
   public String toString() { return "scorer(" + weight + ")"; }
-
-  // TODO: benchmark if the specialized conjunction really benefits
-  // from this, or if instead its from sorting by docFreq, or both
-
-  DocsEnum getDocsEnum() {
-    return docsEnum;
-  }
-  
-  // TODO: generalize something like this for scorers?
-  // even this is just an estimation...
-  
-  int getDocFreq() {
-    return docFreq;
-  }
 }
Index: lucene/core/src/java/org/apache/lucene/search/ConjunctionTermScorer.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ConjunctionTermScorer.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/ConjunctionTermScorer.java	(working copy)
@@ -1,138 +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>() {
-      @Override
-      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 {
-    for(;;) {
-      // doc may already be NO_MORE_DOCS here, but we don't check explicitly
-      // since all scorers should advance to NO_MORE_DOCS, match, then
-      // return that value.
-      advanceHead: for(;;) {
-        for (int i = 1; i < docsAndFreqs.length; i++) {
-          // invariant: docsAndFreqs[i].doc <= doc at this point.
-
-          // docsAndFreqs[i].doc may already be equal to doc if we "broke advanceHead"
-          // on the previous iteration and the advance on the lead scorer exactly matched.
-          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 to the new highest doc.
-              doc = docsAndFreqs[i].doc;
-              break advanceHead;
-            }
-          }
-        }
-        // success - all DocsEnums are on the same doc
-        return doc;
-      }
-      // advance head for next iteration
-      doc = lead.doc = lead.docs.advance(doc);
-    }
-  }
-
-  @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;
-    }
-  }
-}
Index: lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java	(working copy)
@@ -206,6 +206,11 @@
       return docIdSetIterator.advance(target);
     }
     
+    @Override
+    public long cost() {
+      return docIdSetIterator.cost();
+    }
+
     private Collector wrapCollector(final Collector collector) {
       return new Collector() {
         @Override
Index: lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/index/MultiDocsAndPositionsEnum.java	(working copy)
@@ -170,6 +170,15 @@
   }
   
   @Override
+  public long cost() {
+    long cost = 0;
+    for (int i = 0; i < numSubs; i++) {
+      cost += subs[i].docsAndPositionsEnum.cost();
+    }
+    return cost;
+  }
+  
+  @Override
   public String toString() {
     return "MultiDocsAndPositionsEnum(" + Arrays.toString(getSubs()) + ")";
   }
Index: lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/index/FilterAtomicReader.java	(working copy)
@@ -251,6 +251,11 @@
     public AttributeSource attributes() {
       return in.attributes();
     }
+
+    @Override
+    public long cost() {
+      return in.cost();
+    }
   }
 
   /** Base class for filtering {@link DocsAndPositionsEnum} implementations. */
@@ -310,6 +315,11 @@
     public AttributeSource attributes() {
       return in.attributes();
     }
+    
+    @Override
+    public long cost() {
+      return in.cost();
+    }
   }
 
   /** The underlying AtomicReader. */
Index: lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/index/MultiDocsEnum.java	(working copy)
@@ -129,6 +129,15 @@
       }
     }
   }
+  
+  @Override
+  public long cost() {
+    long cost = 0;
+    for (int i = 0; i < numSubs; i++) {
+      cost += subs[i].docsEnum.cost();
+    }
+    return cost;
+  }
 
   // TODO: implement bulk read more efficiently than super
   /** Holds a {@link DocsEnum} along with the
Index: lucene/core/src/java/org/apache/lucene/util/OpenBitSetIterator.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/util/OpenBitSetIterator.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/util/OpenBitSetIterator.java	(working copy)
@@ -190,4 +190,8 @@
     return curDocId;
   }
   
+  @Override
+  public long cost() {
+    return words / 64;
+  }
 }
Index: lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java
===================================================================
--- lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java	(revision 1455706)
+++ lucene/core/src/java/org/apache/lucene/util/DocIdBitSet.java	(working copy)
@@ -95,5 +95,11 @@
       docId = d == -1 ? NO_MORE_DOCS : d;
       return docId;
     }
+    
+    @Override
+    public long cost() {
+      // upper bound
+      return bitSet.length();
+    }
   }
 }
Index: lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java	(revision 1455706)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java	(working copy)
@@ -301,6 +301,11 @@
       }
       return childDoc;
     }
+
+    @Override
+    public long cost() {
+      return parentScorer.cost();
+    }
   }
 
   @Override
Index: lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java	(revision 1455706)
+++ lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java	(working copy)
@@ -160,18 +160,21 @@
         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);
         }
       }
     };
@@ -183,16 +186,18 @@
     final BytesRef spare = new BytesRef();
     final Bits acceptDocs;
     final TermsEnum termsEnum;
+    final long cost;
 
     int upto;
     DocsEnum docsEnum;
     DocsEnum reuse;
     int scoreUpto;
 
-    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;
     }
 
     @Override
@@ -261,6 +266,11 @@
     public int freq() {
       return 1;
     }
+
+    @Override
+    public long cost() {
+      return cost;
+    }
   }
 
   // This impl that tracks whether a docid has already been emitted. This check makes sure that docs aren't emitted
@@ -270,8 +280,8 @@
 
     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);
     }
 
@@ -326,15 +336,17 @@
 
     final DocIdSetIterator matchingDocsIterator;
     final float[] scores;
+    final long cost;
 
     int currentDoc = -1;
 
-    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 {
@@ -378,13 +390,18 @@
     public int advance(int target) throws IOException {
       return currentDoc = matchingDocsIterator.advance(target);
     }
+
+    @Override
+    public long cost() {
+      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
Index: lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java	(revision 1455706)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java	(working copy)
@@ -409,6 +409,10 @@
       );
     }
 
+    @Override
+    public long cost() {
+      return childScorer.cost();
+    }
   }
 
   @Override
Index: lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java
===================================================================
--- lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java	(revision 1455706)
+++ lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinCollector.java	(working copy)
@@ -347,6 +347,11 @@
     public int nextDoc() {
       throw new UnsupportedOperationException();
     }
+
+    @Override
+    public long cost() {
+      return 1;
+    }
   }
 
   private OneGroup[] sortedGroups;
Index: solr/core/src/java/org/apache/solr/schema/LatLonType.java
===================================================================
--- solr/core/src/java/org/apache/solr/schema/LatLonType.java	(revision 1455706)
+++ solr/core/src/java/org/apache/solr/schema/LatLonType.java	(working copy)
@@ -498,6 +498,11 @@
       return 1;
     }
 
+    @Override
+    public long cost() {
+      return maxDoc;
+    }
+
     public Explanation explain(int doc) throws IOException {
       advance(doc);
       boolean matched = this.doc == doc;
Index: solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java	(revision 1455706)
+++ solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java	(working copy)
@@ -196,6 +196,11 @@
     public int advance(int target) throws IOException {
       return docIdSetIterator.advance(target);
     }
+
+    @Override
+    public long cost() {
+      return docIdSetIterator.cost();
+    }
   }
 
   @Override
Index: solr/core/src/java/org/apache/solr/search/SortedIntDocSet.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/SortedIntDocSet.java	(revision 1455706)
+++ solr/core/src/java/org/apache/solr/search/SortedIntDocSet.java	(working copy)
@@ -755,6 +755,10 @@
                 }
               }
 
+              @Override
+              public long cost() {
+                return docs.length;
+              }
             };
           }
 
Index: solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java	(revision 1455706)
+++ solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java	(working copy)
@@ -546,6 +546,11 @@
     public int advance(int target) throws IOException {
       return iter.advance(target);
     }
+
+    @Override
+    public long cost() {
+      return iter.cost();
+    }
   }
 
 
Index: solr/core/src/java/org/apache/solr/search/BitDocSet.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/BitDocSet.java	(revision 1455706)
+++ solr/core/src/java/org/apache/solr/search/BitDocSet.java	(working copy)
@@ -292,6 +292,17 @@
                 pos = bs.nextSetBit(target+base);
                 return adjustedDoc = (pos>=0 && pos<max) ? pos-base : NO_MORE_DOCS;
               }
+
+              @Override
+              public long cost() {
+                // we don't want to actually compute cardinality, but
+                // if its already been computed, we use it
+                if (size != -1) {
+                  return size;
+                } else {
+                  return bs.capacity();
+                }
+              }
             };
           }
 
Index: solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java	(revision 1455706)
+++ solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java	(working copy)
@@ -2307,6 +2307,11 @@
     public int advance(int target) throws IOException {
       return doNext(first.advance(target));
     }
+
+    @Override
+    public long cost() {
+      return first.cost();
+    }
   }
 
   private static class DualFilterIterator extends DocIdSetIterator {
@@ -2344,6 +2349,11 @@
         if (other == doc) return doc;
       }
     }
+
+    @Override
+    public long cost() {
+      return Math.min(a.cost(), b.cost());
+    }
   }
 
 }
Index: solr/core/src/java/org/apache/solr/search/DocSetBase.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/DocSetBase.java	(revision 1455706)
+++ solr/core/src/java/org/apache/solr/search/DocSetBase.java	(working copy)
@@ -186,6 +186,11 @@
                 pos = bs.nextSetBit(target+base);
                 return adjustedDoc = (pos>=0 && pos<max) ? pos-base : NO_MORE_DOCS;
               }
+
+              @Override
+              public long cost() {
+                return bs.capacity();
+              }
             };
           }
 
