diff --git a/lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java
index 8f15b83..8a5e46b 100644
--- a/lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java
+++ b/lucene/src/java/org/apache/lucene/search/MultiPhraseQuery.java
@@ -139,6 +139,7 @@ public class MultiPhraseQuery extends Query {
     private float queryNorm;
     private float queryWeight;
 
+    // nocommit: MultiPhraseWeight must not know of idf.
     public MultiPhraseWeight(IndexSearcher searcher)
       throws IOException {
       this.similarity = searcher.getSimilarityProvider().get(field);
@@ -151,7 +152,8 @@ public class MultiPhraseQuery extends Query {
           allTerms.add(TermContext.build(context, term, true));
         }
       }
-      idfExp = similarity.computeWeight(searcher, field, allTerms.toArray(new TermContext[allTerms.size()]));
+      idfExp = ((TFIDFSimilarity)similarity).computeStats(
+          searcher, field, allTerms.toArray(new TermContext[allTerms.size()])).idf;
       idf = idfExp.getIdf();
     }
 
diff --git a/lucene/src/java/org/apache/lucene/search/PhraseQuery.java b/lucene/src/java/org/apache/lucene/search/PhraseQuery.java
index 092300e..d57240b 100644
--- a/lucene/src/java/org/apache/lucene/search/PhraseQuery.java
+++ b/lucene/src/java/org/apache/lucene/search/PhraseQuery.java
@@ -181,6 +181,7 @@ public class PhraseQuery extends Query {
     private IDFExplanation idfExp;
     private transient TermContext states[];
 
+    // nocommit: PhraseWeight must not know of idf.
     public PhraseWeight(IndexSearcher searcher)
       throws IOException {
       this.similarity = searcher.getSimilarityProvider().get(field);
@@ -188,7 +189,8 @@ public class PhraseQuery extends Query {
       states = new TermContext[terms.size()];
       for (int i = 0; i < terms.size(); i++)
         states[i] = TermContext.build(context, terms.get(i), true);
-      idfExp = similarity.computeWeight(searcher, field, states);
+      idfExp = ((TFIDFSimilarity)similarity).computeStats(
+          searcher, field, states).idf;
       idf = idfExp.getIdf();
     }
 
diff --git a/lucene/src/java/org/apache/lucene/search/Similarity.java b/lucene/src/java/org/apache/lucene/search/Similarity.java
index b56ebca..1700676 100644
--- a/lucene/src/java/org/apache/lucene/search/Similarity.java
+++ b/lucene/src/java/org/apache/lucene/search/Similarity.java
@@ -22,7 +22,6 @@ import java.io.IOException;
 
 import org.apache.lucene.index.FieldInvertState;
 import org.apache.lucene.index.IndexReader.AtomicReaderContext;
-import org.apache.lucene.search.Explanation.IDFExplanation;
 import org.apache.lucene.util.TermContext;
 
 
@@ -114,7 +113,7 @@ public abstract class Similarity {
     return 1;
   }
   
-  public abstract IDFExplanation computeWeight(IndexSearcher searcher, String fieldName, TermContext... termStats) throws IOException;
+  public abstract Stats computeStats(IndexSearcher searcher, String fieldName, TermContext... termStats) throws IOException;
   
   public abstract ExactDocScorer exactDocScorer(Weight weight, String fieldName, AtomicReaderContext context) throws IOException;
   public abstract SloppyDocScorer sloppyDocScorer(Weight weight, String fieldName, AtomicReaderContext context) throws IOException;
@@ -126,4 +125,11 @@ public abstract class Similarity {
   public abstract class SloppyDocScorer {
     public abstract float score(int doc, float freq);
   }
+  
+  /** Stores the statistics for the indexed collection. This abstract
+   * implementation is empty; descendants of {@code Similarity} should
+   * subclass {@code Stats} and define the statistics they require in the
+   * subclass. Examples include idf, average field length, etc.
+   */
+  public static abstract class Stats {}
 }
diff --git a/lucene/src/java/org/apache/lucene/search/TFIDFSimilarity.java b/lucene/src/java/org/apache/lucene/search/TFIDFSimilarity.java
index daf991b..27626d0 100644
--- a/lucene/src/java/org/apache/lucene/search/TFIDFSimilarity.java
+++ b/lucene/src/java/org/apache/lucene/search/TFIDFSimilarity.java
@@ -21,11 +21,11 @@ package org.apache.lucene.search;
 import java.io.IOException;
 import java.io.Serializable;
 
-import org.apache.lucene.index.IndexReader.AtomicReaderContext;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.index.IndexReader.AtomicReaderContext;
 import org.apache.lucene.search.Explanation.IDFExplanation;
-import org.apache.lucene.util.TermContext;
 import org.apache.lucene.util.SmallFloat;
+import org.apache.lucene.util.TermContext;
 
 
 /** 
@@ -687,11 +687,11 @@ public abstract class TFIDFSimilarity extends Similarity implements Serializable
   }
  
   @Override
-  public final IDFExplanation computeWeight(IndexSearcher searcher, String fieldName,
+  public final IDFStats computeStats(IndexSearcher searcher, String fieldName,
       TermContext... termStats) throws IOException {
-    return termStats.length == 1
-    ? idfExplain(termStats[0], searcher)
-    : idfExplain(termStats, searcher);
+    return new IDFStats(termStats.length == 1
+                          ? idfExplain(termStats[0], searcher)
+                          : idfExplain(termStats, searcher));
   }
 
   @Override
@@ -783,4 +783,16 @@ public abstract class TFIDFSimilarity extends Similarity implements Serializable
       return tf(freq)*weightValue;        // compute tf(f)*weight
     }
   }
+  
+  /** Collection statistics for the TF-IDF model. The only statistic of interest
+   * to this model is idf. */
+  public static class IDFStats extends Stats {
+    /** The idf and its explanation. */
+    public final IDFExplanation idf;
+    
+    public IDFStats(IDFExplanation idf) {
+      // TODO: Validate?
+      this.idf = idf;
+    }
+  }
 }
diff --git a/lucene/src/java/org/apache/lucene/search/TermQuery.java b/lucene/src/java/org/apache/lucene/search/TermQuery.java
index 7d7a305..bccb900 100644
--- a/lucene/src/java/org/apache/lucene/search/TermQuery.java
+++ b/lucene/src/java/org/apache/lucene/search/TermQuery.java
@@ -50,12 +50,14 @@ public class TermQuery extends Query {
     private final IDFExplanation idfExp;
     private transient TermContext termStates;
 
+    // nocommit: MultiPhraseWeight must not know of idf.
     public TermWeight(IndexSearcher searcher, TermContext termStates)
       throws IOException {
       assert termStates != null : "TermContext must not be null";
       this.termStates = termStates;
       this.similarity = searcher.getSimilarityProvider().get(term.field());
-      idfExp = similarity.computeWeight(searcher, term.field(), termStates);
+      idfExp = ((TFIDFSimilarity)similarity).computeStats(
+          searcher, term.field(), termStates).idf;
       idf = idfExp.getIdf();
     }
 
diff --git a/lucene/src/java/org/apache/lucene/search/spans/SpanWeight.java b/lucene/src/java/org/apache/lucene/search/spans/SpanWeight.java
index f2b084a..c473ea2 100644
--- a/lucene/src/java/org/apache/lucene/search/spans/SpanWeight.java
+++ b/lucene/src/java/org/apache/lucene/search/spans/SpanWeight.java
@@ -42,6 +42,7 @@ public class SpanWeight extends Weight {
   protected SpanQuery query;
   private IDFExplanation idfExp;
 
+  // nocommit: SpanWeight must not know of idf.
   public SpanWeight(SpanQuery query, IndexSearcher searcher)
     throws IOException {
     this.similarity = searcher.getSimilarityProvider().get(query.getField());
@@ -54,7 +55,8 @@ public class SpanWeight extends Weight {
     int i = 0;
     for (Term term : terms)
       states[i++] = TermContext.build(context, term, true);
-    idfExp = similarity.computeWeight(searcher, query.getField(), states);
+    idfExp = ((TFIDFSimilarity)similarity).computeStats(
+        searcher, query.getField(), states).idf;
     idf = idfExp.getIdf();
   }
 
diff --git a/lucene/src/test/org/apache/lucene/search/MockBM25Similarity.java b/lucene/src/test/org/apache/lucene/search/MockBM25Similarity.java
index 1e12cde..3e22b0f 100644
--- a/lucene/src/test/org/apache/lucene/search/MockBM25Similarity.java
+++ b/lucene/src/test/org/apache/lucene/search/MockBM25Similarity.java
@@ -20,12 +20,12 @@ package org.apache.lucene.search;
 import java.io.IOException;
 
 import org.apache.lucene.index.FieldInvertState;
-import org.apache.lucene.index.IndexReader.ReaderContext;
 import org.apache.lucene.index.IndexReader.AtomicReaderContext;
+import org.apache.lucene.index.IndexReader.ReaderContext;
 import org.apache.lucene.search.Explanation.IDFExplanation;
-import org.apache.lucene.util.ReaderUtil;
-import org.apache.lucene.util.TermContext;
+import org.apache.lucene.search.TFIDFSimilarity.IDFStats;
 import org.apache.lucene.util.SmallFloat;
+import org.apache.lucene.util.TermContext;
 
 /**
  * BM25 Similarity.
@@ -36,6 +36,10 @@ public class MockBM25Similarity extends Similarity {
   private static final float k1 = 2f;
   private static final float b = 0.75f;
   
+  /** The collection statistics. */
+  // nocommit: I am pretty sure won't be good -- at least in the provider, we only have 1 object.
+  private BM25Stats stats;
+  
   /**
    * Our normalization is k1 * ((1 - b) + b * numTerms / avgNumTerms)
    * currently we put doclen into the boost byte (divided by boost) for simple quantization
@@ -74,11 +78,27 @@ public class MockBM25Similarity extends Similarity {
     return 1.0f / (distance + 1);
   }
 
+  @Override
+  public BM25Stats computeStats(IndexSearcher searcher, String fieldName, TermContext... termStats) throws IOException {
+    stats = new BM25Stats(idfExplain(searcher, termStats),
+                          avgFieldLength(searcher, fieldName));
+    return stats;
+  }
+
+  private float avgFieldLength(IndexSearcher searcher, String fieldName) throws IOException {
+//    context = ReaderUtil.getTopLevelContext(context);
+    ReaderContext context = searcher.getTopReaderContext();
+    long normsum = context.reader.getSumOfNorms(fieldName);
+    long maxdoc = context.reader.maxDoc();
+    int avgnorm = (int) (normsum / (double) maxdoc);
+    return decodeNormValue((byte)avgnorm);
+  }
+  
+  /** Computes the IDF. */
   // weight for a term as log(1 + ((n - dfj + 0.5F)/(dfj + 0.5F)))
   // nocommit: nuke IDFExplanation!
   // nocommit: are we summing this in the right place for phrase estimation????
-  @Override
-  public IDFExplanation computeWeight(IndexSearcher searcher, String fieldName, TermContext... termStats) throws IOException {
+  private IDFExplanation idfExplain(IndexSearcher searcher, TermContext... termStats) {
     float value = 0.0f;
     final StringBuilder exp = new StringBuilder();
 
@@ -101,32 +121,23 @@ public class MockBM25Similarity extends Similarity {
       public String explain() {
         return exp.toString();
       }
-    };
+    };    
   }
-
+  
   @Override
   public ExactDocScorer exactDocScorer(Weight weight, String fieldName, AtomicReaderContext context) throws IOException {
     byte[] norms = context.reader.norms(fieldName);
-    float avgdl = norms == null ? 0f : avgDocumentLength(fieldName, context);
+    float avgdl = norms == null ? 0f : stats.avgdl;
     return new ExactBM25DocScorer((float) Math.sqrt(weight.getValue()), norms, avgdl);
   }
 
   @Override
   public SloppyDocScorer sloppyDocScorer(Weight weight, String fieldName, AtomicReaderContext context) throws IOException {
     byte[] norms = context.reader.norms(fieldName);
-    float avgdl = norms == null ? 0f : avgDocumentLength(fieldName, context);
+    float avgdl = norms == null ? 0f : stats.avgdl;
     return new SloppyBM25DocScorer((float) Math.sqrt(weight.getValue()), norms, avgdl);
   }
   
-  private float avgDocumentLength(String field, ReaderContext context) throws IOException {
-    // nocommit: crap that we calc this over and over redundantly for each segment (we should just do it once in the weight, once its generalized)
-    context = ReaderUtil.getTopLevelContext(context);
-    long normsum = context.reader.getSumOfNorms(field);
-    long maxdoc = context.reader.maxDoc();
-    int avgnorm = (int) (normsum / (double) maxdoc);
-    return decodeNormValue((byte)avgnorm);
-  }
-
   private class ExactBM25DocScorer extends ExactDocScorer {
     private final float weightValue;
     private final byte[] norms;
@@ -164,4 +175,15 @@ public class MockBM25Similarity extends Similarity {
       return weightValue * (freq * (k1 + 1)) / (freq + norm);
     }
   }
+  
+  /** Collection statistics for the BM25 model. */
+  public static class BM25Stats extends IDFStats {
+    /** The average document length. */
+    public final float avgdl;
+    
+    public BM25Stats(IDFExplanation idf, float avgLength) {
+      super(idf);
+      this.avgdl = avgLength;
+    }
+  }
 }
diff --git a/lucene/src/test/org/apache/lucene/search/MockLMSimilarity.java b/lucene/src/test/org/apache/lucene/search/MockLMSimilarity.java
index 99c0123..0a140d8 100644
--- a/lucene/src/test/org/apache/lucene/search/MockLMSimilarity.java
+++ b/lucene/src/test/org/apache/lucene/search/MockLMSimilarity.java
@@ -23,6 +23,7 @@ import org.apache.lucene.index.FieldInvertState;
 import org.apache.lucene.index.MultiFields;
 import org.apache.lucene.index.IndexReader.AtomicReaderContext;
 import org.apache.lucene.search.Explanation.IDFExplanation;
+import org.apache.lucene.search.TFIDFSimilarity.IDFStats;
 import org.apache.lucene.util.TermContext;
 import org.apache.lucene.util.SmallFloat;
 
@@ -88,11 +89,15 @@ public class MockLMSimilarity extends Similarity {
     return 1.0f / (distance + 1);
   }
 
+  @Override
+  public IDFStats computeStats(IndexSearcher searcher, String fieldName, TermContext... termStats) throws IOException {
+    return new IDFStats(idfExplain(searcher, fieldName, termStats));
+  }
+  
   // weight for a term as 1 / (mu * (totalTermFrequency / sumOfTotalTermFrequency))
   // nocommit: nuke IDFExplanation!
   // nocommit: evil how we shove this crap in weight and unsquare it.. need to generalize weight
-  @Override
-  public IDFExplanation computeWeight(IndexSearcher searcher, String fieldName, TermContext... termStats) throws IOException {
+  private IDFExplanation idfExplain(IndexSearcher searcher, String fieldName, TermContext... termStats) throws IOException {
     float value = 0.0f;
     final StringBuilder exp = new StringBuilder();
     final long sumOfTotalTermFreq = MultiFields.getTerms(searcher.getIndexReader(), fieldName).getSumTotalTermFreq();
@@ -114,7 +119,7 @@ public class MockLMSimilarity extends Similarity {
       public String explain() {
         return exp.toString();
       }
-    };
+    };    
   }
 
   @Override
