add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / test / org / apache / lucene / search / TestWildcard.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.store.Directory;
21 import org.apache.lucene.util.LuceneTestCase;
22 import org.apache.lucene.analysis.MockAnalyzer;
23 import org.apache.lucene.document.Document;
24 import org.apache.lucene.document.Field;
25 import org.apache.lucene.document.Field.Store;
26 import org.apache.lucene.document.Field.Index;
27 import org.apache.lucene.index.RandomIndexWriter;
28 import org.apache.lucene.index.Term;
29 import org.apache.lucene.queryParser.QueryParser;
30
31 import java.io.IOException;
32
33 /**
34  * TestWildcard tests the '*' and '?' wildcard characters.
35  */
36 public class TestWildcard
37     extends LuceneTestCase {
38   
39   @Override
40   public void setUp() throws Exception {
41     super.setUp();
42   }
43
44   public void testEquals() {
45     WildcardQuery wq1 = new WildcardQuery(new Term("field", "b*a"));
46     WildcardQuery wq2 = new WildcardQuery(new Term("field", "b*a"));
47     WildcardQuery wq3 = new WildcardQuery(new Term("field", "b*a"));
48
49     // reflexive?
50     assertEquals(wq1, wq2);
51     assertEquals(wq2, wq1);
52
53     // transitive?
54     assertEquals(wq2, wq3);
55     assertEquals(wq1, wq3);
56
57     assertFalse(wq1.equals(null));
58
59     FuzzyQuery fq = new FuzzyQuery(new Term("field", "b*a"));
60     assertFalse(wq1.equals(fq));
61     assertFalse(fq.equals(wq1));
62   }
63   
64   /**
65    * Tests if a WildcardQuery that has no wildcard in the term is rewritten to a single
66    * TermQuery. The boost should be preserved, and the rewrite should return
67    * a ConstantScoreQuery if the WildcardQuery had a ConstantScore rewriteMethod.
68    */
69   public void testTermWithoutWildcard() throws IOException {
70       Directory indexStore = getIndexStore("field", new String[]{"nowildcard", "nowildcardx"});
71       IndexSearcher searcher = new IndexSearcher(indexStore, true);
72
73       MultiTermQuery wq = new WildcardQuery(new Term("field", "nowildcard"));
74       assertMatches(searcher, wq, 1);
75
76       wq.setRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE);
77       wq.setBoost(0.1F);
78       Query q = searcher.rewrite(wq);
79       assertTrue(q instanceof TermQuery);
80       assertEquals(q.getBoost(), wq.getBoost());
81       
82       wq.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_FILTER_REWRITE);
83       wq.setBoost(0.2F);
84       q = searcher.rewrite(wq);
85       assertTrue(q instanceof ConstantScoreQuery);
86       assertEquals(q.getBoost(), wq.getBoost());
87       
88       wq.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT);
89       wq.setBoost(0.3F);
90       q = searcher.rewrite(wq);
91       assertTrue(q instanceof ConstantScoreQuery);
92       assertEquals(q.getBoost(), wq.getBoost());
93       
94       wq.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE);
95       wq.setBoost(0.4F);
96       q = searcher.rewrite(wq);
97       assertTrue(q instanceof ConstantScoreQuery);
98       assertEquals(q.getBoost(), wq.getBoost());
99       searcher.close();
100       indexStore.close();
101   }
102   
103   /**
104    * Tests if a WildcardQuery with an empty term is rewritten to an empty BooleanQuery
105    */
106   public void testEmptyTerm() throws IOException {
107     Directory indexStore = getIndexStore("field", new String[]{"nowildcard", "nowildcardx"});
108     IndexSearcher searcher = new IndexSearcher(indexStore, true);
109
110     MultiTermQuery wq = new WildcardQuery(new Term("field", ""));
111     wq.setRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE);
112     assertMatches(searcher, wq, 0);
113     Query q = searcher.rewrite(wq);
114     assertTrue(q instanceof BooleanQuery);
115     assertEquals(0, ((BooleanQuery) q).clauses().size());
116     searcher.close();
117     indexStore.close();
118   }
119   
120   /**
121    * Tests if a WildcardQuery that has only a trailing * in the term is
122    * rewritten to a single PrefixQuery. The boost and rewriteMethod should be
123    * preserved.
124    */
125   public void testPrefixTerm() throws IOException {
126     Directory indexStore = getIndexStore("field", new String[]{"prefix", "prefixx"});
127     IndexSearcher searcher = new IndexSearcher(indexStore, true);
128
129     MultiTermQuery wq = new WildcardQuery(new Term("field", "prefix*"));
130     assertMatches(searcher, wq, 2);
131     assertTrue(wq.getEnum(searcher.getIndexReader()) instanceof PrefixTermEnum);
132    
133     searcher.close();
134     indexStore.close();
135   }
136
137   /**
138    * Tests Wildcard queries with an asterisk.
139    */
140   public void testAsterisk()
141       throws IOException {
142     Directory indexStore = getIndexStore("body", new String[]
143     {"metal", "metals"});
144     IndexSearcher searcher = new IndexSearcher(indexStore, true);
145     Query query1 = new TermQuery(new Term("body", "metal"));
146     Query query2 = new WildcardQuery(new Term("body", "metal*"));
147     Query query3 = new WildcardQuery(new Term("body", "m*tal"));
148     Query query4 = new WildcardQuery(new Term("body", "m*tal*"));
149     Query query5 = new WildcardQuery(new Term("body", "m*tals"));
150
151     BooleanQuery query6 = new BooleanQuery();
152     query6.add(query5, BooleanClause.Occur.SHOULD);
153
154     BooleanQuery query7 = new BooleanQuery();
155     query7.add(query3, BooleanClause.Occur.SHOULD);
156     query7.add(query5, BooleanClause.Occur.SHOULD);
157
158     // Queries do not automatically lower-case search terms:
159     Query query8 = new WildcardQuery(new Term("body", "M*tal*"));
160
161     assertMatches(searcher, query1, 1);
162     assertMatches(searcher, query2, 2);
163     assertMatches(searcher, query3, 1);
164     assertMatches(searcher, query4, 2);
165     assertMatches(searcher, query5, 1);
166     assertMatches(searcher, query6, 1);
167     assertMatches(searcher, query7, 2);
168     assertMatches(searcher, query8, 0);
169     assertMatches(searcher, new WildcardQuery(new Term("body", "*tall")), 0);
170     assertMatches(searcher, new WildcardQuery(new Term("body", "*tal")), 1);
171     assertMatches(searcher, new WildcardQuery(new Term("body", "*tal*")), 2);
172     searcher.close();
173     indexStore.close();
174   }
175
176   /**
177    * LUCENE-2620
178    */
179   public void testLotsOfAsterisks()
180       throws IOException {
181     Directory indexStore = getIndexStore("body", new String[]
182     {"metal", "metals"});
183     IndexSearcher searcher = new IndexSearcher(indexStore, true);
184     StringBuilder term = new StringBuilder();
185     term.append("m");
186     for (int i = 0; i < 512; i++)
187       term.append("*");
188     term.append("tal");
189     Query query3 = new WildcardQuery(new Term("body", term.toString()));
190
191     assertMatches(searcher, query3, 1);
192     searcher.close();
193     indexStore.close();
194   }
195   
196   /**
197    * Tests Wildcard queries with a question mark.
198    *
199    * @throws IOException if an error occurs
200    */
201   public void testQuestionmark()
202       throws IOException {
203     Directory indexStore = getIndexStore("body", new String[]
204     {"metal", "metals", "mXtals", "mXtXls"});
205     IndexSearcher searcher = new IndexSearcher(indexStore, true);
206     Query query1 = new WildcardQuery(new Term("body", "m?tal"));
207     Query query2 = new WildcardQuery(new Term("body", "metal?"));
208     Query query3 = new WildcardQuery(new Term("body", "metals?"));
209     Query query4 = new WildcardQuery(new Term("body", "m?t?ls"));
210     Query query5 = new WildcardQuery(new Term("body", "M?t?ls"));
211     Query query6 = new WildcardQuery(new Term("body", "meta??"));
212     
213     assertMatches(searcher, query1, 1); 
214     assertMatches(searcher, query2, 1);
215     assertMatches(searcher, query3, 0);
216     assertMatches(searcher, query4, 3);
217     assertMatches(searcher, query5, 0);
218     assertMatches(searcher, query6, 1); // Query: 'meta??' matches 'metals' not 'metal'
219     searcher.close();
220     indexStore.close();
221   }
222
223   private Directory getIndexStore(String field, String[] contents)
224       throws IOException {
225     Directory indexStore = newDirectory();
226     RandomIndexWriter writer = new RandomIndexWriter(random, indexStore);
227     for (int i = 0; i < contents.length; ++i) {
228       Document doc = new Document();
229       doc.add(newField(field, contents[i], Field.Store.YES, Field.Index.ANALYZED));
230       writer.addDocument(doc);
231     }
232     writer.close();
233
234     return indexStore;
235   }
236
237   private void assertMatches(IndexSearcher searcher, Query q, int expectedMatches)
238       throws IOException {
239     ScoreDoc[] result = searcher.search(q, null, 1000).scoreDocs;
240     assertEquals(expectedMatches, result.length);
241   }
242
243   /**
244    * Test that wild card queries are parsed to the correct type and are searched correctly.
245    * This test looks at both parsing and execution of wildcard queries.
246    * Although placed here, it also tests prefix queries, verifying that
247    * prefix queries are not parsed into wild card queries, and viceversa.
248    * @throws Exception
249    */
250   public void testParsingAndSearching() throws Exception {
251     String field = "content";
252     QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, field, new MockAnalyzer(random));
253     qp.setAllowLeadingWildcard(true);
254     String docs[] = {
255         "\\ abcdefg1",
256         "\\79 hijklmn1",
257         "\\\\ opqrstu1",
258     };
259     // queries that should find all docs
260     String matchAll[] = {
261         "*", "*1", "**1", "*?", "*?1", "?*1", "**", "***", "\\\\*"
262     };
263     // queries that should find no docs
264     String matchNone[] = {
265         "a*h", "a?h", "*a*h", "?a", "a?",
266     };
267     // queries that should be parsed to prefix queries
268     String matchOneDocPrefix[][] = {
269         {"a*", "ab*", "abc*", }, // these should find only doc 0 
270         {"h*", "hi*", "hij*", "\\\\7*"}, // these should find only doc 1
271         {"o*", "op*", "opq*", "\\\\\\\\*"}, // these should find only doc 2
272     };
273     // queries that should be parsed to wildcard queries
274     String matchOneDocWild[][] = {
275         {"*a*", "*ab*", "*abc**", "ab*e*", "*g?", "*f?1", "abc**"}, // these should find only doc 0
276         {"*h*", "*hi*", "*hij**", "hi*k*", "*n?", "*m?1", "hij**"}, // these should find only doc 1
277         {"*o*", "*op*", "*opq**", "op*q*", "*u?", "*t?1", "opq**"}, // these should find only doc 2
278     };
279
280     // prepare the index
281     Directory dir = newDirectory();
282     RandomIndexWriter iw = new RandomIndexWriter(random, dir, 
283         newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random))
284         .setMergePolicy(newLogMergePolicy()));
285     for (int i = 0; i < docs.length; i++) {
286       Document doc = new Document();
287       doc.add(newField(field,docs[i],Store.NO,Index.ANALYZED));
288       iw.addDocument(doc);
289     }
290     iw.close();
291     
292     IndexSearcher searcher = new IndexSearcher(dir, true);
293     
294     // test queries that must find all
295     for (int i = 0; i < matchAll.length; i++) {
296       String qtxt = matchAll[i];
297       Query q = qp.parse(qtxt);
298       if (VERBOSE) System.out.println("matchAll: qtxt="+qtxt+" q="+q+" "+q.getClass().getName());
299       ScoreDoc[] hits = searcher.search(q, null, 1000).scoreDocs;
300       assertEquals(docs.length,hits.length);
301     }
302     
303     // test queries that must find none
304     for (int i = 0; i < matchNone.length; i++) {
305       String qtxt = matchNone[i];
306       Query q = qp.parse(qtxt);
307       if (VERBOSE) System.out.println("matchNone: qtxt="+qtxt+" q="+q+" "+q.getClass().getName());
308       ScoreDoc[] hits = searcher.search(q, null, 1000).scoreDocs;
309       assertEquals(0,hits.length);
310     }
311
312     // test queries that must be prefix queries and must find only one doc
313     for (int i = 0; i < matchOneDocPrefix.length; i++) {
314       for (int j = 0; j < matchOneDocPrefix[i].length; j++) {
315         String qtxt = matchOneDocPrefix[i][j];
316         Query q = qp.parse(qtxt);
317         if (VERBOSE) System.out.println("match 1 prefix: doc="+docs[i]+" qtxt="+qtxt+" q="+q+" "+q.getClass().getName());
318         assertEquals(PrefixQuery.class, q.getClass());
319         ScoreDoc[] hits = searcher.search(q, null, 1000).scoreDocs;
320         assertEquals(1,hits.length);
321         assertEquals(i,hits[0].doc);
322       }
323     }
324
325     // test queries that must be wildcard queries and must find only one doc
326     for (int i = 0; i < matchOneDocPrefix.length; i++) {
327       for (int j = 0; j < matchOneDocWild[i].length; j++) {
328         String qtxt = matchOneDocWild[i][j];
329         Query q = qp.parse(qtxt);
330         if (VERBOSE) System.out.println("match 1 wild: doc="+docs[i]+" qtxt="+qtxt+" q="+q+" "+q.getClass().getName());
331         assertEquals(WildcardQuery.class, q.getClass());
332         ScoreDoc[] hits = searcher.search(q, null, 1000).scoreDocs;
333         assertEquals(1,hits.length);
334         assertEquals(i,hits[0].doc);
335       }
336     }
337
338     searcher.close();
339     dir.close();
340   }
341   
342 }