You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by ho...@apache.org on 2007/07/29 08:28:42 UTC
svn commit: r560686 - in /lucene/solr/trunk: ./
src/java/org/apache/solr/common/params/ src/java/org/apache/solr/request/
src/java/org/apache/solr/schema/ src/test/org/apache/solr/
Author: hossman
Date: Sat Jul 28 23:28:41 2007
New Revision: 560686
URL: http://svn.apache.org/viewvc?view=rev&rev=560686
Log:
SOLR-258: Date Faceting added to SimpleFacets
Modified:
lucene/solr/trunk/CHANGES.txt
lucene/solr/trunk/src/java/org/apache/solr/common/params/SolrParams.java
lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java
lucene/solr/trunk/src/java/org/apache/solr/schema/DateField.java
lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java
Modified: lucene/solr/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/CHANGES.txt?view=diff&rev=560686&r1=560685&r2=560686
==============================================================================
--- lucene/solr/trunk/CHANGES.txt (original)
+++ lucene/solr/trunk/CHANGES.txt Sat Jul 28 23:28:41 2007
@@ -113,6 +113,10 @@
20. SOLR-102: Added RegexFragmenter, which splits text for highlighting
based on a given pattern. (klaas)
+21. SOLR-258: Date Faceting added to SimpleFacets. Facet counts
+ computed for ranges of size facet.date.gap (a DateMath expression)
+ between facet.date.start and facet.date.end. (hossman)
+
Changes in runtime behavior
Optimizations
Modified: lucene/solr/trunk/src/java/org/apache/solr/common/params/SolrParams.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/common/params/SolrParams.java?view=diff&rev=560686&r1=560685&r2=560686
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/common/params/SolrParams.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/common/params/SolrParams.java Sat Jul 28 23:28:41 2007
@@ -121,8 +121,74 @@
* only use the filterCache for terms with a df >= to this parameter.
*/
public static final String FACET_ENUM_CACHE_MINDF = "facet.enum.cache.minDf";
+ /**
+ * Any field whose terms the user wants to enumerate over for
+ * Facet Contraint Counts (multi-value)
+ */
+ public static final String FACET_DATE = "facet.date";
+ /**
+ * Date string indicating the starting point for a date facet range.
+ * Can be overriden on a per field basis.
+ */
+ public static final String FACET_DATE_START = "facet.date.start";
+ /**
+ * Date string indicating the endinging point for a date facet range.
+ * Can be overriden on a per field basis.
+ */
+ public static final String FACET_DATE_END = "facet.date.end";
+ /**
+ * Date Math string indicating the interval of sub-ranges for a date
+ * facet range.
+ * Can be overriden on a per field basis.
+ */
+ public static final String FACET_DATE_GAP = "facet.date.gap";
+ /**
+ * Boolean indicating how counts should be computed if the range
+ * between 'start' and 'end' is not evenly divisible by 'gap'. If
+ * this value is true, then all counts of ranges involving the 'end'
+ * point will use the exact endpoint specified -- this includes the
+ * 'between' and 'after' counts as well as the last range computed
+ * using the 'gap'. If the value is false, then 'gap' is used to
+ * compute the effective endpoint closest to the 'end' param which
+ * results in the range between 'start' and 'end' being evenly
+ * divisible by 'gap'.
+ * The default is false.
+ * Can be overriden on a per field basis.
+ */
+ public static final String FACET_DATE_HARD_END = "facet.date.hardend";
+ /**
+ * String indicating what "other" ranges should be computed for a
+ * date facet range (multi-value).
+ * Can be overriden on a per field basis.
+ * @see FacetDateOther
+ */
+ public static final String FACET_DATE_OTHER = "facet.date.other";
-
+ /**
+ * An enumeration of the legal values for FACET_DATE_OTHER...
+ * <ul>
+ * <li>before = the count of matches before the start date</li>
+ * <li>after = the count of matches after the end date</li>
+ * <li>between = the count of all matches between start and end</li>
+ * <li>all = all of the above (default value)</li>
+ * <li>none = no additional info requested</li>
+ * </ul>
+ * @see #FACET_DATE_OTHER
+ */
+ public enum FacetDateOther {
+ BEFORE, AFTER, BETWEEN, ALL, NONE;
+ public String toString() { return super.toString().toLowerCase(); }
+ public static FacetDateOther get(String label) {
+ try {
+ return valueOf(label.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ label+" is not a valid type of 'other' date facet information",e);
+ }
+ }
+ }
+
/** If the content stream should come from a URL (using URLConnection) */
public static final String STREAM_URL = "stream.url";
Modified: lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java?view=diff&rev=560686&r1=560685&r2=560686
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java Sat Jul 28 23:28:41 2007
@@ -25,6 +25,7 @@
import org.apache.lucene.search.*;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.params.SolrParams.FacetDateOther;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.core.SolrCore;
@@ -33,12 +34,18 @@
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.BoolField;
+import org.apache.solr.schema.DateField;
import org.apache.solr.search.*;
import org.apache.solr.util.BoundedTreeSet;
+import org.apache.solr.util.DateMathParser;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Set;
+import java.util.EnumSet;
/**
* A class that generates simple Facet information for a request.
@@ -70,6 +77,7 @@
*
* @see #getFacetQueryCounts
* @see #getFacetFieldCounts
+ * @see #getFacetDateCounts
* @see SolrParams#FACET
* @return a NamedList of Facet Count info or null
*/
@@ -83,9 +91,9 @@
try {
res.add("facet_queries", getFacetQueryCounts());
-
res.add("facet_fields", getFacetFieldCounts());
-
+ res.add("facet_dates", getFacetDateCounts());
+
} catch (Exception e) {
SolrException.logOnce(SolrCore.log, "Exception during facet counts", e);
res.add("exception", SolrException.toStr(e));
@@ -402,6 +410,148 @@
return res;
}
+ /**
+ * Returns a list of value constraints and the associated facet counts
+ * for each facet date field, range, and interval specified in the
+ * SolrParams
+ *
+ * @see SolrParams#FACET_DATE
+ */
+ public NamedList getFacetDateCounts()
+ throws IOException {
+
+ final SolrParams required = new RequiredSolrParams(params);
+ final NamedList resOuter = new SimpleOrderedMap();
+ final String[] fields = params.getParams(SolrParams.FACET_DATE);
+ final Date NOW = new Date();
+
+ if (null == fields || 0 == fields.length) return resOuter;
+
+ final IndexSchema schema = searcher.getSchema();
+ for (String f : fields) {
+ final NamedList resInner = new SimpleOrderedMap();
+ resOuter.add(f, resInner);
+ final FieldType trash = schema.getFieldType(f);
+ if (! (trash instanceof DateField)) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ "Can not date facet on a field which is not a DateField: " + f);
+ }
+ final DateField ft = (DateField) trash;
+ final String startS
+ = required.getFieldParam(f,SolrParams.FACET_DATE_START);
+ final Date start;
+ try {
+ start = ft.parseMath(NOW, startS);
+ } catch (SolrException e) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ "date facet 'start' is not a valid Date string: " + startS, e);
+ }
+ final String endS
+ = required.getFieldParam(f,SolrParams.FACET_DATE_END);
+ Date end; // not final, hardend may change this
+ try {
+ end = ft.parseMath(NOW, endS);
+ } catch (SolrException e) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ "date facet 'end' is not a valid Date string: " + endS, e);
+ }
+
+ if (end.before(start)) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ "date facet 'end' comes before 'start': "+endS+" < "+startS);
+ }
+
+ final String gap = required.getFieldParam(f,SolrParams.FACET_DATE_GAP);
+ final DateMathParser dmp = new DateMathParser(ft.UTC, Locale.US);
+ dmp.setNow(NOW);
+
+ try {
+
+ Date low = start;
+ while (low.before(end)) {
+ dmp.setNow(low);
+ final String lowI = ft.toInternal(low);
+ final String label = ft.indexedToReadable(lowI);
+ Date high = dmp.parseMath(gap);
+ if (end.before(high)) {
+ if (params.getFieldBool(f,SolrParams.FACET_DATE_HARD_END,false)) {
+ high = end;
+ } else {
+ end = high;
+ }
+ }
+ if (high.before(low)) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ "date facet infinite loop (is gap negative?)");
+ }
+ final String highI = ft.toInternal(high);
+ resInner.add(label, rangeCount(f,lowI,highI,true,true));
+ low = high;
+ }
+ } catch (java.text.ParseException e) {
+ throw new SolrException
+ (SolrException.ErrorCode.BAD_REQUEST,
+ "date facet 'gap' is not a valid Date Math string: " + gap, e);
+ }
+
+ // explicitly return the gap and end so all the counts are meaningful
+ resInner.add("gap", gap);
+ resInner.add("end", end);
+
+ final String[] othersP =
+ params.getFieldParams(f,SolrParams.FACET_DATE_OTHER);
+ if (null != othersP && 0 < othersP.length ) {
+ Set<FacetDateOther> others = EnumSet.noneOf(FacetDateOther.class);
+
+ for (final String o : othersP) {
+ others.add(FacetDateOther.get(o));
+ }
+
+ // no matter what other values are listed, we don't do
+ // anything if "none" is specified.
+ if (! others.contains(FacetDateOther.NONE) ) {
+ final String startI = ft.toInternal(start);
+ final String endI = ft.toInternal(end);
+
+ boolean all = others.contains(FacetDateOther.ALL);
+
+ if (all || others.contains(FacetDateOther.BEFORE)) {
+ resInner.add(FacetDateOther.BEFORE.toString(),
+ rangeCount(f,null,startI,false,false));
+ }
+ if (all || others.contains(FacetDateOther.AFTER)) {
+ resInner.add(FacetDateOther.AFTER.toString(),
+ rangeCount(f,endI,null,false,false));
+ }
+ if (all || others.contains(FacetDateOther.BETWEEN)) {
+ resInner.add(FacetDateOther.BETWEEN.toString(),
+ rangeCount(f,startI,endI,true,true));
+ }
+ }
+ }
+ }
+
+ return resOuter;
+ }
+
+ /**
+ * Macro for getting the numDocs of a ConstantScoreRangeQuery over docs
+ * @see docs
+ * @see SolrIndexSearcher#numDocs
+ * @see ConstantScoreRangeQuery
+ */
+ protected int rangeCount(String field, String low, String high,
+ boolean iLow, boolean iHigh) throws IOException {
+ return searcher.numDocs(new ConstantScoreRangeQuery(field,low,high,
+ iHigh,iLow),
+ docs);
+ }
+
/**
* A simple key=>val pair whose natural order is such that
* <b>higher</b> vals come before lower vals.
Modified: lucene/solr/trunk/src/java/org/apache/solr/schema/DateField.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/java/org/apache/solr/schema/DateField.java?view=diff&rev=560686&r1=560685&r2=560686
==============================================================================
--- lucene/solr/trunk/src/java/org/apache/solr/schema/DateField.java (original)
+++ lucene/solr/trunk/src/java/org/apache/solr/schema/DateField.java Sat Jul 28 23:28:41 2007
@@ -70,9 +70,10 @@
*
* <p>
* This FieldType also supports incoming "Date Math" strings for computing
- * values by adding/rounding internals of time relative "NOW",
- * ie: "NOW+1YEAR", "NOW/DAY", etc.. -- see {@link DateMathParser}
- * for more examples.
+ * values by adding/rounding internals of time relative either an explicit
+ * datetime (in theformat specified above) or the literal string "NOW",
+ * ie: "NOW+1YEAR", "NOW/DAY", 1995-12-31T23:59:59.999Z+5MINUTES, etc...
+ * -- see {@link DateMathParser} for more examples.
* </p>
*
* @version $Id$
@@ -91,20 +92,61 @@
protected void init(IndexSchema schema, Map<String,String> args) {
}
+ protected static String NOW = "NOW";
+ protected static char Z = 'Z';
+
public String toInternal(String val) {
- int len=val.length();
- if (val.charAt(len-1)=='Z') {
+ final int len=val.length();
+ if (val.charAt(len-1) == Z) {
+ // check common case first, simple datetime
+ // NOTE: not parsed to ensure correctness
return val.substring(0,len-1);
- } else if (val.startsWith("NOW")) {
- /* :TODO: let Locale/TimeZone come from init args for rounding only */
- DateMathParser p = new DateMathParser(UTC, Locale.US);
- try {
- return toInternal(p.parseMath(val.substring(3)));
- } catch (ParseException e) {
- throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Invalid Date Math String:'" +val+'\'',e);
+ }
+ return toInternal(parseMath(null, val));
+ }
+
+ /**
+ * Parses a String which may be a date (in the standard format)
+ * followed by an optional math expression.
+ * @param now an optional fixed date to use as "NOW" in the DateMathParser
+ * @param val the string to parse
+ */
+ public Date parseMath(Date now, String val) {
+ String math = null;
+ /* :TODO: let Locale/TimeZone come from init args for rounding only */
+ final DateMathParser p = new DateMathParser(UTC, Locale.US);
+
+ if (null != now) p.setNow(now);
+
+ if (val.startsWith(NOW)) {
+ math = val.substring(NOW.length());
+ } else {
+ final int zz = val.indexOf(Z);
+ if (0 < zz) {
+ math = val.substring(zz+1);
+ try {
+ p.setNow(toObject(val.substring(0,zz)));
+ } catch (ParseException e) {
+ throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
+ "Invalid Date in Date Math String:'"
+ +val+'\'',e);
+ }
+ } else {
+ throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
+ "Invalid Date String:'" +val+'\'');
}
}
- throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Invalid Date String:'" +val+'\'');
+
+ if (null == math || math.equals("")) {
+ return p.getNow();
+ }
+
+ try {
+ return p.parseMath(math);
+ } catch (ParseException e) {
+ throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
+ "Invalid Date Math String:'" +val+'\'',e);
+ }
}
public String toInternal(Date val) {
@@ -112,11 +154,14 @@
}
public String indexedToReadable(String indexedForm) {
- return indexedForm + 'Z';
+ return indexedForm + Z;
}
public String toExternal(Fieldable f) {
return indexedToReadable(f.stringValue());
+ }
+ public Date toObject(String indexedForm) throws java.text.ParseException {
+ return getThreadLocalDateFormat().parse(indexedToReadable(indexedForm));
}
@Override
Modified: lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java?view=diff&rev=560686&r1=560685&r2=560686
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java (original)
+++ lucene/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java Sat Jul 28 23:28:41 2007
@@ -612,6 +612,122 @@
}
+ public void testDateFacets() {
+ final String f = "bday";
+ final String pre = "//lst[@name='facet_dates']/lst[@name='"+f+"']";
+
+ assertU(adoc("id", "1", f, "1976-07-04T12:08:56.235Z"));
+ assertU(adoc("id", "2", f, "1976-07-05T00:00:00.000Z"));
+ assertU(adoc("id", "3", f, "1976-07-15T00:07:67.890Z"));
+ assertU(adoc("id", "4", f, "1976-07-21T00:07:67.890Z"));
+ assertU(adoc("id", "5", f, "1976-07-13T12:12:25.255Z"));
+ assertU(adoc("id", "6", f, "1976-07-03T17:01:23.456Z"));
+ assertU(adoc("id", "7", f, "1976-07-12T12:12:25.255Z"));
+ assertU(adoc("id", "8", f, "1976-07-15T15:15:15.155Z"));
+ assertU(adoc("id", "9", f, "1907-07-12T13:13:23.235Z"));
+ assertU(adoc("id", "10", f, "1976-07-03T11:02:45.678Z"));
+ assertU(adoc("id", "11", f, "1907-07-12T12:12:25.255Z"));
+ assertU(adoc("id", "12", f, "2007-07-30T07:07:07.070Z"));
+ assertU(adoc("id", "13", f, "1976-07-30T22:22:22.222Z"));
+ assertU(adoc("id", "14", f, "1976-07-05T22:22:22.222Z"));
+ assertU(commit());
+
+ assertQ("check counts for month of facet by day",
+ req( "q", "*:*"
+ ,"rows", "0"
+ ,"facet", "true"
+ ,"facet.date", f
+ ,"facet.date.start", "1976-07-01T00:00:00.000Z"
+ ,"facet.date.end", "1976-07-01T00:00:00.000Z+1MONTH"
+ ,"facet.date.gap", "+1DAY"
+ ,"facet.date.other", "all"
+ )
+ // 31 days + pre+post+inner = 34
+ ,"*[count("+pre+"/int)=34]"
+ ,pre+"/int[@name='1976-07-01T00:00:00.000Z'][.='0' ]"
+ ,pre+"/int[@name='1976-07-02T00:00:00.000Z'][.='0' ]"
+ ,pre+"/int[@name='1976-07-03T00:00:00.000Z'][.='2' ]"
+ // july4th = 2 because exists doc @ 00:00:00.000 on July5
+ // (date faceting is inclusive)
+ ,pre+"/int[@name='1976-07-04T00:00:00.000Z'][.='2' ]"
+ ,pre+"/int[@name='1976-07-05T00:00:00.000Z'][.='2' ]"
+ ,pre+"/int[@name='1976-07-06T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-07T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-08T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-09T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-10T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-11T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-12T00:00:00.000Z'][.='1' ]"
+ ,pre+"/int[@name='1976-07-13T00:00:00.000Z'][.='1' ]"
+ ,pre+"/int[@name='1976-07-14T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-15T00:00:00.000Z'][.='2' ]"
+ ,pre+"/int[@name='1976-07-16T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-17T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-18T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-19T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-21T00:00:00.000Z'][.='1' ]"
+ ,pre+"/int[@name='1976-07-22T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-23T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-24T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-25T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-26T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-27T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-28T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-29T00:00:00.000Z'][.='0']"
+ ,pre+"/int[@name='1976-07-30T00:00:00.000Z'][.='1' ]"
+ ,pre+"/int[@name='1976-07-31T00:00:00.000Z'][.='0']"
+
+ ,pre+"/int[@name='before' ][.='2']"
+ ,pre+"/int[@name='after' ][.='1']"
+ ,pre+"/int[@name='between'][.='11']"
+
+ );
+
+ assertQ("check hardend=false",
+ req( "q", "*:*"
+ ,"rows", "0"
+ ,"facet", "true"
+ ,"facet.date", f
+ ,"facet.date.start", "1976-07-01T00:00:00.000Z"
+ ,"facet.date.end", "1976-07-13T00:00:00.000Z"
+ ,"facet.date.gap", "+5DAYS"
+ ,"facet.date.other", "all"
+ ,"facet.date.hardend","false"
+ )
+ // 3 gaps + pre+post+inner = 6
+ ,"*[count("+pre+"/int)=6]"
+ ,pre+"/int[@name='1976-07-01T00:00:00.000Z'][.='5' ]"
+ ,pre+"/int[@name='1976-07-06T00:00:00.000Z'][.='0' ]"
+ ,pre+"/int[@name='1976-07-11T00:00:00.000Z'][.='4' ]"
+
+ ,pre+"/int[@name='before' ][.='2']"
+ ,pre+"/int[@name='after' ][.='3']"
+ ,pre+"/int[@name='between'][.='9']"
+ );
+
+ assertQ("check hardend=true",
+ req( "q", "*:*"
+ ,"rows", "0"
+ ,"facet", "true"
+ ,"facet.date", f
+ ,"facet.date.start", "1976-07-01T00:00:00.000Z"
+ ,"facet.date.end", "1976-07-13T00:00:00.000Z"
+ ,"facet.date.gap", "+5DAYS"
+ ,"facet.date.other", "all"
+ ,"facet.date.hardend","true"
+ )
+ // 3 gaps + pre+post+inner = 6
+ ,"*[count("+pre+"/int)=6]"
+ ,pre+"/int[@name='1976-07-01T00:00:00.000Z'][.='5' ]"
+ ,pre+"/int[@name='1976-07-06T00:00:00.000Z'][.='0' ]"
+ ,pre+"/int[@name='1976-07-11T00:00:00.000Z'][.='1' ]"
+
+ ,pre+"/int[@name='before' ][.='2']"
+ ,pre+"/int[@name='after' ][.='6']"
+ ,pre+"/int[@name='between'][.='6']"
+ );
+
+ }
public void testFacetMultiValued() {
doFacets("t_s");
@@ -1128,15 +1244,29 @@
// ...
// BUT: we can test that crazy combinations of "NOW" all work correctly,
// assuming the test doesn't take too long to run...
-
- assertU(adoc("id", "1", "bday", "1976-07-04T12:08:56.235Z"));
+
+ final String july4 = "1976-07-04T12:08:56.235Z";
+ assertU(adoc("id", "1", "bday", july4));
assertU(adoc("id", "2", "bday", "NOW"));
assertU(adoc("id", "3", "bday", "NOW/HOUR"));
assertU(adoc("id", "4", "bday", "NOW-30MINUTES"));
assertU(adoc("id", "5", "bday", "NOW+30MINUTES"));
assertU(adoc("id", "6", "bday", "NOW+2YEARS"));
assertU(commit());
-
+
+ assertQ("check math on absolute date#1",
+ req("q", "bday:[* TO "+july4+"/SECOND]"),
+ "*[count(//doc)=0]");
+ assertQ("check math on absolute date#2",
+ req("q", "bday:[* TO "+july4+"/SECOND+1SECOND]"),
+ "*[count(//doc)=1]");
+ assertQ("check math on absolute date#3",
+ req("q", "bday:["+july4+"/SECOND TO "+july4+"/SECOND+1SECOND]"),
+ "*[count(//doc)=1]");
+ assertQ("check math on absolute date#4",
+ req("q", "bday:["+july4+"/MINUTE+1MINUTE TO *]"),
+ "*[count(//doc)=5]");
+
assertQ("check count for before now",
req("q", "bday:[* TO NOW]"), "*[count(//doc)=4]");