pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / test / org / apache / lucene / search / TestDisjunctionMaxQuery.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.analysis.MockAnalyzer;
22 import org.apache.lucene.document.Document;
23 import org.apache.lucene.document.Field;
24 import org.apache.lucene.index.IndexReader;
25 import org.apache.lucene.index.FieldInvertState;
26 import org.apache.lucene.index.RandomIndexWriter;
27 import org.apache.lucene.index.Term;
28 import org.apache.lucene.store.Directory;
29
30 import java.text.DecimalFormat;
31 import java.io.IOException;
32
33 /**
34  * Test of the DisjunctionMaxQuery.
35  * 
36  */
37 public class TestDisjunctionMaxQuery extends LuceneTestCase {
38   
39   /** threshold for comparing floats */
40   public static final float SCORE_COMP_THRESH = 0.0000f;
41   
42   /**
43    * Similarity to eliminate tf, idf and lengthNorm effects to isolate test
44    * case.
45    * 
46    * <p>
47    * same as TestRankingSimilarity in TestRanking.zip from
48    * http://issues.apache.org/jira/browse/LUCENE-323
49    * </p>
50    */
51   private static class TestSimilarity extends DefaultSimilarity {
52     
53     public TestSimilarity() {}
54     
55     @Override
56     public float tf(float freq) {
57       if (freq > 0.0f) return 1.0f;
58       else return 0.0f;
59     }
60     
61     @Override
62     public float computeNorm(String fieldName, FieldInvertState state) {
63       // Disable length norm
64       return state.getBoost();
65     }
66     
67     @Override
68     public float idf(int docFreq, int numDocs) {
69       return 1.0f;
70     }
71   }
72   
73   public Similarity sim = new TestSimilarity();
74   public Directory index;
75   public IndexReader r;
76   public IndexSearcher s;
77   
78   @Override
79   public void setUp() throws Exception {
80     super.setUp();
81     
82     index = newDirectory();
83     RandomIndexWriter writer = new RandomIndexWriter(random, index,
84         newIndexWriterConfig( TEST_VERSION_CURRENT, new MockAnalyzer(random))
85                                                      .setSimilarity(sim).setMergePolicy(newLogMergePolicy()));
86     
87     // hed is the most important field, dek is secondary
88     
89     // d1 is an "ok" match for: albino elephant
90     {
91       Document d1 = new Document();
92       d1.add(newField("id", "d1", Field.Store.YES, Field.Index.NOT_ANALYZED));// Field.Keyword("id",
93                                                                                // "d1"));
94       d1
95           .add(newField("hed", "elephant", Field.Store.YES,
96               Field.Index.ANALYZED));// Field.Text("hed", "elephant"));
97       d1
98           .add(newField("dek", "elephant", Field.Store.YES,
99               Field.Index.ANALYZED));// Field.Text("dek", "elephant"));
100       writer.addDocument(d1);
101     }
102     
103     // d2 is a "good" match for: albino elephant
104     {
105       Document d2 = new Document();
106       d2.add(newField("id", "d2", Field.Store.YES, Field.Index.NOT_ANALYZED));// Field.Keyword("id",
107                                                                                // "d2"));
108       d2
109           .add(newField("hed", "elephant", Field.Store.YES,
110               Field.Index.ANALYZED));// Field.Text("hed", "elephant"));
111       d2.add(newField("dek", "albino", Field.Store.YES, Field.Index.ANALYZED));// Field.Text("dek",
112                                                                                 // "albino"));
113       d2
114           .add(newField("dek", "elephant", Field.Store.YES,
115               Field.Index.ANALYZED));// Field.Text("dek", "elephant"));
116       writer.addDocument(d2);
117     }
118     
119     // d3 is a "better" match for: albino elephant
120     {
121       Document d3 = new Document();
122       d3.add(newField("id", "d3", Field.Store.YES, Field.Index.NOT_ANALYZED));// Field.Keyword("id",
123                                                                                // "d3"));
124       d3.add(newField("hed", "albino", Field.Store.YES, Field.Index.ANALYZED));// Field.Text("hed",
125                                                                                 // "albino"));
126       d3
127           .add(newField("hed", "elephant", Field.Store.YES,
128               Field.Index.ANALYZED));// Field.Text("hed", "elephant"));
129       writer.addDocument(d3);
130     }
131     
132     // d4 is the "best" match for: albino elephant
133     {
134       Document d4 = new Document();
135       d4.add(newField("id", "d4", Field.Store.YES, Field.Index.NOT_ANALYZED));// Field.Keyword("id",
136                                                                                // "d4"));
137       d4.add(newField("hed", "albino", Field.Store.YES, Field.Index.ANALYZED));// Field.Text("hed",
138                                                                                 // "albino"));
139       d4
140           .add(newField("hed", "elephant", Field.Store.YES,
141               Field.Index.ANALYZED));// Field.Text("hed", "elephant"));
142       d4.add(newField("dek", "albino", Field.Store.YES, Field.Index.ANALYZED));// Field.Text("dek",
143                                                                                 // "albino"));
144       writer.addDocument(d4);
145     }
146
147     writer.forceMerge(1);
148     r = writer.getReader();
149     writer.close();
150     s = newSearcher(r);
151     s.setSimilarity(sim);
152   }
153   
154   @Override
155   public void tearDown() throws Exception {
156     s.close();
157     r.close();
158     index.close();
159     super.tearDown();
160   }
161   
162   public void testSkipToFirsttimeMiss() throws IOException {
163     final DisjunctionMaxQuery dq = new DisjunctionMaxQuery(0.0f);
164     dq.add(tq("id", "d1"));
165     dq.add(tq("dek", "DOES_NOT_EXIST"));
166     
167     QueryUtils.check(random, dq, s);
168     
169     final Weight dw = s.createNormalizedWeight(dq);
170     IndexReader sub = s.getIndexReader().getSequentialSubReaders() == null ?
171         s.getIndexReader() : s.getIndexReader().getSequentialSubReaders()[0];
172     final Scorer ds = dw.scorer(sub, true, false);
173     final boolean skipOk = ds.advance(3) != DocIdSetIterator.NO_MORE_DOCS;
174     if (skipOk) {
175       fail("firsttime skipTo found a match? ... "
176           + r.document(ds.docID()).get("id"));
177     }
178   }
179   
180   public void testSkipToFirsttimeHit() throws IOException {
181     final DisjunctionMaxQuery dq = new DisjunctionMaxQuery(0.0f);
182     dq.add(tq("dek", "albino"));
183     dq.add(tq("dek", "DOES_NOT_EXIST"));
184     
185     QueryUtils.check(random, dq, s);
186
187     final Weight dw = s.createNormalizedWeight(dq);
188     IndexReader sub = s.getIndexReader().getSequentialSubReaders() == null ?
189         s.getIndexReader() : s.getIndexReader().getSequentialSubReaders()[0];
190     final Scorer ds = dw.scorer(sub, true, false);
191     assertTrue("firsttime skipTo found no match",
192         ds.advance(3) != DocIdSetIterator.NO_MORE_DOCS);
193     assertEquals("found wrong docid", "d4", r.document(ds.docID()).get("id"));
194   }
195   
196   public void testSimpleEqualScores1() throws Exception {
197     
198     DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.0f);
199     q.add(tq("hed", "albino"));
200     q.add(tq("hed", "elephant"));
201     QueryUtils.check(random, q, s);
202     
203     ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
204     
205     try {
206       assertEquals("all docs should match " + q.toString(), 4, h.length);
207       
208       float score = h[0].score;
209       for (int i = 1; i < h.length; i++) {
210         assertEquals("score #" + i + " is not the same", score, h[i].score,
211             SCORE_COMP_THRESH);
212       }
213     } catch (Error e) {
214       printHits("testSimpleEqualScores1", h, s);
215       throw e;
216     }
217     
218   }
219   
220   public void testSimpleEqualScores2() throws Exception {
221     
222     DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.0f);
223     q.add(tq("dek", "albino"));
224     q.add(tq("dek", "elephant"));
225     QueryUtils.check(random, q, s);
226     
227     ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
228     
229     try {
230       assertEquals("3 docs should match " + q.toString(), 3, h.length);
231       float score = h[0].score;
232       for (int i = 1; i < h.length; i++) {
233         assertEquals("score #" + i + " is not the same", score, h[i].score,
234             SCORE_COMP_THRESH);
235       }
236     } catch (Error e) {
237       printHits("testSimpleEqualScores2", h, s);
238       throw e;
239     }
240     
241   }
242   
243   public void testSimpleEqualScores3() throws Exception {
244     
245     DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.0f);
246     q.add(tq("hed", "albino"));
247     q.add(tq("hed", "elephant"));
248     q.add(tq("dek", "albino"));
249     q.add(tq("dek", "elephant"));
250     QueryUtils.check(random, q, s);
251     
252     ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
253     
254     try {
255       assertEquals("all docs should match " + q.toString(), 4, h.length);
256       float score = h[0].score;
257       for (int i = 1; i < h.length; i++) {
258         assertEquals("score #" + i + " is not the same", score, h[i].score,
259             SCORE_COMP_THRESH);
260       }
261     } catch (Error e) {
262       printHits("testSimpleEqualScores3", h, s);
263       throw e;
264     }
265     
266   }
267   
268   public void testSimpleTiebreaker() throws Exception {
269     
270     DisjunctionMaxQuery q = new DisjunctionMaxQuery(0.01f);
271     q.add(tq("dek", "albino"));
272     q.add(tq("dek", "elephant"));
273     QueryUtils.check(random, q, s);
274     
275     ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
276     
277     try {
278       assertEquals("3 docs should match " + q.toString(), 3, h.length);
279       assertEquals("wrong first", "d2", s.doc(h[0].doc).get("id"));
280       float score0 = h[0].score;
281       float score1 = h[1].score;
282       float score2 = h[2].score;
283       assertTrue("d2 does not have better score then others: " + score0
284           + " >? " + score1, score0 > score1);
285       assertEquals("d4 and d1 don't have equal scores", score1, score2,
286           SCORE_COMP_THRESH);
287     } catch (Error e) {
288       printHits("testSimpleTiebreaker", h, s);
289       throw e;
290     }
291   }
292   
293   public void testBooleanRequiredEqualScores() throws Exception {
294     
295     BooleanQuery q = new BooleanQuery();
296     {
297       DisjunctionMaxQuery q1 = new DisjunctionMaxQuery(0.0f);
298       q1.add(tq("hed", "albino"));
299       q1.add(tq("dek", "albino"));
300       q.add(q1, BooleanClause.Occur.MUST);// true,false);
301       QueryUtils.check(random, q1, s);
302       
303     }
304     {
305       DisjunctionMaxQuery q2 = new DisjunctionMaxQuery(0.0f);
306       q2.add(tq("hed", "elephant"));
307       q2.add(tq("dek", "elephant"));
308       q.add(q2, BooleanClause.Occur.MUST);// true,false);
309       QueryUtils.check(random, q2, s);
310     }
311     
312     QueryUtils.check(random, q, s);
313     
314     ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
315     
316     try {
317       assertEquals("3 docs should match " + q.toString(), 3, h.length);
318       float score = h[0].score;
319       for (int i = 1; i < h.length; i++) {
320         assertEquals("score #" + i + " is not the same", score, h[i].score,
321             SCORE_COMP_THRESH);
322       }
323     } catch (Error e) {
324       printHits("testBooleanRequiredEqualScores1", h, s);
325       throw e;
326     }
327   }
328   
329   public void testBooleanOptionalNoTiebreaker() throws Exception {
330     
331     BooleanQuery q = new BooleanQuery();
332     {
333       DisjunctionMaxQuery q1 = new DisjunctionMaxQuery(0.0f);
334       q1.add(tq("hed", "albino"));
335       q1.add(tq("dek", "albino"));
336       q.add(q1, BooleanClause.Occur.SHOULD);// false,false);
337     }
338     {
339       DisjunctionMaxQuery q2 = new DisjunctionMaxQuery(0.0f);
340       q2.add(tq("hed", "elephant"));
341       q2.add(tq("dek", "elephant"));
342       q.add(q2, BooleanClause.Occur.SHOULD);// false,false);
343     }
344     QueryUtils.check(random, q, s);
345     
346     ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
347     
348     try {
349       assertEquals("4 docs should match " + q.toString(), 4, h.length);
350       float score = h[0].score;
351       for (int i = 1; i < h.length - 1; i++) { /* note: -1 */
352         assertEquals("score #" + i + " is not the same", score, h[i].score,
353             SCORE_COMP_THRESH);
354       }
355       assertEquals("wrong last", "d1", s.doc(h[h.length - 1].doc).get("id"));
356       float score1 = h[h.length - 1].score;
357       assertTrue("d1 does not have worse score then others: " + score + " >? "
358           + score1, score > score1);
359     } catch (Error e) {
360       printHits("testBooleanOptionalNoTiebreaker", h, s);
361       throw e;
362     }
363   }
364   
365   public void testBooleanOptionalWithTiebreaker() throws Exception {
366     
367     BooleanQuery q = new BooleanQuery();
368     {
369       DisjunctionMaxQuery q1 = new DisjunctionMaxQuery(0.01f);
370       q1.add(tq("hed", "albino"));
371       q1.add(tq("dek", "albino"));
372       q.add(q1, BooleanClause.Occur.SHOULD);// false,false);
373     }
374     {
375       DisjunctionMaxQuery q2 = new DisjunctionMaxQuery(0.01f);
376       q2.add(tq("hed", "elephant"));
377       q2.add(tq("dek", "elephant"));
378       q.add(q2, BooleanClause.Occur.SHOULD);// false,false);
379     }
380     QueryUtils.check(random, q, s);
381     
382     ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
383     
384     try {
385       
386       assertEquals("4 docs should match " + q.toString(), 4, h.length);
387       
388       float score0 = h[0].score;
389       float score1 = h[1].score;
390       float score2 = h[2].score;
391       float score3 = h[3].score;
392       
393       String doc0 = s.doc(h[0].doc).get("id");
394       String doc1 = s.doc(h[1].doc).get("id");
395       String doc2 = s.doc(h[2].doc).get("id");
396       String doc3 = s.doc(h[3].doc).get("id");
397       
398       assertTrue("doc0 should be d2 or d4: " + doc0, doc0.equals("d2")
399           || doc0.equals("d4"));
400       assertTrue("doc1 should be d2 or d4: " + doc0, doc1.equals("d2")
401           || doc1.equals("d4"));
402       assertEquals("score0 and score1 should match", score0, score1,
403           SCORE_COMP_THRESH);
404       assertEquals("wrong third", "d3", doc2);
405       assertTrue("d3 does not have worse score then d2 and d4: " + score1
406           + " >? " + score2, score1 > score2);
407       
408       assertEquals("wrong fourth", "d1", doc3);
409       assertTrue("d1 does not have worse score then d3: " + score2 + " >? "
410           + score3, score2 > score3);
411       
412     } catch (Error e) {
413       printHits("testBooleanOptionalWithTiebreaker", h, s);
414       throw e;
415     }
416     
417   }
418   
419   public void testBooleanOptionalWithTiebreakerAndBoost() throws Exception {
420     
421     BooleanQuery q = new BooleanQuery();
422     {
423       DisjunctionMaxQuery q1 = new DisjunctionMaxQuery(0.01f);
424       q1.add(tq("hed", "albino", 1.5f));
425       q1.add(tq("dek", "albino"));
426       q.add(q1, BooleanClause.Occur.SHOULD);// false,false);
427     }
428     {
429       DisjunctionMaxQuery q2 = new DisjunctionMaxQuery(0.01f);
430       q2.add(tq("hed", "elephant", 1.5f));
431       q2.add(tq("dek", "elephant"));
432       q.add(q2, BooleanClause.Occur.SHOULD);// false,false);
433     }
434     QueryUtils.check(random, q, s);
435     
436     ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
437     
438     try {
439       
440       assertEquals("4 docs should match " + q.toString(), 4, h.length);
441       
442       float score0 = h[0].score;
443       float score1 = h[1].score;
444       float score2 = h[2].score;
445       float score3 = h[3].score;
446       
447       String doc0 = s.doc(h[0].doc).get("id");
448       String doc1 = s.doc(h[1].doc).get("id");
449       String doc2 = s.doc(h[2].doc).get("id");
450       String doc3 = s.doc(h[3].doc).get("id");
451       
452       assertEquals("doc0 should be d4: ", "d4", doc0);
453       assertEquals("doc1 should be d3: ", "d3", doc1);
454       assertEquals("doc2 should be d2: ", "d2", doc2);
455       assertEquals("doc3 should be d1: ", "d1", doc3);
456       
457       assertTrue("d4 does not have a better score then d3: " + score0 + " >? "
458           + score1, score0 > score1);
459       assertTrue("d3 does not have a better score then d2: " + score1 + " >? "
460           + score2, score1 > score2);
461       assertTrue("d3 does not have a better score then d1: " + score2 + " >? "
462           + score3, score2 > score3);
463       
464     } catch (Error e) {
465       printHits("testBooleanOptionalWithTiebreakerAndBoost", h, s);
466       throw e;
467     }
468   }
469   
470   /** macro */
471   protected Query tq(String f, String t) {
472     return new TermQuery(new Term(f, t));
473   }
474   
475   /** macro */
476   protected Query tq(String f, String t, float b) {
477     Query q = tq(f, t);
478     q.setBoost(b);
479     return q;
480   }
481   
482   protected void printHits(String test, ScoreDoc[] h, Searcher searcher)
483       throws Exception {
484     
485     System.err.println("------- " + test + " -------");
486     
487     DecimalFormat f = new DecimalFormat("0.000000000");
488     
489     for (int i = 0; i < h.length; i++) {
490       Document d = searcher.doc(h[i].doc);
491       float score = h[i].score;
492       System.err
493           .println("#" + i + ": " + f.format(score) + " - " + d.get("id"));
494     }
495   }
496 }