Index: src/java/org/apache/lucene/search/BooleanFilterClause.java
===================================================================
--- src/java/org/apache/lucene/search/BooleanFilterClause.java	(revision 0)
+++ src/java/org/apache/lucene/search/BooleanFilterClause.java	(revision 0)
@@ -0,0 +1,83 @@
+package org.apache.lucene.search;
+
+import org.apache.lucene.util.Parameter;
+
+/**
+ * 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.
+ */
+
+/** A filter clause in a BooleanQuery.
+    A Filter may only occur as MUST or MUST_NOT.
+  */
+public class BooleanFilterClause implements java.io.Serializable {
+  
+  /** The filter whose matching documents are combined by the boolean query. */
+  private Filter filter;
+
+  private BooleanClause.Occur occur; // Could move to superclass
+
+  /** Constructs a BooleanFilterClause.
+  */ 
+  public BooleanFilterClause(Filter filter, BooleanClause.Occur occur) {
+    if (occur == BooleanClause.Occur.SHOULD) {
+      throw new IllegalArgumentException("Filter can only occur as MUST or MUST_NOT");
+    }
+    this.filter = filter;
+    this.occur = occur;
+  }
+
+  public BooleanClause.Occur getOccur() {
+    return occur;
+  }
+
+  public void setOccur(BooleanClause.Occur occur) {
+    if (occur == BooleanClause.Occur.SHOULD) {
+      throw new IllegalArgumentException("Filter can only occur as MUST or MUST_NOT");
+    }
+    this.occur = occur;
+  }
+
+  public Filter getFilter() {
+    return filter;
+  }
+
+  public void setFilter(Filter filter) {
+    assert filter == null;
+    this.filter = filter;
+  }
+  
+  public boolean isRequired() {
+    return BooleanClause.Occur.MUST.equals(occur);
+  }
+
+  /** Returns true iff <code>o</code> is equal to this. */
+  public boolean equals(Object o) {
+    if (!(o instanceof BooleanClause))
+      return false;
+    BooleanFilterClause other = (BooleanFilterClause)o;
+    return this.occur.equals(other.occur)
+      && this.filter.equals(other.filter);
+  }
+
+  /** Returns a hash code value for this object.*/
+  public int hashCode() {
+    return filter.hashCode() ^ occur.hashCode();
+  }
+
+  public String toString() {
+    return occur.toString() + filter.toString();
+  }
+}
Index: src/java/org/apache/lucene/search/BooleanClause.java
===================================================================
--- src/java/org/apache/lucene/search/BooleanClause.java	(revision 680483)
+++ src/java/org/apache/lucene/search/BooleanClause.java	(working copy)
@@ -19,7 +19,7 @@
  * limitations under the License.
  */
 
-/** A clause in a BooleanQuery. */
+/** A query clause in a BooleanQuery. */
 public class BooleanClause implements java.io.Serializable {
   
   /** Specifies how clauses are to occur in matching documents. */
@@ -36,6 +36,10 @@
       return "";
     }
 
+    public int hashCode() {
+      return (Occur.MUST.equals(this)?1:0) ^ (Occur.MUST_NOT.equals(this)?2:0);
+    }
+
     /** Use this operator for clauses that <i>must</i> appear in the matching documents. */
     public static final Occur MUST = new Occur("MUST");
     /** Use this operator for clauses that <i>should</i> appear in the 
@@ -105,7 +109,7 @@
 
   /** Returns a hash code value for this object.*/
   public int hashCode() {
-    return query.hashCode() ^ (Occur.MUST.equals(occur)?1:0) ^ (Occur.MUST_NOT.equals(occur)?2:0);
+    return query.hashCode() ^ occur.hashCode();
   }
 
 
Index: src/java/org/apache/lucene/search/BooleanQuery.java
===================================================================
--- src/java/org/apache/lucene/search/BooleanQuery.java	(revision 680483)
+++ src/java/org/apache/lucene/search/BooleanQuery.java	(working copy)
@@ -50,6 +50,7 @@
   /** Return the maximum number of clauses permitted, 1024 by default.
    * Attempts to add more than the permitted number of clauses cause {@link
    * TooManyClauses} to be thrown.
+   * Both query clauses and filter clauses are counted as clauses.
    * @see #setMaxClauseCount(int)
    */
   public static int getMaxClauseCount() { return maxClauseCount; }
@@ -74,6 +75,7 @@
   }
 
   private ArrayList clauses = new ArrayList();
+  private ArrayList filterClauses = new ArrayList();
   private boolean disableCoord;
 
   /** Constructs an empty boolean query. */
@@ -141,13 +143,13 @@
 
   /**
    * Gets the minimum number of the optional BooleanClauses
-   * which must be satisifed.
+   * which must be satisfied.
    */
   public int getMinimumNumberShouldMatch() {
     return minNrShouldMatch;
   }
 
-  /** Adds a clause to a boolean query.
+  /** Adds a query clause to a boolean query.
    *
    * @throws TooManyClauses if the new number of clauses exceeds the maximum clause number
    * @see #getMaxClauseCount()
@@ -156,28 +158,58 @@
     add(new BooleanClause(query, occur));
   }
 
-  /** Adds a clause to a boolean query.
+  /** Adds a filter clause to a boolean query.
+   * The filter can only occur as #Boolean.Occur.MUST or #BooleanClause.Occur.MUST_NOT .
+   *
    * @throws TooManyClauses if the new number of clauses exceeds the maximum clause number
    * @see #getMaxClauseCount()
    */
-  public void add(BooleanClause clause) {
-    if (clauses.size() >= maxClauseCount)
+  public void add(Filter filter, BooleanClause.Occur occur) {
+    add(new BooleanFilterClause(filter, occur));
+  }
+
+  private void checkMaxClauseCount() {
+    if ((clauses.size() + filterClauses.size()) >= maxClauseCount)
       throw new TooManyClauses();
+  }
 
+  /** Adds a query clause to a boolean query.
+   * @throws TooManyClauses if the new number of clauses exceeds the maximum clause number
+   * @see #getMaxClauseCount()
+   */
+  public void add(BooleanClause clause) {
+    checkMaxClauseCount();
     clauses.add(clause);
   }
 
-  /** Returns the set of clauses in this query. */
+  /** Adds a filter clause to a boolean query.
+   * @throws TooManyClauses if the new number of clauses exceeds the maximum clause number
+   * @see #getMaxClauseCount()
+   */
+  public void add(BooleanFilterClause clause) {
+    checkMaxClauseCount();
+    filterClauses.add(clause);
+  }
+
+  /** Returns the set of query clauses in this query. */
   public BooleanClause[] getClauses() {
     return (BooleanClause[])clauses.toArray(new BooleanClause[clauses.size()]);
   }
 
-  /** Returns the list of clauses in this query. */
+  /** Returns the set of filter clauses in this query. */
+  public BooleanFilterClause[] getFilterClauses() {
+    return (BooleanFilterClause[])filterClauses.toArray(new BooleanFilterClause[filterClauses.size()]);
+  }
+
+  /** Returns the list of query clauses in this query. */
   public List clauses() { return clauses; }
 
+  /** Returns the list of filter clauses in this query. */
+  public List filterClauses() { return filterClauses; }
+
   private class BooleanWeight implements Weight {
     protected Similarity similarity;
-    protected ArrayList weights = new ArrayList();
+    protected ArrayList weights = new ArrayList(); // only query clauses, not for filter clauses.
 
     public BooleanWeight(Searcher searcher)
       throws IOException {
@@ -198,9 +230,8 @@
         Weight w = (Weight)weights.get(i);
         // call sumOfSquaredWeights for all clauses in case of side effects
         float s = w.sumOfSquaredWeights();         // sum sub weights
-        if (!c.isProhibited())
-          // only add to sum for non-prohibited clauses
-          sum += s;
+        if (!c.isProhibited()) 
+          sum += s;  // only add to sum for non-prohibited query clauses
       }
 
       sum *= getBoost() * getBoost();             // boost each sub-weight
@@ -213,7 +244,7 @@
       norm *= getBoost();                         // incorporate boost
       for (int i = 0 ; i < weights.size(); i++) {
         Weight w = (Weight)weights.get(i);
-        // normalize all clauses, (even if prohibited in case of side affects)
+        // normalize all query clauses, (even if prohibited in case of side affects)
         w.normalize(norm);
       }
     }
@@ -225,7 +256,6 @@
       BooleanScorer2 result = new BooleanScorer2(similarity,
                                                  minNrShouldMatch,
                                                  allowDocsOutOfOrder);
-
       for (int i = 0 ; i < weights.size(); i++) {
         BooleanClause c = (BooleanClause)clauses.get(i);
         Weight w = (Weight)weights.get(i);
@@ -235,6 +265,11 @@
         else if (c.isRequired())
           return null;
       }
+      for (int i = 0 ; i < filterClauses.size(); i++) {
+        BooleanFilterClause fc = (BooleanFilterClause)filterClauses.get(i);
+        DocIdSetIterator disi = fc.getFilter().getDocIdSet(reader).iterator();
+        result.add(disi, fc.isRequired());
+      }
 
       return result;
     }
@@ -254,7 +289,9 @@
         BooleanClause c = (BooleanClause)clauses.get(i);
         Weight w = (Weight)weights.get(i);
         Explanation e = w.explain(reader, doc);
-        if (!c.isProhibited()) maxCoord++;
+        if (!c.isProhibited()) {
+          maxCoord++;
+        }
         if (e.isMatch()) {
           if (!c.isProhibited()) {
             sumExpl.addDetail(e);
@@ -276,6 +313,28 @@
           fail = true;
         }
       }
+      for (int i = 0 ; i < filterClauses.size(); i++) {
+        BooleanFilterClause fc = (BooleanFilterClause) filterClauses.get(i);
+        Filter f = fc.getFilter();
+        DocIdSetIterator disi = f.getDocIdSet(reader).iterator();
+        Explanation r;
+        boolean match = (disi.skipTo(doc) && (disi.doc() == doc));
+        if (match) {
+          if (fc.isRequired()) {
+            r = new Explanation(0.0f, "match on required filter clause (" + f.toString() + ")");
+          } else {
+            r = new Explanation(0.0f, "match on prohibited filter clause (" + f.toString() + ")");
+          }
+        } else { // no match
+          if (fc.isRequired()) {
+            r = new Explanation(0.0f, "no match on required filter clause (" + f.toString() + ")");
+          } else {
+            r = new Explanation(0.0f, "no match on prohibited filter clause (" + f.toString() + ")");
+          }
+        }
+        fail = (match != fc.isRequired());
+        sumExpl.addDetail(r);
+      }
       if (fail) {
         sumExpl.setMatch(Boolean.FALSE);
         sumExpl.setValue(0.0f);
@@ -363,10 +422,10 @@
   }
 
   public Query rewrite(IndexReader reader) throws IOException {
-    if (minNrShouldMatch == 0 && clauses.size() == 1) {                    // optimize 1-clause queries
+    if (minNrShouldMatch == 0 && clauses.size() == 1 && filterClauses.size() == 0) {
+      // optimize 1-clause queries
       BooleanClause c = (BooleanClause)clauses.get(0);
-      if (!c.isProhibited()) {			  // just return clause
-
+      if (!c.isProhibited()) {  // just return clause
         Query query = c.getQuery().rewrite(reader);    // rewrite first
 
         if (getBoost() != 1.0f) {                 // incorporate boost
@@ -384,8 +443,9 @@
       BooleanClause c = (BooleanClause)clauses.get(i);
       Query query = c.getQuery().rewrite(reader);
       if (query != c.getQuery()) {                     // clause rewrote: must clone
-        if (clone == null)
+        if (clone == null) {
           clone = (BooleanQuery)this.clone();
+        }
         clone.clauses.set(i, new BooleanClause(query, c.getOccur()));
       }
     }
@@ -400,12 +460,13 @@
       for (Iterator i = clauses.iterator(); i.hasNext();) {
           BooleanClause clause = (BooleanClause) i.next();
           clause.getQuery().extractTerms(terms);
-        }
+      }
   }
 
   public Object clone() {
     BooleanQuery clone = (BooleanQuery)super.clone();
     clone.clauses = (ArrayList)this.clauses.clone();
+    clone.filterClauses = (ArrayList)this.filterClauses.clone();
     return clone;
   }
 
@@ -419,22 +480,27 @@
 
     for (int i = 0 ; i < clauses.size(); i++) {
       BooleanClause c = (BooleanClause)clauses.get(i);
-      if (c.isProhibited())
-        buffer.append("-");
-      else if (c.isRequired())
-        buffer.append("+");
+      buffer.append(c.getOccur().toString());
 
       Query subQuery = c.getQuery();
       if (subQuery instanceof BooleanQuery) {	  // wrap sub-bools in parens
         buffer.append("(");
         buffer.append(c.getQuery().toString(field));
         buffer.append(")");
-      } else
+      } else {
         buffer.append(c.getQuery().toString(field));
+      }
 
       if (i != clauses.size()-1)
         buffer.append(" ");
     }
+    
+    for (int i = 0 ; i < filterClauses.size(); i++) {
+      BooleanFilterClause fc = (BooleanFilterClause)filterClauses.get(i);
+      buffer.append(" ");
+      buffer.append(fc.getOccur().toString());
+      buffer.append(fc.getFilter().toString());
+    }
 
     if (needParens) {
       buffer.append(")");
@@ -460,12 +526,15 @@
     BooleanQuery other = (BooleanQuery)o;
     return (this.getBoost() == other.getBoost())
         && this.clauses.equals(other.clauses)
+        && this.filterClauses.equals(other.filterClauses)
         && this.getMinimumNumberShouldMatch() == other.getMinimumNumberShouldMatch();
   }
 
   /** Returns a hash code value for this object.*/
   public int hashCode() {
-    return Float.floatToIntBits(getBoost()) ^ clauses.hashCode()
+    return Float.floatToIntBits(getBoost())
+             ^ clauses.hashCode()
+             ^ filterClauses.hashCode()
            + getMinimumNumberShouldMatch();
   }
 
Index: src/java/org/apache/lucene/search/BooleanScorer2.java
===================================================================
--- src/java/org/apache/lucene/search/BooleanScorer2.java	(revision 680483)
+++ src/java/org/apache/lucene/search/BooleanScorer2.java	(working copy)
@@ -24,13 +24,17 @@
 
 /** An alternative to BooleanScorer that also allows a minimum number
  * of optional scorers that should match.
- * <br>Implements skipTo(), and has no limitations on the numbers of added scorers.
+ * Also supports required and prohibited DocIdSetIterators.
+ * <br>Implements skipTo(), and has no limitations on the numbers
+ * of added Scorers and DocIdSetIterators.
  * <br>Uses ConjunctionScorer, DisjunctionScorer, ReqOptScorer and ReqExclScorer.
  */
 class BooleanScorer2 extends Scorer {
   private ArrayList requiredScorers = new ArrayList();
+  private ArrayList requiredDisis = new ArrayList();
   private ArrayList optionalScorers = new ArrayList();
-  private ArrayList prohibitedScorers = new ArrayList();
+  private ArrayList prohibitedDisis = new ArrayList();
+  private ArrayList prohibitedScorers = new ArrayList(); // only used when allowDocsOutOfOrder == true
 
   private class Coordinator {
     int maxCoord = 0; // to be increased for each non prohibited scorer
@@ -126,12 +130,26 @@
       }
       requiredScorers.add(scorer);
     } else if (prohibited) {
-      prohibitedScorers.add(scorer);
+      prohibitedDisis.add(scorer); // score() will not be called on prohibited scorers.
+      if (allowDocsOutOfOrder) {
+        if (prohibitedScorers == null) {
+          prohibitedScorers = new ArrayList();
+        }
+        prohibitedScorers.add(scorer); // also keep as scorer to pass to BooleanScorer when possible.
+      }
     } else {
       optionalScorers.add(scorer);
     }
   }
 
+  public void add(final DocIdSetIterator disi, boolean required) {
+    if (required) {
+      requiredDisis.add(disi);
+    } else {
+      prohibitedDisis.add(disi);
+    }
+  }
+
   /** Initialize the match counting scorer that sums all the
    * scores. <p>
    * When "counting" is used in a name it means counting the number
@@ -149,9 +167,13 @@
     private Scorer scorer;
     private int lastScoredDoc = -1;
 
-    SingleMatchScorer(Scorer scorer) {
+    SingleMatchScorer(Scorer scorer, List requiredDisis) throws IOException {
       super(scorer.getSimilarity());
-      this.scorer = scorer;
+      if (requiredDisis.size() == 0) {
+        this.scorer = scorer;
+      } else {
+        this.scorer = new ConjunctionScorer(defaultSimilarity, new Scorer[]{scorer}, requiredDisis);
+      }
     }
     public float score() throws IOException {
       if (this.doc() >= lastScoredDoc) {
@@ -175,10 +197,11 @@
   }
 
   private Scorer countingDisjunctionSumScorer(final List scorers,
-                                              int minNrShouldMatch)
+                                              int minNrShouldMatch,
+                                              List requiredDisis) throws IOException
   // each scorer from the list counted as a single matcher
   {
-    return new DisjunctionSumScorer(scorers, minNrShouldMatch) {
+    Scorer scorer = new DisjunctionSumScorer(scorers, minNrShouldMatch) {
       private int lastScoredDoc = -1;
       public float score() throws IOException {
         if (this.doc() >= lastScoredDoc) {
@@ -188,14 +211,19 @@
         return super.score();
       }
     };
+ 
+    return (requiredDisis.size() == 0)
+      ? scorer
+      : new ConjunctionScorer(defaultSimilarity, new Scorer[]{scorer}, requiredDisis);
   }
 
   private static Similarity defaultSimilarity = new DefaultSimilarity();
 
-  private Scorer countingConjunctionSumScorer(List requiredScorers) throws IOException {
+  private Scorer countingConjunctionSumScorer(List requiredScorers,
+                                              List requiredDisis) throws IOException {
     // each scorer from the list counted as a single matcher
     final int requiredNrMatchers = requiredScorers.size();
-    return new ConjunctionScorer(defaultSimilarity, requiredScorers) {
+    return new ConjunctionScorer(defaultSimilarity, requiredScorers, requiredDisis) {
       private int lastScoredDoc = -1;
 
       public float score() throws IOException {
@@ -240,12 +268,12 @@
       } else { // optionalScorers.size() >= nrOptRequired, no required scorers
         Scorer requiredCountingSumScorer =
               (optionalScorers.size() > nrOptRequired)
-              ? countingDisjunctionSumScorer(optionalScorers, nrOptRequired)
+              ? countingDisjunctionSumScorer(optionalScorers, nrOptRequired, requiredDisis)
               : // optionalScorers.size() == nrOptRequired (all optional scorers are required), no required scorers
               (optionalScorers.size() == 1)
-              ? new SingleMatchScorer((Scorer) optionalScorers.get(0))
-              : countingConjunctionSumScorer(optionalScorers);
-        return addProhibitedScorers(requiredCountingSumScorer);
+              ? new SingleMatchScorer((Scorer) optionalScorers.get(0), requiredDisis)
+              : countingConjunctionSumScorer(optionalScorers, requiredDisis);
+        return addProhibitedDisis(requiredCountingSumScorer);
       }
     }
   }
@@ -254,43 +282,44 @@
     if (optionalScorers.size() < minNrShouldMatch) {
       return new NonMatchingScorer(); // fewer optional clauses than minimum that should match
     } else if (optionalScorers.size() == minNrShouldMatch) { // all optional scorers also required.
-      ArrayList allReq = new ArrayList(requiredScorers);
-      allReq.addAll(optionalScorers);
-      return addProhibitedScorers(countingConjunctionSumScorer(allReq));
+      ArrayList allReqScorers = new ArrayList(requiredScorers);
+      allReqScorers.addAll(optionalScorers);
+      return addProhibitedDisis(countingConjunctionSumScorer(allReqScorers, requiredDisis));
     } else { // optionalScorers.size() > minNrShouldMatch, and at least one required scorer
       Scorer requiredCountingSumScorer =
             (requiredScorers.size() == 1)
-            ? new SingleMatchScorer((Scorer) requiredScorers.get(0))
-            : countingConjunctionSumScorer(requiredScorers);
+            ? new SingleMatchScorer((Scorer) requiredScorers.get(0), requiredDisis)
+            : countingConjunctionSumScorer(requiredScorers, requiredDisis);
       if (minNrShouldMatch > 0) { // use a required disjunction scorer over the optional scorers
-        return addProhibitedScorers( 
+        return addProhibitedDisis( 
                       dualConjunctionSumScorer( // non counting
                               requiredCountingSumScorer,
                               countingDisjunctionSumScorer(
                                       optionalScorers,
-                                      minNrShouldMatch)));
+                                      minNrShouldMatch,
+                                      requiredDisis)));
       } else { // minNrShouldMatch == 0
         return new ReqOptSumScorer(
-                      addProhibitedScorers(requiredCountingSumScorer),
+                      addProhibitedDisis(requiredCountingSumScorer),
                       ((optionalScorers.size() == 1)
-                        ? new SingleMatchScorer((Scorer) optionalScorers.get(0))
-                        : countingDisjunctionSumScorer(optionalScorers, 1))); // require 1 in combined, optional scorer.
+                        ? new SingleMatchScorer((Scorer) optionalScorers.get(0), requiredDisis)
+                        : countingDisjunctionSumScorer(optionalScorers, 1, requiredDisis))); // require 1 in combined, optional scorer.
       }
     }
   }
   
   /** Returns the scorer to be used for match counting and score summing.
-   * Uses the given required scorer and the prohibitedScorers.
+   * Uses the given required scorer and the prohibitedDisis.
    * @param requiredCountingSumScorer A required scorer already built.
    */
-  private Scorer addProhibitedScorers(Scorer requiredCountingSumScorer)
+  private Scorer addProhibitedDisis(Scorer requiredCountingSumScorer)
   {
-    return (prohibitedScorers.size() == 0)
-          ? requiredCountingSumScorer // no prohibited
+    return (prohibitedDisis.size() == 0)
+          ? requiredCountingSumScorer
           : new ReqExclScorer(requiredCountingSumScorer,
-                              ((prohibitedScorers.size() == 1)
-                                ? (Scorer) prohibitedScorers.get(0)
-                                : new DisjunctionSumScorer(prohibitedScorers)));
+                              ((prohibitedDisis.size() == 1)
+                                ? (DocIdSetIterator) prohibitedDisis.get(0)
+                                : new DisjunctionDISI(prohibitedDisis)));
   }
 
   /** Scores and collects all matching documents.
@@ -299,8 +328,14 @@
    * <br>When this method is used the {@link #explain(int)} method should not be used.
    */
   public void score(HitCollector hc) throws IOException {
-    if (allowDocsOutOfOrder && requiredScorers.size() == 0
-            && prohibitedScorers.size() < 32) {
+    if (allowDocsOutOfOrder
+            && requiredScorers.size() == 0 // required by BooleanScorer.
+            && prohibitedScorers.size() < 32 // required by BooleanScorer.
+            /* BooleanScorer cannot handle required or prohibited DocIdSetIterators.
+             * prohibited Scorers are also kept as DocIdSetIterators when allowDocsOutOfOrder
+             */
+            && requiredDisis.size() == 0 
+            && prohibitedDisis.size() == prohibitedScorers.size()) {
       // fall back to BooleanScorer, scores documents somewhat out of order
       BooleanScorer bs = new BooleanScorer(getSimilarity(), minNrShouldMatch);
       Iterator si = optionalScorers.iterator();
Index: src/java/org/apache/lucene/search/ReqExclScorer.java
===================================================================
--- src/java/org/apache/lucene/search/ReqExclScorer.java	(revision 680483)
+++ src/java/org/apache/lucene/search/ReqExclScorer.java	(working copy)
@@ -20,32 +20,34 @@
 import java.io.IOException;
 
 
-/** A Scorer for queries with a required subscorer and an excluding (prohibited) subscorer.
+/** A Scorer for queries with a required subscorer
+ * and an excluding (prohibited) sub DocIdSetIterator.
  * <br>
  * This <code>Scorer</code> implements {@link Scorer#skipTo(int)},
  * and it uses the skipTo() on the given scorers.
  */
-public class ReqExclScorer extends Scorer {
-  private Scorer reqScorer, exclScorer;
+class ReqExclScorer extends Scorer {
+  private Scorer reqScorer;
+  private DocIdSetIterator exclDisi;
 
   /** Construct a <code>ReqExclScorer</code>.
    * @param reqScorer The scorer that must match, except where
-   * @param exclScorer indicates exclusion.
+   * @param exclDisi indicates exclusion.
    */
   public ReqExclScorer(
       Scorer reqScorer,
-      Scorer exclScorer) {
+      DocIdSetIterator exclDisi) {
     super(null); // No similarity used.
     this.reqScorer = reqScorer;
-    this.exclScorer = exclScorer;
+    this.exclDisi = exclDisi;
   }
 
   private boolean firstTime = true;
   
   public boolean next() throws IOException {
     if (firstTime) {
-      if (! exclScorer.next()) {
-        exclScorer = null; // exhausted at start
+      if (! exclDisi.next()) {
+        exclDisi = null; // exhausted at start
       }
       firstTime = false;
     }
@@ -56,7 +58,7 @@
       reqScorer = null; // exhausted, nothing left
       return false;
     }
-    if (exclScorer == null) {
+    if (exclDisi == null) {
       return true; // reqScorer.next() already returned true
     }
     return toNonExcluded();
@@ -66,7 +68,7 @@
    * <br>On entry:
    * <ul>
    * <li>reqScorer != null,
-   * <li>exclScorer != null,
+   * <li>exclDisi != null,
    * <li>reqScorer was advanced once via next() or skipTo()
    *      and reqScorer.doc() may still be excluded.
    * </ul>
@@ -74,17 +76,17 @@
    * @return true iff there is a non excluded required doc.
    */
   private boolean toNonExcluded() throws IOException {
-    int exclDoc = exclScorer.doc();
+    int exclDoc = exclDisi.doc();
     do {  
       int reqDoc = reqScorer.doc(); // may be excluded
       if (reqDoc < exclDoc) {
         return true; // reqScorer advanced to before exclScorer, ie. not excluded
       } else if (reqDoc > exclDoc) {
-        if (! exclScorer.skipTo(reqDoc)) {
-          exclScorer = null; // exhausted, no more exclusions
+        if (! exclDisi.skipTo(reqDoc)) {
+          exclDisi = null; // exhausted, no more exclusions
           return true;
         }
-        exclDoc = exclScorer.doc();
+        exclDoc = exclDisi.doc();
         if (exclDoc > reqDoc) {
           return true; // not excluded
         }
@@ -115,14 +117,14 @@
   public boolean skipTo(int target) throws IOException {
     if (firstTime) {
       firstTime = false;
-      if (! exclScorer.skipTo(target)) {
-        exclScorer = null; // exhausted
+      if (! exclDisi.skipTo(target)) {
+        exclDisi = null; // exhausted
       }
     }
     if (reqScorer == null) {
       return false;
     }
-    if (exclScorer == null) {
+    if (exclDisi == null) {
       return reqScorer.skipTo(target);
     }
     if (! reqScorer.skipTo(target)) {
@@ -134,7 +136,7 @@
 
   public Explanation explain(int doc) throws IOException {
     Explanation res = new Explanation();
-    if (exclScorer.skipTo(doc) && (exclScorer.doc() == doc)) {
+    if (exclDisi.skipTo(doc) && (exclDisi.doc() == doc)) {
       res.setDescription("excluded");
     } else {
       res.setDescription("not excluded");
Index: src/java/org/apache/lucene/search/ConjunctionScorer.java
===================================================================
--- src/java/org/apache/lucene/search/ConjunctionScorer.java	(revision 680483)
+++ src/java/org/apache/lucene/search/ConjunctionScorer.java	(working copy)
@@ -19,12 +19,14 @@
 
 import java.io.IOException;
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.Arrays;
 import java.util.Comparator;
 
 /** Scorer for conjunctions, sets of queries, all of which are required. */
 class ConjunctionScorer extends Scorer {
-  private final Scorer[] scorers;
+  private Scorer[] scorers;
+  private DocIdSetIterator[] disis; // also includes all scorers.
 
   private boolean firstTime=true;
   private boolean more;
@@ -35,12 +37,29 @@
     this(similarity, (Scorer[])scorers.toArray(new Scorer[scorers.size()]));
   }
 
+  public ConjunctionScorer(Similarity similarity, Collection scorers, Collection disis) throws IOException {
+    this(similarity, (Scorer[])scorers.toArray(new Scorer[scorers.size()]), disis);
+  }
+
   public ConjunctionScorer(Similarity similarity, Scorer[] scorers) throws IOException {
     super(similarity);
     this.scorers = scorers;
+    this.disis = scorers;
     coord = getSimilarity().coord(this.scorers.length, this.scorers.length);
   }
 
+  ConjunctionScorer(Similarity similarity, Scorer[] scorers, Collection disis) throws IOException {
+    this(similarity, scorers);
+    if (disis.size() > 0) {
+      this.disis = new DocIdSetIterator[scorers.length + disis.size()];
+      System.arraycopy(this.scorers, 0, this.disis, 0, this.scorers.length);
+      Iterator disisIt = disis.iterator();
+      for (int i = 0; disisIt.hasNext(); i++) {
+        this.disis[i + this.scorers.length] = (DocIdSetIterator) disisIt.next();
+      }
+    }
+  }
+
   public int doc() { return lastDoc; }
 
   public boolean next() throws IOException {
@@ -53,12 +72,12 @@
 
   private boolean doNext() throws IOException {
     int first=0;
-    Scorer lastScorer = scorers[scorers.length-1];
-    Scorer firstScorer;
-    while (more && (firstScorer=scorers[first]).doc() < (lastDoc=lastScorer.doc())) {
-      more = firstScorer.skipTo(lastDoc);
-      lastScorer = firstScorer;
-      first = (first == (scorers.length-1)) ? 0 : first+1;
+    DocIdSetIterator lastDisi = disis[disis.length-1];
+    DocIdSetIterator firstDisi;
+    while (more && (firstDisi=disis[first]).doc() < (lastDoc=lastDisi.doc())) {
+      more = firstDisi.skipTo(lastDoc);
+      lastDisi = firstDisi;
+      first = (first == (disis.length-1)) ? 0 : first+1;
     }
     return more;
   }
@@ -67,7 +86,7 @@
     if (firstTime)
       return init(target);
     else if (more)
-      more = scorers[(scorers.length-1)].skipTo(target);
+      more = disis[(disis.length-1)].skipTo(target);
     return doNext();
   }
 
@@ -75,9 +94,9 @@
   // thus skipping a check for firstTime per call to next() and skipTo()
   private boolean init(int target) throws IOException {
     firstTime=false;
-    more = scorers.length>1;
-    for (int i=0; i<scorers.length; i++) {
-      more = target==0 ? scorers[i].next() : scorers[i].skipTo(target);
+    more = disis.length>1;
+    for (int i=0; i<disis.length; i++) {
+      more = target==0 ? disis[i].next() : disis[i].skipTo(target);
       if (!more)
         return false;
     }
@@ -87,25 +106,25 @@
     // it will already start off sorted (all scorers on same doc).
 
     // note that this comparator is not consistent with equals!
-    Arrays.sort(scorers, new Comparator() {         // sort the array
+    Arrays.sort(disis, new Comparator() {         // sort the array
         public int compare(Object o1, Object o2) {
-          return ((Scorer)o1).doc() - ((Scorer)o2).doc();
+          return ((DocIdSetIterator)o1).doc() - ((DocIdSetIterator)o2).doc();
         }
       });
 
     doNext();
 
     // If first-time skip distance is any predictor of
-    // scorer sparseness, then we should always try to skip first on
-    // those scorers.
-    // Keep last scorer in it's last place (it will be the first
+    // DocIdSetIterator sparseness, then we should always try to skip first on
+    // those DocIdSetIterators.
+    // Keep last DocIdSetIterator in it's last place (it will be the first
     // to be skipped on), but reverse all of the others so that
     // they will be skipped on in order of original high skip.
-    int end=(scorers.length-1);
+    int end=(disis.length-1);
     for (int i=0; i<(end>>1); i++) {
-      Scorer tmp = scorers[i];
-      scorers[i] = scorers[end-i-1];
-      scorers[end-i-1] = tmp;
+      DocIdSetIterator tmp = disis[i];
+      disis[i] = disis[end-i-1];
+      disis[end-i-1] = tmp;
     }
 
     return more;
@@ -113,7 +132,7 @@
 
   public float score() throws IOException {
     float sum = 0.0f;
-    for (int i = 0; i < scorers.length; i++) {
+    for (int i = 0; i < scorers.length; i++) { // use scorers here, not disis.
       sum += scorers[i].score();
     }
     return sum * coord;
Index: src/java/org/apache/lucene/search/DisjunctionDISI.java
===================================================================
--- src/java/org/apache/lucene/search/DisjunctionDISI.java	(revision 0)
+++ src/java/org/apache/lucene/search/DisjunctionDISI.java	(revision 0)
@@ -0,0 +1,188 @@
+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.util.List;
+import java.util.Iterator;
+import java.io.IOException;
+
+import org.apache.lucene.util.DisiDocQueue;
+
+/* Derived from org.apache.lucene.search.DisjunctionSumScorer of July 2008 */
+
+/** A disjunction of <code>DocIdSetIterator</code>s.
+ */
+class DisjunctionDISI extends DocIdSetIterator {
+  private final int nrDisis;
+  
+  protected final List subDisis;
+  
+  /** The minimum number of DocIdSetIterators that should match. */
+  private final int minimumNrMatchers;
+  
+  /** The disiDocQueue contains all DocIdSetIterators ordered by their current doc(),
+   * with the minimum at the top.
+   * <br>The disiDocQueue is initialized the first time next() or skipTo() is called.
+   * <br>An exhausted DocIdSetIterator is immediately removed from the disiDocQueue.
+   * <br>If less than the minimumNrMatchers DocIdSetIterators
+   * remain in the disiDocQueue next() and skipTo() return false.
+   * <p>
+   * After each to call to next() or skipTo()
+   * <code>nrMatchers</code> is the number of matching DocIdSetIterators,
+   * and all DocIdSetIterators are after the matching doc, or are exhausted.
+   */
+  private DisiDocQueue disiDocQueue = null;
+  
+  /** The document number of the current match. */
+  private int currentDoc = -1;
+
+  /** The number of subscorers that provide the current match. */
+  protected int nrMatchers = -1;
+
+  private float currentScore = Float.NaN;
+  
+  /** Construct a <code>DisjunctionScorer</code>.
+   * @param subScorers A collection of at least two subscorers.
+   * @param minimumNrMatchers The positive minimum number of subscorers that should
+   * match to match this query.
+   * <br>When <code>minimumNrMatchers</code> is bigger than
+   * the number of <code>subScorers</code>,
+   * no matches will be produced.
+   * <br>When minimumNrMatchers equals the number of subScorers,
+   * it more efficient to use <code>ConjunctionScorer</code>.
+   */
+  public DisjunctionDISI(List subDisis, int minimumNrMatchers) {
+    nrDisis = subDisis.size();
+
+    if (minimumNrMatchers <= 0) {
+      throw new IllegalArgumentException("Minimum nr of matchers must be positive");
+    }
+    if (nrDisis <= 1) {
+      throw new IllegalArgumentException("There must be at least 2 DocIdSetIterators");
+    }
+
+    this.minimumNrMatchers = minimumNrMatchers;
+    this.subDisis = subDisis;
+  }
+  
+  /** Construct a <code>DisjunctionDISI</code>, using one as the minimum number
+   * of matching DocIdSetIterators.
+   */
+  public DisjunctionDISI(List subDisis) {
+    this(subDisis, 1);
+  }
+
+  /** Called the first time next() or skipTo() is called to
+   * initialize <code>disiDocQueue</code>.
+   */
+  private void initDisiDocQueue() throws IOException {
+    Iterator disisIt = subDisis.iterator();
+    disiDocQueue = new DisiDocQueue(nrDisis);
+    while (disisIt.hasNext()) {
+      DocIdSetIterator disi = (DocIdSetIterator) disisIt.next();
+      if (disi.next()) { // doc() method will be used in scorerDocQueue.
+        disiDocQueue.insert(disi);
+      }
+    }
+  }
+
+  public boolean next() throws IOException {
+    if (disiDocQueue == null) {
+      initDisiDocQueue();
+    }
+    return (disiDocQueue.size() >= minimumNrMatchers)
+          && advanceAfterCurrent();
+  }
+
+
+  /** Advance all sub DocIdSetIterators after the current document determined by the
+   * top of the <code>disiDocQueue</code>.
+   * Repeat until at least the minimum number of sub DocIdSetIterators match on the same
+   * document and all sub DocIdSetIterators are after that document or are exhausted.
+   * <br>On entry the <code>disiDocQueue</code> has at least <code>minimumNrMatchers</code>
+   * available. At least the DocIdSetIterator with the minimum document number will be advanced.
+   * @return true iff there is a match.
+   * <br>In case there is a match, </code>currentDoc</code>
+   * and </code>nrMatchers</code> describe the match.
+   *
+   * @todo Investigate whether it is possible to use skipTo() when
+   * the minimum number of matchers is bigger than one, ie. try and use the
+   * character of ConjunctionScorer for the minimum number of matchers.
+   * <br>For this, a Scorer array with minimumNrMatchers elements might
+   * hold Scorers at currentDoc that are temporarily popped from scorerQueue.
+   */
+  protected boolean advanceAfterCurrent() throws IOException {
+    do { // repeat until minimum nr of matchers
+      currentDoc = disiDocQueue.topDoc();
+      nrMatchers = 1;
+      do { // Until all subscorers are after currentDoc
+        if (! disiDocQueue.topNextAndAdjustElsePop()) {
+          if (disiDocQueue.size() == 0) {
+            break; // nothing more to advance, check for last match.
+          }
+        }
+        if (disiDocQueue.topDoc() != currentDoc) {
+          break; // All remaining subscorers are after currentDoc.
+        }
+        nrMatchers++;
+      } while (true);
+      
+      if (nrMatchers >= minimumNrMatchers) {
+        return true;
+      } else if (disiDocQueue.size() < minimumNrMatchers) {
+        return false;
+      }
+    } while (true);
+  }
+  
+  public int doc() { return currentDoc; }
+
+  /** Returns the number of subscorers matching the current document.
+   * Initially invalid, until {@link #next()} is called the first time.
+   */
+  public int nrMatchers() {
+    return nrMatchers;
+  }
+
+  /** Skips to the first match beyond the current whose document number is
+   * greater than or equal to a given target.
+   * <br>When this method is used the {@link #explain(int)} method should not be used.
+   * @param target The target document number.
+   * @return true iff there is such a match.
+   */
+  public boolean skipTo(int target) throws IOException {
+    if (disiDocQueue == null) {
+      initDisiDocQueue();
+    }
+    if (disiDocQueue.size() < minimumNrMatchers) {
+      return false;
+    }
+    if (target <= currentDoc) {
+      return true;
+    }
+    do {
+      if (disiDocQueue.topDoc() >= target) {
+        return advanceAfterCurrent();
+      } else if (! disiDocQueue.topSkipToAndAdjustElsePop(target)) {
+        if (disiDocQueue.size() < minimumNrMatchers) {
+          return false;
+        }
+      }
+    } while (true);
+  }
+}
Index: src/java/org/apache/lucene/util/DisiDocQueue.java
===================================================================
--- src/java/org/apache/lucene/util/DisiDocQueue.java	(revision 0)
+++ src/java/org/apache/lucene/util/DisiDocQueue.java	(revision 0)
@@ -0,0 +1,207 @@
+package org.apache.lucene.util;
+
+/**
+ * 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.
+ */
+
+/* Derived from org.apache.lucene.util.ScorerDocQueue of July 2008 */
+
+import java.io.IOException;
+import org.apache.lucene.search.DocIdSetIterator;
+
+/** A DisiDocQueue maintains a partial ordering of its DocIdSetIterators such that the
+ *  least DocIdSetIterator (disi) can always be found in constant time.
+ *  Put()'s and pop()'s require log(size) time.
+ *  The ordering is by DocIdSetIterator.doc().
+ */
+public class DisiDocQueue {
+  private final HeapedDisiDoc[] heap;
+  private final int maxSize;
+  private int size;
+  
+  private class HeapedDisiDoc {
+    DocIdSetIterator disi;
+    int doc;
+    
+    HeapedDisiDoc(DocIdSetIterator disi) { this(disi, disi.doc()); }
+    
+    HeapedDisiDoc(DocIdSetIterator disi, int doc) {
+      this.disi = disi;
+      this.doc = doc;
+    }
+    
+    void adjust() { doc = disi.doc(); }
+  }
+  
+  private HeapedDisiDoc topHDD; // same as heap[1], only for speed
+
+  /** Create a DisiDocQueue with a maximum size. */
+  public DisiDocQueue(int maxSize) {
+    // assert maxSize >= 0;
+    size = 0;
+    int heapSize = maxSize + 1;
+    heap = new HeapedDisiDoc[heapSize];
+    this.maxSize = maxSize;
+    topHDD = heap[1]; // initially null
+  }
+
+  /**
+   * Adds a Scorer to a ScorerDocQueue in log(size) time.
+   * If one tries to add more Scorers than maxSize
+   * a RuntimeException (ArrayIndexOutOfBound) is thrown.
+   */
+  public final void put(DocIdSetIterator disi) {
+    size++;
+    heap[size] = new HeapedDisiDoc(disi);
+    upHeap();
+  }
+
+  /**
+   * Adds a DocIdSetIterator to the DisiDocQueue in log(size) time if either
+   * the DisiDocQueue is not full, or not lessThan(disi, top()).
+   * @param disi
+   * @return true if DocIdSetIterator is added, false otherwise.
+   */
+  public boolean insert(DocIdSetIterator disi){
+    if (size < maxSize) {
+      put(disi);
+      return true;
+    } else {
+      int docNr = disi.doc();
+      if ((size > 0) && (! (docNr < topHDD.doc))) { // heap[1] is top()
+        heap[1] = new HeapedDisiDoc(disi, docNr);
+        downHeap();
+        return true;
+      } else {
+        return false;
+      }
+    }
+   }
+
+  /** Returns the least DocIdSetIterator of the DisiDocQueue in constant time.
+   * Should not be used when the queue is empty.
+   */
+  public final DocIdSetIterator top() {
+    return topHDD.disi;
+  }
+
+  /** Returns document number of the least Scorer of the ScorerDocQueue
+   * in constant time.
+   * Should not be used when the queue is empty.
+   */
+  public final int topDoc() {
+    return topHDD.doc;
+  }
+  
+  public final boolean topNextAndAdjustElsePop() throws IOException {
+    return checkAdjustElsePop( topHDD.disi.next());
+  }
+
+  public final boolean topSkipToAndAdjustElsePop(int target) throws IOException {
+    return checkAdjustElsePop( topHDD.disi.skipTo(target));
+  }
+  
+  private boolean checkAdjustElsePop(boolean cond) {
+    if (cond) { // see also adjustTop
+      topHDD.doc = topHDD.disi.doc();
+    } else { // see also popNoResult
+      heap[1] = heap[size]; // move last to first
+      heap[size] = null;
+      size--;
+    }
+    downHeap();
+    return cond;
+  }
+
+  /** Removes and returns the least disi of the DisiDocQueue in log(size)
+   * time.
+   * Should not be used when the queue is empty.
+   */
+  public final DocIdSetIterator pop() {
+    DocIdSetIterator result = topHDD.disi;
+    popNoResult();
+    return result;
+  }
+  
+  /** Removes the least disi of the DisiDocQueue in log(size) time.
+   * Should not be used when the queue is empty.
+   */
+  private final void popNoResult() {
+    heap[1] = heap[size]; // move last to first
+    heap[size] = null;
+    size--;
+    downHeap();	// adjust heap
+  }
+
+  /** Should be called when the disi at top changes doc() value.
+   * Still log(n) worst case, but it's at least twice as fast to <pre>
+   *  { pq.top().change(); pq.adjustTop(); }
+   * </pre> instead of <pre>
+   *  { o = pq.pop(); o.change(); pq.push(o); }
+   * </pre>
+   */
+  public final void adjustTop() {
+    topHDD.adjust();
+    downHeap();
+  }
+
+  /** Returns the number of disis currently stored in the DisiDocQueue. */
+  public final int size() {
+    return size;
+  }
+
+  /** Removes all entries from the DisiDocQueue. */
+  public final void clear() {
+    for (int i = 0; i <= size; i++) {
+      heap[i] = null;
+    }
+    size = 0;
+  }
+
+  private final void upHeap() {
+    int i = size;
+    HeapedDisiDoc node = heap[i];		  // save bottom node
+    int j = i >>> 1;
+    while ((j > 0) && (node.doc < heap[j].doc)) {
+      heap[i] = heap[j];			  // shift parents down
+      i = j;
+      j = j >>> 1;
+    }
+    heap[i] = node;				  // install saved node
+    topHDD = heap[1];
+  }
+
+  private final void downHeap() {
+    int i = 1;
+    HeapedDisiDoc node = heap[i];	          // save top node
+    int j = i << 1;				  // find smaller child
+    int k = j + 1;
+    if ((k <= size) && (heap[k].doc < heap[j].doc)) {
+      j = k;
+    }
+    while ((j <= size) && (heap[j].doc < node.doc)) {
+      heap[i] = heap[j];			  // shift up child
+      i = j;
+      j = i << 1;
+      k = j + 1;
+      if (k <= size && (heap[k].doc < heap[j].doc)) {
+	j = k;
+      }
+    }
+    heap[i] = node;				  // install saved node
+    topHDD = heap[1];
+  }
+}

Property changes on: src/java/org/apache/lucene/util/DisiDocQueue.java
___________________________________________________________________
Name: svn:executable
   + *

Index: src/java/org/apache/lucene/search/DisjunctionSumScorer.java
===================================================================
--- src/java/org/apache/lucene/search/DisjunctionSumScorer.java	(revision 680483)
+++ src/java/org/apache/lucene/search/DisjunctionSumScorer.java	(working copy)
@@ -50,7 +50,6 @@
    * and all scorers are after the matching doc, or are exhausted.
    */
   private ScorerDocQueue scorerDocQueue = null;
-  private int queueSize = -1; // used to avoid size() method calls on scorerDocQueue
   
   /** The document number of the current match. */
   private int currentDoc = -1;
@@ -99,13 +98,10 @@
   private void initScorerDocQueue() throws IOException {
     Iterator si = subScorers.iterator();
     scorerDocQueue = new ScorerDocQueue(nrScorers);
-    queueSize = 0;
     while (si.hasNext()) {
       Scorer se = (Scorer) si.next();
       if (se.next()) { // doc() method will be used in scorerDocQueue.
-        if (scorerDocQueue.insert(se)) {
-          queueSize++;
-        }
+        scorerDocQueue.insert(se);
       }
     }
   }
@@ -173,7 +169,7 @@
       nrMatchers = 1;
       do { // Until all subscorers are after currentDoc
         if (! scorerDocQueue.topNextAndAdjustElsePop()) {
-          if (--queueSize == 0) {
+          if (scorerDocQueue.size() == 0) {
             break; // nothing more to advance, check for last match.
           }
         }
@@ -186,7 +182,7 @@
       
       if (nrMatchers >= minimumNrMatchers) {
         return true;
-      } else if (queueSize < minimumNrMatchers) {
+      } else if (scorerDocQueue.size() < minimumNrMatchers) {
         return false;
       }
     } while (true);
@@ -217,7 +213,7 @@
     if (scorerDocQueue == null) {
       initScorerDocQueue();
     }
-    if (queueSize < minimumNrMatchers) {
+    if (scorerDocQueue.size() < minimumNrMatchers) {
       return false;
     }
     if (target <= currentDoc) {
@@ -227,7 +223,7 @@
       if (scorerDocQueue.topDoc() >= target) {
         return advanceAfterCurrent();
       } else if (! scorerDocQueue.topSkipToAndAdjustElsePop(target)) {
-        if (--queueSize < minimumNrMatchers) {
+        if (scorerDocQueue.size() < minimumNrMatchers) {
           return false;
         }
       }
