Index: lucene/contrib/queries/src/java/org/apache/lucene/search/BooleanFilter.java =================================================================== --- lucene/contrib/queries/src/java/org/apache/lucene/search/BooleanFilter.java (revision 1207773) +++ lucene/contrib/queries/src/java/org/apache/lucene/search/BooleanFilter.java (working copy) @@ -47,8 +47,10 @@ public DocIdSet getDocIdSet(IndexReader reader) throws IOException { FixedBitSet res = null; + boolean hasShouldClauses = false; for (final FilterClause fc : clauses) { if (fc.getOccur() == Occur.SHOULD) { + hasShouldClauses = true; final DocIdSetIterator disi = getDISI(fc.getFilter(), reader); if (disi == null) continue; if (res == null) { @@ -57,10 +59,13 @@ res.or(disi); } } + if (hasShouldClauses && res == null) + return DocIdSet.EMPTY_DOCIDSET; for (final FilterClause fc : clauses) { if (fc.getOccur() == Occur.MUST_NOT) { if (res == null) { + assert !hasShouldClauses; res = new FixedBitSet(reader.maxDoc()); res.set(0, reader.maxDoc()); // NOTE: may set bits on deleted docs } Index: lucene/contrib/queries/src/test/org/apache/lucene/search/BooleanFilterTest.java =================================================================== --- lucene/contrib/queries/src/test/org/apache/lucene/search/BooleanFilterTest.java (revision 1207773) +++ lucene/contrib/queries/src/test/org/apache/lucene/search/BooleanFilterTest.java (working copy) @@ -28,6 +28,7 @@ import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.store.Directory; +import org.apache.lucene.util.FixedBitSet; import org.apache.lucene.util.LuceneTestCase; public class BooleanFilterTest extends LuceneTestCase { @@ -84,6 +85,15 @@ return new QueryWrapperFilter(new TermQuery(new Term(field, text))); } + private Filter getEmptyFilter() { + return new Filter() { + @Override + public DocIdSet getDocIdSet(IndexReader context) { + return new FixedBitSet(context.maxDoc()); + } + }; + } + private Filter getNullDISFilter() { return new Filter() { @Override @@ -301,4 +311,21 @@ booleanFilter.add(getNullDISIFilter(), Occur.MUST_NOT); tstFilterCard("A single MUST_NOT filter that returns a null DIS should be invisible", 5, booleanFilter); } + + public void testNonMatchingShouldsAndMusts() throws Exception { + BooleanFilter booleanFilter = new BooleanFilter(); + booleanFilter.add(getEmptyFilter(), Occur.SHOULD); + booleanFilter.add(getTermsFilter("accessRights", "admin"), Occur.MUST); + tstFilterCard(">0 shoulds with no matches should return no docs", 0, booleanFilter); + + booleanFilter = new BooleanFilter(); + booleanFilter.add(getNullDISFilter(), Occur.SHOULD); + booleanFilter.add(getTermsFilter("accessRights", "admin"), Occur.MUST); + tstFilterCard(">0 shoulds with no matches should return no docs", 0, booleanFilter); + + booleanFilter = new BooleanFilter(); + booleanFilter.add(getNullDISIFilter(), Occur.SHOULD); + booleanFilter.add(getTermsFilter("accessRights", "admin"), Occur.MUST); + tstFilterCard(">0 shoulds with no matches should return no docs", 0, booleanFilter); + } }