add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / backwards / src / test / org / apache / lucene / search / payloads / TestPayloadNearQuery.java
1 package org.apache.lucene.search.payloads;
2 /**
3  * Licensed to the Apache Software Foundation (ASF) under one or more
4  * contributor license agreements.  See the NOTICE file distributed with
5  * this work for additional information regarding copyright ownership.
6  * The ASF licenses this file to You under the Apache License, Version 2.0
7  * (the "License"); you may not use this file except in compliance with
8  * the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 import java.io.IOException;
19 import java.io.Reader;
20 import java.util.Collection;
21
22 import org.apache.lucene.analysis.Analyzer;
23 import org.apache.lucene.analysis.LowerCaseTokenizer;
24 import org.apache.lucene.analysis.TokenFilter;
25 import org.apache.lucene.analysis.TokenStream;
26 import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
27 import org.apache.lucene.document.Document;
28 import org.apache.lucene.document.Field;
29 import org.apache.lucene.index.FieldInvertState;
30 import org.apache.lucene.index.IndexReader;
31 import org.apache.lucene.index.Payload;
32 import org.apache.lucene.index.RandomIndexWriter;
33 import org.apache.lucene.index.Term;
34 import org.apache.lucene.search.DefaultSimilarity;
35 import org.apache.lucene.search.Explanation;
36 import org.apache.lucene.search.IndexSearcher;
37 import org.apache.lucene.search.QueryUtils;
38 import org.apache.lucene.search.ScoreDoc;
39 import org.apache.lucene.search.Searcher;
40 import org.apache.lucene.search.TopDocs;
41 import org.apache.lucene.search.spans.SpanQuery;
42 import org.apache.lucene.search.spans.SpanNearQuery;
43 import org.apache.lucene.search.spans.SpanTermQuery;
44 import org.apache.lucene.store.Directory;
45 import org.apache.lucene.util.English;
46 import org.apache.lucene.util.LuceneTestCase;
47 import org.apache.lucene.search.Explanation.IDFExplanation;
48 import org.junit.AfterClass;
49 import org.junit.BeforeClass;
50
51
52 public class TestPayloadNearQuery extends LuceneTestCase {
53   private static IndexSearcher searcher;
54   private static IndexReader reader;
55   private static Directory directory;
56   private static BoostingSimilarity similarity = new BoostingSimilarity();
57   private static byte[] payload2 = new byte[]{2};
58   private static byte[] payload4 = new byte[]{4};
59
60   private static class PayloadAnalyzer extends Analyzer {
61     @Override
62     public TokenStream tokenStream(String fieldName, Reader reader) {
63       TokenStream result = new LowerCaseTokenizer(TEST_VERSION_CURRENT, reader);
64       result = new PayloadFilter(result, fieldName);
65       return result;
66     }
67   }
68
69   private static class PayloadFilter extends TokenFilter {
70     String fieldName;
71     int numSeen = 0;
72     protected PayloadAttribute payAtt;
73
74     public PayloadFilter(TokenStream input, String fieldName) {
75       super(input);
76       this.fieldName = fieldName;
77       payAtt = addAttribute(PayloadAttribute.class);
78     }
79
80     @Override
81     public boolean incrementToken() throws IOException {
82       boolean result = false;
83       if (input.incrementToken() == true){
84         if (numSeen % 2 == 0) {
85           payAtt.setPayload(new Payload(payload2));
86         } else {
87           payAtt.setPayload(new Payload(payload4));
88         }
89         numSeen++;
90         result = true;
91       }
92       return result;
93     }
94   }
95   
96   private PayloadNearQuery newPhraseQuery (String fieldName, String phrase, boolean inOrder, PayloadFunction function ) {
97     String[] words = phrase.split("[\\s]+");
98     SpanQuery clauses[] = new SpanQuery[words.length];
99     for (int i=0;i<clauses.length;i++) {
100       clauses[i] = new SpanTermQuery(new Term(fieldName, words[i]));  
101     } 
102     return new PayloadNearQuery(clauses, 0, inOrder, function);
103   }
104
105   @BeforeClass
106   public static void beforeClass() throws Exception {
107     directory = newDirectory();
108     RandomIndexWriter writer = new RandomIndexWriter(random, directory, 
109         newIndexWriterConfig(TEST_VERSION_CURRENT, new PayloadAnalyzer())
110         .setSimilarity(similarity));
111     //writer.infoStream = System.out;
112     for (int i = 0; i < 1000; i++) {
113       Document doc = new Document();
114       doc.add(newField("field", English.intToEnglish(i), Field.Store.YES, Field.Index.ANALYZED));
115       String txt = English.intToEnglish(i) +' '+English.intToEnglish(i+1);
116       doc.add(newField("field2",  txt, Field.Store.YES, Field.Index.ANALYZED));
117       writer.addDocument(doc);
118     }
119     reader = writer.getReader();
120     writer.close();
121
122     searcher = newSearcher(reader);
123     searcher.setSimilarity(similarity);
124   }
125
126   @AfterClass
127   public static void afterClass() throws Exception {
128     searcher.close();
129     searcher = null;
130     reader.close();
131     reader = null;
132     directory.close();
133     directory = null;
134   }
135
136   public void test() throws IOException {
137     PayloadNearQuery query;
138     TopDocs hits;
139
140     query = newPhraseQuery("field", "twenty two", true, new AveragePayloadFunction());
141     QueryUtils.check(query);
142                 
143     // all 10 hits should have score = 3 because adjacent terms have payloads of 2,4
144     // and all the similarity factors are set to 1
145     hits = searcher.search(query, null, 100);
146     assertTrue("hits is null and it shouldn't be", hits != null);
147     assertTrue("should be 10 hits", hits.totalHits == 10);
148     for (int j = 0; j < hits.scoreDocs.length; j++) {
149       ScoreDoc doc = hits.scoreDocs[j];
150       assertTrue(doc.score + " does not equal: " + 3, doc.score == 3);
151     }
152     for (int i=1;i<10;i++) {
153       query = newPhraseQuery("field", English.intToEnglish(i)+" hundred", true, new AveragePayloadFunction());
154       // all should have score = 3 because adjacent terms have payloads of 2,4
155       // and all the similarity factors are set to 1
156       hits = searcher.search(query, null, 100);
157       assertTrue("hits is null and it shouldn't be", hits != null);
158       assertTrue("should be 100 hits", hits.totalHits == 100);
159       for (int j = 0; j < hits.scoreDocs.length; j++) {
160         ScoreDoc doc = hits.scoreDocs[j];
161         //                              System.out.println("Doc: " + doc.toString());
162         //                              System.out.println("Explain: " + searcher.explain(query, doc.doc));
163         assertTrue(doc.score + " does not equal: " + 3, doc.score == 3);
164       }
165     }
166   }
167
168
169   public void testPayloadNear() throws IOException {
170     SpanNearQuery q1, q2;
171     PayloadNearQuery query;
172     //SpanNearQuery(clauses, 10000, false)
173     q1 = spanNearQuery("field2", "twenty two");
174     q2 = spanNearQuery("field2", "twenty three");
175     SpanQuery[] clauses = new SpanQuery[2];
176     clauses[0] = q1;
177     clauses[1] = q2;
178     query = new PayloadNearQuery(clauses, 10, false); 
179     //System.out.println(query.toString());
180     assertEquals(12, searcher.search(query, null, 100).totalHits);
181     /*
182     System.out.println(hits.totalHits);
183     for (int j = 0; j < hits.scoreDocs.length; j++) {
184       ScoreDoc doc = hits.scoreDocs[j];
185       System.out.println("doc: "+doc.doc+", score: "+doc.score);
186     }
187     */
188   }
189   
190   public void testAverageFunction() throws IOException {
191           PayloadNearQuery query;
192           TopDocs hits;
193
194           query = newPhraseQuery("field", "twenty two", true, new AveragePayloadFunction());
195           QueryUtils.check(query);
196           // all 10 hits should have score = 3 because adjacent terms have payloads of 2,4
197           // and all the similarity factors are set to 1
198           hits = searcher.search(query, null, 100);
199           assertTrue("hits is null and it shouldn't be", hits != null);
200           assertTrue("should be 10 hits", hits.totalHits == 10);
201           for (int j = 0; j < hits.scoreDocs.length; j++) {
202                   ScoreDoc doc = hits.scoreDocs[j];
203                   assertTrue(doc.score + " does not equal: " + 3, doc.score == 3);
204                   Explanation explain = searcher.explain(query, hits.scoreDocs[j].doc);
205                   String exp = explain.toString();
206                   assertTrue(exp, exp.indexOf("AveragePayloadFunction") > -1);
207                   assertTrue(hits.scoreDocs[j].score + " explain value does not equal: " + 3, explain.getValue() == 3f);
208           }
209   }
210   public void testMaxFunction() throws IOException {
211           PayloadNearQuery query;
212           TopDocs hits;
213
214           query = newPhraseQuery("field", "twenty two", true, new MaxPayloadFunction());
215           QueryUtils.check(query);
216           // all 10 hits should have score = 4 (max payload value)
217           hits = searcher.search(query, null, 100);
218           assertTrue("hits is null and it shouldn't be", hits != null);
219           assertTrue("should be 10 hits", hits.totalHits == 10);
220           for (int j = 0; j < hits.scoreDocs.length; j++) {
221                   ScoreDoc doc = hits.scoreDocs[j];
222                   assertTrue(doc.score + " does not equal: " + 4, doc.score == 4);
223                   Explanation explain = searcher.explain(query, hits.scoreDocs[j].doc);
224                   String exp = explain.toString();
225                   assertTrue(exp, exp.indexOf("MaxPayloadFunction") > -1);
226                   assertTrue(hits.scoreDocs[j].score + " explain value does not equal: " + 4, explain.getValue() == 4f);
227           }
228   }  
229   public void testMinFunction() throws IOException {
230           PayloadNearQuery query;
231           TopDocs hits;
232
233           query = newPhraseQuery("field", "twenty two", true, new MinPayloadFunction());
234           QueryUtils.check(query);
235           // all 10 hits should have score = 2 (min payload value)
236           hits = searcher.search(query, null, 100);
237           assertTrue("hits is null and it shouldn't be", hits != null);
238           assertTrue("should be 10 hits", hits.totalHits == 10);
239           for (int j = 0; j < hits.scoreDocs.length; j++) {
240                   ScoreDoc doc = hits.scoreDocs[j];
241                   assertTrue(doc.score + " does not equal: " + 2, doc.score == 2);
242                   Explanation explain = searcher.explain(query, hits.scoreDocs[j].doc);
243                   String exp = explain.toString();
244                   assertTrue(exp, exp.indexOf("MinPayloadFunction") > -1);
245                   assertTrue(hits.scoreDocs[j].score + " explain value does not equal: " + 2, explain.getValue() == 2f);
246           }
247   }  
248   private SpanQuery[] getClauses() {
249             SpanNearQuery q1, q2;
250             q1 = spanNearQuery("field2", "twenty two");
251             q2 = spanNearQuery("field2", "twenty three");
252             SpanQuery[] clauses = new SpanQuery[2];
253             clauses[0] = q1;
254             clauses[1] = q2;
255             return clauses;
256   }
257   private SpanNearQuery spanNearQuery(String fieldName, String words) {
258     String[] wordList = words.split("[\\s]+");
259     SpanQuery clauses[] = new SpanQuery[wordList.length];
260     for (int i=0;i<clauses.length;i++) {
261       clauses[i] = new PayloadTermQuery(new Term(fieldName, wordList[i]), new AveragePayloadFunction());  
262     } 
263     return new SpanNearQuery(clauses, 10000, false);
264   }
265
266   public void testLongerSpan() throws IOException {
267     PayloadNearQuery query;
268     TopDocs hits;
269     query = newPhraseQuery("field", "nine hundred ninety nine", true, new AveragePayloadFunction());
270     hits = searcher.search(query, null, 100);
271     assertTrue("hits is null and it shouldn't be", hits != null);
272     ScoreDoc doc = hits.scoreDocs[0];
273     //          System.out.println("Doc: " + doc.toString());
274     //          System.out.println("Explain: " + searcher.explain(query, doc.doc));
275     assertTrue("there should only be one hit", hits.totalHits == 1);
276     // should have score = 3 because adjacent terms have payloads of 2,4
277     assertTrue(doc.score + " does not equal: " + 3, doc.score == 3); 
278   }
279
280   public void testComplexNested() throws IOException {
281     PayloadNearQuery query;
282     TopDocs hits;
283
284     // combine ordered and unordered spans with some nesting to make sure all payloads are counted
285
286     SpanQuery q1 = newPhraseQuery("field", "nine hundred", true, new AveragePayloadFunction());
287     SpanQuery q2 = newPhraseQuery("field", "ninety nine", true, new AveragePayloadFunction());
288     SpanQuery q3 = newPhraseQuery("field", "nine ninety", false, new AveragePayloadFunction());
289     SpanQuery q4 = newPhraseQuery("field", "hundred nine", false, new AveragePayloadFunction());
290     SpanQuery[]clauses = new SpanQuery[] {new PayloadNearQuery(new SpanQuery[] {q1,q2}, 0, true), new PayloadNearQuery(new SpanQuery[] {q3,q4}, 0, false)};
291     query = new PayloadNearQuery(clauses, 0, false);
292     hits = searcher.search(query, null, 100);
293     assertTrue("hits is null and it shouldn't be", hits != null);
294     // should be only 1 hit - doc 999
295     assertTrue("should only be one hit", hits.scoreDocs.length == 1);
296     // the score should be 3 - the average of all the underlying payloads
297     ScoreDoc doc = hits.scoreDocs[0];
298     //          System.out.println("Doc: " + doc.toString());
299     //          System.out.println("Explain: " + searcher.explain(query, doc.doc));
300     assertTrue(doc.score + " does not equal: " + 3, doc.score == 3);  
301   }
302
303   // must be static for weight serialization tests 
304   static class BoostingSimilarity extends DefaultSimilarity {
305
306     @Override public float scorePayload(int docId, String fieldName, int start, int end, byte[] payload, int offset, int length) {
307       //we know it is size 4 here, so ignore the offset/length
308       return payload[0];
309     }
310     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
311     //Make everything else 1 so we see the effect of the payload
312     //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
313     @Override public float computeNorm(String fieldName, FieldInvertState state) {
314       return state.getBoost();
315     }
316
317     @Override public float queryNorm(float sumOfSquaredWeights) {
318       return 1.0f;
319     }
320
321     @Override public float sloppyFreq(int distance) {
322       return 1.0f;
323     }
324
325     @Override public float coord(int overlap, int maxOverlap) {
326       return 1.0f;
327     }
328     @Override public float tf(float freq) {
329       return 1.0f;
330     }
331     // idf used for phrase queries
332     @Override public IDFExplanation idfExplain(Collection<Term> terms, Searcher searcher) throws IOException {
333       return new IDFExplanation() {
334         @Override
335         public float getIdf() {
336           return 1.0f;
337         }
338         @Override
339         public String explain() {
340           return "Inexplicable";
341         }
342       };
343     }
344   }
345 }