Index: lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java =================================================================== --- lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java (revision 1201104) +++ lucene/contrib/memory/src/test/org/apache/lucene/index/memory/MemoryIndexTest.java (working copy) @@ -144,11 +144,9 @@ * Return a random analyzer (Simple, Stop, Standard) to analyze the terms. */ private Analyzer randomAnalyzer() { - switch(random.nextInt(3)) { - case 0: return new MockAnalyzer(random, MockTokenizer.SIMPLE, true); - case 1: return new MockAnalyzer(random, MockTokenizer.SIMPLE, true, MockTokenFilter.ENGLISH_STOPSET, true); - default: return new MockAnalyzer(random, MockTokenizer.WHITESPACE, false); - } + return random.nextBoolean() ? + new MockAnalyzer(random, MockTokenizer.SIMPLE, true) : + new MockAnalyzer(random, MockTokenizer.WHITESPACE, false); } /** Index: lucene/src/java/org/apache/lucene/search/BooleanQuery.java =================================================================== --- lucene/src/java/org/apache/lucene/search/BooleanQuery.java (revision 1201104) +++ lucene/src/java/org/apache/lucene/search/BooleanQuery.java (working copy) @@ -168,9 +168,9 @@ */ protected class BooleanWeight extends Weight { /** The Similarity implementation. */ - protected SimilarityProvider similarityProvider; - protected ArrayList weights; - protected int maxCoord; // num optional + num required + protected final SimilarityProvider similarityProvider; + protected final ArrayList weights; + protected final int maxCoord; // num optional + num required private final boolean disableCoord; private final boolean termConjunction; @@ -180,16 +180,27 @@ this.disableCoord = disableCoord; weights = new ArrayList(clauses.size()); boolean termConjunction = clauses.isEmpty() || minNrShouldMatch != 0 ? false : true; - for (int i = 0 ; i < clauses.size(); i++) { - BooleanClause c = clauses.get(i); - Weight w = c.getQuery().createWeight(searcher); + int maxCoord = 0; + for (final BooleanClause c : clauses) { + final Weight w = c.getQuery().createWeight(searcher); if (!(c.isRequired() && (w instanceof TermWeight))) { termConjunction = false; } + if (!c.isProhibited()) { + maxCoord++; + } weights.add(w); - if (!c.isProhibited()) maxCoord++; } + + // check for only prohibited clauses + if (maxCoord == 0 && clauses.size() > 0) { + throw new UnsupportedOperationException( + "A BooleanQuery must have at least one SHOULD or MUST clause, if MUST_NOT clauses are used." + ); + } + this.termConjunction = termConjunction; + this.maxCoord = maxCoord; } @Override Index: lucene/src/test/org/apache/lucene/search/TestBoolean2.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestBoolean2.java (revision 1201104) +++ lucene/src/test/org/apache/lucene/search/TestBoolean2.java (working copy) @@ -195,7 +195,11 @@ query.add(new TermQuery(new Term(field, "xx")), BooleanClause.Occur.MUST_NOT); query.add(new TermQuery(new Term(field, "w5")), BooleanClause.Occur.MUST_NOT); int[] expDocNrs = {}; - queriesTest(query, expDocNrs); + try { + queriesTest(query, expDocNrs); + fail("BQ with only prohibited clauses should throw UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + } } @Test @@ -249,48 +253,39 @@ int tot=0; BooleanQuery q1 = null; - try { - // increase number of iterations for more complete testing - int num = atLeast(10); - for (int i=0; i0) { qType = rnd.nextInt(10); @@ -322,6 +318,7 @@ BooleanClause.Occur occur; if (r<2) { occur=BooleanClause.Occur.MUST_NOT; + containsProhibitedClause = true; } else if (r<5) { if (allowMust) { @@ -329,8 +326,10 @@ } else { occur=BooleanClause.Occur.SHOULD; } + containsPositiveClause = true; } else { occur=BooleanClause.Occur.SHOULD; + containsPositiveClause = true; } current.add(q, occur); Index: lucene/src/test/org/apache/lucene/search/TestComplexExplanations.java =================================================================== --- lucene/src/test/org/apache/lucene/search/TestComplexExplanations.java (revision 1201104) +++ lucene/src/test/org/apache/lucene/search/TestComplexExplanations.java (working copy) @@ -94,12 +94,6 @@ dm.add(xxYYZZ); - BooleanQuery xxW1 = new BooleanQuery(); - xxW1.add(new TermQuery(new Term(FIELD, "xx")), Occur.MUST_NOT); - xxW1.add(new TermQuery(new Term(FIELD, "w1")), Occur.MUST_NOT); - - dm.add(xxW1); - DisjunctionMaxQuery dm2 = new DisjunctionMaxQuery(0.5f); dm2.add(new TermQuery(new Term(FIELD, "w1"))); dm2.add(new TermQuery(new Term(FIELD, "w2"))); @@ -157,12 +151,6 @@ dm.add(xxYYZZ); - BooleanQuery xxW1 = new BooleanQuery(); - xxW1.add(new TermQuery(new Term(FIELD, "xx")), Occur.MUST_NOT); - xxW1.add(new TermQuery(new Term(FIELD, "w1")), Occur.MUST_NOT); - - dm.add(xxW1); - DisjunctionMaxQuery dm2 = new DisjunctionMaxQuery(0.5f); dm2.add(new TermQuery(new Term(FIELD, "w1"))); dm2.add(new TermQuery(new Term(FIELD, "w2"))); Index: modules/queries/src/java/org/apache/lucene/queries/BooleanFilter.java =================================================================== --- modules/queries/src/java/org/apache/lucene/queries/BooleanFilter.java (revision 1201104) +++ modules/queries/src/java/org/apache/lucene/queries/BooleanFilter.java (working copy) @@ -38,8 +38,8 @@ * SHOULD, MUST NOT, MUST * The results Filter BitSet is constructed as follows: * SHOULD Filters are OR'd together + * The resulting Filter is AND'd with the MUST Filters * The resulting Filter is NOT'd with the NOT Filters - * The resulting Filter is AND'd with the MUST Filters */ public class BooleanFilter extends Filter implements Iterable { @@ -53,6 +53,18 @@ public DocIdSet getDocIdSet(AtomicReaderContext context, Bits acceptDocs) throws IOException { FixedBitSet res = null; final IndexReader reader = context.reader; + + int prohibitedClauseCount = 0; + for (final FilterClause fc : clauses) { + if (fc.getOccur() == Occur.MUST_NOT) { + prohibitedClauseCount++; + } + } + if (prohibitedClauseCount > 0 && clauses.size() == prohibitedClauseCount) { + throw new UnsupportedOperationException( + "A BooleanFilter must have at least one SHOULD or MUST clause, if MUST_NOT clauses are used." + ); + } for (final FilterClause fc : clauses) { if (fc.getOccur() == Occur.SHOULD) { @@ -66,19 +78,6 @@ } for (final FilterClause fc : clauses) { - if (fc.getOccur() == Occur.MUST_NOT) { - if (res == null) { - res = new FixedBitSet(reader.maxDoc()); - res.set(0, reader.maxDoc()); // NOTE: may set bits on deleted docs - } - final DocIdSetIterator disi = getDISI(fc.getFilter(), context); - if (disi != null) { - res.andNot(disi); - } - } - } - - for (final FilterClause fc : clauses) { if (fc.getOccur() == Occur.MUST) { final DocIdSetIterator disi = getDISI(fc.getFilter(), context); if (disi == null) { @@ -93,6 +92,14 @@ } } + for (final FilterClause fc : clauses) { + if (fc.getOccur() == Occur.MUST_NOT) { + final DocIdSetIterator disi = getDISI(fc.getFilter(), context); + if (disi == null) continue; + res.andNot(disi); + } + } + return res != null ? BitsFilteredDocIdSet.wrap(res, acceptDocs) : 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 1201104) +++ modules/queries/src/test/org/apache/lucene/queries/BooleanFilterTest.java (working copy) @@ -226,12 +226,20 @@ public void testJustMustNot() throws Exception { BooleanFilter booleanFilter = new BooleanFilter(); booleanFilter.add(getTermsFilter("inStock", "N"), Occur.MUST_NOT); - tstFilterCard("MUST_NOT", 4, booleanFilter); + try { + tstFilterCard("MUST_NOT", 0, booleanFilter); + fail("This must throw UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + } // same with a real DISI (no OpenBitSetIterator) booleanFilter = new BooleanFilter(); booleanFilter.add(getWrappedTermQuery("inStock", "N"), Occur.MUST_NOT); - tstFilterCard("MUST_NOT", 4, booleanFilter); + try { + tstFilterCard("MUST_NOT", 0, booleanFilter); + fail("This must throw UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + } } public void testMustAndMustNot() throws Exception { @@ -303,10 +311,18 @@ booleanFilter = new BooleanFilter(); booleanFilter.add(getNullDISFilter(), Occur.MUST_NOT); - tstFilterCard("A single MUST_NOT filter that returns a null DIS should be invisible", 5, booleanFilter); + try { + tstFilterCard("MUST_NOT", 0, booleanFilter); + fail("This must throw UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + } booleanFilter = new BooleanFilter(); booleanFilter.add(getNullDISIFilter(), Occur.MUST_NOT); - tstFilterCard("A single MUST_NOT filter that returns a null DIS should be invisible", 5, booleanFilter); + try { + tstFilterCard("MUST_NOT", 0, booleanFilter); + fail("This must throw UnsupportedOperationException"); + } catch (UnsupportedOperationException uoe) { + } } }