Index: modules/queries/src/java/org/apache/lucene/queries/BooleanFilter.java =================================================================== --- modules/queries/src/java/org/apache/lucene/queries/BooleanFilter.java (revision 1174729) +++ modules/queries/src/java/org/apache/lucene/queries/BooleanFilter.java (working copy) @@ -35,8 +35,8 @@ * SHOULD, MUST NOT, MUST * The results Filter BitSet is constructed as follows: * SHOULD Filters are OR'd together - * The resulting Filter is NOT'd with the NOT Filters * The resulting Filter is AND'd with the MUST Filters + * The resulting Filter is NOT'd with the NOT Filters */ public class BooleanFilter extends Filter { @@ -63,19 +63,6 @@ } } - if (notFilters != null) { - for (int i = 0; i < notFilters.size(); i++) { - if (res == null) { - res = new FixedBitSet(reader.maxDoc()); - res.set(0, reader.maxDoc()); // NOTE: may set bits on deleted docs - } - final DocIdSetIterator disi = getDISI(notFilters, i, context); - if (disi != null) { - res.andNot(disi); - } - } - } - if (mustFilters != null) { for (int i = 0; i < mustFilters.size(); i++) { final DocIdSetIterator disi = getDISI(mustFilters, i, context); @@ -91,6 +78,15 @@ } } + // to apply NOT filters we need at least some matching docs (res != null) + if (res != null && notFilters != null) { + for (int i = 0; i < notFilters.size(); i++) { + final DocIdSetIterator disi = getDISI(notFilters, i, context); + if (disi == null) continue; + res.andNot(disi); + } + } + return res != null ? res : DocIdSet.EMPTY_DOCIDSET; } Index: modules/queries/src/test/org/apache/lucene/queries/BooleanFilterTest.java =================================================================== --- modules/queries/src/test/org/apache/lucene/queries/BooleanFilterTest.java (revision 1174729) +++ modules/queries/src/test/org/apache/lucene/queries/BooleanFilterTest.java (working copy) @@ -225,12 +225,12 @@ public void testJustMustNot() throws Throwable { BooleanFilter booleanFilter = new BooleanFilter(); booleanFilter.add(new FilterClause(getTermsFilter("inStock", "N"), BooleanClause.Occur.MUST_NOT)); - tstFilterCard("MUST_NOT", 4, booleanFilter); + tstFilterCard("MUST_NOT", 0, booleanFilter); // same with a real DISI (no OpenBitSetIterator) booleanFilter = new BooleanFilter(); booleanFilter.add(new FilterClause(getWrappedTermQuery("inStock", "N"), BooleanClause.Occur.MUST_NOT)); - tstFilterCard("MUST_NOT", 4, booleanFilter); + tstFilterCard("MUST_NOT", 0, booleanFilter); } public void testMustAndMustNot() throws Throwable { @@ -297,10 +297,10 @@ booleanFilter = new BooleanFilter(); booleanFilter.add(new FilterClause(getNullDISFilter(), BooleanClause.Occur.MUST_NOT)); - tstFilterCard("A single MUST_NOT filter that returns a null DIS should be invisible", 5, booleanFilter); + tstFilterCard("A single MUST_NOT filter that returns a null DIS should return no results", 0, booleanFilter); booleanFilter = new BooleanFilter(); booleanFilter.add(new FilterClause(getNullDISIFilter(), BooleanClause.Occur.MUST_NOT)); - tstFilterCard("A single MUST_NOT filter that returns a null DIS should be invisible", 5, booleanFilter); + tstFilterCard("A single MUST_NOT filter that returns a null DIS should return no results", 0, booleanFilter); } }