add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / backwards / src / test / org / apache / lucene / search / TestBooleanMinShouldMatch.java
1 package org.apache.lucene.search;
2
3 /**
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
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  */
19
20 import org.apache.lucene.util.LuceneTestCase;
21 import org.apache.lucene.document.Document;
22 import org.apache.lucene.document.Field;
23 import org.apache.lucene.index.IndexReader;
24 import org.apache.lucene.index.RandomIndexWriter;
25 import org.apache.lucene.index.Term;
26 import org.apache.lucene.store.Directory;
27 import org.junit.AfterClass;
28 import org.junit.BeforeClass;
29
30 import java.text.DecimalFormat;
31 import java.util.Random;
32
33 /** Test that BooleanQuery.setMinimumNumberShouldMatch works.
34  */
35 public class TestBooleanMinShouldMatch extends LuceneTestCase {
36
37     private static Directory index;
38     private static IndexReader r;
39     private static IndexSearcher s;
40
41     @BeforeClass
42     public static void beforeClass() throws Exception {
43         String[] data = new String [] {
44             "A 1 2 3 4 5 6",
45             "Z       4 5 6",
46             null,
47             "B   2   4 5 6",
48             "Y     3   5 6",
49             null,
50             "C     3     6",
51             "X       4 5 6"
52         };
53
54         index = newDirectory();
55         RandomIndexWriter w = new RandomIndexWriter(random, index);
56
57         for (int i = 0; i < data.length; i++) {
58             Document doc = new Document();
59             doc.add(newField("id", String.valueOf(i), Field.Store.YES, Field.Index.NOT_ANALYZED));//Field.Keyword("id",String.valueOf(i)));
60             doc.add(newField("all", "all", Field.Store.YES, Field.Index.NOT_ANALYZED));//Field.Keyword("all","all"));
61             if (null != data[i]) {
62                 doc.add(newField("data", data[i], Field.Store.YES, Field.Index.ANALYZED));//Field.Text("data",data[i]));
63             }
64             w.addDocument(doc);
65         }
66
67         r = w.getReader();
68         s = newSearcher(r);
69         w.close();
70 //System.out.println("Set up " + getName());
71     }
72     
73     @AfterClass
74     public static void afterClass() throws Exception {
75       s.close();
76       s = null;
77       r.close();
78       r = null;
79       index.close();
80       index = null;
81     }
82
83
84     public void verifyNrHits(Query q, int expected) throws Exception {
85         ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
86         if (expected != h.length) {
87             printHits(getName(), h, s);
88         }
89         assertEquals("result count", expected, h.length);
90         QueryUtils.check(random, q,s);
91     }
92
93     public void testAllOptional() throws Exception {
94
95         BooleanQuery q = new BooleanQuery();
96         for (int i = 1; i <=4; i++) {
97             q.add(new TermQuery(new Term("data",""+i)), BooleanClause.Occur.SHOULD);//false, false);
98         }
99         q.setMinimumNumberShouldMatch(2); // match at least two of 4
100         verifyNrHits(q, 2);
101     }
102
103     public void testOneReqAndSomeOptional() throws Exception {
104
105         /* one required, some optional */
106         BooleanQuery q = new BooleanQuery();
107         q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
108         q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
109         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
110         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.SHOULD);//false, false);
111
112         q.setMinimumNumberShouldMatch(2); // 2 of 3 optional 
113
114         verifyNrHits(q, 5);
115     }
116
117     public void testSomeReqAndSomeOptional() throws Exception {
118
119         /* two required, some optional */
120         BooleanQuery q = new BooleanQuery();
121         q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
122         q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
123         q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
124         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
125         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.SHOULD);//false, false);
126
127         q.setMinimumNumberShouldMatch(2); // 2 of 3 optional 
128
129         verifyNrHits(q, 5);
130     }
131
132     public void testOneProhibAndSomeOptional() throws Exception {
133
134         /* one prohibited, some optional */
135         BooleanQuery q = new BooleanQuery();
136         q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
137         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
138         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
139         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
140
141         q.setMinimumNumberShouldMatch(2); // 2 of 3 optional 
142
143         verifyNrHits(q, 1);
144     }
145
146     public void testSomeProhibAndSomeOptional() throws Exception {
147
148         /* two prohibited, some optional */
149         BooleanQuery q = new BooleanQuery();
150         q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
151         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
152         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
153         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
154         q.add(new TermQuery(new Term("data", "C"  )), BooleanClause.Occur.MUST_NOT);//false, true );
155
156         q.setMinimumNumberShouldMatch(2); // 2 of 3 optional 
157
158         verifyNrHits(q, 1);
159     }
160
161     public void testOneReqOneProhibAndSomeOptional() throws Exception {
162
163         /* one required, one prohibited, some optional */
164         BooleanQuery q = new BooleanQuery();
165         q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);// true,  false);
166         q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
167         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
168         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
169         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
170         q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
171
172         q.setMinimumNumberShouldMatch(3); // 3 of 4 optional 
173
174         verifyNrHits(q, 1);
175     }
176
177     public void testSomeReqOneProhibAndSomeOptional() throws Exception {
178
179         /* two required, one prohibited, some optional */
180         BooleanQuery q = new BooleanQuery();
181         q.add(new TermQuery(new Term("all",  "all")), BooleanClause.Occur.MUST);//true,  false);
182         q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
183         q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
184         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
185         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
186         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
187         q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
188
189         q.setMinimumNumberShouldMatch(3); // 3 of 4 optional 
190
191         verifyNrHits(q, 1);
192     }
193
194     public void testOneReqSomeProhibAndSomeOptional() throws Exception {
195
196         /* one required, two prohibited, some optional */
197         BooleanQuery q = new BooleanQuery();
198         q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
199         q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
200         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
201         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
202         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
203         q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
204         q.add(new TermQuery(new Term("data", "C"  )), BooleanClause.Occur.MUST_NOT);//false, true );
205
206         q.setMinimumNumberShouldMatch(3); // 3 of 4 optional 
207
208         verifyNrHits(q, 1);
209     }
210
211     public void testSomeReqSomeProhibAndSomeOptional() throws Exception {
212
213         /* two required, two prohibited, some optional */
214         BooleanQuery q = new BooleanQuery();
215         q.add(new TermQuery(new Term("all",  "all")), BooleanClause.Occur.MUST);//true,  false);
216         q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
217         q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
218         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
219         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
220         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
221         q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
222         q.add(new TermQuery(new Term("data", "C"  )), BooleanClause.Occur.MUST_NOT);//false, true );
223
224         q.setMinimumNumberShouldMatch(3); // 3 of 4 optional 
225
226         verifyNrHits(q, 1);
227     }
228
229     public void testMinHigherThenNumOptional() throws Exception {
230
231         /* two required, two prohibited, some optional */
232         BooleanQuery q = new BooleanQuery();
233         q.add(new TermQuery(new Term("all",  "all")), BooleanClause.Occur.MUST);//true,  false);
234         q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
235         q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
236         q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
237         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
238         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
239         q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
240         q.add(new TermQuery(new Term("data", "C"  )), BooleanClause.Occur.MUST_NOT);//false, true );
241
242         q.setMinimumNumberShouldMatch(90); // 90 of 4 optional ?!?!?!
243
244         verifyNrHits(q, 0);
245     }
246
247     public void testMinEqualToNumOptional() throws Exception {
248
249         /* two required, two optional */
250         BooleanQuery q = new BooleanQuery();
251         q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.SHOULD);//false, false);
252         q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
253         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST);//true,  false);
254         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
255
256         q.setMinimumNumberShouldMatch(2); // 2 of 2 optional 
257
258         verifyNrHits(q, 1);
259     }
260
261     public void testOneOptionalEqualToMin() throws Exception {
262
263         /* two required, one optional */
264         BooleanQuery q = new BooleanQuery();
265         q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
266         q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.SHOULD);//false, false);
267         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.MUST);//true,  false);
268
269         q.setMinimumNumberShouldMatch(1); // 1 of 1 optional 
270
271         verifyNrHits(q, 1);
272     }
273
274     public void testNoOptionalButMin() throws Exception {
275
276         /* two required, no optional */
277         BooleanQuery q = new BooleanQuery();
278         q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
279         q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.MUST);//true,  false);
280
281         q.setMinimumNumberShouldMatch(1); // 1 of 0 optional 
282
283         verifyNrHits(q, 0);
284     }
285
286     public void testNoOptionalButMin2() throws Exception {
287
288         /* one required, no optional */
289         BooleanQuery q = new BooleanQuery();
290         q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
291
292         q.setMinimumNumberShouldMatch(1); // 1 of 0 optional 
293
294         verifyNrHits(q, 0);
295     }
296
297     public void testRandomQueries() throws Exception {
298       String field="data";
299       String[] vals = {"1","2","3","4","5","6","A","Z","B","Y","Z","X","foo"};
300       int maxLev=4;
301
302       // callback object to set a random setMinimumNumberShouldMatch
303       TestBoolean2.Callback minNrCB = new TestBoolean2.Callback() {
304         public void postCreate(BooleanQuery q) {
305           BooleanClause[] c =q.getClauses();
306           int opt=0;
307           for (int i=0; i<c.length;i++) {
308             if (c[i].getOccur() == BooleanClause.Occur.SHOULD) opt++;
309           }
310           q.setMinimumNumberShouldMatch(random.nextInt(opt+2));
311         }
312       };
313
314
315
316       // increase number of iterations for more complete testing      
317       int num = atLeast(10);
318       for (int i = 0; i < num; i++) {
319         int lev = random.nextInt(maxLev);
320         final long seed = random.nextLong();
321         BooleanQuery q1 = TestBoolean2.randBoolQuery(new Random(seed), true, lev, field, vals, null);
322         // BooleanQuery q2 = TestBoolean2.randBoolQuery(new Random(seed), lev, field, vals, minNrCB);
323         BooleanQuery q2 = TestBoolean2.randBoolQuery(new Random(seed), true, lev, field, vals, null);
324         // only set minimumNumberShouldMatch on the top level query since setting
325         // at a lower level can change the score.
326         minNrCB.postCreate(q2);
327
328         // Can't use Hits because normalized scores will mess things
329         // up.  The non-sorting version of search() that returns TopDocs
330         // will not normalize scores.
331         TopDocs top1 = s.search(q1,null,100);
332         TopDocs top2 = s.search(q2,null,100);
333         if (i < 100) {
334           QueryUtils.check(random, q1,s);
335           QueryUtils.check(random, q2,s);
336         }
337         // The constrained query
338         // should be a superset to the unconstrained query.
339         if (top2.totalHits > top1.totalHits) {
340           fail("Constrained results not a subset:\n"
341                         + CheckHits.topdocsString(top1,0,0)
342                         + CheckHits.topdocsString(top2,0,0)
343                         + "for query:" + q2.toString());
344         }
345
346         for (int hit=0; hit<top2.totalHits; hit++) {
347           int id = top2.scoreDocs[hit].doc;
348           float score = top2.scoreDocs[hit].score;
349           boolean found=false;
350           // find this doc in other hits
351           for (int other=0; other<top1.totalHits; other++) {
352             if (top1.scoreDocs[other].doc == id) {
353               found=true;
354               float otherScore = top1.scoreDocs[other].score;
355               // check if scores match
356               if (Math.abs(otherScore-score)>1.0e-6f) {
357                         fail("Doc " + id + " scores don't match\n"
358                 + CheckHits.topdocsString(top1,0,0)
359                 + CheckHits.topdocsString(top2,0,0)
360                 + "for query:" + q2.toString());
361               }
362             }
363           }
364
365           // check if subset
366           if (!found) fail("Doc " + id + " not found\n"
367                 + CheckHits.topdocsString(top1,0,0)
368                 + CheckHits.topdocsString(top2,0,0)
369                 + "for query:" + q2.toString());
370         }
371       }
372       // System.out.println("Total hits:"+tot);
373     }
374
375
376
377     protected void printHits(String test, ScoreDoc[] h, Searcher searcher) throws Exception {
378
379         System.err.println("------- " + test + " -------");
380
381         DecimalFormat f = new DecimalFormat("0.000000");
382
383         for (int i = 0; i < h.length; i++) {
384             Document d = searcher.doc(h[i].doc);
385             float score = h[i].score;
386             System.err.println("#" + i + ": " + f.format(score) + " - " +
387                                d.get("id") + " - " + d.get("data"));
388         }
389     }
390 }