Index: CHANGES.txt =================================================================== --- CHANGES.txt (revision 795703) +++ CHANGES.txt (working copy) @@ -629,6 +629,11 @@ Removes conversions between Set and array. (Simon Willnauer via Mark Miller) +11. LUCENE-1754: BooleanQuery.weight.scorer() will return null if it won't match + any documents (e.g. if there are no required and optional scorers, or not + enough optional scorers to satisfy minShouldMatch). + (Shai Erera via ?) + Documentation Build Index: src/java/org/apache/lucene/search/BooleanQuery.java =================================================================== --- src/java/org/apache/lucene/search/BooleanQuery.java (revision 795703) +++ src/java/org/apache/lucene/search/BooleanQuery.java (working copy) @@ -227,9 +227,12 @@ float sum = 0.0f; boolean fail = false; int shouldMatchCount = 0; - for (int i = 0 ; i < weights.size(); i++) { - BooleanClause c = (BooleanClause)clauses.get(i); - QueryWeight w = (QueryWeight)weights.get(i); + for (Iterator wIter = weights.iterator(), cIter = clauses.iterator(); wIter.hasNext();) { + QueryWeight w = (QueryWeight) wIter.next(); + BooleanClause c = (BooleanClause) cIter.next(); + if (w.scorer(reader, true, true) == null) { + continue; + } Explanation e = w.explain(reader, doc); if (!c.isProhibited()) maxCoord++; if (e.isMatch()) { @@ -244,7 +247,7 @@ sumExpl.addDetail(r); fail = true; } - if (c.getOccur().equals(Occur.SHOULD)) + if (c.getOccur() == Occur.SHOULD) shouldMatchCount++; } else if (c.isRequired()) { Explanation r = new Explanation(0.0f, "no match on required clause (" + c.getQuery().toString() + ")"); @@ -312,6 +315,16 @@ return new BooleanScorer(similarity, minNrShouldMatch, optional, prohibited); } + if (required.size() == 0 && optional.size() == 0) { + // no required and optional clauses. + return null; + } else if (optional.size() < minNrShouldMatch) { + // either >1 req scorer, or there are 0 req scorers and at least 1 + // optional scorer. Therefore if there are not enough optional scorers + // no documents will be matched by the query + return null; + } + // Return a BooleanScorer2 return new BooleanScorer2(similarity, minNrShouldMatch, required, prohibited, optional); } Index: src/java/org/apache/lucene/search/BooleanScorer2.java =================================================================== --- src/java/org/apache/lucene/search/BooleanScorer2.java (revision 795703) +++ src/java/org/apache/lucene/search/BooleanScorer2.java (working copy) @@ -218,36 +218,26 @@ } private Scorer makeCountingSumScorerNoReq() throws IOException { // No required scorers - if (optionalScorers.size() == 0) { - return new NonMatchingScorer(); // no clauses or only prohibited clauses - } else { // No required scorers. At least one optional scorer. - // minNrShouldMatch optional scorers are required, but at least 1 - int nrOptRequired = (minNrShouldMatch < 1) ? 1 : minNrShouldMatch; - if (optionalScorers.size() < nrOptRequired) { - return new NonMatchingScorer(); // fewer optional clauses than minimum (at least 1) that should match - } else { // optionalScorers.size() >= nrOptRequired, no required scorers - Scorer requiredCountingSumScorer = - (optionalScorers.size() > nrOptRequired) - ? countingDisjunctionSumScorer(optionalScorers, nrOptRequired) - : // 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); - } - } + // minNrShouldMatch optional scorers are required, but at least 1 + int nrOptRequired = (minNrShouldMatch < 1) ? 1 : minNrShouldMatch; + Scorer requiredCountingSumScorer; + if (optionalScorers.size() > nrOptRequired) + requiredCountingSumScorer = countingDisjunctionSumScorer(optionalScorers, nrOptRequired); + else if (optionalScorers.size() == 1) + requiredCountingSumScorer = new SingleMatchScorer((Scorer) optionalScorers.get(0)); + else + requiredCountingSumScorer = countingConjunctionSumScorer(optionalScorers); + return addProhibitedScorers(requiredCountingSumScorer); } private Scorer makeCountingSumScorerSomeReq() throws IOException { // At least one required scorer. - 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. + if (optionalScorers.size() == minNrShouldMatch) { // all optional scorers also required. ArrayList allReq = new ArrayList(requiredScorers); allReq.addAll(optionalScorers); return addProhibitedScorers(countingConjunctionSumScorer(allReq)); } else { // optionalScorers.size() > minNrShouldMatch, and at least one required scorer Scorer requiredCountingSumScorer = - (requiredScorers.size() == 1) + requiredScorers.size() == 1 ? new SingleMatchScorer((Scorer) requiredScorers.get(0)) : countingConjunctionSumScorer(requiredScorers); if (minNrShouldMatch > 0) { // use a required disjunction scorer over the optional scorers @@ -260,9 +250,10 @@ } else { // minNrShouldMatch == 0 return new ReqOptSumScorer( addProhibitedScorers(requiredCountingSumScorer), - ((optionalScorers.size() == 1) + optionalScorers.size() == 1 ? new SingleMatchScorer((Scorer) optionalScorers.get(0)) - : countingDisjunctionSumScorer(optionalScorers, 1))); // require 1 in combined, optional scorer. + // require 1 in combined, optional scorer. + : countingDisjunctionSumScorer(optionalScorers, 1)); } } } Index: src/java/org/apache/lucene/search/NonMatchingScorer.java =================================================================== --- src/java/org/apache/lucene/search/NonMatchingScorer.java (revision 795703) +++ src/java/org/apache/lucene/search/NonMatchingScorer.java (working copy) @@ -1,50 +0,0 @@ -package org.apache.lucene.search; - -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.io.IOException; - -/** A scorer that matches no document at all. */ -class NonMatchingScorer extends Scorer { - public NonMatchingScorer() { super(null); } // no similarity used - - /** @deprecated use {@link #docID()} instead. */ - public int doc() { throw new UnsupportedOperationException(); } - - public int docID() { return NO_MORE_DOCS; } - - /** @deprecated use {@link #nextDoc()} instead. */ - public boolean next() throws IOException { return false; } - - public int nextDoc() throws IOException { return NO_MORE_DOCS; } - - public float score() { throw new UnsupportedOperationException(); } - - /** @deprecated use {@link #advance(int)} instead. */ - public boolean skipTo(int target) { return false; } - - public int advance(int target) { return NO_MORE_DOCS; } - - public Explanation explain(int doc) { - Explanation e = new Explanation(); - e.setDescription("No document matches."); - return e; - } -} - - Index: src/java/org/apache/lucene/search/QueryWeight.java =================================================================== --- src/java/org/apache/lucene/search/QueryWeight.java (revision 795703) +++ src/java/org/apache/lucene/search/QueryWeight.java (working copy) @@ -75,8 +75,11 @@ *
* NOTE: even if scoreDocsInOrder is false, it is
* recommended to check whether the returned Scorer indeed scores
- * documents out of order (i.e., call {@link #scoresDocsOutOfOrder()}), as some
- * Scorer implementations will always return documents in-order.
+ * documents out of order (i.e., call {@link #scoresDocsOutOfOrder()}), as
+ * some Scorer implementations will always return documents
+ * in-order.
+ * NOTE: null will be returned if no documents will be scored by this
+ * query.
*
* @param reader
* the {@link IndexReader} for which to return the {@link Scorer}.
Index: src/test/org/apache/lucene/search/QueryUtils.java
===================================================================
--- src/test/org/apache/lucene/search/QueryUtils.java (revision 795703)
+++ src/test/org/apache/lucene/search/QueryUtils.java (working copy)
@@ -152,7 +152,10 @@
final QueryWeight w = q.queryWeight(s);
final Scorer scorer = w.scorer(s.getIndexReader(), true, false);
-
+ if (scorer == null) {
+ continue;
+ }
+
// FUTURE: ensure scorer.doc()==-1
final int[] sdoc = new int[] {-1};
@@ -253,8 +256,10 @@
});
QueryWeight w = q.queryWeight(s);
Scorer scorer = w.scorer(s.getIndexReader(), true, false);
- boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
- if (more)
- Assert.assertFalse("query's last doc was "+lastDoc[0]+" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
+ if (scorer != null) {
+ boolean more = scorer.advance(lastDoc[0] + 1) != DocIdSetIterator.NO_MORE_DOCS;
+ if (more)
+ Assert.assertFalse("query's last doc was "+lastDoc[0]+" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.docID(),more);
+ }
}
}
Index: tags/lucene_2_4_back_compat_tests_20090710a/src/test/org/apache/lucene/search/QueryUtils.java
===================================================================
--- tags/lucene_2_4_back_compat_tests_20090710a/src/test/org/apache/lucene/search/QueryUtils.java (revision 795773)
+++ tags/lucene_2_4_back_compat_tests_20090710a/src/test/org/apache/lucene/search/QueryUtils.java (working copy)
@@ -146,7 +146,9 @@
final Weight w = q.weight(s);
final Scorer scorer = w.scorer(s.getIndexReader());
-
+ if (scorer == null) {
+ continue;
+ }
// FUTURE: ensure scorer.doc()==-1
final int[] sdoc = new int[] {-1};
@@ -219,8 +221,10 @@
});
Weight w = q.weight(s);
Scorer scorer = w.scorer(s.getIndexReader());
- boolean more = scorer.skipTo(lastDoc[0]+1);
- if (more)
- TestCase.assertFalse("query's last doc was "+lastDoc[0]+" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.doc(),more);
+ if (scorer != null) {
+ boolean more = scorer.skipTo(lastDoc[0]+1);
+ if (more)
+ TestCase.assertFalse("query's last doc was "+lastDoc[0]+" but skipTo("+(lastDoc[0]+1)+") got to "+scorer.doc(),more);
+ }
}
}