pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / contrib / queryparser / src / test / org / apache / lucene / queryParser / standard / TestQPHelper.java
1 package org.apache.lucene.queryParser.standard;
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 java.io.IOException;
21 import java.io.Reader;
22 import java.text.Collator;
23 import java.text.DateFormat;
24 import java.util.Calendar;
25 import java.util.Date;
26 import java.util.GregorianCalendar;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Collections;
32
33 import org.apache.lucene.analysis.Analyzer;
34 import org.apache.lucene.analysis.KeywordAnalyzer;
35 import org.apache.lucene.analysis.LowerCaseTokenizer;
36 import org.apache.lucene.analysis.MockAnalyzer;
37 import org.apache.lucene.analysis.MockTokenizer;
38 import org.apache.lucene.analysis.SimpleAnalyzer;
39 import org.apache.lucene.analysis.StopAnalyzer;
40 import org.apache.lucene.analysis.StopFilter;
41 import org.apache.lucene.analysis.TokenFilter;
42 import org.apache.lucene.analysis.TokenStream;
43 import org.apache.lucene.analysis.Tokenizer;
44 import org.apache.lucene.analysis.WhitespaceAnalyzer;
45 import org.apache.lucene.analysis.standard.StandardAnalyzer;
46 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
47 import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
48 import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
49 import org.apache.lucene.document.DateField;
50 import org.apache.lucene.document.DateTools;
51 import org.apache.lucene.document.Document;
52 import org.apache.lucene.document.Field;
53 import org.apache.lucene.index.IndexWriter;
54 import org.apache.lucene.index.IndexReader;
55 import org.apache.lucene.index.Term;
56 import org.apache.lucene.messages.MessageImpl;
57 import org.apache.lucene.queryParser.core.QueryNodeException;
58 import org.apache.lucene.queryParser.core.messages.QueryParserMessages;
59 import org.apache.lucene.queryParser.core.nodes.FuzzyQueryNode;
60 import org.apache.lucene.queryParser.core.nodes.QueryNode;
61 import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorImpl;
62 import org.apache.lucene.queryParser.core.processors.QueryNodeProcessorPipeline;
63 import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler;
64 import org.apache.lucene.queryParser.standard.config.StandardQueryConfigHandler.Operator;
65 import org.apache.lucene.queryParser.standard.nodes.WildcardQueryNode;
66 import org.apache.lucene.search.BooleanClause;
67 import org.apache.lucene.search.BooleanQuery;
68 import org.apache.lucene.search.FuzzyQuery;
69 import org.apache.lucene.search.IndexSearcher;
70 import org.apache.lucene.search.MatchAllDocsQuery;
71 import org.apache.lucene.search.MultiPhraseQuery;
72 import org.apache.lucene.search.MultiTermQuery;
73 import org.apache.lucene.search.PhraseQuery;
74 import org.apache.lucene.search.PrefixQuery;
75 import org.apache.lucene.search.Query;
76 import org.apache.lucene.search.ScoreDoc;
77 import org.apache.lucene.search.TermQuery;
78 import org.apache.lucene.search.TermRangeQuery;
79 import org.apache.lucene.search.WildcardQuery;
80 import org.apache.lucene.store.Directory;
81 import org.apache.lucene.util.LuceneTestCase;
82
83 /**
84  * This test case is a copy of the core Lucene query parser test, it was adapted
85  * to use new QueryParserHelper instead of the old query parser.
86  * 
87  * Tests QueryParser.
88  */
89 public class TestQPHelper extends LuceneTestCase {
90
91   public static Analyzer qpAnalyzer = new QPTestAnalyzer();
92
93   public static final class QPTestFilter extends TokenFilter {
94     private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
95     private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class);
96
97     /**
98      * Filter which discards the token 'stop' and which expands the token
99      * 'phrase' into 'phrase1 phrase2'
100      */
101     public QPTestFilter(TokenStream in) {
102       super(in);
103     }
104
105     private boolean inPhrase = false;
106     private int savedStart = 0;
107     private int savedEnd = 0;
108
109     @Override
110     public boolean incrementToken() throws IOException {
111       if (inPhrase) {
112         inPhrase = false;
113         clearAttributes();
114         termAtt.setEmpty().append("phrase2");
115         offsetAtt.setOffset(savedStart, savedEnd);
116         return true;
117       } else
118         while (input.incrementToken()) {
119           if (termAtt.toString().equals("phrase")) {
120             inPhrase = true;
121             savedStart = offsetAtt.startOffset();
122             savedEnd = offsetAtt.endOffset();
123             termAtt.setEmpty().append("phrase1");
124             offsetAtt.setOffset(savedStart, savedEnd);
125             return true;
126           } else if (!termAtt.toString().equals("stop"))
127             return true;
128         }
129       return false;
130     }
131
132     @Override
133     public void reset() throws IOException {
134       super.reset();
135       this.inPhrase = false;
136       this.savedStart = 0;
137       this.savedEnd = 0;
138     }
139   }
140
141   public static final class QPTestAnalyzer extends Analyzer {
142
143     /** Filters LowerCaseTokenizer with StopFilter. */
144     @Override
145     public final TokenStream tokenStream(String fieldName, Reader reader) {
146       return new QPTestFilter(new LowerCaseTokenizer(TEST_VERSION_CURRENT, reader));
147     }
148   }
149
150   public static class QPTestParser extends StandardQueryParser {
151     public QPTestParser(Analyzer a) {
152       ((QueryNodeProcessorPipeline)getQueryNodeProcessor())
153           .add(new QPTestParserQueryNodeProcessor());
154       this.setAnalyzer(a);
155
156     }
157
158     private static class QPTestParserQueryNodeProcessor extends
159         QueryNodeProcessorImpl {
160
161       @Override
162       protected QueryNode postProcessNode(QueryNode node)
163           throws QueryNodeException {
164
165         return node;
166
167       }
168
169       @Override
170       protected QueryNode preProcessNode(QueryNode node)
171           throws QueryNodeException {
172
173         if (node instanceof WildcardQueryNode || node instanceof FuzzyQueryNode) {
174
175           throw new QueryNodeException(new MessageImpl(
176               QueryParserMessages.EMPTY_MESSAGE));
177
178         }
179
180         return node;
181
182       }
183
184       @Override
185       protected List<QueryNode> setChildrenOrder(List<QueryNode> children)
186           throws QueryNodeException {
187
188         return children;
189
190       }
191
192     }
193
194   }
195
196   private int originalMaxClauses;
197
198   @Override
199   public void setUp() throws Exception {
200     super.setUp();
201     originalMaxClauses = BooleanQuery.getMaxClauseCount();
202   }
203
204   public StandardQueryParser getParser(Analyzer a) throws Exception {
205     if (a == null)
206       a = new SimpleAnalyzer(TEST_VERSION_CURRENT);
207     StandardQueryParser qp = new StandardQueryParser();
208     qp.setAnalyzer(a);
209
210     qp.setDefaultOperator(StandardQueryConfigHandler.Operator.OR);
211
212     return qp;
213
214   }
215
216   public Query getQuery(String query, Analyzer a) throws Exception {
217     return getParser(a).parse(query, "field");
218   }
219
220   public Query getQueryAllowLeadingWildcard(String query, Analyzer a) throws Exception {
221     StandardQueryParser parser = getParser(a);
222     parser.setAllowLeadingWildcard(true);
223     return parser.parse(query, "field");
224   }
225
226   public void assertQueryEquals(String query, Analyzer a, String result)
227       throws Exception {
228     Query q = getQuery(query, a);
229     String s = q.toString("field");
230     if (!s.equals(result)) {
231       fail("Query /" + query + "/ yielded /" + s + "/, expecting /" + result
232           + "/");
233     }
234   }
235
236   public void assertQueryEqualsAllowLeadingWildcard(String query, Analyzer a, String result)
237       throws Exception {
238     Query q = getQueryAllowLeadingWildcard(query, a);
239     String s = q.toString("field");
240     if (!s.equals(result)) {
241       fail("Query /" + query + "/ yielded /" + s + "/, expecting /" + result
242           + "/");
243     }
244   }
245
246   public void assertQueryEquals(StandardQueryParser qp, String field,
247       String query, String result) throws Exception {
248     Query q = qp.parse(query, field);
249     String s = q.toString(field);
250     if (!s.equals(result)) {
251       fail("Query /" + query + "/ yielded /" + s + "/, expecting /" + result
252           + "/");
253     }
254   }
255
256   public void assertEscapedQueryEquals(String query, Analyzer a, String result)
257       throws Exception {
258     String escapedQuery = QueryParserUtil.escape(query);
259     if (!escapedQuery.equals(result)) {
260       fail("Query /" + query + "/ yielded /" + escapedQuery + "/, expecting /"
261           + result + "/");
262     }
263   }
264
265   public void assertWildcardQueryEquals(String query, boolean lowercase,
266       String result, boolean allowLeadingWildcard) throws Exception {
267     StandardQueryParser qp = getParser(null);
268     qp.setLowercaseExpandedTerms(lowercase);
269     qp.setAllowLeadingWildcard(allowLeadingWildcard);
270     Query q = qp.parse(query, "field");
271     String s = q.toString("field");
272     if (!s.equals(result)) {
273       fail("WildcardQuery /" + query + "/ yielded /" + s + "/, expecting /"
274           + result + "/");
275     }
276   }
277
278   public void assertWildcardQueryEquals(String query, boolean lowercase,
279       String result) throws Exception {
280     assertWildcardQueryEquals(query, lowercase, result, false);
281   }
282
283   public void assertWildcardQueryEquals(String query, String result)
284       throws Exception {
285     StandardQueryParser qp = getParser(null);
286     Query q = qp.parse(query, "field");
287     String s = q.toString("field");
288     if (!s.equals(result)) {
289       fail("WildcardQuery /" + query + "/ yielded /" + s + "/, expecting /"
290           + result + "/");
291     }
292   }
293
294   public Query getQueryDOA(String query, Analyzer a) throws Exception {
295     if (a == null)
296       a = new SimpleAnalyzer(TEST_VERSION_CURRENT);
297     StandardQueryParser qp = new StandardQueryParser();
298     qp.setAnalyzer(a);
299     qp.setDefaultOperator(Operator.AND);
300
301     return qp.parse(query, "field");
302
303   }
304
305   public void assertQueryEqualsDOA(String query, Analyzer a, String result)
306       throws Exception {
307     Query q = getQueryDOA(query, a);
308     String s = q.toString("field");
309     if (!s.equals(result)) {
310       fail("Query /" + query + "/ yielded /" + s + "/, expecting /" + result
311           + "/");
312     }
313   }
314
315   public void testConstantScoreAutoRewrite() throws Exception {
316     StandardQueryParser qp = new StandardQueryParser(new MockAnalyzer(random, MockTokenizer.WHITESPACE, false));
317     Query q = qp.parse("foo*bar", "field");
318     assertTrue(q instanceof WildcardQuery);
319     assertEquals(MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT, ((MultiTermQuery) q).getRewriteMethod());
320
321     q = qp.parse("foo*", "field");
322     assertTrue(q instanceof PrefixQuery);
323     assertEquals(MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT, ((MultiTermQuery) q).getRewriteMethod());
324
325     q = qp.parse("[a TO z]", "field");
326     assertTrue(q instanceof TermRangeQuery);
327     assertEquals(MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT, ((MultiTermQuery) q).getRewriteMethod());
328   }
329
330   public void testCJK() throws Exception {
331     // Test Ideographic Space - As wide as a CJK character cell (fullwidth)
332     // used google to translate the word "term" to japanese -> ??
333     assertQueryEquals("term\u3000term\u3000term", null,
334         "term\u0020term\u0020term");
335     assertQueryEqualsAllowLeadingWildcard("??\u3000??\u3000??", null, "??\u0020??\u0020??");
336   }
337   
338   public void testCJKTerm() throws Exception {
339     // individual CJK chars as terms
340     StandardAnalyzer analyzer = new StandardAnalyzer(TEST_VERSION_CURRENT);
341     
342     BooleanQuery expected = new BooleanQuery();
343     expected.add(new TermQuery(new Term("field", "中")), BooleanClause.Occur.SHOULD);
344     expected.add(new TermQuery(new Term("field", "国")), BooleanClause.Occur.SHOULD);
345     assertEquals(expected, getQuery("中国", analyzer));
346     
347     expected = new BooleanQuery();
348     expected.add(new TermQuery(new Term("field", "中")), BooleanClause.Occur.MUST);
349     BooleanQuery inner = new BooleanQuery();
350     inner.add(new TermQuery(new Term("field", "中")), BooleanClause.Occur.SHOULD);
351     inner.add(new TermQuery(new Term("field", "国")), BooleanClause.Occur.SHOULD);
352     expected.add(inner, BooleanClause.Occur.MUST);
353     assertEquals(expected, getQuery("中 AND ä¸­å›½", analyzer));
354
355   }
356   
357   public void testCJKBoostedTerm() throws Exception {
358     // individual CJK chars as terms
359     StandardAnalyzer analyzer = new StandardAnalyzer(TEST_VERSION_CURRENT);
360     
361     BooleanQuery expected = new BooleanQuery();
362     expected.setBoost(0.5f);
363     expected.add(new TermQuery(new Term("field", "中")), BooleanClause.Occur.SHOULD);
364     expected.add(new TermQuery(new Term("field", "国")), BooleanClause.Occur.SHOULD);
365     
366     assertEquals(expected, getQuery("中国^0.5", analyzer));
367   }
368   
369   public void testCJKPhrase() throws Exception {
370     // individual CJK chars as terms
371     StandardAnalyzer analyzer = new StandardAnalyzer(TEST_VERSION_CURRENT);
372     
373     PhraseQuery expected = new PhraseQuery();
374     expected.add(new Term("field", "中"));
375     expected.add(new Term("field", "国"));
376     
377     assertEquals(expected, getQuery("\"中国\"", analyzer));
378   }
379   
380   public void testCJKBoostedPhrase() throws Exception {
381     // individual CJK chars as terms
382     StandardAnalyzer analyzer = new StandardAnalyzer(TEST_VERSION_CURRENT);
383     
384     PhraseQuery expected = new PhraseQuery();
385     expected.setBoost(0.5f);
386     expected.add(new Term("field", "中"));
387     expected.add(new Term("field", "国"));
388     
389     assertEquals(expected, getQuery("\"中国\"^0.5", analyzer));
390   }
391   
392   public void testCJKSloppyPhrase() throws Exception {
393     // individual CJK chars as terms
394     StandardAnalyzer analyzer = new StandardAnalyzer(TEST_VERSION_CURRENT);
395     
396     PhraseQuery expected = new PhraseQuery();
397     expected.setSlop(3);
398     expected.add(new Term("field", "中"));
399     expected.add(new Term("field", "国"));
400     
401     assertEquals(expected, getQuery("\"中国\"~3", analyzer));
402   }
403
404   public void testSimple() throws Exception {
405     assertQueryEquals("\"term germ\"~2", null, "\"term germ\"~2");
406     assertQueryEquals("term term term", null, "term term term");
407     assertQueryEquals("t�rm term term", new MockAnalyzer(random, MockTokenizer.WHITESPACE, false),
408         "t�rm term term");
409     assertQueryEquals("�mlaut", new MockAnalyzer(random, MockTokenizer.WHITESPACE, false), "�mlaut");
410
411     assertQueryEquals("\"\"", new KeywordAnalyzer(), "");
412     assertQueryEquals("foo:\"\"", new KeywordAnalyzer(), "foo:");
413
414     assertQueryEquals("a AND b", null, "+a +b");
415     assertQueryEquals("(a AND b)", null, "+a +b");
416     assertQueryEquals("c OR (a AND b)", null, "c (+a +b)");
417
418     assertQueryEquals("a AND NOT b", null, "+a -b");
419
420     assertQueryEquals("a AND -b", null, "+a -b");
421
422     assertQueryEquals("a AND !b", null, "+a -b");
423
424     assertQueryEquals("a && b", null, "+a +b");
425
426     assertQueryEquals("a && ! b", null, "+a -b");
427
428     assertQueryEquals("a OR b", null, "a b");
429     assertQueryEquals("a || b", null, "a b");
430
431     assertQueryEquals("a OR !b", null, "a -b");
432
433     assertQueryEquals("a OR ! b", null, "a -b");
434
435     assertQueryEquals("a OR -b", null, "a -b");
436
437     assertQueryEquals("+term -term term", null, "+term -term term");
438     assertQueryEquals("foo:term AND field:anotherTerm", null,
439         "+foo:term +anotherterm");
440     assertQueryEquals("term AND \"phrase phrase\"", null,
441         "+term +\"phrase phrase\"");
442     assertQueryEquals("\"hello there\"", null, "\"hello there\"");
443     assertTrue(getQuery("a AND b", null) instanceof BooleanQuery);
444     assertTrue(getQuery("hello", null) instanceof TermQuery);
445     assertTrue(getQuery("\"hello there\"", null) instanceof PhraseQuery);
446
447     assertQueryEquals("germ term^2.0", null, "germ term^2.0");
448     assertQueryEquals("(term)^2.0", null, "term^2.0");
449     assertQueryEquals("(germ term)^2.0", null, "(germ term)^2.0");
450     assertQueryEquals("term^2.0", null, "term^2.0");
451     assertQueryEquals("term^2", null, "term^2.0");
452     assertQueryEquals("\"germ term\"^2.0", null, "\"germ term\"^2.0");
453     assertQueryEquals("\"term germ\"^2", null, "\"term germ\"^2.0");
454
455     assertQueryEquals("(foo OR bar) AND (baz OR boo)", null,
456         "+(foo bar) +(baz boo)");
457     assertQueryEquals("((a OR b) AND NOT c) OR d", null, "(+(a b) -c) d");
458     assertQueryEquals("+(apple \"steve jobs\") -(foo bar baz)", null,
459         "+(apple \"steve jobs\") -(foo bar baz)");
460     assertQueryEquals("+title:(dog OR cat) -author:\"bob dole\"", null,
461         "+(title:dog title:cat) -author:\"bob dole\"");
462
463   }
464
465   public void testPunct() throws Exception {
466     Analyzer a = new MockAnalyzer(random, MockTokenizer.WHITESPACE, false);
467     assertQueryEquals("a&b", a, "a&b");
468     assertQueryEquals("a&&b", a, "a&&b");
469     assertQueryEquals(".NET", a, ".NET");
470   }
471
472   public void testSlop() throws Exception {
473
474     assertQueryEquals("\"term germ\"~2", null, "\"term germ\"~2");
475     assertQueryEquals("\"term germ\"~2 flork", null, "\"term germ\"~2 flork");
476     assertQueryEquals("\"term\"~2", null, "term");
477     assertQueryEquals("\" \"~2 germ", null, "germ");
478     assertQueryEquals("\"term germ\"~2^2", null, "\"term germ\"~2^2.0");
479   }
480
481   public void testNumber() throws Exception {
482     // The numbers go away because SimpleAnalzyer ignores them
483     assertQueryEquals("3", null, "");
484     assertQueryEquals("term 1.0 1 2", null, "term");
485     assertQueryEquals("term term1 term2", null, "term term term");
486
487     Analyzer a = new MockAnalyzer(random, MockTokenizer.WHITESPACE, false);
488     assertQueryEquals("3", a, "3");
489     assertQueryEquals("term 1.0 1 2", a, "term 1.0 1 2");
490     assertQueryEquals("term term1 term2", a, "term term1 term2");
491   }
492
493   public void testWildcard() throws Exception {
494     assertQueryEquals("term*", null, "term*");
495     assertQueryEquals("term*^2", null, "term*^2.0");
496     assertQueryEquals("term~", null, "term~0.5");
497     assertQueryEquals("term~0.7", null, "term~0.7");
498
499     assertQueryEquals("term~^2", null, "term~0.5^2.0");
500
501     assertQueryEquals("term^2~", null, "term~0.5^2.0");
502     assertQueryEquals("term*germ", null, "term*germ");
503     assertQueryEquals("term*germ^3", null, "term*germ^3.0");
504
505     assertTrue(getQuery("term*", null) instanceof PrefixQuery);
506     assertTrue(getQuery("term*^2", null) instanceof PrefixQuery);
507     assertTrue(getQuery("term~", null) instanceof FuzzyQuery);
508     assertTrue(getQuery("term~0.7", null) instanceof FuzzyQuery);
509     FuzzyQuery fq = (FuzzyQuery) getQuery("term~0.7", null);
510     assertEquals(0.7f, fq.getMinSimilarity(), 0.1f);
511     assertEquals(FuzzyQuery.defaultPrefixLength, fq.getPrefixLength());
512     fq = (FuzzyQuery) getQuery("term~", null);
513     assertEquals(0.5f, fq.getMinSimilarity(), 0.1f);
514     assertEquals(FuzzyQuery.defaultPrefixLength, fq.getPrefixLength());
515
516     assertQueryNodeException("term~1.1"); // value > 1, throws exception
517
518     assertTrue(getQuery("term*germ", null) instanceof WildcardQuery);
519
520     /*
521      * Tests to see that wild card terms are (or are not) properly lower-cased
522      * with propery parser configuration
523      */
524     // First prefix queries:
525     // by default, convert to lowercase:
526     assertWildcardQueryEquals("Term*", true, "term*");
527     // explicitly set lowercase:
528     assertWildcardQueryEquals("term*", true, "term*");
529     assertWildcardQueryEquals("Term*", true, "term*");
530     assertWildcardQueryEquals("TERM*", true, "term*");
531     // explicitly disable lowercase conversion:
532     assertWildcardQueryEquals("term*", false, "term*");
533     assertWildcardQueryEquals("Term*", false, "Term*");
534     assertWildcardQueryEquals("TERM*", false, "TERM*");
535     // Then 'full' wildcard queries:
536     // by default, convert to lowercase:
537     assertWildcardQueryEquals("Te?m", "te?m");
538     // explicitly set lowercase:
539     assertWildcardQueryEquals("te?m", true, "te?m");
540     assertWildcardQueryEquals("Te?m", true, "te?m");
541     assertWildcardQueryEquals("TE?M", true, "te?m");
542     assertWildcardQueryEquals("Te?m*gerM", true, "te?m*germ");
543     // explicitly disable lowercase conversion:
544     assertWildcardQueryEquals("te?m", false, "te?m");
545     assertWildcardQueryEquals("Te?m", false, "Te?m");
546     assertWildcardQueryEquals("TE?M", false, "TE?M");
547     assertWildcardQueryEquals("Te?m*gerM", false, "Te?m*gerM");
548     // Fuzzy queries:
549     assertWildcardQueryEquals("Term~", "term~0.5");
550     assertWildcardQueryEquals("Term~", true, "term~0.5");
551     assertWildcardQueryEquals("Term~", false, "Term~0.5");
552     // Range queries:
553
554     // TODO: implement this on QueryParser
555     // Q0002E_INVALID_SYNTAX_CANNOT_PARSE: Syntax Error, cannot parse '[A TO
556     // C]': Lexical error at line 1, column 1. Encountered: "[" (91), after
557     // : ""
558     assertWildcardQueryEquals("[A TO C]", "[a TO c]");
559     assertWildcardQueryEquals("[A TO C]", true, "[a TO c]");
560     assertWildcardQueryEquals("[A TO C]", false, "[A TO C]");
561     // Test suffix queries: first disallow
562     try {
563       assertWildcardQueryEquals("*Term", true, "*term");
564       fail();
565     } catch (QueryNodeException pe) {
566       // expected exception
567     }
568     try {
569       assertWildcardQueryEquals("?Term", true, "?term");
570       fail();
571     } catch (QueryNodeException pe) {
572       // expected exception
573     }
574     // Test suffix queries: then allow
575     assertWildcardQueryEquals("*Term", true, "*term", true);
576     assertWildcardQueryEquals("?Term", true, "?term", true);
577   }
578
579   public void testLeadingWildcardType() throws Exception {
580     StandardQueryParser qp = getParser(null);
581     qp.setAllowLeadingWildcard(true);
582     assertEquals(WildcardQuery.class, qp.parse("t*erm*", "field").getClass());
583     assertEquals(WildcardQuery.class, qp.parse("?term*", "field").getClass());
584     assertEquals(WildcardQuery.class, qp.parse("*term*", "field").getClass());
585   }
586
587   public void testQPA() throws Exception {
588     assertQueryEquals("term term^3.0 term", qpAnalyzer, "term term^3.0 term");
589     assertQueryEquals("term stop^3.0 term", qpAnalyzer, "term term");
590
591     assertQueryEquals("term term term", qpAnalyzer, "term term term");
592     assertQueryEquals("term +stop term", qpAnalyzer, "term term");
593     assertQueryEquals("term -stop term", qpAnalyzer, "term term");
594
595     assertQueryEquals("drop AND (stop) AND roll", qpAnalyzer, "+drop +roll");
596     assertQueryEquals("term +(stop) term", qpAnalyzer, "term term");
597     assertQueryEquals("term -(stop) term", qpAnalyzer, "term term");
598
599     assertQueryEquals("drop AND stop AND roll", qpAnalyzer, "+drop +roll");
600     assertQueryEquals("term phrase term", qpAnalyzer,
601         "term (phrase1 phrase2) term");
602
603     assertQueryEquals("term AND NOT phrase term", qpAnalyzer,
604         "+term -(phrase1 phrase2) term");
605
606     assertQueryEquals("stop^3", qpAnalyzer, "");
607     assertQueryEquals("stop", qpAnalyzer, "");
608     assertQueryEquals("(stop)^3", qpAnalyzer, "");
609     assertQueryEquals("((stop))^3", qpAnalyzer, "");
610     assertQueryEquals("(stop^3)", qpAnalyzer, "");
611     assertQueryEquals("((stop)^3)", qpAnalyzer, "");
612     assertQueryEquals("(stop)", qpAnalyzer, "");
613     assertQueryEquals("((stop))", qpAnalyzer, "");
614     assertTrue(getQuery("term term term", qpAnalyzer) instanceof BooleanQuery);
615     assertTrue(getQuery("term +stop", qpAnalyzer) instanceof TermQuery);
616   }
617
618   public void testRange() throws Exception {
619     assertQueryEquals("[ a TO z]", null, "[a TO z]");
620     assertEquals(MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT, ((TermRangeQuery)getQuery("[ a TO z]", null)).getRewriteMethod());
621
622     StandardQueryParser qp = new StandardQueryParser();
623     
624     qp.setMultiTermRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE);
625     assertEquals(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE,((TermRangeQuery)qp.parse("[ a TO z]", "field")).getRewriteMethod());
626
627     assertQueryEquals("[ a TO z ]", null, "[a TO z]");
628     assertQueryEquals("{ a TO z}", null, "{a TO z}");
629     assertQueryEquals("{ a TO z }", null, "{a TO z}");
630     assertQueryEquals("{ a TO z }^2.0", null, "{a TO z}^2.0");
631     assertQueryEquals("[ a TO z] OR bar", null, "[a TO z] bar");
632     assertQueryEquals("[ a TO z] AND bar", null, "+[a TO z] +bar");
633     assertQueryEquals("( bar blar { a TO z}) ", null, "bar blar {a TO z}");
634     assertQueryEquals("gack ( bar blar { a TO z}) ", null,
635         "gack (bar blar {a TO z})");
636   }
637
638   public void testFarsiRangeCollating() throws Exception {
639     Directory ramDir = newDirectory();
640     IndexWriter iw = new IndexWriter(ramDir, newIndexWriterConfig(TEST_VERSION_CURRENT, new WhitespaceAnalyzer(TEST_VERSION_CURRENT)));
641     Document doc = new Document();
642     doc.add(newField("content", "\u0633\u0627\u0628", Field.Store.YES,
643         Field.Index.NOT_ANALYZED));
644     iw.addDocument(doc);
645     iw.close();
646     IndexSearcher is = new IndexSearcher(ramDir, true);
647
648     StandardQueryParser qp = new StandardQueryParser();
649     qp.setAnalyzer(new WhitespaceAnalyzer(TEST_VERSION_CURRENT));
650
651     // Neither Java 1.4.2 nor 1.5.0 has Farsi Locale collation available in
652     // RuleBasedCollator. However, the Arabic Locale seems to order the
653     // Farsi
654     // characters properly.
655     Collator c = Collator.getInstance(new Locale("ar"));
656     qp.setRangeCollator(c);
657
658     // Unicode order would include U+0633 in [ U+062F - U+0698 ], but Farsi
659     // orders the U+0698 character before the U+0633 character, so the
660     // single
661     // index Term below should NOT be returned by a ConstantScoreRangeQuery
662     // with a Farsi Collator (or an Arabic one for the case when Farsi is
663     // not
664     // supported).
665
666     // Test ConstantScoreRangeQuery
667     qp.setMultiTermRewriteMethod(MultiTermQuery.CONSTANT_SCORE_FILTER_REWRITE);
668     ScoreDoc[] result = is.search(qp.parse("[ \u062F TO \u0698 ]", "content"),
669         null, 1000).scoreDocs;
670     assertEquals("The index Term should not be included.", 0, result.length);
671
672     result = is.search(qp.parse("[ \u0633 TO \u0638 ]", "content"), null, 1000).scoreDocs;
673     assertEquals("The index Term should be included.", 1, result.length);
674
675     // Test RangeQuery
676     qp.setMultiTermRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE);
677     result = is.search(qp.parse("[ \u062F TO \u0698 ]", "content"), null, 1000).scoreDocs;
678     assertEquals("The index Term should not be included.", 0, result.length);
679
680     result = is.search(qp.parse("[ \u0633 TO \u0638 ]", "content"), null, 1000).scoreDocs;
681     assertEquals("The index Term should be included.", 1, result.length);
682
683     is.close();
684     ramDir.close();
685   }
686
687   /** for testing legacy DateField support */
688   private String getLegacyDate(String s) throws Exception {
689     DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
690     return DateField.dateToString(df.parse(s));
691   }
692
693   /** for testing DateTools support */
694   private String getDate(String s, DateTools.Resolution resolution)
695       throws Exception {
696     DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
697     return getDate(df.parse(s), resolution);
698   }
699
700   /** for testing DateTools support */
701   private String getDate(Date d, DateTools.Resolution resolution)
702       throws Exception {
703     if (resolution == null) {
704       return DateField.dateToString(d);
705     } else {
706       return DateTools.dateToString(d, resolution);
707     }
708   }
709   
710   private String escapeDateString(String s) {
711     if (s.contains(" ")) {
712       return "\"" + s + "\"";
713     } else {
714       return s;
715     }
716   }
717
718   private String getLocalizedDate(int year, int month, int day) {
719     DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
720     Calendar calendar = new GregorianCalendar();
721     calendar.clear();
722     calendar.set(year, month, day);
723     calendar.set(Calendar.HOUR_OF_DAY, 23);
724     calendar.set(Calendar.MINUTE, 59);
725     calendar.set(Calendar.SECOND, 59);
726     calendar.set(Calendar.MILLISECOND, 999);
727     return df.format(calendar.getTime());
728   }
729
730   /** for testing legacy DateField support */
731   public void testLegacyDateRange() throws Exception {
732     String startDate = getLocalizedDate(2002, 1, 1);
733     String endDate = getLocalizedDate(2002, 1, 4);
734     Calendar endDateExpected = new GregorianCalendar();
735     endDateExpected.clear();
736     endDateExpected.set(2002, 1, 4, 23, 59, 59);
737     endDateExpected.set(Calendar.MILLISECOND, 999);
738     assertQueryEquals("[ " + escapeDateString(startDate) + " TO " + escapeDateString(endDate) + "]", null, "["
739         + getLegacyDate(startDate) + " TO "
740         + DateField.dateToString(endDateExpected.getTime()) + "]");
741     assertQueryEquals("{  " + escapeDateString(startDate) + "    " + escapeDateString(endDate) + "   }", null, "{"
742         + getLegacyDate(startDate) + " TO " + getLegacyDate(endDate) + "}");
743   }
744
745   public void testDateRange() throws Exception {
746     String startDate = getLocalizedDate(2002, 1, 1);
747     String endDate = getLocalizedDate(2002, 1, 4);
748     Calendar endDateExpected = new GregorianCalendar();
749     endDateExpected.clear();
750     endDateExpected.set(2002, 1, 4, 23, 59, 59);
751     endDateExpected.set(Calendar.MILLISECOND, 999);
752     final String defaultField = "default";
753     final String monthField = "month";
754     final String hourField = "hour";
755     StandardQueryParser qp = new StandardQueryParser();
756
757     // Don't set any date resolution and verify if DateField is used
758     assertDateRangeQueryEquals(qp, defaultField, startDate, endDate,
759         endDateExpected.getTime(), null);
760
761     Map<CharSequence, DateTools.Resolution> dateRes =  new HashMap<CharSequence, DateTools.Resolution>();
762     
763     // set a field specific date resolution    
764     dateRes.put(monthField, DateTools.Resolution.MONTH);
765     qp.setDateResolution(dateRes);
766
767     // DateField should still be used for defaultField
768     assertDateRangeQueryEquals(qp, defaultField, startDate, endDate,
769         endDateExpected.getTime(), null);
770
771     // set default date resolution to MILLISECOND
772     qp.setDateResolution(DateTools.Resolution.MILLISECOND);
773
774     // set second field specific date resolution
775     dateRes.put(hourField, DateTools.Resolution.HOUR);
776     qp.setDateResolution(dateRes);
777
778     // for this field no field specific date resolution has been set,
779     // so verify if the default resolution is used
780     assertDateRangeQueryEquals(qp, defaultField, startDate, endDate,
781         endDateExpected.getTime(), DateTools.Resolution.MILLISECOND);
782
783     // verify if field specific date resolutions are used for these two
784     // fields
785     assertDateRangeQueryEquals(qp, monthField, startDate, endDate,
786         endDateExpected.getTime(), DateTools.Resolution.MONTH);
787
788     assertDateRangeQueryEquals(qp, hourField, startDate, endDate,
789         endDateExpected.getTime(), DateTools.Resolution.HOUR);
790   }
791
792   public void assertDateRangeQueryEquals(StandardQueryParser qp,
793       String field, String startDate, String endDate, Date endDateInclusive,
794       DateTools.Resolution resolution) throws Exception {
795     assertQueryEquals(qp, field, field + ":[" + escapeDateString(startDate) + " TO " + escapeDateString(endDate)
796         + "]", "[" + getDate(startDate, resolution) + " TO "
797         + getDate(endDateInclusive, resolution) + "]");
798     assertQueryEquals(qp, field, field + ":{" + escapeDateString(startDate) + " TO " + escapeDateString(endDate)
799         + "}", "{" + getDate(startDate, resolution) + " TO "
800         + getDate(endDate, resolution) + "}");
801   }
802
803   public void testEscaped() throws Exception {
804     Analyzer a = new MockAnalyzer(random, MockTokenizer.WHITESPACE, false);
805
806     /*
807      * assertQueryEquals("\\[brackets", a, "\\[brackets");
808      * assertQueryEquals("\\[brackets", null, "brackets");
809      * assertQueryEquals("\\\\", a, "\\\\"); assertQueryEquals("\\+blah", a,
810      * "\\+blah"); assertQueryEquals("\\(blah", a, "\\(blah");
811      * 
812      * assertQueryEquals("\\-blah", a, "\\-blah"); assertQueryEquals("\\!blah",
813      * a, "\\!blah"); assertQueryEquals("\\{blah", a, "\\{blah");
814      * assertQueryEquals("\\}blah", a, "\\}blah"); assertQueryEquals("\\:blah",
815      * a, "\\:blah"); assertQueryEquals("\\^blah", a, "\\^blah");
816      * assertQueryEquals("\\[blah", a, "\\[blah"); assertQueryEquals("\\]blah",
817      * a, "\\]blah"); assertQueryEquals("\\\"blah", a, "\\\"blah");
818      * assertQueryEquals("\\(blah", a, "\\(blah"); assertQueryEquals("\\)blah",
819      * a, "\\)blah"); assertQueryEquals("\\~blah", a, "\\~blah");
820      * assertQueryEquals("\\*blah", a, "\\*blah"); assertQueryEquals("\\?blah",
821      * a, "\\?blah"); //assertQueryEquals("foo \\&\\& bar", a,
822      * "foo \\&\\& bar"); //assertQueryEquals("foo \\|| bar", a,
823      * "foo \\|| bar"); //assertQueryEquals("foo \\AND bar", a,
824      * "foo \\AND bar");
825      */
826
827     assertQueryEquals("\\*", a, "*");
828     
829     assertQueryEquals("\\a", a, "a");
830
831     assertQueryEquals("a\\-b:c", a, "a-b:c");
832     assertQueryEquals("a\\+b:c", a, "a+b:c");
833     assertQueryEquals("a\\:b:c", a, "a:b:c");
834     assertQueryEquals("a\\\\b:c", a, "a\\b:c");
835
836     assertQueryEquals("a:b\\-c", a, "a:b-c");
837     assertQueryEquals("a:b\\+c", a, "a:b+c");
838     assertQueryEquals("a:b\\:c", a, "a:b:c");
839     assertQueryEquals("a:b\\\\c", a, "a:b\\c");
840
841     assertQueryEquals("a:b\\-c*", a, "a:b-c*");
842     assertQueryEquals("a:b\\+c*", a, "a:b+c*");
843     assertQueryEquals("a:b\\:c*", a, "a:b:c*");
844
845     assertQueryEquals("a:b\\\\c*", a, "a:b\\c*");
846
847     assertQueryEquals("a:b\\-?c", a, "a:b-?c");
848     assertQueryEquals("a:b\\+?c", a, "a:b+?c");
849     assertQueryEquals("a:b\\:?c", a, "a:b:?c");
850
851     assertQueryEquals("a:b\\\\?c", a, "a:b\\?c");
852
853     assertQueryEquals("a:b\\-c~", a, "a:b-c~0.5");
854     assertQueryEquals("a:b\\+c~", a, "a:b+c~0.5");
855     assertQueryEquals("a:b\\:c~", a, "a:b:c~0.5");
856     assertQueryEquals("a:b\\\\c~", a, "a:b\\c~0.5");
857
858     // TODO: implement Range queries on QueryParser
859     assertQueryEquals("[ a\\- TO a\\+ ]", null, "[a- TO a+]");
860     assertQueryEquals("[ a\\: TO a\\~ ]", null, "[a: TO a~]");
861     assertQueryEquals("[ a\\\\ TO a\\* ]", null, "[a\\ TO a*]");
862
863     assertQueryEquals(
864         "[\"c\\:\\\\temp\\\\\\~foo0.txt\" TO \"c\\:\\\\temp\\\\\\~foo9.txt\"]",
865         a, "[c:\\temp\\~foo0.txt TO c:\\temp\\~foo9.txt]");
866
867     assertQueryEquals("a\\\\\\+b", a, "a\\+b");
868
869     assertQueryEquals("a \\\"b c\\\" d", a, "a \"b c\" d");
870     assertQueryEquals("\"a \\\"b c\\\" d\"", a, "\"a \"b c\" d\"");
871     assertQueryEquals("\"a \\+b c d\"", a, "\"a +b c d\"");
872
873     assertQueryEquals("c\\:\\\\temp\\\\\\~foo.txt", a, "c:\\temp\\~foo.txt");
874
875     assertQueryNodeException("XY\\"); // there must be a character after the
876     // escape char
877
878     // test unicode escaping
879     assertQueryEquals("a\\u0062c", a, "abc");
880     assertQueryEquals("XY\\u005a", a, "XYZ");
881     assertQueryEquals("XY\\u005A", a, "XYZ");
882     assertQueryEquals("\"a \\\\\\u0028\\u0062\\\" c\"", a, "\"a \\(b\" c\"");
883
884     assertQueryNodeException("XY\\u005G"); // test non-hex character in escaped
885     // unicode sequence
886     assertQueryNodeException("XY\\u005"); // test incomplete escaped unicode
887     // sequence
888
889     // Tests bug LUCENE-800
890     assertQueryEquals("(item:\\\\ item:ABCD\\\\)", a, "item:\\ item:ABCD\\");
891     assertQueryNodeException("(item:\\\\ item:ABCD\\\\))"); // unmatched closing
892     // paranthesis
893     assertQueryEquals("\\*", a, "*");
894     assertQueryEquals("\\\\", a, "\\"); // escaped backslash
895
896     assertQueryNodeException("\\"); // a backslash must always be escaped
897
898     // LUCENE-1189
899     assertQueryEquals("(\"a\\\\\") or (\"b\")", a, "a\\ or b");
900   }
901
902   public void testQueryStringEscaping() throws Exception {
903     Analyzer a = new MockAnalyzer(random, MockTokenizer.WHITESPACE, false);
904
905     assertEscapedQueryEquals("a-b:c", a, "a\\-b\\:c");
906     assertEscapedQueryEquals("a+b:c", a, "a\\+b\\:c");
907     assertEscapedQueryEquals("a:b:c", a, "a\\:b\\:c");
908     assertEscapedQueryEquals("a\\b:c", a, "a\\\\b\\:c");
909
910     assertEscapedQueryEquals("a:b-c", a, "a\\:b\\-c");
911     assertEscapedQueryEquals("a:b+c", a, "a\\:b\\+c");
912     assertEscapedQueryEquals("a:b:c", a, "a\\:b\\:c");
913     assertEscapedQueryEquals("a:b\\c", a, "a\\:b\\\\c");
914
915     assertEscapedQueryEquals("a:b-c*", a, "a\\:b\\-c\\*");
916     assertEscapedQueryEquals("a:b+c*", a, "a\\:b\\+c\\*");
917     assertEscapedQueryEquals("a:b:c*", a, "a\\:b\\:c\\*");
918
919     assertEscapedQueryEquals("a:b\\\\c*", a, "a\\:b\\\\\\\\c\\*");
920
921     assertEscapedQueryEquals("a:b-?c", a, "a\\:b\\-\\?c");
922     assertEscapedQueryEquals("a:b+?c", a, "a\\:b\\+\\?c");
923     assertEscapedQueryEquals("a:b:?c", a, "a\\:b\\:\\?c");
924
925     assertEscapedQueryEquals("a:b?c", a, "a\\:b\\?c");
926
927     assertEscapedQueryEquals("a:b-c~", a, "a\\:b\\-c\\~");
928     assertEscapedQueryEquals("a:b+c~", a, "a\\:b\\+c\\~");
929     assertEscapedQueryEquals("a:b:c~", a, "a\\:b\\:c\\~");
930     assertEscapedQueryEquals("a:b\\c~", a, "a\\:b\\\\c\\~");
931
932     assertEscapedQueryEquals("[ a - TO a+ ]", null, "\\[ a \\- TO a\\+ \\]");
933     assertEscapedQueryEquals("[ a : TO a~ ]", null, "\\[ a \\: TO a\\~ \\]");
934     assertEscapedQueryEquals("[ a\\ TO a* ]", null, "\\[ a\\\\ TO a\\* \\]");
935
936     // LUCENE-881
937     assertEscapedQueryEquals("|| abc ||", a, "\\|\\| abc \\|\\|");
938     assertEscapedQueryEquals("&& abc &&", a, "\\&\\& abc \\&\\&");
939   }
940
941   public void testTabNewlineCarriageReturn() throws Exception {
942     assertQueryEqualsDOA("+weltbank +worlbank", null, "+weltbank +worlbank");
943
944     assertQueryEqualsDOA("+weltbank\n+worlbank", null, "+weltbank +worlbank");
945     assertQueryEqualsDOA("weltbank \n+worlbank", null, "+weltbank +worlbank");
946     assertQueryEqualsDOA("weltbank \n +worlbank", null, "+weltbank +worlbank");
947
948     assertQueryEqualsDOA("+weltbank\r+worlbank", null, "+weltbank +worlbank");
949     assertQueryEqualsDOA("weltbank \r+worlbank", null, "+weltbank +worlbank");
950     assertQueryEqualsDOA("weltbank \r +worlbank", null, "+weltbank +worlbank");
951
952     assertQueryEqualsDOA("+weltbank\r\n+worlbank", null, "+weltbank +worlbank");
953     assertQueryEqualsDOA("weltbank \r\n+worlbank", null, "+weltbank +worlbank");
954     assertQueryEqualsDOA("weltbank \r\n +worlbank", null, "+weltbank +worlbank");
955     assertQueryEqualsDOA("weltbank \r \n +worlbank", null,
956         "+weltbank +worlbank");
957
958     assertQueryEqualsDOA("+weltbank\t+worlbank", null, "+weltbank +worlbank");
959     assertQueryEqualsDOA("weltbank \t+worlbank", null, "+weltbank +worlbank");
960     assertQueryEqualsDOA("weltbank \t +worlbank", null, "+weltbank +worlbank");
961   }
962
963   public void testSimpleDAO() throws Exception {
964     assertQueryEqualsDOA("term term term", null, "+term +term +term");
965     assertQueryEqualsDOA("term +term term", null, "+term +term +term");
966     assertQueryEqualsDOA("term term +term", null, "+term +term +term");
967     assertQueryEqualsDOA("term +term +term", null, "+term +term +term");
968     assertQueryEqualsDOA("-term term term", null, "-term +term +term");
969   }
970
971   public void testBoost() throws Exception {
972     StandardAnalyzer oneStopAnalyzer = new StandardAnalyzer(TEST_VERSION_CURRENT, Collections.singleton("on"));
973     StandardQueryParser qp = new StandardQueryParser();
974     qp.setAnalyzer(oneStopAnalyzer);
975
976     Query q = qp.parse("on^1.0", "field");
977     assertNotNull(q);
978     q = qp.parse("\"hello\"^2.0", "field");
979     assertNotNull(q);
980     assertEquals(q.getBoost(), (float) 2.0, (float) 0.5);
981     q = qp.parse("hello^2.0", "field");
982     assertNotNull(q);
983     assertEquals(q.getBoost(), (float) 2.0, (float) 0.5);
984     q = qp.parse("\"on\"^1.0", "field");
985     assertNotNull(q);
986
987     StandardQueryParser qp2 = new StandardQueryParser();
988     qp2.setAnalyzer(new StandardAnalyzer(TEST_VERSION_CURRENT));
989
990     q = qp2.parse("the^3", "field");
991     // "the" is a stop word so the result is an empty query:
992     assertNotNull(q);
993     assertEquals("", q.toString());
994     assertEquals(1.0f, q.getBoost(), 0.01f);
995   }
996
997   public void assertQueryNodeException(String queryString) throws Exception {
998     try {
999       getQuery(queryString, null);
1000     } catch (QueryNodeException expected) {
1001       return;
1002     }
1003     fail("ParseException expected, not thrown");
1004   }
1005
1006   public void testException() throws Exception {
1007     assertQueryNodeException("*leadingWildcard"); // disallowed by default
1008     assertQueryNodeException("\"some phrase");
1009     assertQueryNodeException("(foo bar");
1010     assertQueryNodeException("foo bar))");
1011     assertQueryNodeException("field:term:with:colon some more terms");
1012     assertQueryNodeException("(sub query)^5.0^2.0 plus more");
1013     assertQueryNodeException("secret AND illegal) AND access:confidential");    
1014   }
1015
1016   public void testCustomQueryParserWildcard() {
1017     try {
1018       new QPTestParser(new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).parse("a?t", "contents");
1019       fail("Wildcard queries should not be allowed");
1020     } catch (QueryNodeException expected) {
1021       // expected exception
1022     }
1023   }
1024
1025   public void testCustomQueryParserFuzzy() throws Exception {
1026     try {
1027       new QPTestParser(new MockAnalyzer(random, MockTokenizer.WHITESPACE, false)).parse("xunit~", "contents");
1028       fail("Fuzzy queries should not be allowed");
1029     } catch (QueryNodeException expected) {
1030       // expected exception
1031     }
1032   }
1033
1034   public void testBooleanQuery() throws Exception {
1035     BooleanQuery.setMaxClauseCount(2);
1036     try {
1037       StandardQueryParser qp = new StandardQueryParser();
1038       qp.setAnalyzer(new MockAnalyzer(random, MockTokenizer.WHITESPACE, false));
1039       qp.parse("one two three", "field");
1040       fail("ParseException expected due to too many boolean clauses");
1041     } catch (QueryNodeException expected) {
1042       // too many boolean clauses, so ParseException is expected
1043     }
1044   }
1045
1046   /**
1047    * This test differs from TestPrecedenceQueryParser
1048    */
1049   public void testPrecedence() throws Exception {
1050     StandardQueryParser qp = new StandardQueryParser();
1051     qp.setAnalyzer(new MockAnalyzer(random, MockTokenizer.WHITESPACE, false));
1052
1053     Query query1 = qp.parse("A AND B OR C AND D", "field");
1054     Query query2 = qp.parse("+A +B +C +D", "field");
1055
1056     assertEquals(query1, query2);
1057   }
1058
1059   public void testLocalDateFormat() throws IOException, QueryNodeException {
1060     Directory ramDir = newDirectory();
1061     IndexWriter iw = new IndexWriter(ramDir, newIndexWriterConfig(TEST_VERSION_CURRENT, new WhitespaceAnalyzer(TEST_VERSION_CURRENT)));
1062     addDateDoc("a", 2005, 12, 2, 10, 15, 33, iw);
1063     addDateDoc("b", 2005, 12, 4, 22, 15, 00, iw);
1064     iw.close();
1065     IndexSearcher is = new IndexSearcher(ramDir, true);
1066     assertHits(1, "[12/1/2005 TO 12/3/2005]", is);
1067     assertHits(2, "[12/1/2005 TO 12/4/2005]", is);
1068     assertHits(1, "[12/3/2005 TO 12/4/2005]", is);
1069     assertHits(1, "{12/1/2005 TO 12/3/2005}", is);
1070     assertHits(1, "{12/1/2005 TO 12/4/2005}", is);
1071     assertHits(0, "{12/3/2005 TO 12/4/2005}", is);
1072     is.close();
1073     ramDir.close();
1074   }
1075
1076   public void testStarParsing() throws Exception {
1077     // final int[] type = new int[1];
1078     // StandardQueryParser qp = new StandardQueryParser("field", new
1079     // WhitespaceAnalyzer()) {
1080     // protected Query getWildcardQuery(String field, String termStr) throws
1081     // ParseException {
1082     // // override error checking of superclass
1083     // type[0]=1;
1084     // return new TermQuery(new Term(field,termStr));
1085     // }
1086     // protected Query getPrefixQuery(String field, String termStr) throws
1087     // ParseException {
1088     // // override error checking of superclass
1089     // type[0]=2;
1090     // return new TermQuery(new Term(field,termStr));
1091     // }
1092     //
1093     // protected Query getFieldQuery(String field, String queryText) throws
1094     // ParseException {
1095     // type[0]=3;
1096     // return super.getFieldQuery(field, queryText);
1097     // }
1098     // };
1099     //
1100     // TermQuery tq;
1101     //
1102     // tq = (TermQuery)qp.parse("foo:zoo*");
1103     // assertEquals("zoo",tq.getTerm().text());
1104     // assertEquals(2,type[0]);
1105     //
1106     // tq = (TermQuery)qp.parse("foo:zoo*^2");
1107     // assertEquals("zoo",tq.getTerm().text());
1108     // assertEquals(2,type[0]);
1109     // assertEquals(tq.getBoost(),2,0);
1110     //
1111     // tq = (TermQuery)qp.parse("foo:*");
1112     // assertEquals("*",tq.getTerm().text());
1113     // assertEquals(1,type[0]); // could be a valid prefix query in the
1114     // future too
1115     //
1116     // tq = (TermQuery)qp.parse("foo:*^2");
1117     // assertEquals("*",tq.getTerm().text());
1118     // assertEquals(1,type[0]);
1119     // assertEquals(tq.getBoost(),2,0);
1120     //
1121     // tq = (TermQuery)qp.parse("*:foo");
1122     // assertEquals("*",tq.getTerm().field());
1123     // assertEquals("foo",tq.getTerm().text());
1124     // assertEquals(3,type[0]);
1125     //
1126     // tq = (TermQuery)qp.parse("*:*");
1127     // assertEquals("*",tq.getTerm().field());
1128     // assertEquals("*",tq.getTerm().text());
1129     // assertEquals(1,type[0]); // could be handled as a prefix query in the
1130     // future
1131     //
1132     // tq = (TermQuery)qp.parse("(*:*)");
1133     // assertEquals("*",tq.getTerm().field());
1134     // assertEquals("*",tq.getTerm().text());
1135     // assertEquals(1,type[0]);
1136
1137   }
1138
1139   public void testStopwords() throws Exception {
1140     StandardQueryParser qp = new StandardQueryParser();
1141     qp.setAnalyzer(
1142         new StopAnalyzer(TEST_VERSION_CURRENT, StopFilter.makeStopSet(TEST_VERSION_CURRENT, "the", "foo" )));
1143
1144     Query result = qp.parse("a:the OR a:foo", "a");
1145     assertNotNull("result is null and it shouldn't be", result);
1146     assertTrue("result is not a BooleanQuery", result instanceof BooleanQuery);
1147     assertTrue(((BooleanQuery) result).clauses().size() + " does not equal: "
1148         + 0, ((BooleanQuery) result).clauses().size() == 0);
1149     result = qp.parse("a:woo OR a:the", "a");
1150     assertNotNull("result is null and it shouldn't be", result);
1151     assertTrue("result is not a TermQuery", result instanceof TermQuery);
1152     result = qp.parse(
1153         "(fieldX:xxxxx OR fieldy:xxxxxxxx)^2 AND (fieldx:the OR fieldy:foo)",
1154         "a");
1155     assertNotNull("result is null and it shouldn't be", result);
1156     assertTrue("result is not a BooleanQuery", result instanceof BooleanQuery);
1157     if (VERBOSE)
1158       System.out.println("Result: " + result);
1159     assertTrue(((BooleanQuery) result).clauses().size() + " does not equal: "
1160         + 2, ((BooleanQuery) result).clauses().size() == 2);
1161   }
1162
1163   public void testPositionIncrement() throws Exception {
1164     StandardQueryParser qp = new StandardQueryParser();
1165     qp.setAnalyzer(
1166         new StopAnalyzer(TEST_VERSION_CURRENT, StopFilter.makeStopSet(TEST_VERSION_CURRENT, "the", "in", "are", "this" )));
1167
1168     qp.setEnablePositionIncrements(true);
1169
1170     String qtxt = "\"the words in poisitions pos02578 are stopped in this phrasequery\"";
1171     // 0 2 5 7 8
1172     int expectedPositions[] = { 1, 3, 4, 6, 9 };
1173     PhraseQuery pq = (PhraseQuery) qp.parse(qtxt, "a");
1174     // System.out.println("Query text: "+qtxt);
1175     // System.out.println("Result: "+pq);
1176     Term t[] = pq.getTerms();
1177     int pos[] = pq.getPositions();
1178     for (int i = 0; i < t.length; i++) {
1179       // System.out.println(i+". "+t[i]+"  pos: "+pos[i]);
1180       assertEquals("term " + i + " = " + t[i] + " has wrong term-position!",
1181           expectedPositions[i], pos[i]);
1182     }
1183   }
1184
1185   public void testMatchAllDocs() throws Exception {
1186     StandardQueryParser qp = new StandardQueryParser();
1187     qp.setAnalyzer(new MockAnalyzer(random, MockTokenizer.WHITESPACE, false));
1188
1189     assertEquals(new MatchAllDocsQuery(), qp.parse("*:*", "field"));
1190     assertEquals(new MatchAllDocsQuery(), qp.parse("(*:*)", "field"));
1191     BooleanQuery bq = (BooleanQuery) qp.parse("+*:* -*:*", "field");
1192     assertTrue(bq.getClauses()[0].getQuery() instanceof MatchAllDocsQuery);
1193     assertTrue(bq.getClauses()[1].getQuery() instanceof MatchAllDocsQuery);
1194   }
1195
1196   private void assertHits(int expected, String query, IndexSearcher is)
1197       throws IOException, QueryNodeException {
1198     StandardQueryParser qp = new StandardQueryParser();
1199     qp.setAnalyzer(new MockAnalyzer(random, MockTokenizer.WHITESPACE, false));
1200     qp.setLocale(Locale.ENGLISH);
1201
1202     Query q = qp.parse(query, "date");
1203     ScoreDoc[] hits = is.search(q, null, 1000).scoreDocs;
1204     assertEquals(expected, hits.length);
1205   }
1206
1207   private void addDateDoc(String content, int year, int month, int day,
1208       int hour, int minute, int second, IndexWriter iw) throws IOException {
1209     Document d = new Document();
1210     d.add(newField("f", content, Field.Store.YES, Field.Index.ANALYZED));
1211     Calendar cal = Calendar.getInstance(Locale.ENGLISH);
1212     cal.set(year, month - 1, day, hour, minute, second);
1213     d.add(newField("date", DateField.dateToString(cal.getTime()),
1214         Field.Store.YES, Field.Index.NOT_ANALYZED));
1215     iw.addDocument(d);
1216   }
1217
1218   @Override
1219   public void tearDown() throws Exception {
1220     BooleanQuery.setMaxClauseCount(originalMaxClauses);
1221     super.tearDown();
1222   }
1223
1224   private class CannedTokenStream extends Tokenizer {
1225     private int upto = 0;
1226     private final PositionIncrementAttribute posIncr = addAttribute(PositionIncrementAttribute.class);
1227     private final CharTermAttribute term = addAttribute(CharTermAttribute.class);
1228     
1229     @Override
1230     public boolean incrementToken() {
1231       clearAttributes();
1232       if (upto == 4) {
1233         return false;
1234       }
1235       if (upto == 0) {
1236         posIncr.setPositionIncrement(1);
1237         term.setEmpty().append("a");
1238       } else if (upto == 1) {
1239         posIncr.setPositionIncrement(1);
1240         term.setEmpty().append("b");
1241       } else if (upto == 2) {
1242         posIncr.setPositionIncrement(0);
1243         term.setEmpty().append("c");
1244       } else {
1245         posIncr.setPositionIncrement(0);
1246         term.setEmpty().append("d");
1247       }
1248       upto++;
1249       return true;
1250     }
1251
1252     @Override
1253     public void reset() throws IOException {
1254       super.reset();
1255       this.upto = 0;
1256     }
1257   }
1258
1259   private class CannedAnalyzer extends Analyzer {
1260     @Override
1261     public TokenStream tokenStream(String ignored, Reader alsoIgnored) {
1262       return new CannedTokenStream();
1263     }
1264   }
1265
1266   public void testMultiPhraseQuery() throws Exception {
1267     Directory dir = newDirectory();
1268     IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(TEST_VERSION_CURRENT, new CannedAnalyzer()));
1269     Document doc = new Document();
1270     doc.add(newField("field", "", Field.Store.NO, Field.Index.ANALYZED));
1271     w.addDocument(doc);
1272     IndexReader r = IndexReader.open(w, true);
1273     IndexSearcher s = newSearcher(r);
1274     
1275     Query q = new StandardQueryParser(new CannedAnalyzer()).parse("\"a\"", "field");
1276     assertTrue(q instanceof MultiPhraseQuery);
1277     assertEquals(1, s.search(q, 10).totalHits);
1278     s.close();
1279     r.close();
1280     w.close();
1281     dir.close();
1282   }
1283
1284 }