Index: lucene/src/java/org/apache/lucene/queryParser/TokenMgrError.java =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/TokenMgrError.java (revision 1227631) +++ lucene/src/java/org/apache/lucene/queryParser/TokenMgrError.java (working copy) @@ -138,4 +138,4 @@ this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); } } -/* JavaCC - OriginalChecksum=1c94e13236c7e0121e49427992341ee3 (do not edit this line) */ +/* JavaCC - OriginalChecksum=334e679cf1a88b3070bb8e3d80ee3f5e (do not edit this line) */ Index: lucene/src/java/org/apache/lucene/queryParser/QueryParser.jj =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/QueryParser.jj (revision 1227631) +++ lucene/src/java/org/apache/lucene/queryParser/QueryParser.jj (working copy) @@ -795,13 +795,25 @@ boolean inclusive) throws ParseException { if (lowercaseExpandedTerms) { - part1 = part1.toLowerCase(); - part2 = part2.toLowerCase(); + part1 = part1==null ? null : part1.toLowerCase(); + part2 = part2==null ? null : part2.toLowerCase(); } + DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); + df.setLenient(true); + DateTools.Resolution resolution = getDateResolution(field); try { - DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); - df.setLenient(true); Date d1 = df.parse(part1); + if (resolution == null) { + // no default or field specific date resolution has been set, + // use deprecated DateField to maintain compatibility with + // pre-1.9 Lucene versions. + part1 = DateField.dateToString(d1); + } else { + part1 = DateTools.dateToString(d1, resolution); + } + } catch (Exception e) { } + + try { Date d2 = df.parse(part2); if (inclusive) { // The user can only specify the date, not the time, so make sure @@ -815,19 +827,15 @@ cal.set(Calendar.MILLISECOND, 999); d2 = cal.getTime(); } - DateTools.Resolution resolution = getDateResolution(field); if (resolution == null) { // no default or field specific date resolution has been set, // use deprecated DateField to maintain compatibility with // pre-1.9 Lucene versions. - part1 = DateField.dateToString(d1); part2 = DateField.dateToString(d2); } else { - part1 = DateTools.dateToString(d1, resolution); part2 = DateTools.dateToString(d2, resolution); } - } - catch (Exception e) { } + } catch (Exception e) { } return newRangeQuery(field, part1, part2, inclusive); } @@ -899,6 +907,42 @@ return new FuzzyQuery(term,minimumSimilarity,prefixLength); } + protected String analyzeMultitermTerm(String field, String part, Analyzer analyzerIn) { + TokenStream source; + + if (analyzerIn == null) analyzerIn = analyzer; + + try { + source = analyzerIn.tokenStream(field, new StringReader(part)); + source.reset(); + } catch (IOException e) { + throw new RuntimeException("Unable to initialize TokenStream to analyze multiTerm term: " + part, e); + } + + CharTermAttribute termAtt = source.getAttribute(CharTermAttribute.class); + String termRet = ""; + + try { + if (!source.incrementToken()) + throw new IllegalArgumentException("analyzer returned no terms for multiTerm term: " + part); + termRet = termAtt.toString(); + if (source.incrementToken()) + throw new IllegalArgumentException("analyzer returned too many terms for multiTerm term: " + part); + } catch (IOException e) { + throw new RuntimeException("error analyzing range part: " + part, e); + } + + try { + source.end(); + source.close(); + } catch (IOException e) { + throw new RuntimeException("Unable to end & close TokenStream after analyzing multiTerm term: " + part, e); + } + + return termRet; + } + + /** * Builds a new TermRangeQuery instance * @param field Field @@ -1381,27 +1425,43 @@ ) [ boost= ] { - if (goop1.kind == RANGEIN_QUOTED) { - goop1.image = goop1.image.substring(1, goop1.image.length()-1); + { + boolean startOpen=false; + boolean endOpen=false; + if (goop1.kind == RANGEIN_QUOTED) { + goop1.image = goop1.image.substring(1, goop1.image.length()-1); + } else if ("*".equals(goop1.image)) { + startOpen = true; + } + if (goop2.kind == RANGEIN_QUOTED) { + goop2.image = goop2.image.substring(1, goop2.image.length()-1); + } else if ("*".equals(goop2.image)) { + endOpen = true; + } + q = getRangeQuery(field, startOpen ? null : discardEscapeChar(goop1.image), endOpen ? null : discardEscapeChar(goop2.image), true); } - if (goop2.kind == RANGEIN_QUOTED) { - goop2.image = goop2.image.substring(1, goop2.image.length()-1); - } - q = getRangeQuery(field, discardEscapeChar(goop1.image), discardEscapeChar(goop2.image), true); } | ( ( goop1=|goop1= ) [ ] ( goop2=|goop2= ) ) [ boost= ] { - if (goop1.kind == RANGEEX_QUOTED) { - goop1.image = goop1.image.substring(1, goop1.image.length()-1); + { + boolean startOpen=false; + boolean endOpen=false; + if (goop1.kind == RANGEEX_QUOTED) { + goop1.image = goop1.image.substring(1, goop1.image.length()-1); + } else if ("*".equals(goop1.image)) { + startOpen = true; + } + if (goop2.kind == RANGEEX_QUOTED) { + goop2.image = goop2.image.substring(1, goop2.image.length()-1); + } else if ("*".equals(goop2.image)) { + endOpen = true; + } + + q = getRangeQuery(field, startOpen ? null : discardEscapeChar(goop1.image), endOpen ? null : discardEscapeChar(goop2.image), false); } - if (goop2.kind == RANGEEX_QUOTED) { - goop2.image = goop2.image.substring(1, goop2.image.length()-1); - } - - q = getRangeQuery(field, discardEscapeChar(goop1.image), discardEscapeChar(goop2.image), false); } | term= [ fuzzySlop= ] Index: lucene/src/java/org/apache/lucene/queryParser/Token.java =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/Token.java (revision 1227631) +++ lucene/src/java/org/apache/lucene/queryParser/Token.java (working copy) @@ -121,4 +121,4 @@ } } -/* JavaCC - OriginalChecksum=c147cc166a7cf8812c7c39bc8c5eb868 (do not edit this line) */ +/* JavaCC - OriginalChecksum=37b1923f964a5a434f5ea3d6952ff200 (do not edit this line) */ Index: lucene/src/java/org/apache/lucene/queryParser/QueryParser.java =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/QueryParser.java (revision 1227631) +++ lucene/src/java/org/apache/lucene/queryParser/QueryParser.java (working copy) @@ -33,7 +33,6 @@ import org.apache.lucene.search.TermRangeQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.WildcardQuery; -import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.Version; import org.apache.lucene.util.VirtualMethod; @@ -772,13 +771,25 @@ boolean inclusive) throws ParseException { if (lowercaseExpandedTerms) { - part1 = part1.toLowerCase(); - part2 = part2.toLowerCase(); + part1 = part1==null ? null : part1.toLowerCase(); + part2 = part2==null ? null : part2.toLowerCase(); } + DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); + df.setLenient(true); + DateTools.Resolution resolution = getDateResolution(field); try { - DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale); - df.setLenient(true); Date d1 = df.parse(part1); + if (resolution == null) { + // no default or field specific date resolution has been set, + // use deprecated DateField to maintain compatibility with + // pre-1.9 Lucene versions. + part1 = DateField.dateToString(d1); + } else { + part1 = DateTools.dateToString(d1, resolution); + } + } catch (Exception e) { } + + try { Date d2 = df.parse(part2); if (inclusive) { // The user can only specify the date, not the time, so make sure @@ -792,19 +803,15 @@ cal.set(Calendar.MILLISECOND, 999); d2 = cal.getTime(); } - DateTools.Resolution resolution = getDateResolution(field); if (resolution == null) { // no default or field specific date resolution has been set, // use deprecated DateField to maintain compatibility with // pre-1.9 Lucene versions. - part1 = DateField.dateToString(d1); part2 = DateField.dateToString(d2); } else { - part1 = DateTools.dateToString(d1, resolution); part2 = DateTools.dateToString(d2, resolution); } - } - catch (Exception e) { } + } catch (Exception e) { } return newRangeQuery(field, part1, part2, inclusive); } @@ -911,6 +918,7 @@ return termRet; } + /** * Builds a new TermRangeQuery instance * @param field Field @@ -1501,13 +1509,21 @@ jj_la1[15] = jj_gen; ; } - if (goop1.kind == RANGEIN_QUOTED) { - goop1.image = goop1.image.substring(1, goop1.image.length()-1); + { + boolean startOpen=false; + boolean endOpen=false; + if (goop1.kind == RANGEIN_QUOTED) { + goop1.image = goop1.image.substring(1, goop1.image.length()-1); + } else if ("*".equals(goop1.image)) { + startOpen = true; + } + if (goop2.kind == RANGEIN_QUOTED) { + goop2.image = goop2.image.substring(1, goop2.image.length()-1); + } else if ("*".equals(goop2.image)) { + endOpen = true; + } + q = getRangeQuery(field, startOpen ? null : discardEscapeChar(goop1.image), endOpen ? null : discardEscapeChar(goop2.image), true); } - if (goop2.kind == RANGEIN_QUOTED) { - goop2.image = goop2.image.substring(1, goop2.image.length()-1); - } - q = getRangeQuery(field, discardEscapeChar(goop1.image), discardEscapeChar(goop2.image), true); break; case RANGEEX_START: jj_consume_token(RANGEEX_START); @@ -1553,14 +1569,22 @@ jj_la1[19] = jj_gen; ; } - if (goop1.kind == RANGEEX_QUOTED) { - goop1.image = goop1.image.substring(1, goop1.image.length()-1); + { + boolean startOpen=false; + boolean endOpen=false; + if (goop1.kind == RANGEEX_QUOTED) { + goop1.image = goop1.image.substring(1, goop1.image.length()-1); + } else if ("*".equals(goop1.image)) { + startOpen = true; + } + if (goop2.kind == RANGEEX_QUOTED) { + goop2.image = goop2.image.substring(1, goop2.image.length()-1); + } else if ("*".equals(goop2.image)) { + endOpen = true; + } + + q = getRangeQuery(field, startOpen ? null : discardEscapeChar(goop1.image), endOpen ? null : discardEscapeChar(goop2.image), false); } - if (goop2.kind == RANGEEX_QUOTED) { - goop2.image = goop2.image.substring(1, goop2.image.length()-1); - } - - q = getRangeQuery(field, discardEscapeChar(goop1.image), discardEscapeChar(goop2.image), false); break; case QUOTED: term = jj_consume_token(QUOTED); Index: lucene/src/java/org/apache/lucene/queryParser/ParseException.java =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/ParseException.java (revision 1227631) +++ lucene/src/java/org/apache/lucene/queryParser/ParseException.java (working copy) @@ -195,4 +195,4 @@ } } -/* JavaCC - OriginalChecksum=c7631a240f7446940695eac31d9483ca (do not edit this line) */ +/* JavaCC - OriginalChecksum=c63b396885c4ff44d7aa48d3feae60cd (do not edit this line) */ Index: lucene/src/java/org/apache/lucene/queryParser/CharStream.java =================================================================== --- lucene/src/java/org/apache/lucene/queryParser/CharStream.java (revision 1227631) +++ lucene/src/java/org/apache/lucene/queryParser/CharStream.java (working copy) @@ -109,4 +109,4 @@ void Done(); } -/* JavaCC - OriginalChecksum=32a89423891f765dde472f7ef0e3ef7b (do not edit this line) */ +/* JavaCC - OriginalChecksum=a83909a2403f969f94d18375f9f143e4 (do not edit this line) */ Index: lucene/src/java/org/apache/lucene/search/TermRangeQuery.java =================================================================== --- lucene/src/java/org/apache/lucene/search/TermRangeQuery.java (revision 1227631) +++ lucene/src/java/org/apache/lucene/search/TermRangeQuery.java (working copy) @@ -145,9 +145,9 @@ buffer.append(":"); } buffer.append(includeLower ? '[' : '{'); - buffer.append(lowerTerm != null ? lowerTerm : "*"); + buffer.append(lowerTerm != null ? ("*".equals(lowerTerm) ? "\\*" : lowerTerm) : "*"); buffer.append(" TO "); - buffer.append(upperTerm != null ? upperTerm : "*"); + buffer.append(upperTerm != null ? ("*".equals(upperTerm) ? "\\*" : upperTerm) : "*"); buffer.append(includeUpper ? ']' : '}'); buffer.append(ToStringUtils.boost(getBoost())); return buffer.toString(); Index: lucene/src/test-framework/java/org/apache/lucene/queryParser/QueryParserTestBase.java =================================================================== --- lucene/src/test-framework/java/org/apache/lucene/queryParser/QueryParserTestBase.java (revision 1227631) +++ lucene/src/test-framework/java/org/apache/lucene/queryParser/QueryParserTestBase.java (working copy) @@ -524,6 +524,11 @@ assertQueryEquals("[ a TO z] AND bar", null, "+[a TO z] +bar"); assertQueryEquals("( bar blar { a TO z}) ", null, "bar blar {a TO z}"); assertQueryEquals("gack ( bar blar { a TO z}) ", null, "gack (bar blar {a TO z})"); + + assertQueryEquals("[* TO Z]",null,"[* TO z]"); + assertQueryEquals("[A TO *]",null,"[a TO *]"); + assertQueryEquals("[* TO *]",null,"[* TO *]"); + assertQueryEquals("[\\* TO \"*\"]",null,"[\\* TO \\*]"); } public void testFarsiRangeCollating() throws Exception { Index: lucene/CHANGES.txt =================================================================== --- lucene/CHANGES.txt (revision 1227631) +++ lucene/CHANGES.txt (working copy) @@ -75,6 +75,12 @@ * LUCENE-3634: IndexReader's static main method was moved to a new tool, CompoundFileExtractor, in contrib/misc. (Robert Muir, Mike McCandless) + +* LUCENE-995: The QueryParser now interprets * as an open end for range + queries. Literal asterisks may be represented by quoting or escaping + (i.e. \* or "*") Custom QueryParser subclasses overriding getRangeQuery() + will be passed null for any open endpoint. (Ingo Renner, Adriano + Crestani, yonik, Mike McCandless Bug fixes Index: solr/core/src/java/org/apache/solr/search/SolrQueryParser.java =================================================================== --- solr/core/src/java/org/apache/solr/search/SolrQueryParser.java (revision 1227631) +++ solr/core/src/java/org/apache/solr/search/SolrQueryParser.java (working copy) @@ -19,14 +19,16 @@ import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; +import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.Term; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; -import org.apache.lucene.search.*; -import org.apache.lucene.analysis.Analyzer; -import org.apache.solr.analysis.*; +import org.apache.lucene.search.Query; +import org.apache.solr.analysis.ReversedWildcardFilter; +import org.apache.solr.analysis.ReversedWildcardFilterFactory; +import org.apache.solr.analysis.TokenFilterFactory; +import org.apache.solr.analysis.TokenizerChain; import org.apache.solr.common.SolrException; import org.apache.solr.schema.FieldType; import org.apache.solr.schema.IndexSchema; @@ -174,12 +176,8 @@ @Override protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException { checkNullField(field); - SchemaField sf = schema.getField(field); - return sf.getType().getRangeQuery(parser, sf, - "*".equals(part1) ? null : part1, - "*".equals(part2) ? null : part2, - inclusive, inclusive); + return sf.getType().getRangeQuery(parser, sf, part1, part2, inclusive, inclusive); } @Override