You are viewing a plain text version of this content. The canonical link for it is here.
Posted to java-commits@lucene.apache.org by ho...@apache.org on 2007/01/08 21:02:52 UTC
svn commit: r494184 - in /lucene/java/trunk: CHANGES.txt
src/java/org/apache/lucene/queryParser/QueryParser.java
src/java/org/apache/lucene/queryParser/QueryParser.jj
src/test/org/apache/lucene/queryParser/TestQueryParser.java
Author: hossman
Date: Mon Jan 8 12:02:51 2007
New Revision: 494184
URL: http://svn.apache.org/viewvc?view=rev&rev=494184
Log:
LUCENE-732 - DateTools support for QueryParser, Resolution can be set on a per field basis
Modified:
lucene/java/trunk/CHANGES.txt
lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.java
lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.jj
lucene/java/trunk/src/test/org/apache/lucene/queryParser/TestQueryParser.java
Modified: lucene/java/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/java/trunk/CHANGES.txt?view=diff&rev=494184&r1=494183&r2=494184
==============================================================================
--- lucene/java/trunk/CHANGES.txt (original)
+++ lucene/java/trunk/CHANGES.txt Mon Jan 8 12:02:51 2007
@@ -170,6 +170,11 @@
on an older format index will create a single .nrm file for the new
segment. (Doron Cohen via Yonik Seeley)
+14. LUCENE-732: DateTools support has been added to QueryParser, with
+ setters for both the default Resolution, and per-field Resolution.
+ For backwards compatibility, DateField is still used if no Resolutions
+ are specified. (Michael Busch via Chris Hostetter)
+
Bug fixes
1. Fixed the web application demo (built with "ant war-demo") which
Modified: lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.java?view=diff&rev=494184&r1=494183&r2=494184
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.java (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.java Mon Jan 8 12:02:51 2007
@@ -45,14 +45,31 @@
* documentation</a>.
* </p>
*
- * <p>In {@link RangeQuery}s, QueryParser tries to detect date values, e.g. <tt>date:[6/1/2005 TO 6/4/2005]</tt>
- * produces a range query that searches for "date" fields between 2005-06-01 and 2005-06-04. Note
- * that the format of the accpeted input depends on {@link #setLocale(Locale) the locale}. This
- * feature also assumes that your index uses the {@link DateField} class to store dates.
- * If you use a different format (e.g. {@link DateTools}) and you still want QueryParser
- * to turn local dates in range queries into valid queries you need to create your own
+ * <p>
+ * In {@link RangeQuery}s, QueryParser tries to detect date values, e.g.
+ * <tt>date:[6/1/2005 TO 6/4/2005]</tt> produces a range query that searches
+ * for "date" fields between 2005-06-01 and 2005-06-04. Note that the format
+ * of the accepted input depends on {@link #setLocale(Locale) the locale}.
+ * By default a date is converted into a search term using the deprecated
+ * {@link DateField} for compatibility reasons.
+ * To use the new {@link DateTools} to convert dates, a
+ * {@link DateTools.Resolution} has to be set.
+ * </p>
+ * <p>
+ * The date resolution that shall be used for RangeQueries can be set
+ * using {@link #setDateResolution(DateTools.Resolution)}
+ * or {@link #setDateResolution(String, DateTools.Resolution)}. The former
+ * sets the default date resolution for all fields, whereas the latter can
+ * be used to set field specific date resolutions. Field specific date
+ * resolutions take, if set, precedence over the default date resolution.
+ * </p>
+ * <p>
+ * If you use neither {@link DateField} nor {@link DateTools} in your
+ * index, you can create your own
* query parser that inherits QueryParser and overwrites
- * {@link #getRangeQuery(String, String, String, boolean)}.</p>
+ * {@link #getRangeQuery(String, String, String, boolean)} to
+ * use a different method for date conversion.
+ * </p>
*
* <p>Note that QueryParser is <em>not</em> thread-safe.</p>
*
@@ -60,7 +77,6 @@
* @author Peter Halacsy
* @author Tatu Saloranta
*/
-
public class QueryParser implements QueryParserConstants {
private static final int CONJ_NONE = 0;
@@ -92,6 +108,11 @@
int fuzzyPrefixLength = FuzzyQuery.defaultPrefixLength;
Locale locale = Locale.getDefault();
+ // the default date resolution
+ DateTools.Resolution dateResolution = null;
+ // maps field names to date resolutions
+ Map fieldToDateResolution = null;
+
/** The default operator for parsing queries.
* Use {@link QueryParser#setDefaultOperator} to change it.
*/
@@ -286,6 +307,61 @@
return locale;
}
+ /**
+ * Sets the default date resolution used by RangeQueries for fields for which no
+ * specific date resolutions has been set. Field specific resolutions can be set
+ * with {@link #setDateResolution(String, DateTools.Resolution)}.
+ *
+ * @param dateResolution the default date resolution to set
+ */
+ public void setDateResolution(DateTools.Resolution dateResolution) {
+ this.dateResolution = dateResolution;
+ }
+
+ /**
+ * Sets the date resolution used by RangeQueries for a specific field.
+ *
+ * @param field field for which the date resolution is to be set
+ * @param dateResolution date resolution to set
+ */
+ public void setDateResolution(String fieldName, DateTools.Resolution dateResolution) {
+ if (fieldName == null) {
+ throw new IllegalArgumentException("Field cannot be null.");
+ }
+
+ if (fieldToDateResolution == null) {
+ // lazily initialize HashMap
+ fieldToDateResolution = new HashMap();
+ }
+
+ fieldToDateResolution.put(fieldName, dateResolution);
+ }
+
+ /**
+ * Returns the date resolution that is used by RangeQueries for the given field.
+ * Returns null, if no default or field specific date resolution has been set
+ * for the given field.
+ *
+ */
+ public DateTools.Resolution getDateResolution(String fieldName) {
+ if (fieldName == null) {
+ throw new IllegalArgumentException("Field cannot be null.");
+ }
+
+ if (fieldToDateResolution == null) {
+ // no field specific date resolutions set; return default date resolution instead
+ return this.dateResolution;
+ }
+
+ DateTools.Resolution resolution = (DateTools.Resolution) fieldToDateResolution.get(fieldName);
+ if (resolution == null) {
+ // no date resolutions set for the given field; return default date resolution instead
+ resolution = this.dateResolution;
+ }
+
+ return resolution;
+ }
+
protected void addClause(Vector clauses, int conj, int mods, Query q) {
boolean required, prohibited;
@@ -472,8 +548,17 @@
cal.set(Calendar.MILLISECOND, 999);
d2 = cal.getTime();
}
- part1 = DateField.dateToString(d1);
- part2 = DateField.dateToString(d2);
+ DateTools.Resolution resolution = getDateResolution(field);
+ if (resolution == null) {
+ // no default or field specific date resolution has been set,
+ // use deprecated DateField to maintain compatibilty 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) { }
@@ -1158,6 +1243,12 @@
finally { jj_save(0, xla); }
}
+ final private boolean jj_3R_3() {
+ if (jj_scan_token(STAR)) return true;
+ if (jj_scan_token(COLON)) return true;
+ return false;
+ }
+
final private boolean jj_3R_2() {
if (jj_scan_token(TERM)) return true;
if (jj_scan_token(COLON)) return true;
@@ -1171,12 +1262,6 @@
jj_scanpos = xsp;
if (jj_3R_3()) return true;
}
- return false;
- }
-
- final private boolean jj_3R_3() {
- if (jj_scan_token(STAR)) return true;
- if (jj_scan_token(COLON)) return true;
return false;
}
Modified: lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.jj
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.jj?view=diff&rev=494184&r1=494183&r2=494184
==============================================================================
--- lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.jj (original)
+++ lucene/java/trunk/src/java/org/apache/lucene/queryParser/QueryParser.jj Mon Jan 8 12:02:51 2007
@@ -69,14 +69,31 @@
* documentation</a>.
* </p>
*
- * <p>In {@link RangeQuery}s, QueryParser tries to detect date values, e.g. <tt>date:[6/1/2005 TO 6/4/2005]</tt>
- * produces a range query that searches for "date" fields between 2005-06-01 and 2005-06-04. Note
- * that the format of the accpeted input depends on {@link #setLocale(Locale) the locale}. This
- * feature also assumes that your index uses the {@link DateField} class to store dates.
- * If you use a different format (e.g. {@link DateTools}) and you still want QueryParser
- * to turn local dates in range queries into valid queries you need to create your own
+ * <p>
+ * In {@link RangeQuery}s, QueryParser tries to detect date values, e.g.
+ * <tt>date:[6/1/2005 TO 6/4/2005]</tt> produces a range query that searches
+ * for "date" fields between 2005-06-01 and 2005-06-04. Note that the format
+ * of the accepted input depends on {@link #setLocale(Locale) the locale}.
+ * By default a date is converted into a search term using the deprecated
+ * {@link DateField} for compatibility reasons.
+ * To use the new {@link DateTools} to convert dates, a
+ * {@link DateTools.Resolution} has to be set.
+ * </p>
+ * <p>
+ * The date resolution that shall be used for RangeQueries can be set
+ * using {@link #setDateResolution(DateTools.Resolution)}
+ * or {@link #setDateResolution(String, DateTools.Resolution)}. The former
+ * sets the default date resolution for all fields, whereas the latter can
+ * be used to set field specific date resolutions. Field specific date
+ * resolutions take, if set, precedence over the default date resolution.
+ * </p>
+ * <p>
+ * If you use neither {@link DateField} nor {@link DateTools} in your
+ * index, you can create your own
* query parser that inherits QueryParser and overwrites
- * {@link #getRangeQuery(String, String, String, boolean)}.</p>
+ * {@link #getRangeQuery(String, String, String, boolean)} to
+ * use a different method for date conversion.
+ * </p>
*
* <p>Note that QueryParser is <em>not</em> thread-safe.</p>
*
@@ -84,7 +101,6 @@
* @author Peter Halacsy
* @author Tatu Saloranta
*/
-
public class QueryParser {
private static final int CONJ_NONE = 0;
@@ -116,6 +132,11 @@
int fuzzyPrefixLength = FuzzyQuery.defaultPrefixLength;
Locale locale = Locale.getDefault();
+ // the default date resolution
+ DateTools.Resolution dateResolution = null;
+ // maps field names to date resolutions
+ Map fieldToDateResolution = null;
+
/** The default operator for parsing queries.
* Use {@link QueryParser#setDefaultOperator} to change it.
*/
@@ -310,6 +331,61 @@
return locale;
}
+ /**
+ * Sets the default date resolution used by RangeQueries for fields for which no
+ * specific date resolutions has been set. Field specific resolutions can be set
+ * with {@link #setDateResolution(String, DateTools.Resolution)}.
+ *
+ * @param dateResolution the default date resolution to set
+ */
+ public void setDateResolution(DateTools.Resolution dateResolution) {
+ this.dateResolution = dateResolution;
+ }
+
+ /**
+ * Sets the date resolution used by RangeQueries for a specific field.
+ *
+ * @param field field for which the date resolution is to be set
+ * @param dateResolution date resolution to set
+ */
+ public void setDateResolution(String fieldName, DateTools.Resolution dateResolution) {
+ if (fieldName == null) {
+ throw new IllegalArgumentException("Field cannot be null.");
+ }
+
+ if (fieldToDateResolution == null) {
+ // lazily initialize HashMap
+ fieldToDateResolution = new HashMap();
+ }
+
+ fieldToDateResolution.put(fieldName, dateResolution);
+ }
+
+ /**
+ * Returns the date resolution that is used by RangeQueries for the given field.
+ * Returns null, if no default or field specific date resolution has been set
+ * for the given field.
+ *
+ */
+ public DateTools.Resolution getDateResolution(String fieldName) {
+ if (fieldName == null) {
+ throw new IllegalArgumentException("Field cannot be null.");
+ }
+
+ if (fieldToDateResolution == null) {
+ // no field specific date resolutions set; return default date resolution instead
+ return this.dateResolution;
+ }
+
+ DateTools.Resolution resolution = (DateTools.Resolution) fieldToDateResolution.get(fieldName);
+ if (resolution == null) {
+ // no date resolutions set for the given field; return default date resolution instead
+ resolution = this.dateResolution;
+ }
+
+ return resolution;
+ }
+
protected void addClause(Vector clauses, int conj, int mods, Query q) {
boolean required, prohibited;
@@ -496,8 +572,17 @@
cal.set(Calendar.MILLISECOND, 999);
d2 = cal.getTime();
}
- part1 = DateField.dateToString(d1);
- part2 = DateField.dateToString(d2);
+ DateTools.Resolution resolution = getDateResolution(field);
+ if (resolution == null) {
+ // no default or field specific date resolution has been set,
+ // use deprecated DateField to maintain compatibilty 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) { }
Modified: lucene/java/trunk/src/test/org/apache/lucene/queryParser/TestQueryParser.java
URL: http://svn.apache.org/viewvc/lucene/java/trunk/src/test/org/apache/lucene/queryParser/TestQueryParser.java?view=diff&rev=494184&r1=494183&r2=494184
==============================================================================
--- lucene/java/trunk/src/test/org/apache/lucene/queryParser/TestQueryParser.java (original)
+++ lucene/java/trunk/src/test/org/apache/lucene/queryParser/TestQueryParser.java Mon Jan 8 12:02:51 2007
@@ -27,6 +27,7 @@
import org.apache.lucene.analysis.WhitespaceAnalyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.DateField;
+import org.apache.lucene.document.DateTools;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
@@ -38,6 +39,7 @@
import java.io.Reader;
import java.text.DateFormat;
import java.util.Calendar;
+import java.util.Date;
import java.util.Locale;
/**
@@ -127,6 +129,16 @@
}
}
+ public void assertQueryEquals(QueryParser qp, String field, String query, String result)
+ throws Exception {
+ Query q = qp.parse(query);
+ String s = q.toString(field);
+ if (!s.equals(result)) {
+ fail("Query /" + query + "/ yielded /" + s
+ + "/, expecting /" + result + "/");
+ }
+ }
+
public void assertEscapedQueryEquals(String query, Analyzer a, String result)
throws Exception {
String escapedQuery = QueryParser.escape(query);
@@ -378,12 +390,28 @@
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})");
}
-
- private String getDate(String s) throws Exception {
+
+ /** for testing legacy DateField support */
+ private String getLegacyDate(String s) throws Exception {
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
return DateField.dateToString(df.parse(s));
}
+ /** for testing DateTools support */
+ private String getDate(String s, DateTools.Resolution resolution) throws Exception {
+ DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
+ return getDate(df.parse(s), resolution);
+ }
+
+ /** for testing DateTools support */
+ private String getDate(Date d, DateTools.Resolution resolution) throws Exception {
+ if (resolution == null) {
+ return DateField.dateToString(d);
+ } else {
+ return DateTools.dateToString(d, resolution);
+ }
+ }
+
private String getLocalizedDate(int year, int month, int day, boolean extendLastDate) {
DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
Calendar calendar = Calendar.getInstance();
@@ -397,16 +425,66 @@
return df.format(calendar.getTime());
}
- public void testDateRange() throws Exception {
+ /** for testing legacy DateField support */
+ public void testLegacyDateRange() throws Exception {
String startDate = getLocalizedDate(2002, 1, 1, false);
String endDate = getLocalizedDate(2002, 1, 4, false);
Calendar endDateExpected = Calendar.getInstance();
endDateExpected.set(2002, 1, 4, 23, 59, 59);
endDateExpected.set(Calendar.MILLISECOND, 999);
assertQueryEquals("[ " + startDate + " TO " + endDate + "]", null,
- "[" + getDate(startDate) + " TO " + DateField.dateToString(endDateExpected.getTime()) + "]");
+ "[" + getLegacyDate(startDate) + " TO " + DateField.dateToString(endDateExpected.getTime()) + "]");
assertQueryEquals("{ " + startDate + " " + endDate + " }", null,
- "{" + getDate(startDate) + " TO " + getDate(endDate) + "}");
+ "{" + getLegacyDate(startDate) + " TO " + getLegacyDate(endDate) + "}");
+ }
+
+ public void testDateRange() throws Exception {
+ String startDate = getLocalizedDate(2002, 1, 1, false);
+ String endDate = getLocalizedDate(2002, 1, 4, false);
+ Calendar endDateExpected = Calendar.getInstance();
+ endDateExpected.set(2002, 1, 4, 23, 59, 59);
+ endDateExpected.set(Calendar.MILLISECOND, 999);
+ final String defaultField = "default";
+ final String monthField = "month";
+ final String hourField = "hour";
+ QueryParser qp = new QueryParser("field", new SimpleAnalyzer());
+
+ // Don't set any date resolution and verify if DateField is used
+ assertDateRangeQueryEquals(qp, defaultField, startDate, endDate,
+ endDateExpected.getTime(), null);
+
+ // set a field specific date resolution
+ qp.setDateResolution(monthField, DateTools.Resolution.MONTH);
+
+ // DateField should still be used for defaultField
+ assertDateRangeQueryEquals(qp, defaultField, startDate, endDate,
+ endDateExpected.getTime(), null);
+
+ // set default date resolution to MILLISECOND
+ qp.setDateResolution(DateTools.Resolution.MILLISECOND);
+
+ // set second field specific date resolution
+ qp.setDateResolution(hourField, DateTools.Resolution.HOUR);
+
+ // for this field no field specific date resolution has been set,
+ // so verify if the default resolution is used
+ assertDateRangeQueryEquals(qp, defaultField, startDate, endDate,
+ endDateExpected.getTime(), DateTools.Resolution.MILLISECOND);
+
+ // verify if field specific date resolutions are used for these two fields
+ assertDateRangeQueryEquals(qp, monthField, startDate, endDate,
+ endDateExpected.getTime(), DateTools.Resolution.MONTH);
+
+ assertDateRangeQueryEquals(qp, hourField, startDate, endDate,
+ endDateExpected.getTime(), DateTools.Resolution.HOUR);
+ }
+
+ public void assertDateRangeQueryEquals(QueryParser qp, String field, String startDate, String endDate,
+ Date endDateInclusive, DateTools.Resolution resolution) throws Exception {
+ assertQueryEquals(qp, field, field + ":[" + startDate + " TO " + endDate + "]",
+ "[" + getDate(startDate, resolution) + " TO " + getDate(endDateInclusive, resolution) + "]");
+ assertQueryEquals(qp, field, field + ":{" + startDate + " TO " + endDate + "}",
+ "{" + getDate(startDate, resolution) + " TO " + getDate(endDate, resolution) + "}");
}
public void testEscaped() throws Exception {