Date Mapping: Support "date math" when searching, closes #1708.

This commit is contained in:
Shay Banon 2012-02-16 18:10:12 +02:00
parent eb9503f674
commit f997315f54
24 changed files with 361 additions and 89 deletions

View File

@ -42,7 +42,7 @@ public class ExistsFieldQueryExtension implements FieldQueryExtension {
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null) {
if (smartNameFieldMappers.hasMapper()) {
filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true);
filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true, parseContext);
}
}
if (filter == null) {

View File

@ -167,7 +167,7 @@ public class MapperQueryParser extends QueryParser {
if (fieldMappers != null) {
currentMapper = fieldMappers.fieldMappers().mapper();
if (currentMapper != null) {
Query rangeQuery = currentMapper.rangeQuery(part1, part2, inclusive, inclusive);
Query rangeQuery = currentMapper.rangeQuery(part1, part2, inclusive, inclusive, parseContext);
return wrapSmartNameQuery(rangeQuery, fieldMappers, parseContext);
}
}

View File

@ -44,7 +44,7 @@ public class MissingFieldQueryExtension implements FieldQueryExtension {
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null) {
if (smartNameFieldMappers.hasMapper()) {
filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true);
filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true, parseContext);
}
}
if (filter == null) {

View File

@ -0,0 +1,227 @@
package org.elasticsearch.common.joda;
import org.elasticsearch.ElasticSearchParseException;
import org.joda.time.DateTimeZone;
import org.joda.time.MutableDateTime;
import java.util.concurrent.TimeUnit;
/**
*/
public class DateMathParser {
private final FormatDateTimeFormatter dateTimeFormatter;
private final TimeUnit timeUnit;
public DateMathParser(FormatDateTimeFormatter dateTimeFormatter, TimeUnit timeUnit) {
this.dateTimeFormatter = dateTimeFormatter;
this.timeUnit = timeUnit;
}
public long parse(String text, long now) {
return parse(text, now, false, false);
}
public long parseUpperInclusive(String text, long now) {
return parse(text, now, true, true);
}
public long parse(String text, long now, boolean roundUp, boolean upperInclusive) {
long time;
String mathString;
if (text.startsWith("now")) {
time = now;
mathString = text.substring("now".length());
} else {
int index = text.indexOf("||");
String parseString;
if (index == -1) {
parseString = text;
mathString = ""; // nothing else
} else {
parseString = text.substring(0, index);
mathString = text.substring(index + 2);
}
if (upperInclusive) {
time = parseUpperInclusiveStringValue(parseString);
} else {
time = parseStringValue(parseString);
}
}
if (mathString.isEmpty()) {
return time;
}
return parseMath(mathString, time, roundUp);
}
private long parseMath(String mathString, long time, boolean roundUp) throws ElasticSearchParseException {
MutableDateTime dateTime = new MutableDateTime(time, DateTimeZone.UTC);
try {
for (int i = 0; i < mathString.length(); ) {
char c = mathString.charAt(i++);
int type;
if (c == '/') {
type = 0;
} else if (c == '+') {
type = 1;
} else if (c == '-') {
type = 2;
} else {
throw new ElasticSearchParseException("operator not supported for date math [" + mathString + "]");
}
int num;
if (!Character.isDigit(mathString.charAt(i))) {
num = 1;
} else {
int numFrom = i;
while (Character.isDigit(mathString.charAt(i))) {
i++;
}
num = Integer.parseInt(mathString.substring(numFrom, i));
}
if (type == 0) {
// rounding is only allowed on whole numbers
if (num != 1) {
throw new ElasticSearchParseException("rounding `/` can only be used on single unit types [" + mathString + "]");
}
}
char unit = mathString.charAt(i++);
switch (unit) {
case 'M':
if (type == 0) {
if (roundUp) {
dateTime.monthOfYear().roundCeiling();
} else {
dateTime.monthOfYear().roundFloor();
}
} else if (type == 1) {
dateTime.addMonths(num);
} else if (type == 2) {
dateTime.addMonths(-num);
}
break;
case 'w':
if (type == 0) {
if (roundUp) {
dateTime.weekOfWeekyear().roundCeiling();
} else {
dateTime.weekOfWeekyear().roundFloor();
}
} else if (type == 1) {
dateTime.addWeeks(num);
} else if (type == 2) {
dateTime.addWeeks(-num);
}
break;
case 'd':
if (type == 0) {
if (roundUp) {
dateTime.dayOfMonth().roundCeiling();
} else {
dateTime.dayOfMonth().roundFloor();
}
} else if (type == 1) {
dateTime.addDays(num);
} else if (type == 2) {
dateTime.addDays(-num);
}
break;
case 'h':
case 'H':
if (type == 0) {
if (roundUp) {
dateTime.hourOfDay().roundCeiling();
} else {
dateTime.hourOfDay().roundFloor();
}
} else if (type == 1) {
dateTime.addHours(num);
} else if (type == 2) {
dateTime.addHours(-num);
}
break;
case 'm':
if (type == 0) {
if (roundUp) {
dateTime.minuteOfHour().roundCeiling();
} else {
dateTime.minuteOfHour().roundFloor();
}
} else if (type == 1) {
dateTime.addMinutes(num);
} else if (type == 2) {
dateTime.addMinutes(-num);
}
break;
case 's':
if (type == 0) {
if (roundUp) {
dateTime.secondOfMinute().roundCeiling();
} else {
dateTime.secondOfMinute().roundFloor();
}
} else if (type == 1) {
dateTime.addSeconds(num);
} else if (type == 2) {
dateTime.addSeconds(-num);
}
break;
default:
throw new ElasticSearchParseException("unit [" + unit + "] not supported for date math [" + mathString + "]");
}
}
} catch (Exception e) {
if (e instanceof ElasticSearchParseException) {
throw (ElasticSearchParseException) e;
}
throw new ElasticSearchParseException("failed to parse date math [" + mathString + "]");
}
return dateTime.getMillis();
}
private long parseStringValue(String value) {
try {
return dateTimeFormatter.parser().parseMillis(value);
} catch (RuntimeException e) {
try {
long time = Long.parseLong(value);
return timeUnit.toMillis(time);
} catch (NumberFormatException e1) {
throw new ElasticSearchParseException("failed to parse date field [" + value + "], tried both date format [" + dateTimeFormatter.format() + "], and timestamp number", e);
}
}
}
private long parseUpperInclusiveStringValue(String value) {
try {
MutableDateTime dateTime = new MutableDateTime(3000, 12, 31, 23, 59, 59, 999, DateTimeZone.UTC);
int location = dateTimeFormatter.parser().parseInto(dateTime, value, 0);
// if we parsed all the string value, we are good
if (location == value.length()) {
return dateTime.getMillis();
}
// if we did not manage to parse, or the year is really high year which is unreasonable
// see if its a number
if (location <= 0 || dateTime.getYear() > 5000) {
try {
long time = Long.parseLong(value);
return timeUnit.toMillis(time);
} catch (NumberFormatException e1) {
throw new ElasticSearchParseException("failed to parse date field [" + value + "], tried both date format [" + dateTimeFormatter.format() + "], and timestamp number");
}
}
return dateTime.getMillis();
} catch (RuntimeException e) {
try {
long time = Long.parseLong(value);
return timeUnit.toMillis(time);
} catch (NumberFormatException e1) {
throw new ElasticSearchParseException("failed to parse date field [" + value + "], tried both date format [" + dateTimeFormatter.format() + "], and timestamp number", e);
}
}
}
}

View File

@ -198,12 +198,12 @@ public interface FieldMapper<T> {
/**
* Constructs a range query based on the mapper.
*/
Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper);
Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context);
/**
* Constructs a range query filter based on the mapper.
*/
Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper);
Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context);
FieldDataType fieldDataType();
}

View File

@ -395,7 +395,7 @@ public abstract class AbstractFieldMapper<T> implements FieldMapper<T>, Mapper {
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return new TermRangeQuery(names.indexName(),
lowerTerm == null ? null : indexedValue(lowerTerm),
upperTerm == null ? null : indexedValue(upperTerm),
@ -403,7 +403,7 @@ public abstract class AbstractFieldMapper<T> implements FieldMapper<T>, Mapper {
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return new TermRangeFilter(names.indexName(),
lowerTerm == null ? null : indexedValue(lowerTerm),
upperTerm == null ? null : indexedValue(upperTerm),

View File

@ -167,7 +167,7 @@ public class ByteFieldMapper extends NumberFieldMapper<Byte> {
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeQuery.newIntRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Integer.parseInt(lowerTerm),
upperTerm == null ? null : Integer.parseInt(upperTerm),
@ -182,7 +182,7 @@ public class ByteFieldMapper extends NumberFieldMapper<Byte> {
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFilter.newIntRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Integer.parseInt(lowerTerm),
upperTerm == null ? null : Integer.parseInt(upperTerm),
@ -190,7 +190,7 @@ public class ByteFieldMapper extends NumberFieldMapper<Byte> {
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFieldDataFilter.newByteRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : Byte.parseByte(lowerTerm),
upperTerm == null ? null : Byte.parseByte(upperTerm),

View File

@ -29,6 +29,7 @@ import org.apache.lucene.util.NumericUtils;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Numbers;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.joda.DateMathParser;
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.common.unit.TimeValue;
@ -41,8 +42,6 @@ import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.mapper.*;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.search.NumericRangeFieldDataFilter;
import org.joda.time.DateTimeZone;
import org.joda.time.MutableDateTime;
import java.io.IOException;
import java.util.Map;
@ -133,6 +132,8 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
private final boolean parseUpperInclusive;
private final DateMathParser dateMathParser;
private String nullValue;
protected final TimeUnit timeUnit;
@ -148,6 +149,7 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
this.nullValue = nullValue;
this.timeUnit = timeUnit;
this.parseUpperInclusive = parseUpperInclusive;
this.dateMathParser = new DateMathParser(dateTimeFormatter, timeUnit);
}
@Override
@ -205,7 +207,7 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
@Override
public Query fuzzyQuery(String value, String minSim, int prefixLength, int maxExpansions) {
long iValue = parseStringValue(value);
long iValue = dateMathParser.parse(value, System.currentTimeMillis());
long iSim;
try {
iSim = TimeValue.parseTimeValue(minSim, null).millis();
@ -221,7 +223,7 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
@Override
public Query fuzzyQuery(String value, double minSim, int prefixLength, int maxExpansions) {
long iValue = parseStringValue(value);
long iValue = dateMathParser.parse(value, System.currentTimeMillis());
long iSim = (long) (minSim * dFuzzyFactor);
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
iValue - iSim,
@ -231,39 +233,44 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
@Override
public Query fieldQuery(String value, @Nullable QueryParseContext context) {
long lValue = parseStringValue(value);
long now = context == null ? System.currentTimeMillis() : context.nowInMillis();
long lValue = dateMathParser.parse(value, now);
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
lValue, lValue, true, true);
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
long now = context == null ? System.currentTimeMillis() : context.nowInMillis();
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : parseStringValue(lowerTerm),
upperTerm == null ? null : includeUpper ? parseUpperInclusiveStringValue(upperTerm) : parseStringValue(upperTerm),
lowerTerm == null ? null : dateMathParser.parse(lowerTerm, now),
upperTerm == null ? null : (includeUpper && parseUpperInclusive) ? dateMathParser.parseUpperInclusive(upperTerm, now) : dateMathParser.parse(upperTerm, now),
includeLower, includeUpper);
}
@Override
public Filter fieldFilter(String value, @Nullable QueryParseContext context) {
long lValue = parseStringValue(value);
long now = context == null ? System.currentTimeMillis() : context.nowInMillis();
long lValue = dateMathParser.parse(value, now);
return NumericRangeFilter.newLongRange(names.indexName(), precisionStep,
lValue, lValue, true, true);
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
long now = context == null ? System.currentTimeMillis() : context.nowInMillis();
return NumericRangeFilter.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : parseStringValue(lowerTerm),
upperTerm == null ? null : includeUpper ? parseUpperInclusiveStringValue(upperTerm) : parseStringValue(upperTerm),
lowerTerm == null ? null : dateMathParser.parse(lowerTerm, now),
upperTerm == null ? null : (includeUpper && parseUpperInclusive) ? dateMathParser.parseUpperInclusive(upperTerm, now) : dateMathParser.parse(upperTerm, now),
includeLower, includeUpper);
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
long now = context == null ? System.currentTimeMillis() : context.nowInMillis();
return NumericRangeFieldDataFilter.newLongRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : parseStringValue(lowerTerm),
upperTerm == null ? null : includeUpper ? parseUpperInclusiveStringValue(upperTerm) : parseStringValue(upperTerm),
lowerTerm == null ? null : dateMathParser.parse(lowerTerm, now),
upperTerm == null ? null : (includeUpper && parseUpperInclusive) ? dateMathParser.parseUpperInclusive(upperTerm, now) : dateMathParser.parse(upperTerm, now),
includeLower, includeUpper);
}
@ -394,7 +401,7 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
}
}
protected long parseStringValue(String value) {
private long parseStringValue(String value) {
try {
return dateTimeFormatter.parser().parseMillis(value);
} catch (RuntimeException e) {
@ -406,36 +413,4 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
}
}
}
protected long parseUpperInclusiveStringValue(String value) {
if (!parseUpperInclusive) {
return parseStringValue(value);
}
try {
MutableDateTime dateTime = new MutableDateTime(3000, 12, 31, 23, 59, 59, 999, DateTimeZone.UTC);
int location = dateTimeFormatter.parser().parseInto(dateTime, value, 0);
// if we parsed all the string value, we are good
if (location == value.length()) {
return dateTime.getMillis();
}
// if we did not manage to parse, or the year is really high year which is unreasonable
// see if its a number
if (location <= 0 || dateTime.getYear() > 5000) {
try {
long time = Long.parseLong(value);
return timeUnit.toMillis(time);
} catch (NumberFormatException e1) {
throw new MapperParsingException("failed to parse date field [" + value + "], tried both date format [" + dateTimeFormatter.format() + "], and timestamp number");
}
}
return dateTime.getMillis();
} catch (RuntimeException e) {
try {
long time = Long.parseLong(value);
return timeUnit.toMillis(time);
} catch (NumberFormatException e1) {
throw new MapperParsingException("failed to parse date field [" + value + "], tried both date format [" + dateTimeFormatter.format() + "], and timestamp number", e);
}
}
}
}

View File

@ -164,7 +164,7 @@ public class DoubleFieldMapper extends NumberFieldMapper<Double> {
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeQuery.newDoubleRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Double.parseDouble(lowerTerm),
upperTerm == null ? null : Double.parseDouble(upperTerm),
@ -179,7 +179,7 @@ public class DoubleFieldMapper extends NumberFieldMapper<Double> {
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFilter.newDoubleRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Double.parseDouble(lowerTerm),
upperTerm == null ? null : Double.parseDouble(upperTerm),
@ -191,7 +191,7 @@ public class DoubleFieldMapper extends NumberFieldMapper<Double> {
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFieldDataFilter.newDoubleRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : Double.parseDouble(lowerTerm),
upperTerm == null ? null : Double.parseDouble(upperTerm),

View File

@ -163,7 +163,7 @@ public class FloatFieldMapper extends NumberFieldMapper<Float> {
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeQuery.newFloatRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Float.parseFloat(lowerTerm),
upperTerm == null ? null : Float.parseFloat(upperTerm),
@ -178,7 +178,7 @@ public class FloatFieldMapper extends NumberFieldMapper<Float> {
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFilter.newFloatRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Float.parseFloat(lowerTerm),
upperTerm == null ? null : Float.parseFloat(upperTerm),
@ -186,7 +186,7 @@ public class FloatFieldMapper extends NumberFieldMapper<Float> {
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFieldDataFilter.newFloatRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : Float.parseFloat(lowerTerm),
upperTerm == null ? null : Float.parseFloat(upperTerm),

View File

@ -168,7 +168,7 @@ public class IntegerFieldMapper extends NumberFieldMapper<Integer> {
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeQuery.newIntRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Integer.parseInt(lowerTerm),
upperTerm == null ? null : Integer.parseInt(upperTerm),
@ -183,7 +183,7 @@ public class IntegerFieldMapper extends NumberFieldMapper<Integer> {
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFilter.newIntRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Integer.parseInt(lowerTerm),
upperTerm == null ? null : Integer.parseInt(upperTerm),
@ -191,7 +191,7 @@ public class IntegerFieldMapper extends NumberFieldMapper<Integer> {
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFieldDataFilter.newIntRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : Integer.parseInt(lowerTerm),
upperTerm == null ? null : Integer.parseInt(upperTerm),

View File

@ -168,7 +168,7 @@ public class LongFieldMapper extends NumberFieldMapper<Long> {
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Long.parseLong(lowerTerm),
upperTerm == null ? null : Long.parseLong(upperTerm),
@ -183,7 +183,7 @@ public class LongFieldMapper extends NumberFieldMapper<Long> {
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFilter.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Long.parseLong(lowerTerm),
upperTerm == null ? null : Long.parseLong(upperTerm),
@ -191,7 +191,7 @@ public class LongFieldMapper extends NumberFieldMapper<Long> {
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFieldDataFilter.newLongRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : Long.parseLong(lowerTerm),
upperTerm == null ? null : Long.parseLong(upperTerm),

View File

@ -166,7 +166,7 @@ public abstract class NumberFieldMapper<T extends Number> extends AbstractFieldM
*/
@Override
public Query fieldQuery(String value, @Nullable QueryParseContext context) {
return rangeQuery(value, value, true, true);
return rangeQuery(value, value, true, true, context);
}
@Override
@ -181,19 +181,19 @@ public abstract class NumberFieldMapper<T extends Number> extends AbstractFieldM
*/
@Override
public Filter fieldFilter(String value, @Nullable QueryParseContext context) {
return rangeFilter(value, value, true, true);
return rangeFilter(value, value, true, true, context);
}
@Override
public abstract Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper);
public abstract Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context);
@Override
public abstract Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper);
public abstract Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context);
/**
* A range filter based on the field data cache.
*/
public abstract Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper);
public abstract Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context);
/**
* Override the default behavior (to return the string, and return the actual Number instance).

View File

@ -168,7 +168,7 @@ public class ShortFieldMapper extends NumberFieldMapper<Short> {
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeQuery.newIntRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Integer.parseInt(lowerTerm),
upperTerm == null ? null : Integer.parseInt(upperTerm),
@ -183,7 +183,7 @@ public class ShortFieldMapper extends NumberFieldMapper<Short> {
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFilter.newIntRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Integer.parseInt(lowerTerm),
upperTerm == null ? null : Integer.parseInt(upperTerm),
@ -191,7 +191,7 @@ public class ShortFieldMapper extends NumberFieldMapper<Short> {
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFieldDataFilter.newShortRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : Short.parseShort(lowerTerm),
upperTerm == null ? null : Short.parseShort(upperTerm),

View File

@ -26,6 +26,7 @@ import org.apache.lucene.search.NumericRangeFilter;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.util.NumericUtils;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Numbers;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -37,6 +38,7 @@ import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.mapper.*;
import org.elasticsearch.index.mapper.core.FloatFieldMapper;
import org.elasticsearch.index.mapper.core.NumberFieldMapper;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.search.NumericRangeFieldDataFilter;
import java.io.IOException;
@ -165,7 +167,7 @@ public class BoostFieldMapper extends NumberFieldMapper<Float> implements Intern
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeQuery.newFloatRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Float.parseFloat(lowerTerm),
upperTerm == null ? null : Float.parseFloat(upperTerm),
@ -173,7 +175,7 @@ public class BoostFieldMapper extends NumberFieldMapper<Float> implements Intern
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFilter.newFloatRange(names.indexName(), precisionStep,
lowerTerm == null ? null : Float.parseFloat(lowerTerm),
upperTerm == null ? null : Float.parseFloat(upperTerm),
@ -181,7 +183,7 @@ public class BoostFieldMapper extends NumberFieldMapper<Float> implements Intern
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFieldDataFilter.newFloatRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : Float.parseFloat(lowerTerm),
upperTerm == null ? null : Float.parseFloat(upperTerm),

View File

@ -25,6 +25,7 @@ import org.apache.lucene.document.Fieldable;
import org.apache.lucene.search.*;
import org.apache.lucene.util.NumericUtils;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Numbers;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -37,6 +38,7 @@ import org.elasticsearch.index.field.data.FieldDataType;
import org.elasticsearch.index.mapper.*;
import org.elasticsearch.index.mapper.core.LongFieldMapper;
import org.elasticsearch.index.mapper.core.NumberFieldMapper;
import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.index.search.NumericRangeFieldDataFilter;
import java.io.IOException;
@ -201,7 +203,7 @@ public class IpFieldMapper extends NumberFieldMapper<Long> {
}
@Override
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Query rangeQuery(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : ipToLong(lowerTerm),
upperTerm == null ? null : ipToLong(upperTerm),
@ -209,7 +211,7 @@ public class IpFieldMapper extends NumberFieldMapper<Long> {
}
@Override
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFilter.newLongRange(names.indexName(), precisionStep,
lowerTerm == null ? null : ipToLong(lowerTerm),
upperTerm == null ? null : ipToLong(upperTerm),
@ -217,7 +219,7 @@ public class IpFieldMapper extends NumberFieldMapper<Long> {
}
@Override
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper) {
public Filter rangeFilter(FieldDataCache fieldDataCache, String lowerTerm, String upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
return NumericRangeFieldDataFilter.newLongRange(fieldDataCache, names.indexName(),
lowerTerm == null ? null : ipToLong(lowerTerm),
upperTerm == null ? null : ipToLong(upperTerm),

View File

@ -75,7 +75,7 @@ public class ExistsFilterParser implements FilterParser {
Filter filter = null;
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {
filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true);
filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true, parseContext);
}
if (filter == null) {
filter = new TermRangeFilter(fieldName, null, null, true, true);

View File

@ -76,7 +76,7 @@ public class MissingFilterParser implements FilterParser {
Filter filter = null;
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {
filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true);
filter = smartNameFieldMappers.mapper().rangeFilter(null, null, true, true, parseContext);
}
if (filter == null) {
filter = new TermRangeFilter(fieldName, null, null, true, true);

View File

@ -119,7 +119,7 @@ public class NumericRangeFilterParser implements FilterParser {
if (!(mapper instanceof NumberFieldMapper)) {
throw new QueryParsingException(parseContext.index(), "Field [" + fieldName + "] is not a numeric type");
}
Filter filter = ((NumberFieldMapper) mapper).rangeFilter(parseContext.indexCache().fieldData(), from, to, includeLower, includeUpper);
Filter filter = ((NumberFieldMapper) mapper).rangeFilter(parseContext.indexCache().fieldData(), from, to, includeLower, includeUpper, parseContext);
if (cache) {
filter = parseContext.cacheFilter(filter, cacheKey);

View File

@ -293,4 +293,12 @@ public class QueryParseContext {
}
return lookup;
}
public long nowInMillis() {
SearchContext current = SearchContext.current();
if (current != null) {
return current.nowInMillis();
}
return System.currentTimeMillis();
}
}

View File

@ -116,7 +116,7 @@ public class RangeFilterParser implements FilterParser {
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null) {
if (smartNameFieldMappers.hasMapper()) {
filter = smartNameFieldMappers.mapper().rangeFilter(from, to, includeLower, includeUpper);
filter = smartNameFieldMappers.mapper().rangeFilter(from, to, includeLower, includeUpper, parseContext);
}
}
if (filter == null) {

View File

@ -108,7 +108,7 @@ public class RangeQueryParser implements QueryParser {
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
if (smartNameFieldMappers != null) {
if (smartNameFieldMappers.hasMapper()) {
query = smartNameFieldMappers.mapper().rangeQuery(from, to, includeLower, includeUpper);
query = smartNameFieldMappers.mapper().rangeQuery(from, to, includeLower, includeUpper, parseContext);
}
}
if (query == null) {

View File

@ -126,4 +126,19 @@ public class SimpleSearchTests extends AbstractNodesTests {
searchResponse = client.prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-05").lt("2010-01-06")).execute().actionGet();
assertThat(searchResponse.hits().totalHits(), equalTo(1l));
}
@Test
public void simpleDateMathTests() throws Exception {
client.admin().indices().prepareDelete().execute().actionGet();
client.admin().indices().prepareCreate("test").setSettings(ImmutableSettings.settingsBuilder()).execute().actionGet();
client.prepareIndex("test", "type1", "1").setSource("field", "2010-01-05T02:00").execute().actionGet();
client.prepareIndex("test", "type1", "2").setSource("field", "2010-01-06T02:00").execute().actionGet();
client.admin().indices().prepareRefresh().execute().actionGet();
SearchResponse searchResponse = client.prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-03||+2d").lte("2010-01-04||+2d")).execute().actionGet();
assertThat(searchResponse.hits().totalHits(), equalTo(2l));
searchResponse = client.prepareSearch("test").setQuery(QueryBuilders.queryString("field:[2010-01-03||+2d TO 2010-01-04||+2d]")).execute().actionGet();
assertThat(searchResponse.hits().totalHits(), equalTo(2l));
}
}

View File

@ -0,0 +1,43 @@
package org.elasticsearch.test.unit.common.joda;
import org.elasticsearch.common.joda.DateMathParser;
import org.elasticsearch.common.joda.Joda;
import org.testng.annotations.Test;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
/**
*/
@Test
public class DateMathParserTests {
@Test
public void dataMathTests() {
DateMathParser parser = new DateMathParser(Joda.forPattern("dateOptionalTime"), TimeUnit.MILLISECONDS);
assertThat(parser.parse("now", 0), equalTo(0l));
assertThat(parser.parse("now+m", 0), equalTo(TimeUnit.MINUTES.toMillis(1)));
assertThat(parser.parse("now+1m", 0), equalTo(TimeUnit.MINUTES.toMillis(1)));
assertThat(parser.parse("now+11m", 0), equalTo(TimeUnit.MINUTES.toMillis(11)));
assertThat(parser.parse("now+1d", 0), equalTo(TimeUnit.DAYS.toMillis(1)));
assertThat(parser.parse("now+1m+1s", 0), equalTo(TimeUnit.MINUTES.toMillis(1) + TimeUnit.SECONDS.toMillis(1)));
assertThat(parser.parse("now+1m-1s", 0), equalTo(TimeUnit.MINUTES.toMillis(1) - TimeUnit.SECONDS.toMillis(1)));
assertThat(parser.parse("now+1m+1s/m", 0), equalTo(TimeUnit.MINUTES.toMillis(1)));
assertThat(parser.parseUpperInclusive("now+1m+1s/m", 0), equalTo(TimeUnit.MINUTES.toMillis(2)));
}
@Test
public void actualDateTests() {
DateMathParser parser = new DateMathParser(Joda.forPattern("dateOptionalTime"), TimeUnit.MILLISECONDS);
assertThat(parser.parse("1970-01-01", 0), equalTo(0l));
assertThat(parser.parse("1970-01-01||+1m", 0), equalTo(TimeUnit.MINUTES.toMillis(1)));
assertThat(parser.parse("1970-01-01||+1m+1s", 0), equalTo(TimeUnit.MINUTES.toMillis(1) + TimeUnit.SECONDS.toMillis(1)));
}
}