Index: lucene/src/test/org/apache/lucene/queryParser/TestQueryParser.java =================================================================== --- lucene/src/test/org/apache/lucene/queryParser/TestQueryParser.java (revision 946605) +++ lucene/src/test/org/apache/lucene/queryParser/TestQueryParser.java (working copy) @@ -437,9 +437,9 @@ assertQueryEquals("drop AND stop AND roll", qpAnalyzer, "+drop +roll"); assertQueryEquals("term phrase term", qpAnalyzer, - "term \"phrase1 phrase2\" term"); + "term (phrase1 phrase2) term"); assertQueryEquals("term AND NOT phrase term", qpAnalyzer, - "+term -\"phrase1 phrase2\" term"); + "+term -(phrase1 phrase2) term"); assertQueryEquals("stop^3", qpAnalyzer, ""); assertQueryEquals("stop", qpAnalyzer, ""); assertQueryEquals("(stop)^3", qpAnalyzer, ""); Index: lucene/src/java/org/apache/lucene/queryParser/QueryParser.java =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/QueryParser.java (revision 946605) +++ lucene/src/java/org/apache/lucene/queryParser/QueryParser.java (working copy) @@ -34,6 +34,7 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.WildcardQuery; import org.apache.lucene.util.Version; +import org.apache.lucene.util.VirtualMethod; /** * This class is generated by JavaCC. The most important method is @@ -150,6 +151,18 @@ // for use when constructing RangeQuerys. Collator rangeCollator = null; + private Version matchVersion; + /** @deprecated remove when getFieldQuery is removed */ + private static final VirtualMethod getFieldQueryMethod = + new VirtualMethod(QueryParser.class, "getFieldQuery", String.class, String.class); + /** @deprecated remove when getFieldQuery is removed */ + private static final VirtualMethod getFieldQueryWithQuotedMethod = + new VirtualMethod(QueryParser.class, "getFieldQuery", String.class, String.class, boolean.class); + /** @deprecated remove when getFieldQuery is removed */ + final boolean hasNewAPI = + VirtualMethod.compareImplementationDistance(getClass(), + getFieldQueryWithQuotedMethod, getFieldQueryMethod) >= 0; // its ok for both to be overridden + /** The default operator for parsing queries. * Use {@link QueryParser#setDefaultOperator} to change it. */ @@ -162,6 +175,7 @@ */ public QueryParser(Version matchVersion, String f, Analyzer a) { this(new FastCharStream(new StringReader(""))); + this.matchVersion = matchVersion; analyzer = a; field = f; if (matchVersion.onOrAfter(Version.LUCENE_29)) { @@ -506,11 +520,19 @@ throw new RuntimeException("Clause cannot be both required and prohibited"); } + /** + * @deprecated Use {@link #getFieldQuery(String,String,boolean)} instead. + */ + @Deprecated + protected Query getFieldQuery(String field, String queryText) throws ParseException { + // treat the text as if it was quoted, to drive phrase logic with old versions. + return getFieldQuery(field, queryText, true); + } /** * @exception ParseException throw in overridden method to disallow */ - protected Query getFieldQuery(String field, String queryText) throws ParseException { + protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException { // Use the analyzer to get all the tokens, and then build a TermQuery, // PhraseQuery, or nothing based on the term count @@ -587,10 +609,14 @@ } return newTermQuery(new Term(field, term)); } else { - if (severalTokensAtSamePosition) { - if (positionCount == 1) { + if (severalTokensAtSamePosition || !quoted) { + if (positionCount == 1 || !quoted) { // no phrase query: - BooleanQuery q = newBooleanQuery(true); + BooleanQuery q = newBooleanQuery(positionCount == 1); + + BooleanClause.Occur occur = positionCount > 1 && operator == AND_OPERATOR ? + BooleanClause.Occur.MUST : BooleanClause.Occur.SHOULD; + for (int i = 0; i < numTokens; i++) { String term = null; try { @@ -603,7 +629,7 @@ Query currentQuery = newTermQuery( new Term(field, term)); - q.add(currentQuery, BooleanClause.Occur.SHOULD); + q.add(currentQuery, occur); } return q; } @@ -682,7 +708,7 @@ /** - * Base implementation delegates to {@link #getFieldQuery(String,String)}. + * Base implementation delegates to {@link #getFieldQuery(String,String,boolean)}. * This method may be overridden, for example, to return * a SpanNearQuery instead of a PhraseQuery. * @@ -690,7 +716,7 @@ */ protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException { - Query query = getFieldQuery(field, queryText); + Query query = hasNewAPI ? getFieldQuery(field, queryText, true) : getFieldQuery(field, queryText); if (query instanceof PhraseQuery) { ((PhraseQuery) query).setSlop(slop); @@ -1359,7 +1385,7 @@ } q = getFuzzyQuery(field, termImage,fms); } else { - q = getFieldQuery(field, termImage); + q = hasNewAPI ? getFieldQuery(field, termImage, !matchVersion.onOrAfter(Version.LUCENE_31)) : getFieldQuery(field, termImage); } break; case RANGEIN_START: @@ -1528,6 +1554,12 @@ finally { jj_save(0, xla); } } + private boolean jj_3R_3() { + if (jj_scan_token(STAR)) return true; + if (jj_scan_token(COLON)) return true; + return false; + } + private boolean jj_3R_2() { if (jj_scan_token(TERM)) return true; if (jj_scan_token(COLON)) return true; @@ -1544,12 +1576,6 @@ return false; } - private boolean jj_3R_3() { - if (jj_scan_token(STAR)) return true; - if (jj_scan_token(COLON)) return true; - return false; - } - /** Generated Token Manager. */ public QueryParserTokenManager token_source; /** Current token. */ Index: lucene/src/java/org/apache/lucene/queryParser/QueryParser.jj =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/QueryParser.jj (revision 946605) +++ lucene/src/java/org/apache/lucene/queryParser/QueryParser.jj (working copy) @@ -58,6 +58,7 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.WildcardQuery; import org.apache.lucene.util.Version; +import org.apache.lucene.util.VirtualMethod; /** * This class is generated by JavaCC. The most important method is @@ -174,6 +175,18 @@ // for use when constructing RangeQuerys. Collator rangeCollator = null; + private Version matchVersion; + /** @deprecated remove when getFieldQuery is removed */ + private static final VirtualMethod getFieldQueryMethod = + new VirtualMethod(QueryParser.class, "getFieldQuery", String.class, String.class); + /** @deprecated remove when getFieldQuery is removed */ + private static final VirtualMethod getFieldQueryWithQuotedMethod = + new VirtualMethod(QueryParser.class, "getFieldQuery", String.class, String.class, boolean.class); + /** @deprecated remove when getFieldQuery is removed */ + final boolean hasNewAPI = + VirtualMethod.compareImplementationDistance(getClass(), + getFieldQueryWithQuotedMethod, getFieldQueryMethod) >= 0; // its ok for both to be overridden + /** The default operator for parsing queries. * Use {@link QueryParser#setDefaultOperator} to change it. */ @@ -186,6 +199,7 @@ */ public QueryParser(Version matchVersion, String f, Analyzer a) { this(new FastCharStream(new StringReader(""))); + this.matchVersion = matchVersion; analyzer = a; field = f; if (matchVersion.onOrAfter(Version.LUCENE_29)) { @@ -530,11 +544,19 @@ throw new RuntimeException("Clause cannot be both required and prohibited"); } + /** + * @deprecated Use {@link #getFieldQuery(String,String,boolean)} instead. + */ + @Deprecated + protected Query getFieldQuery(String field, String queryText) throws ParseException { + // treat the text as if it was quoted, to drive phrase logic with old versions. + return getFieldQuery(field, queryText, true); + } /** * @exception ParseException throw in overridden method to disallow */ - protected Query getFieldQuery(String field, String queryText) throws ParseException { + protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException { // Use the analyzer to get all the tokens, and then build a TermQuery, // PhraseQuery, or nothing based on the term count @@ -611,10 +633,14 @@ } return newTermQuery(new Term(field, term)); } else { - if (severalTokensAtSamePosition) { - if (positionCount == 1) { + if (severalTokensAtSamePosition || !quoted) { + if (positionCount == 1 || !quoted) { // no phrase query: - BooleanQuery q = newBooleanQuery(true); + BooleanQuery q = newBooleanQuery(positionCount == 1); + + BooleanClause.Occur occur = positionCount > 1 && operator == AND_OPERATOR ? + BooleanClause.Occur.MUST : BooleanClause.Occur.SHOULD; + for (int i = 0; i < numTokens; i++) { String term = null; try { @@ -627,7 +653,7 @@ Query currentQuery = newTermQuery( new Term(field, term)); - q.add(currentQuery, BooleanClause.Occur.SHOULD); + q.add(currentQuery, occur); } return q; } @@ -706,7 +732,7 @@ /** - * Base implementation delegates to {@link #getFieldQuery(String,String)}. + * Base implementation delegates to {@link #getFieldQuery(String,String,boolean)}. * This method may be overridden, for example, to return * a SpanNearQuery instead of a PhraseQuery. * @@ -714,7 +740,7 @@ */ protected Query getFieldQuery(String field, String queryText, int slop) throws ParseException { - Query query = getFieldQuery(field, queryText); + Query query = hasNewAPI ? getFieldQuery(field, queryText, true) : getFieldQuery(field, queryText); if (query instanceof PhraseQuery) { ((PhraseQuery) query).setSlop(slop); @@ -1314,7 +1340,7 @@ } q = getFuzzyQuery(field, termImage,fms); } else { - q = getFieldQuery(field, termImage); + q = hasNewAPI ? getFieldQuery(field, termImage, !matchVersion.onOrAfter(Version.LUCENE_31)) : getFieldQuery(field, termImage); } } | ( ( goop1=|goop1= ) Index: lucene/src/java/org/apache/lucene/queryParser/QueryParserTokenManager.java =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/QueryParserTokenManager.java (revision 946605) +++ lucene/src/java/org/apache/lucene/queryParser/QueryParserTokenManager.java (working copy) @@ -32,6 +32,7 @@ import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.WildcardQuery; import org.apache.lucene.util.Version; +import org.apache.lucene.util.VirtualMethod; /** Token Manager. */ public class QueryParserTokenManager implements QueryParserConstants Index: lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/ext/ExtendableQueryParser.java =================================================================== --- lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/ext/ExtendableQueryParser.java (revision 946605) +++ lucene/contrib/queryparser/src/java/org/apache/lucene/queryParser/ext/ExtendableQueryParser.java (working copy) @@ -126,7 +126,7 @@ } @Override - protected Query getFieldQuery(final String field, final String queryText) + protected Query getFieldQuery(final String field, final String queryText, boolean quoted) throws ParseException { final Pair splitExtensionField = this.extensions .splitExtensionField(defaultField, field); @@ -136,7 +136,12 @@ return extension.parse(new ExtensionQuery(this, splitExtensionField.cur, queryText)); } - return super.getFieldQuery(field, queryText); + return super.getFieldQuery(field, queryText, quoted); } - + + @Override + protected Query getFieldQuery(String field, String queryText) throws ParseException { + // for back compat + return getFieldQuery(field, queryText, true); + } } Index: lucene/contrib/highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java =================================================================== --- lucene/contrib/highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java (revision 946605) +++ lucene/contrib/highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java (working copy) @@ -55,7 +55,7 @@ } public void testFlattenTermAndPhrase2gram() throws Exception { - Query query = paB.parse( "AA AND BCD OR EFGH" ); + Query query = paB.parse( "AA AND \"BCD\" OR \"EFGH\"" ); FieldQuery fq = new FieldQuery( query, true, true ); Set flatQueries = new HashSet(); fq.flatten( query, flatQueries ); @@ -679,7 +679,7 @@ } public void testQueryPhraseMapOverlap2gram() throws Exception { - Query query = paB.parse( "abc AND bcd" ); + Query query = paB.parse( "\"abc\" AND \"bcd\"" ); // phraseHighlight = true, fieldMatch = true FieldQuery fq = new FieldQuery( query, true, true ); Index: lucene/backwards/src/test/org/apache/lucene/queryParser/TestQueryParser.java =================================================================== --- lucene/backwards/src/test/org/apache/lucene/queryParser/TestQueryParser.java (revision 946605) +++ lucene/backwards/src/test/org/apache/lucene/queryParser/TestQueryParser.java (working copy) @@ -133,7 +133,7 @@ public static class QPTestParser extends QueryParser { public QPTestParser(String f, Analyzer a) { - super(Version.LUCENE_CURRENT, f, a); + super(TEST_VERSION_CURRENT, f, a); } @Override @@ -158,7 +158,7 @@ public QueryParser getParser(Analyzer a) throws Exception { if (a == null) a = new SimpleAnalyzer(); - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", a); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", a); qp.setDefaultOperator(QueryParser.OR_OPERATOR); return qp; } @@ -228,7 +228,7 @@ throws Exception { if (a == null) a = new SimpleAnalyzer(); - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", a); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", a); qp.setDefaultOperator(QueryParser.AND_OPERATOR); return qp.parse(query); } @@ -300,7 +300,7 @@ assertQueryEquals("+title:(dog OR cat) -author:\"bob dole\"", null, "+(title:dog title:cat) -author:\"bob dole\""); - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", new StandardAnalyzer(org.apache.lucene.util.Version.LUCENE_CURRENT)); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new StandardAnalyzer(TEST_VERSION_CURRENT)); // make sure OR is the default: assertEquals(QueryParser.OR_OPERATOR, qp.getDefaultOperator()); qp.setDefaultOperator(QueryParser.AND_OPERATOR); @@ -330,7 +330,7 @@ assertQueryEquals("term 1.0 1 2", null, "term"); assertQueryEquals("term term1 term2", null, "term term term"); - Analyzer a = new StandardAnalyzer(org.apache.lucene.util.Version.LUCENE_CURRENT); + Analyzer a = new StandardAnalyzer(TEST_VERSION_CURRENT); assertQueryEquals("3", a, "3"); assertQueryEquals("term 1.0 1 2", a, "term 1.0 1 2"); assertQueryEquals("term term1 term2", a, "term term1 term2"); @@ -455,7 +455,7 @@ assertQueryEquals("[ a TO z]", null, "[a TO z]"); assertEquals(MultiTermQuery.CONSTANT_SCORE_AUTO_REWRITE_DEFAULT, ((TermRangeQuery)getQuery("[ a TO z]", null)).getRewriteMethod()); - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", new SimpleAnalyzer()); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new SimpleAnalyzer()); qp.setMultiTermRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE); assertEquals(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE,((TermRangeQuery)qp.parse("[ a TO z]")).getRewriteMethod()); @@ -481,7 +481,7 @@ iw.close(); IndexSearcher is = new IndexSearcher(ramDir, true); - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "content", new WhitespaceAnalyzer()); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "content", new WhitespaceAnalyzer()); // Neither Java 1.4.2 nor 1.5.0 has Farsi Locale collation available in // RuleBasedCollator. However, the Arabic Locale seems to order the Farsi @@ -578,7 +578,7 @@ final String defaultField = "default"; final String monthField = "month"; final String hourField = "hour"; - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", new SimpleAnalyzer()); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new SimpleAnalyzer()); // Don't set any date resolution and verify if DateField is used assertDateRangeQueryEquals(qp, defaultField, startDate, endDate, @@ -800,8 +800,8 @@ throws Exception { Set stopWords = new HashSet(1); stopWords.add("on"); - StandardAnalyzer oneStopAnalyzer = new StandardAnalyzer(org.apache.lucene.util.Version.LUCENE_CURRENT, stopWords); - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", oneStopAnalyzer); + StandardAnalyzer oneStopAnalyzer = new StandardAnalyzer(TEST_VERSION_CURRENT, stopWords); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", oneStopAnalyzer); Query q = qp.parse("on^1.0"); assertNotNull(q); q = qp.parse("\"hello\"^2.0"); @@ -813,7 +813,7 @@ q = qp.parse("\"on\"^1.0"); assertNotNull(q); - QueryParser qp2 = new QueryParser(Version.LUCENE_CURRENT, "field", new StandardAnalyzer(org.apache.lucene.util.Version.LUCENE_CURRENT)); + QueryParser qp2 = new QueryParser(TEST_VERSION_CURRENT, "field", new StandardAnalyzer(TEST_VERSION_CURRENT)); q = qp2.parse("the^3"); // "the" is a stop word so the result is an empty query: assertNotNull(q); @@ -861,7 +861,7 @@ public void testBooleanQuery() throws Exception { BooleanQuery.setMaxClauseCount(2); try { - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", new WhitespaceAnalyzer()); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new WhitespaceAnalyzer()); qp.parse("one two three"); fail("ParseException expected due to too many boolean clauses"); } catch (ParseException expected) { @@ -873,7 +873,7 @@ * This test differs from TestPrecedenceQueryParser */ public void testPrecedence() throws Exception { - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", new WhitespaceAnalyzer()); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new WhitespaceAnalyzer()); Query query1 = qp.parse("A AND B OR C AND D"); Query query2 = qp.parse("+A +B +C +D"); assertEquals(query1, query2); @@ -897,7 +897,7 @@ public void testStarParsing() throws Exception { final int[] type = new int[1]; - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", new WhitespaceAnalyzer()) { + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new WhitespaceAnalyzer()) { @Override protected Query getWildcardQuery(String field, String termStr) throws ParseException { // override error checking of superclass @@ -956,7 +956,7 @@ } public void testStopwords() throws Exception { - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "a", new StopAnalyzer(Version.LUCENE_CURRENT, StopFilter.makeStopSet("the", "foo"))); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "a", new StopAnalyzer(TEST_VERSION_CURRENT, StopFilter.makeStopSet("the", "foo"))); Query result = qp.parse("a:the OR a:foo"); assertNotNull("result is null and it shouldn't be", result); assertTrue("result is not a BooleanQuery", result instanceof BooleanQuery); @@ -972,7 +972,7 @@ } public void testPositionIncrement() throws Exception { - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "a", new StopAnalyzer(Version.LUCENE_CURRENT, StopFilter.makeStopSet("the", "in", "are", "this"))); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "a", new StopAnalyzer(TEST_VERSION_CURRENT, StopFilter.makeStopSet("the", "in", "are", "this"))); qp.setEnablePositionIncrements(true); String qtxt = "\"the words in poisitions pos02578 are stopped in this phrasequery\""; // 0 2 5 7 8 @@ -989,7 +989,7 @@ } public void testMatchAllDocs() throws Exception { - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "field", new WhitespaceAnalyzer()); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "field", new WhitespaceAnalyzer()); assertEquals(new MatchAllDocsQuery(), qp.parse("*:*")); assertEquals(new MatchAllDocsQuery(), qp.parse("(*:*)")); BooleanQuery bq = (BooleanQuery)qp.parse("+*:* -*:*"); @@ -998,7 +998,7 @@ } private void assertHits(int expected, String query, IndexSearcher is) throws ParseException, IOException { - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "date", new WhitespaceAnalyzer()); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "date", new WhitespaceAnalyzer()); qp.setLocale(Locale.ENGLISH); Query q = qp.parse(query); ScoreDoc[] hits = is.search(q, null, 1000).scoreDocs; @@ -1026,7 +1026,7 @@ // "match" public void testPositionIncrements() throws Exception { Directory dir = new MockRAMDirectory(); - Analyzer a = new StandardAnalyzer(Version.LUCENE_CURRENT); + Analyzer a = new StandardAnalyzer(TEST_VERSION_CURRENT); IndexWriter w = new IndexWriter(dir, a, IndexWriter.MaxFieldLength.UNLIMITED); Document doc = new Document(); doc.add(new Field("f", "the wizard of ozzy", Field.Store.NO, Field.Index.ANALYZED)); @@ -1034,7 +1034,7 @@ IndexReader r = w.getReader(); w.close(); IndexSearcher s = new IndexSearcher(r); - QueryParser qp = new QueryParser(Version.LUCENE_CURRENT, "f", a); + QueryParser qp = new QueryParser(TEST_VERSION_CURRENT, "f", a); Query q = qp.parse("\"wizard of ozzy\""); assertEquals(1, s.search(q, 1).totalHits); r.close(); Index: lucene/backwards/src/test/org/apache/lucene/util/LuceneTestCase.java =================================================================== --- lucene/backwards/src/test/org/apache/lucene/util/LuceneTestCase.java (revision 946605) +++ lucene/backwards/src/test/org/apache/lucene/util/LuceneTestCase.java (working copy) @@ -31,6 +31,7 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.FieldCache; import org.apache.lucene.search.FieldCache.CacheEntry; +import org.apache.lucene.util.Version; import org.apache.lucene.util.FieldCacheSanityChecker.Insanity; /** @@ -53,6 +54,8 @@ */ public abstract class LuceneTestCase extends TestCase { + /** Use this constant when creating Analyzers and any other version-dependent stuff. */ + public static final Version TEST_VERSION_CURRENT = Version.LUCENE_30; private int savedBoolMaxClauseCount;