Index: solr/core/src/java/org/apache/solr/schema/LatLonType.java
===================================================================
--- solr/core/src/java/org/apache/solr/schema/LatLonType.java	(revision 1181600)
+++ solr/core/src/java/org/apache/solr/schema/LatLonType.java	(working copy)
@@ -504,6 +504,11 @@
       return (float)(dist * qWeight);
     }
 
+    @Override
+    public float freq() throws IOException {
+      return 1;
+    }
+
     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 1181600)
+++ solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java	(working copy)
@@ -184,6 +184,11 @@
     public float score() throws IOException {
       return theScore;
     }
+    
+    @Override
+    public float freq() throws IOException {
+      return 1;
+    }
 
     @Override
     public int advance(int target) throws IOException {
Index: solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
===================================================================
--- solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java	(revision 1181600)
+++ solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java	(working copy)
@@ -532,6 +532,11 @@
     public float score() throws IOException {
       return score;
     }
+    
+    @Override
+    public float freq() throws IOException {
+      return 1;
+    }
 
     @Override
     public int advance(int target) throws IOException {
Index: modules/join/src/java/org/apache/lucene/search/join/BlockJoinQuery.java
===================================================================
--- modules/join/src/java/org/apache/lucene/search/join/BlockJoinQuery.java	(revision 1181600)
+++ modules/join/src/java/org/apache/lucene/search/join/BlockJoinQuery.java	(working copy)
@@ -315,6 +315,11 @@
     public float score() throws IOException {
       return parentScore;
     }
+    
+    @Override
+    public float freq() {
+      throw new UnsupportedOperationException(); // nocommit: anything make sense here?
+    }
 
     @Override
     public int advance(int parentTarget) throws IOException {
Index: modules/join/src/java/org/apache/lucene/search/join/BlockJoinCollector.java
===================================================================
--- modules/join/src/java/org/apache/lucene/search/join/BlockJoinCollector.java	(revision 1181600)
+++ modules/join/src/java/org/apache/lucene/search/join/BlockJoinCollector.java	(working copy)
@@ -337,6 +337,11 @@
     public float score() {
       return score;
     }
+    
+    @Override
+    public float freq() {
+      throw new UnsupportedOperationException(); // nocommit: wtf does this class do... duplicate of grouping's FakeScorer btw?
+    }
 
     @Override
     public int docID() {
Index: modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java
===================================================================
--- modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java	(revision 1181600)
+++ modules/grouping/src/java/org/apache/lucene/search/grouping/BlockGroupingCollector.java	(working copy)
@@ -106,6 +106,11 @@
     public float score() {
       return score;
     }
+    
+    @Override
+    public float freq() {
+      throw new UnsupportedOperationException(); // nocommit: wtf does this class do?
+    }
 
     @Override
     public int docID() {
Index: modules/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java
===================================================================
--- modules/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java	(revision 1181600)
+++ modules/queries/src/java/org/apache/lucene/queries/function/BoostedQuery.java	(working copy)
@@ -24,6 +24,8 @@
 import org.apache.lucene.util.ToStringUtils;
 
 import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Set;
 import java.util.Map;
 
@@ -161,6 +163,16 @@
       return score>Float.NEGATIVE_INFINITY ? score : -Float.MAX_VALUE;
     }
 
+    @Override
+    public float freq() throws IOException {
+      return scorer.freq();
+    }
+
+    @Override
+    public Collection<ChildScorer> getChildren() {
+      return Collections.singletonList(new ChildScorer(scorer, "CUSTOM"));
+    }
+
     public Explanation explain(int doc) throws IOException {
       Explanation subQueryExpl = weight.qWeight.explain(readerContext ,doc);
       if (!subQueryExpl.isMatch()) {
Index: modules/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java
===================================================================
--- modules/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java	(revision 1181600)
+++ modules/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java	(working copy)
@@ -157,6 +157,11 @@
       return score>Float.NEGATIVE_INFINITY ? score : -Float.MAX_VALUE;
     }
 
+    @Override
+    public float freq() throws IOException {
+      return 1;
+    }
+
     public Explanation explain(int doc) throws IOException {
       float sc = qWeight * vals.floatVal(doc);
 
Index: modules/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java
===================================================================
--- modules/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java	(revision 1181600)
+++ modules/queries/src/java/org/apache/lucene/queries/function/ValueSourceScorer.java	(working copy)
@@ -82,4 +82,9 @@
   public float score() throws IOException {
     return values.floatVal(doc);
   }
+
+  @Override
+  public float freq() throws IOException {
+    return 1;
+  }
 }
Index: modules/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java
===================================================================
--- modules/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java	(revision 1181600)
+++ modules/queries/src/java/org/apache/lucene/queries/CustomScoreQuery.java	(working copy)
@@ -18,6 +18,8 @@
  */
 
 import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Set;
 import java.util.Arrays;
 
@@ -325,6 +327,16 @@
     }
 
     @Override
+    public float freq() throws IOException {
+      return subQueryScorer.freq();
+    }
+
+    @Override
+    public Collection<ChildScorer> getChildren() {
+      return Collections.singletonList(new ChildScorer(subQueryScorer, "CUSTOM"));
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       int doc = subQueryScorer.advance(target);
       if (doc != NO_MORE_DOCS) {
Index: lucene/src/test/org/apache/lucene/search/TestBooleanScorer.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/TestBooleanScorer.java	(revision 1181600)
+++ lucene/src/test/org/apache/lucene/search/TestBooleanScorer.java	(working copy)
@@ -80,6 +80,7 @@
     Scorer[] scorers = new Scorer[] {new Scorer(weight) {
       private int doc = -1;
       @Override public float score() throws IOException { return 0; }
+      @Override public float freq() throws IOException { return 0; }
       @Override public int docID() { return doc; }
       
       @Override public int nextDoc() throws IOException {
Index: lucene/src/test/org/apache/lucene/search/TestSubScorerFreqs.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/TestSubScorerFreqs.java	(revision 1181600)
+++ lucene/src/test/org/apache/lucene/search/TestSubScorerFreqs.java	(working copy)
@@ -77,7 +77,7 @@
     private final Set<String> relationships;
 
     public CountingCollector(Collector other) {
-      this(other, new HashSet<String>(Arrays.asList(Occur.MUST.toString(), Occur.SHOULD.toString(), Occur.MUST_NOT.toString())));
+      this(other, new HashSet<String>(Arrays.asList("MUST", "SHOULD", "MUST_NOT")));
     }
 
     public CountingCollector(Collector other, Set<String> relationships) {
@@ -164,8 +164,8 @@
     query.add(aQuery, Occur.MUST);
     query.add(dQuery, Occur.MUST);
     Set<String>[] occurList = new Set[] {
-        Collections.singleton(Occur.MUST.toString()), 
-        new HashSet<String>(Arrays.asList(Occur.MUST.toString(), Occur.SHOULD.toString()))
+        Collections.singleton("MUST"), 
+        new HashSet<String>(Arrays.asList("MUST", "SHOULD"))
     };
     for (Set<String> occur : occurList) {
       CountingCollector c = new CountingCollector(TopScoreDocCollector.create(
@@ -173,7 +173,7 @@
       s.search(query, null, c);
       final int maxDocs = s.maxDoc();
       assertEquals(maxDocs, c.docCounts.size());
-      boolean includeOptional = occur.contains(Occur.SHOULD.toString());
+      boolean includeOptional = occur.contains("SHOULD");
       for (int i = 0; i < maxDocs; i++) {
         Map<Query, Float> doc0 = c.docCounts.get(i);
         assertEquals(includeOptional ? 5 : 4, doc0.size());
Index: lucene/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java	(revision 1181600)
+++ lucene/src/test/org/apache/lucene/search/TestScoreCachingWrappingScorer.java	(working copy)
@@ -43,6 +43,10 @@
       // once per document.
       return idx == scores.length ? Float.NaN : scores[idx++];
     }
+    
+    @Override public float freq() throws IOException {
+      return 1;
+    }
 
     @Override public int docID() { return doc; }
 
Index: lucene/src/test/org/apache/lucene/search/TestCachingCollector.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/TestCachingCollector.java	(revision 1181600)
+++ lucene/src/test/org/apache/lucene/search/TestCachingCollector.java	(working copy)
@@ -34,6 +34,9 @@
     
     @Override
     public float score() throws IOException { return 0; }
+    
+    @Override
+    public float freq() throws IOException { return 0; }
 
     @Override
     public int docID() { return 0; }
Index: lucene/src/test/org/apache/lucene/search/JustCompileSearch.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/JustCompileSearch.java	(revision 1181600)
+++ lucene/src/test/org/apache/lucene/search/JustCompileSearch.java	(working copy)
@@ -230,6 +230,11 @@
     public float score() throws IOException {
       throw new UnsupportedOperationException(UNSUPPORTED_MSG);
     }
+    
+    @Override
+    public float freq() throws IOException {
+      throw new UnsupportedOperationException(UNSUPPORTED_MSG);
+    }
 
     @Override
     public int docID() {
Index: lucene/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java
===================================================================
--- lucene/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java	(revision 1181600)
+++ lucene/src/test/org/apache/lucene/search/TestPositiveScoresOnlyCollector.java	(working copy)
@@ -37,6 +37,10 @@
     @Override public float score() throws IOException {
       return idx == scores.length ? Float.NaN : scores[idx];
     }
+    
+    @Override public float freq() throws IOException {
+      return 1;
+    }
 
     @Override public int docID() { return idx; }
 
Index: lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/ConstantScoreQuery.java	(working copy)
@@ -200,6 +200,11 @@
     }
 
     @Override
+    public float freq() throws IOException {
+      return 1;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       return docIdSetIterator.advance(target);
     }
Index: lucene/src/java/org/apache/lucene/search/BooleanScorer2.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/BooleanScorer2.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/BooleanScorer2.java	(working copy)
@@ -132,6 +132,11 @@
     }
 
     @Override
+    public float freq() throws IOException {
+      return 1; // nocommit
+    }
+
+    @Override
     public int docID() {
       return scorer.docID();
     }
@@ -311,8 +316,8 @@
   }
 
   @Override
-  public float freq() {
-    return coordinator.nrMatchers;
+  public float freq() throws IOException {
+    return countingSumScorer.freq();
   }
 
   @Override
@@ -324,13 +329,13 @@
   public Collection<ChildScorer> getChildren() {
     ArrayList<ChildScorer> children = new ArrayList<ChildScorer>();
     for (Scorer s : optionalScorers) {
-      children.add(new ChildScorer(s, Occur.SHOULD.toString()));
+      children.add(new ChildScorer(s, "SHOULD"));
     }
     for (Scorer s : prohibitedScorers) {
-      children.add(new ChildScorer(s, Occur.MUST_NOT.toString()));
+      children.add(new ChildScorer(s, "MUST_NOT"));
     }
     for (Scorer s : requiredScorers) {
-      children.add(new ChildScorer(s, Occur.MUST.toString()));
+      children.add(new ChildScorer(s, "MUST"));
     }
     return children;
   }
Index: lucene/src/java/org/apache/lucene/search/MatchAllDocsQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/MatchAllDocsQuery.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/MatchAllDocsQuery.java	(working copy)
@@ -68,6 +68,11 @@
     }
 
     @Override
+    public float freq() throws IOException {
+      return 1;
+    }
+
+    @Override
     public int advance(int target) throws IOException {
       doc = target-1;
       return nextDoc();
Index: lucene/src/java/org/apache/lucene/search/ConjunctionScorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/ConjunctionScorer.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/ConjunctionScorer.java	(working copy)
@@ -19,6 +19,7 @@
 
 import org.apache.lucene.util.ArrayUtil;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Comparator;
 
@@ -136,4 +137,18 @@
     }
     return sum * coord;
   }
+
+  @Override
+  public float freq() throws IOException {
+    return scorers.length;
+  }
+
+  @Override
+  public Collection<ChildScorer> getChildren() {
+    ArrayList<ChildScorer> children = new ArrayList<ChildScorer>(scorers.length);
+    for (Scorer scorer : scorers) {
+      children.add(new ChildScorer(scorer, "MUST"));
+    }
+    return children;
+  }
 }
Index: lucene/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/DisjunctionMaxScorer.java	(working copy)
@@ -17,6 +17,8 @@
  */
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
 
 /**
  * The Scorer for DisjunctionMaxQuery.  The union of all documents generated by the the subquery scorers
@@ -111,6 +113,33 @@
   }
 
   @Override
+  public float freq() throws IOException {
+    int doc = subScorers[0].docID();
+    int size = numScorers;
+    return 1 + freq(1, size, doc) + freq(2, size, doc);
+  }
+  
+  // Recursively iterate all subScorers that generated last doc computing sum and max
+  private int freq(int root, int size, int doc) throws IOException {
+    int freq = 0;
+    if (root < size && subScorers[root].docID() == doc) {
+      freq++;
+      freq += freq((root<<1)+1, size, doc);
+      freq += freq((root<<1)+2, size, doc);
+    }
+    return freq;
+  }
+
+  @Override
+  public Collection<ChildScorer> getChildren() {
+    ArrayList<ChildScorer> children = new ArrayList<ChildScorer>(numScorers);
+    for (int i = 0; i < numScorers; i++) {
+      children.add(new ChildScorer(subScorers[i], "SHOULD"));
+    }
+    return super.getChildren();
+  }
+
+  @Override
   public int advance(int target) throws IOException {
     if (numScorers == 0) return doc = NO_MORE_DOCS;
     while (subScorers[0].docID() < target) {
Index: lucene/src/java/org/apache/lucene/search/FilteredQuery.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/FilteredQuery.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/FilteredQuery.java	(working copy)
@@ -24,6 +24,8 @@
 import org.apache.lucene.util.ToStringUtils;
 
 import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.Set;
 
 
@@ -153,6 +155,14 @@
 
           @Override
           public float score() throws IOException { return scorer.score(); }
+          
+          @Override
+          public float freq() throws IOException { return scorer.freq(); }
+
+          @Override
+          public Collection<ChildScorer> getChildren() {
+            return Collections.singletonList(new ChildScorer(scorer, "FILTERED"));
+          }
         };
       }
     };
Index: lucene/src/java/org/apache/lucene/search/ConjunctionTermScorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/ConjunctionTermScorer.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/ConjunctionTermScorer.java	(working copy)
@@ -94,6 +94,11 @@
     }
     return sum * coord;
   }
+  
+  @Override
+  public float freq() throws IOException {
+    return docsAndFreqs.length;
+  }
 
   static final class DocsAndFreqs {
     final DocsEnum docs;
Index: lucene/src/java/org/apache/lucene/search/ReqOptSumScorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/ReqOptSumScorer.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/ReqOptSumScorer.java	(working copy)
@@ -17,6 +17,8 @@
  */
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
 
 /** A Scorer for queries with a required part and an optional part.
  * Delays skipTo() on the optional part until a score() is needed.
@@ -80,5 +82,19 @@
     return optScorerDoc == curDoc ? reqScore + optScorer.score() : reqScore;
   }
 
+  @Override
+  public float freq() throws IOException {
+    // we might have deferred advance()
+    score();
+    return optScorer.docID() == reqScorer.docID() ? 2 : 1;
+  }
+
+  @Override
+  public Collection<ChildScorer> getChildren() {
+    ArrayList<ChildScorer> children = new ArrayList<ChildScorer>(2);
+    children.add(new ChildScorer(reqScorer, "MUST"));
+    children.add(new ChildScorer(optScorer, "SHOULD"));
+    return children;
+  }
 }
 
Index: lucene/src/java/org/apache/lucene/search/DisjunctionSumScorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/DisjunctionSumScorer.java	(revision 1181780)
+++ lucene/src/java/org/apache/lucene/search/DisjunctionSumScorer.java	(working copy)
@@ -17,6 +17,8 @@
  * limitations under the License.
  */
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.io.IOException;
 
@@ -205,10 +207,25 @@
   /** Returns the number of subscorers matching the current document.
    * Initially invalid, until {@link #nextDoc()} is called the first time.
    */
+  // TODO: deprecate? we have freq()
   public int nrMatchers() {
     return nrMatchers;
   }
 
+  @Override
+  public float freq() throws IOException {
+    return nrMatchers;
+  }
+
+  @Override
+  public Collection<ChildScorer> getChildren() {
+    ArrayList<ChildScorer> children = new ArrayList<ChildScorer>();
+    for (Scorer scorer : subScorers) {
+      children.add(new ChildScorer(scorer, "SHOULD"));
+    }
+    return children;
+  }
+
   /**
    * Advances to the first match beyond the current whose document number is
    * greater than or equal to a given target. <br>
Index: lucene/src/java/org/apache/lucene/search/BooleanScorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/BooleanScorer.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/BooleanScorer.java	(working copy)
@@ -338,6 +338,11 @@
   }
 
   @Override
+  public float freq() throws IOException {
+    return current.coord;
+  }
+
+  @Override
   public void score(Collector collector) throws IOException {
     score(collector, Integer.MAX_VALUE, nextDoc());
   }
@@ -358,7 +363,8 @@
   public Collection<ChildScorer> getChildren() {
     List<ChildScorer> children = new ArrayList<ChildScorer>();
     for (SubScorer sub = scorers; sub != null; sub = sub.next) {
-      children.add(new ChildScorer(sub.scorer, sub.prohibited ? Occur.MUST_NOT.toString() : Occur.SHOULD.toString()));
+      // TODO: fix this if BQ ever sends us required clauses
+      children.add(new ChildScorer(sub.scorer, sub.prohibited ? "MUST_NOT" : "SHOULD"));
     }
     return children;
   }
Index: lucene/src/java/org/apache/lucene/search/Scorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/Scorer.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/Scorer.java	(working copy)
@@ -98,9 +98,7 @@
    *  "sloppy" the match was.
    *
    * @lucene.experimental */
-  public float freq() throws IOException {
-    throw new UnsupportedOperationException(this + " does not implement freq()");
-  }
+  public abstract float freq() throws IOException;
   
   /** returns parent Weight
    * @lucene.experimental
Index: lucene/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/ScoreCachingWrappingScorer.java	(working copy)
@@ -18,6 +18,8 @@
  */
 
 import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
 
 /**
  * A {@link Scorer} which wraps another scorer and caches the score of the
@@ -59,6 +61,11 @@
   }
 
   @Override
+  public float freq() throws IOException {
+    return scorer.freq();
+  }
+
+  @Override
   public int docID() {
     return scorer.docID();
   }
@@ -77,5 +84,9 @@
   public int advance(int target) throws IOException {
     return scorer.advance(target);
   }
-  
+
+  @Override
+  public Collection<ChildScorer> getChildren() {
+    return Collections.singletonList(new ChildScorer(scorer, "CACHED"));
+  }
 }
Index: lucene/src/java/org/apache/lucene/search/ReqExclScorer.java
===================================================================
--- lucene/src/java/org/apache/lucene/search/ReqExclScorer.java	(revision 1181600)
+++ lucene/src/java/org/apache/lucene/search/ReqExclScorer.java	(working copy)
@@ -18,8 +18,9 @@
  */
 
 import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
 
-
 /** A Scorer for queries with a required subscorer
  * and an excluding (prohibited) sub DocIdSetIterator.
  * <br>
@@ -104,6 +105,16 @@
   }
   
   @Override
+  public float freq() throws IOException {
+    return reqScorer.freq();
+  }
+
+  @Override
+  public Collection<ChildScorer> getChildren() {
+    return Collections.singletonList(new ChildScorer(reqScorer, "FILTERED"));
+  }
+
+  @Override
   public int advance(int target) throws IOException {
     if (reqScorer == null) {
       return doc = NO_MORE_DOCS;
