Index: src/java/org/apache/lucene/search/BooleanScorer2.java
===================================================================
--- src/java/org/apache/lucene/search/BooleanScorer2.java	(revision 293416)
+++ src/java/org/apache/lucene/search/BooleanScorer2.java	(working copy)
@@ -37,6 +37,27 @@
     private float[] coordFactors = null;
     
     void init() { // use after all scorers have been added.
+
+      /* first thing first: if minNrShouldMatch, then we need to rollup
+       * the optional scorers and make the new scorer required.
+       */
+      if (0 < minNrShouldMatch) {
+        if (1 < optionalScorers.size()) {
+            requiredScorers.add(countingDisjunctionSumScorer(optionalScorers,
+                                                             minNrShouldMatch));
+            maxCoord++;
+            optionalScorers = new ArrayList();
+        } else if (1 == optionalScorers.size() && 1 == minNrShouldMatch) {
+            requiredScorers.add(optionalScorers.get(0));
+            maxCoord++;
+            optionalScorers = new ArrayList();
+        } else {
+            /* remaining situations don't allow for any matches. */
+            requiredScorers.add(new NonMatchingScorer());
+            maxCoord++;
+        }
+      }
+        
       coordFactors = new float[maxCoord + 1];
       Similarity sim = getSimilarity();
       for (int i = 0; i <= maxCoord; i++) {
@@ -62,10 +83,17 @@
    */
   private Scorer countingSumScorer = null;
 
-  public BooleanScorer2(Similarity similarity) {
+  /** The number of optionalScorers that need to match (if there are any) */
+  private int minNrShouldMatch = 0;
+    
+  public BooleanScorer2(Similarity similarity, int minNrShouldMatch) {
     super(similarity);
     coordinator = new Coordinator();
+    this.minNrShouldMatch = minNrShouldMatch;
   }
+  public BooleanScorer2(Similarity similarity) {
+    this(similarity, 0);
+  }
 
   public void add(final Scorer scorer, boolean required, boolean prohibited) {
     if (!prohibited) {
@@ -126,10 +154,11 @@
     }
   }
 
-  private Scorer countingDisjunctionSumScorer(List scorers)
+  private Scorer countingDisjunctionSumScorer(List scorers,
+                                              int minMrShouldMatch)
   // each scorer from the list counted as a single matcher
   {
-    return new DisjunctionSumScorer(scorers) {
+    return new DisjunctionSumScorer(scorers, minMrShouldMatch) {
       private int lastScoredDoc = -1;
       public float score() throws IOException {
         if (doc() > lastScoredDoc) {
@@ -184,7 +213,7 @@
                   new ArrayList()); // no optional scorers left
       } else { // more than 1 optionalScorers, no required scorers
         return makeCountingSumScorer2( // at least one optional scorer is required
-                  countingDisjunctionSumScorer(optionalScorers), 
+                  countingDisjunctionSumScorer(optionalScorers, 1),
                   new ArrayList()); // no optional scorers left
       }
     } else if (requiredScorers.size() == 1) { // 1 required
@@ -226,7 +255,7 @@
    } else { // more optional
       return makeCountingSumScorer3(
                       requiredCountingSumScorer,
-                      countingDisjunctionSumScorer(optionalScorers));
+                      countingDisjunctionSumScorer(optionalScorers,1));
     }
   }
 
Index: src/java/org/apache/lucene/search/BooleanQuery.java
===================================================================
--- src/java/org/apache/lucene/search/BooleanQuery.java	(revision 293416)
+++ src/java/org/apache/lucene/search/BooleanQuery.java	(working copy)
@@ -104,6 +104,34 @@
     return result;
   }
 
+  /**
+   * Specifies a minimum number of the optional BooleanClauses
+   * which must be satisifed.
+   *
+   * <p>
+   * By default no optional clauses are neccessary for a match
+   * (unless there are no required clauses).  If this method is used,
+   * then the specified numebr of clauses is required.
+   * </p>
+   * <p>
+   * Use of this method is totally independant of specifying that
+   * any specific clauses are required (or prohibited).  This number will
+   * only be compared against the number of matching optional clauses. 
+   * </p>
+   * <p>
+   * EXPERT NOTE: Using this method will force the use of BooleanWeight2,
+   * regardless of wether setUseScorer14(true) has been called.
+   * </p>
+   *
+   * @param min the number of optional clauses that must match
+   * @see #setUseScorer14
+   */
+  public void setMinimumNumberShouldMatch(int min) {
+      this.minNrShouldMatch = min;
+  }
+  protected int minNrShouldMatch = 0;
+
+    
   /** Adds a clause to a boolean query.  Clauses may be:
    * <ul>
    * <li><code>required</code> which means that documents which <i>do not</i>
@@ -296,7 +324,8 @@
      *          and scores documents in document number order.
      */
     public Scorer scorer(IndexReader reader) throws IOException {
-      BooleanScorer2 result = new BooleanScorer2(similarity);
+      BooleanScorer2 result = new BooleanScorer2(similarity,
+                                                 minNrShouldMatch);
 
       for (int i = 0 ; i < weights.size(); i++) {
         BooleanClause c = (BooleanClause)clauses.elementAt(i);
@@ -324,6 +353,12 @@
   }
   
   protected Weight createWeight(Searcher searcher) throws IOException {
+      
+    if (0 < minNrShouldMatch) {
+      // :TODO: should we throw an exception if getUseScorer14 ?
+      return new BooleanWeight2(searcher);
+    }
+      
     return getUseScorer14() ? (Weight) new BooleanWeight(searcher)
                             : (Weight) new BooleanWeight2(searcher);
   }
