1 package org.apache.lucene.search;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.List;
23 import java.util.concurrent.ExecutorService;
24 import java.util.concurrent.Executors;
25 import java.util.concurrent.TimeUnit;
27 import org.apache.lucene.analysis.MockAnalyzer;
28 import org.apache.lucene.document.Document;
29 import org.apache.lucene.document.Field;
30 import org.apache.lucene.index.IndexReader;
31 import org.apache.lucene.index.MultiReader;
32 import org.apache.lucene.index.RandomIndexWriter;
33 import org.apache.lucene.index.Term;
34 import org.apache.lucene.queryParser.QueryParser;
35 import org.apache.lucene.store.Directory;
36 import org.apache.lucene.util.LuceneTestCase;
37 import org.apache.lucene.util._TestUtil;
39 public class TestBooleanQuery extends LuceneTestCase {
41 public void testEquality() throws Exception {
42 BooleanQuery bq1 = new BooleanQuery();
43 bq1.add(new TermQuery(new Term("field", "value1")), BooleanClause.Occur.SHOULD);
44 bq1.add(new TermQuery(new Term("field", "value2")), BooleanClause.Occur.SHOULD);
45 BooleanQuery nested1 = new BooleanQuery();
46 nested1.add(new TermQuery(new Term("field", "nestedvalue1")), BooleanClause.Occur.SHOULD);
47 nested1.add(new TermQuery(new Term("field", "nestedvalue2")), BooleanClause.Occur.SHOULD);
48 bq1.add(nested1, BooleanClause.Occur.SHOULD);
50 BooleanQuery bq2 = new BooleanQuery();
51 bq2.add(new TermQuery(new Term("field", "value1")), BooleanClause.Occur.SHOULD);
52 bq2.add(new TermQuery(new Term("field", "value2")), BooleanClause.Occur.SHOULD);
53 BooleanQuery nested2 = new BooleanQuery();
54 nested2.add(new TermQuery(new Term("field", "nestedvalue1")), BooleanClause.Occur.SHOULD);
55 nested2.add(new TermQuery(new Term("field", "nestedvalue2")), BooleanClause.Occur.SHOULD);
56 bq2.add(nested2, BooleanClause.Occur.SHOULD);
58 assertEquals(bq1, bq2);
61 public void testException() {
63 BooleanQuery.setMaxClauseCount(0);
65 } catch (IllegalArgumentException e) {
71 public void testNullOrSubScorer() throws Throwable {
72 Directory dir = newDirectory();
73 RandomIndexWriter w = new RandomIndexWriter(random, dir);
74 Document doc = new Document();
75 doc.add(newField("field", "a b c d", Field.Store.NO, Field.Index.ANALYZED));
78 IndexReader r = w.getReader();
79 IndexSearcher s = newSearcher(r);
80 BooleanQuery q = new BooleanQuery();
81 q.add(new TermQuery(new Term("field", "a")), BooleanClause.Occur.SHOULD);
83 // LUCENE-2617: make sure that a term not in the index still contributes to the score via coord factor
84 float score = s.search(q, 10).getMaxScore();
85 Query subQuery = new TermQuery(new Term("field", "not_in_index"));
87 q.add(subQuery, BooleanClause.Occur.SHOULD);
88 float score2 = s.search(q, 10).getMaxScore();
89 assertEquals(score*.5, score2, 1e-6);
91 // now test BooleanScorer2
92 subQuery = new TermQuery(new Term("field", "b"));
94 q.add(subQuery, BooleanClause.Occur.MUST);
95 score2 = s.search(q, 10).getMaxScore();
96 assertEquals(score*(2.0/3), score2, 1e-6);
98 // PhraseQuery w/ no terms added returns a null scorer
99 PhraseQuery pq = new PhraseQuery();
100 q.add(pq, BooleanClause.Occur.SHOULD);
101 assertEquals(1, s.search(q, 10).totalHits);
103 // A required clause which returns null scorer should return null scorer to
105 q = new BooleanQuery();
106 pq = new PhraseQuery();
107 q.add(new TermQuery(new Term("field", "a")), BooleanClause.Occur.SHOULD);
108 q.add(pq, BooleanClause.Occur.MUST);
109 assertEquals(0, s.search(q, 10).totalHits);
111 DisjunctionMaxQuery dmq = new DisjunctionMaxQuery(1.0f);
112 dmq.add(new TermQuery(new Term("field", "a")));
114 assertEquals(1, s.search(dmq, 10).totalHits);
122 public void testDeMorgan() throws Exception {
123 Directory dir1 = newDirectory();
124 RandomIndexWriter iw1 = new RandomIndexWriter(random, dir1);
125 Document doc1 = new Document();
126 doc1.add(newField("field", "foo bar", Field.Index.ANALYZED));
127 iw1.addDocument(doc1);
128 IndexReader reader1 = iw1.getReader();
131 Directory dir2 = newDirectory();
132 RandomIndexWriter iw2 = new RandomIndexWriter(random, dir2);
133 Document doc2 = new Document();
134 doc2.add(newField("field", "foo baz", Field.Index.ANALYZED));
135 iw2.addDocument(doc2);
136 IndexReader reader2 = iw2.getReader();
139 QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new MockAnalyzer(random));
140 qp.setMultiTermRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE);
142 MultiReader multireader = new MultiReader(reader1, reader2);
143 IndexSearcher searcher = newSearcher(multireader);
144 assertEquals(0, searcher.search(qp.parse("+foo -ba*"), 10).totalHits);
147 final ExecutorService es = Executors.newCachedThreadPool();
148 searcher = new IndexSearcher(multireader, es);
150 System.out.println("rewritten form: " + searcher.rewrite(qp.parse("+foo -ba*")));
151 assertEquals(0, searcher.search(qp.parse("+foo -ba*"), 10).totalHits);
153 es.awaitTermination(1, TimeUnit.SECONDS);
162 public void testBS2DisjunctionNextVsAdvance() throws Exception {
163 final Directory d = newDirectory();
164 final RandomIndexWriter w = new RandomIndexWriter(random, d);
165 final int numDocs = atLeast(300);
166 for(int docUpto=0;docUpto<numDocs;docUpto++) {
167 String contents = "a";
168 if (random.nextInt(20) <= 16) {
171 if (random.nextInt(20) <= 8) {
174 if (random.nextInt(20) <= 4) {
177 if (random.nextInt(20) <= 2) {
180 if (random.nextInt(20) <= 1) {
183 Document doc = new Document();
184 doc.add(new Field("field", contents, Field.Store.NO, Field.Index.ANALYZED));
188 final IndexReader r = w.getReader();
189 final IndexSearcher s = newSearcher(r);
192 for(int iter=0;iter<10*RANDOM_MULTIPLIER;iter++) {
194 System.out.println("iter=" + iter);
196 final List<String> terms = new ArrayList<String>(Arrays.asList("a", "b", "c", "d", "e", "f"));
197 final int numTerms = _TestUtil.nextInt(random, 1, terms.size());
198 while(terms.size() > numTerms) {
199 terms.remove(random.nextInt(terms.size()));
203 System.out.println(" terms=" + terms);
206 final BooleanQuery q = new BooleanQuery();
207 for(String term : terms) {
208 q.add(new BooleanClause(new TermQuery(new Term("field", term)), BooleanClause.Occur.SHOULD));
211 Weight weight = s.createNormalizedWeight(q);
213 Scorer scorer = weight.scorer(s.subReaders[0],
216 // First pass: just use .nextDoc() to gather all hits
217 final List<ScoreDoc> hits = new ArrayList<ScoreDoc>();
218 while(scorer.nextDoc() != DocIdSetIterator.NO_MORE_DOCS) {
219 hits.add(new ScoreDoc(scorer.docID(), scorer.score()));
223 System.out.println(" " + hits.size() + " hits");
226 // Now, randomly next/advance through the list and
227 // verify exact match:
228 for(int iter2=0;iter2<10;iter2++) {
230 weight = s.createNormalizedWeight(q);
231 scorer = weight.scorer(s.subReaders[0],
235 System.out.println(" iter2=" + iter2);
239 while(upto < hits.size()) {
242 final int left = hits.size() - upto;
243 if (left == 1 || random.nextBoolean()) {
246 nextDoc = scorer.nextDoc();
249 int inc = _TestUtil.nextInt(random, 1, left-1);
250 nextUpto = inc + upto;
251 nextDoc = scorer.advance(hits.get(nextUpto).doc);
254 if (nextUpto == hits.size()) {
255 assertEquals(DocIdSetIterator.NO_MORE_DOCS, nextDoc);
257 final ScoreDoc hit = hits.get(nextUpto);
258 assertEquals(hit.doc, nextDoc);
259 // Test for precise float equality:
260 assertTrue("doc " + hit.doc + " has wrong score: expected=" + hit.score + " actual=" + scorer.score(), hit.score == scorer.score());