Search: add time zone setting for relative date math in range filter/query
Filters and Queries now supports `time_zone` parameter which defines which time zone should be applied to the query or filter to convert it to UTC time based value. When applied on `date` fields the `range` filter and queries accept also a `time_zone` parameter. The `time_zone` parameter will be applied to your input lower and upper bounds and will move them to UTC time based date: [source,js] -------------------------------------------------- { "constant_score": { "filter": { "range" : { "born" : { "gte": "2012-01-01", "lte": "now", "time_zone": "+1:00" } } } } } { "range" : { "born" : { "gte": "2012-01-01", "lte": "now", "time_zone": "+1:00" } } } -------------------------------------------------- In the above examples, `gte` will be actually moved to `2011-12-31T23:00:00` UTC date. NOTE: if you give a date with a timezone explicitly defined and use the `time_zone` parameter, `time_zone` will be ignored. For example, setting `from` to `2012-01-01T00:00:00+01:00` with `"time_zone":"+10:00"` will still use `+01:00` time zone. Closes #3729.
This commit is contained in:
parent
d2fea5378a
commit
873a45eaba
|
@ -30,6 +30,34 @@ The `range` filter accepts the following parameters:
|
|||
`lte`:: Less-than or equal to
|
||||
`lt`:: Less-than
|
||||
|
||||
coming[1.4.0]
|
||||
|
||||
When applied on `date` fields the `range` filter accepts also a `time_zone` parameter.
|
||||
The `time_zone` parameter will be applied to your input lower and upper bounds and will
|
||||
move them to UTC time based date:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{
|
||||
"constant_score": {
|
||||
"filter": {
|
||||
"range" : {
|
||||
"born" : {
|
||||
"gte": "2012-01-01",
|
||||
"lte": "now",
|
||||
"time_zone": "+1:00"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
In the above example, `gte` will be actually moved to `2011-12-31T23:00:00` UTC date.
|
||||
|
||||
NOTE: if you give a date with a timezone explicitly defined and use the `time_zone` parameter, `time_zone` will be
|
||||
ignored. For example, setting `from` to `2012-01-01T00:00:00+01:00` with `"time_zone":"+10:00"` will still use `+01:00` time zone.
|
||||
|
||||
[float]
|
||||
==== Execution
|
||||
|
||||
|
|
|
@ -29,3 +29,27 @@ The `range` query accepts the following parameters:
|
|||
`lt`:: Less-than
|
||||
`boost`:: Sets the boost value of the query, defaults to `1.0`
|
||||
|
||||
coming[1.4.0]
|
||||
|
||||
When applied on `date` fields the `range` filter accepts also a `time_zone` parameter.
|
||||
The `time_zone` parameter will be applied to your input lower and upper bounds and will
|
||||
move them to UTC time based date:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{
|
||||
"range" : {
|
||||
"born" : {
|
||||
"gte": "2012-01-01",
|
||||
"lte": "now",
|
||||
"time_zone": "+1:00"
|
||||
}
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
||||
In the above example, `gte` will be actually moved to `2011-12-31T23:00:00` UTC date.
|
||||
|
||||
NOTE: if you give a date with a timezone explicitly defined and use the `time_zone` parameter, `time_zone` will be
|
||||
ignored. For example, setting `from` to `2012-01-01T00:00:00+01:00` with `"time_zone":"+10:00"` will still use `+01:00` time zone.
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ package org.elasticsearch.common.joda;
|
|||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.MutableDateTime;
|
||||
import org.joda.time.format.DateTimeFormatter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
|
@ -39,14 +41,26 @@ public class DateMathParser {
|
|||
}
|
||||
|
||||
public long parse(String text, long now) {
|
||||
return parse(text, now, false);
|
||||
return parse(text, now, false, null);
|
||||
}
|
||||
|
||||
public long parse(String text, long now, DateTimeZone timeZone) {
|
||||
return parse(text, now, false, timeZone);
|
||||
}
|
||||
|
||||
public long parseRoundCeil(String text, long now) {
|
||||
return parse(text, now, true);
|
||||
return parse(text, now, true, null);
|
||||
}
|
||||
|
||||
public long parseRoundCeil(String text, long now, DateTimeZone timeZone) {
|
||||
return parse(text, now, true, timeZone);
|
||||
}
|
||||
|
||||
public long parse(String text, long now, boolean roundCeil) {
|
||||
return parse(text, now, roundCeil, null);
|
||||
}
|
||||
|
||||
public long parse(String text, long now, boolean roundCeil, DateTimeZone timeZone) {
|
||||
long time;
|
||||
String mathString;
|
||||
if (text.startsWith("now")) {
|
||||
|
@ -63,9 +77,9 @@ public class DateMathParser {
|
|||
mathString = text.substring(index + 2);
|
||||
}
|
||||
if (roundCeil) {
|
||||
time = parseRoundCeilStringValue(parseString);
|
||||
time = parseRoundCeilStringValue(parseString, timeZone);
|
||||
} else {
|
||||
time = parseStringValue(parseString);
|
||||
time = parseStringValue(parseString, timeZone);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,11 +229,29 @@ public class DateMathParser {
|
|||
return dateTime.getMillis();
|
||||
}
|
||||
|
||||
private long parseStringValue(String value) {
|
||||
/**
|
||||
* Get a DateTimeFormatter parser applying timezone if any.
|
||||
*/
|
||||
public static DateTimeFormatter getDateTimeFormatterParser(FormatDateTimeFormatter dateTimeFormatter, DateTimeZone timeZone) {
|
||||
if (dateTimeFormatter == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
DateTimeFormatter parser = dateTimeFormatter.parser();
|
||||
if (timeZone != null) {
|
||||
parser = parser.withZone(timeZone);
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
|
||||
private long parseStringValue(String value, DateTimeZone timeZone) {
|
||||
try {
|
||||
return dateTimeFormatter.parser().parseMillis(value);
|
||||
DateTimeFormatter parser = getDateTimeFormatterParser(dateTimeFormatter, timeZone);
|
||||
return parser.parseMillis(value);
|
||||
} catch (RuntimeException e) {
|
||||
try {
|
||||
// When date is given as a numeric value, it's a date in ms since epoch
|
||||
// By definition, it's a UTC date.
|
||||
long time = Long.parseLong(value);
|
||||
return timeUnit.toMillis(time);
|
||||
} catch (NumberFormatException e1) {
|
||||
|
@ -228,14 +260,15 @@ public class DateMathParser {
|
|||
}
|
||||
}
|
||||
|
||||
private long parseRoundCeilStringValue(String value) {
|
||||
private long parseRoundCeilStringValue(String value, DateTimeZone timeZone) {
|
||||
try {
|
||||
// we create a date time for inclusive upper range, we "include" by default the day level data
|
||||
// so something like 2011-01-01 will include the full first day of 2011.
|
||||
// we also use 1970-01-01 as the base for it so we can handle searches like 10:12:55 (just time)
|
||||
// since when we index those, the base is 1970-01-01
|
||||
MutableDateTime dateTime = new MutableDateTime(1970, 1, 1, 23, 59, 59, 999, DateTimeZone.UTC);
|
||||
int location = dateTimeFormatter.parser().parseInto(dateTime, value, 0);
|
||||
DateTimeFormatter parser = getDateTimeFormatterParser(dateTimeFormatter, timeZone);
|
||||
int location = parser.parseInto(dateTime, value, 0);
|
||||
// if we parsed all the string value, we are good
|
||||
if (location == value.length()) {
|
||||
return dateTime.getMillis();
|
||||
|
@ -260,4 +293,20 @@ public class DateMathParser {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTimeZone parseZone(String text) throws IOException {
|
||||
int index = text.indexOf(':');
|
||||
if (index != -1) {
|
||||
int beginIndex = text.charAt(0) == '+' ? 1 : 0;
|
||||
// format like -02:30
|
||||
return DateTimeZone.forOffsetHoursMinutes(
|
||||
Integer.parseInt(text.substring(beginIndex, index)),
|
||||
Integer.parseInt(text.substring(index + 1))
|
||||
);
|
||||
} else {
|
||||
// id, listed here: http://joda-time.sourceforge.net/timezones.html
|
||||
return DateTimeZone.forID(text);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.elasticsearch.index.mapper.core.LongFieldMapper.CustomLongNumericFiel
|
|||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.index.search.NumericRangeFieldDataFilter;
|
||||
import org.elasticsearch.index.similarity.SimilarityProvider;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
@ -298,16 +299,20 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
|
|||
}
|
||||
|
||||
public long parseToMilliseconds(Object value, @Nullable QueryParseContext context, boolean includeUpper) {
|
||||
return parseToMilliseconds(value, context, includeUpper, null);
|
||||
}
|
||||
|
||||
public long parseToMilliseconds(Object value, @Nullable QueryParseContext context, boolean includeUpper, @Nullable DateTimeZone zone) {
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
long now = context == null ? System.currentTimeMillis() : context.nowInMillis();
|
||||
return includeUpper && roundCeil ? dateMathParser.parseRoundCeil(convertToString(value), now) : dateMathParser.parse(convertToString(value), now);
|
||||
return parseToMilliseconds(convertToString(value), context, includeUpper, zone);
|
||||
}
|
||||
|
||||
public long parseToMilliseconds(String value, @Nullable QueryParseContext context, boolean includeUpper) {
|
||||
public long parseToMilliseconds(String value, @Nullable QueryParseContext context, boolean includeUpper, @Nullable DateTimeZone zone) {
|
||||
long now = context == null ? System.currentTimeMillis() : context.nowInMillis();
|
||||
return includeUpper && roundCeil ? dateMathParser.parseRoundCeil(value, now) : dateMathParser.parse(value, now);
|
||||
long time = includeUpper && roundCeil ? dateMathParser.parseRoundCeil(value, now, zone) : dateMathParser.parse(value, now, zone);
|
||||
return time;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -319,58 +324,37 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
|
|||
|
||||
@Override
|
||||
public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
|
||||
return rangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, null, context);
|
||||
}
|
||||
|
||||
public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable DateTimeZone timeZone, @Nullable QueryParseContext context) {
|
||||
return NumericRangeQuery.newLongRange(names.indexName(), precisionStep,
|
||||
lowerTerm == null ? null : parseToMilliseconds(lowerTerm, context),
|
||||
upperTerm == null ? null : parseToMilliseconds(upperTerm, context, includeUpper),
|
||||
lowerTerm == null ? null : parseToMilliseconds(lowerTerm, context, false, timeZone),
|
||||
upperTerm == null ? null : parseToMilliseconds(upperTerm, context, includeUpper, timeZone),
|
||||
includeLower, includeUpper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter rangeFilter(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
|
||||
return rangeFilter(lowerTerm, upperTerm, includeLower, includeUpper, context, false);
|
||||
return rangeFilter(lowerTerm, upperTerm, includeLower, includeUpper, null, context, false);
|
||||
}
|
||||
|
||||
public Filter rangeFilter(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context, boolean explicitCaching) {
|
||||
boolean cache = explicitCaching;
|
||||
Long lowerVal = null;
|
||||
Long upperVal = null;
|
||||
if (lowerTerm != null) {
|
||||
if (lowerTerm instanceof Number) {
|
||||
lowerVal = ((Number) lowerTerm).longValue();
|
||||
} else {
|
||||
String value = convertToString(lowerTerm);
|
||||
cache = explicitCaching || !hasNowExpressionWithNoRounding(value);
|
||||
lowerVal = parseToMilliseconds(value, context, false);
|
||||
}
|
||||
}
|
||||
if (upperTerm != null) {
|
||||
if (upperTerm instanceof Number) {
|
||||
upperVal = ((Number) upperTerm).longValue();
|
||||
} else {
|
||||
String value = convertToString(upperTerm);
|
||||
cache = explicitCaching || !hasNowExpressionWithNoRounding(value);
|
||||
upperVal = parseToMilliseconds(value, context, includeUpper);
|
||||
}
|
||||
}
|
||||
|
||||
Filter filter = NumericRangeFilter.newLongRange(
|
||||
names.indexName(), precisionStep, lowerVal, upperVal, includeLower, includeUpper
|
||||
);
|
||||
if (!cache) {
|
||||
// We don't cache range filter if `now` date expression is used and also when a compound filter wraps
|
||||
// a range filter with a `now` date expressions.
|
||||
return NoCacheFilter.wrap(filter);
|
||||
} else {
|
||||
return filter;
|
||||
}
|
||||
public Filter rangeFilter(Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable DateTimeZone timeZone, @Nullable QueryParseContext context, boolean explicitCaching) {
|
||||
return rangeFilter(null, lowerTerm, upperTerm, includeLower, includeUpper, timeZone, context, explicitCaching);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter rangeFilter(QueryParseContext parseContext, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context) {
|
||||
return rangeFilter(parseContext, lowerTerm, upperTerm, includeLower, includeUpper, context, false);
|
||||
return rangeFilter(parseContext, lowerTerm, upperTerm, includeLower, includeUpper, null, context, false);
|
||||
}
|
||||
|
||||
public Filter rangeFilter(QueryParseContext parseContext, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable QueryParseContext context, boolean explicitCaching) {
|
||||
/*
|
||||
* `timeZone` parameter is only applied when:
|
||||
* - not null
|
||||
* - the object to parse is a String (does not apply to ms since epoch which are UTC based time values)
|
||||
* - the String to parse does not have already a timezone defined (ie. `2014-01-01T00:00:00+03:00`)
|
||||
*/
|
||||
public Filter rangeFilter(QueryParseContext parseContext, Object lowerTerm, Object upperTerm, boolean includeLower, boolean includeUpper, @Nullable DateTimeZone timeZone, @Nullable QueryParseContext context, boolean explicitCaching) {
|
||||
boolean cache = explicitCaching;
|
||||
Long lowerVal = null;
|
||||
Long upperVal = null;
|
||||
|
@ -380,7 +364,7 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
|
|||
} else {
|
||||
String value = convertToString(lowerTerm);
|
||||
cache = explicitCaching || !hasNowExpressionWithNoRounding(value);
|
||||
lowerVal = parseToMilliseconds(value, context, false);
|
||||
lowerVal = parseToMilliseconds(value, context, false, timeZone);
|
||||
}
|
||||
}
|
||||
if (upperTerm != null) {
|
||||
|
@ -389,13 +373,21 @@ public class DateFieldMapper extends NumberFieldMapper<Long> {
|
|||
} else {
|
||||
String value = convertToString(upperTerm);
|
||||
cache = explicitCaching || !hasNowExpressionWithNoRounding(value);
|
||||
upperVal = parseToMilliseconds(value, context, includeUpper);
|
||||
upperVal = parseToMilliseconds(value, context, includeUpper, timeZone);
|
||||
}
|
||||
}
|
||||
|
||||
Filter filter = NumericRangeFieldDataFilter.newLongRange(
|
||||
(IndexNumericFieldData) parseContext.getForField(this), lowerVal,upperVal, includeLower, includeUpper
|
||||
);
|
||||
Filter filter;
|
||||
if (parseContext != null) {
|
||||
filter = NumericRangeFieldDataFilter.newLongRange(
|
||||
(IndexNumericFieldData) parseContext.getForField(this), lowerVal,upperVal, includeLower, includeUpper
|
||||
);
|
||||
} else {
|
||||
filter = NumericRangeFilter.newLongRange(
|
||||
names.indexName(), precisionStep, lowerVal, upperVal, includeLower, includeUpper
|
||||
);
|
||||
}
|
||||
|
||||
if (!cache) {
|
||||
// We don't cache range filter if `now` date expression is used and also when a compound filter wraps
|
||||
// a range filter with a `now` date expressions.
|
||||
|
|
|
@ -35,6 +35,7 @@ public class RangeFilterBuilder extends BaseFilterBuilder {
|
|||
private Object from;
|
||||
|
||||
private Object to;
|
||||
private String timeZone;
|
||||
|
||||
private boolean includeLower = true;
|
||||
|
||||
|
@ -371,6 +372,14 @@ public class RangeFilterBuilder extends BaseFilterBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* In case of date field, we can adjust the from/to fields using a timezone
|
||||
*/
|
||||
public RangeFilterBuilder timeZone(String timeZone) {
|
||||
this.timeZone = timeZone;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(RangeFilterParser.NAME);
|
||||
|
@ -378,6 +387,9 @@ public class RangeFilterBuilder extends BaseFilterBuilder {
|
|||
builder.startObject(name);
|
||||
builder.field("from", from);
|
||||
builder.field("to", to);
|
||||
if (timeZone != null) {
|
||||
builder.field("time_zone", timeZone);
|
||||
}
|
||||
builder.field("include_lower", includeLower);
|
||||
builder.field("include_upper", includeUpper);
|
||||
builder.endObject();
|
||||
|
@ -397,4 +409,4 @@ public class RangeFilterBuilder extends BaseFilterBuilder {
|
|||
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package org.elasticsearch.index.query;
|
|||
import org.apache.lucene.search.Filter;
|
||||
import org.apache.lucene.search.TermRangeFilter;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.lucene.BytesRefs;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.cache.filter.support.CacheKeyFilter;
|
||||
|
@ -29,6 +30,7 @@ import org.elasticsearch.index.mapper.FieldMapper;
|
|||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.core.DateFieldMapper;
|
||||
import org.elasticsearch.index.mapper.core.NumberFieldMapper;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -61,6 +63,7 @@ public class RangeFilterParser implements FilterParser {
|
|||
Object to = null;
|
||||
boolean includeLower = true;
|
||||
boolean includeUpper = true;
|
||||
DateTimeZone timeZone = null;
|
||||
String execution = "index";
|
||||
|
||||
String filterName = null;
|
||||
|
@ -95,6 +98,8 @@ public class RangeFilterParser implements FilterParser {
|
|||
} else if ("lte".equals(currentFieldName) || "le".equals(currentFieldName)) {
|
||||
to = parser.objectBytes();
|
||||
includeUpper = true;
|
||||
} else if ("time_zone".equals(currentFieldName) || "timeZone".equals(currentFieldName)) {
|
||||
timeZone = DateMathParser.parseZone(parser.text());
|
||||
} else {
|
||||
throw new QueryParsingException(parseContext.index(), "[range] filter does not support [" + currentFieldName + "]");
|
||||
}
|
||||
|
@ -130,8 +135,14 @@ public class RangeFilterParser implements FilterParser {
|
|||
}
|
||||
FieldMapper mapper = smartNameFieldMappers.mapper();
|
||||
if (mapper instanceof DateFieldMapper) {
|
||||
filter = ((DateFieldMapper) mapper).rangeFilter(from, to, includeLower, includeUpper, parseContext, explicitlyCached);
|
||||
if ((from instanceof Number || to instanceof Number) && timeZone != null) {
|
||||
throw new QueryParsingException(parseContext.index(), "[range] time_zone when using ms since epoch format as it's UTC based can not be applied to [" + fieldName + "]");
|
||||
}
|
||||
filter = ((DateFieldMapper) mapper).rangeFilter(from, to, includeLower, includeUpper, timeZone, parseContext, explicitlyCached);
|
||||
} else {
|
||||
if (timeZone != null) {
|
||||
throw new QueryParsingException(parseContext.index(), "[range] time_zone can not be applied to non date field [" + fieldName + "]");
|
||||
}
|
||||
filter = mapper.rangeFilter(from, to, includeLower, includeUpper, parseContext);
|
||||
}
|
||||
} else if ("fielddata".equals(execution)) {
|
||||
|
@ -143,8 +154,14 @@ public class RangeFilterParser implements FilterParser {
|
|||
throw new QueryParsingException(parseContext.index(), "[range] filter field [" + fieldName + "] is not a numeric type");
|
||||
}
|
||||
if (mapper instanceof DateFieldMapper) {
|
||||
filter = ((DateFieldMapper) mapper).rangeFilter(parseContext, from, to, includeLower, includeUpper, parseContext, explicitlyCached);
|
||||
if ((from instanceof Number || to instanceof Number) && timeZone != null) {
|
||||
throw new QueryParsingException(parseContext.index(), "[range] time_zone when using ms since epoch format as it's UTC based can not be applied to [" + fieldName + "]");
|
||||
}
|
||||
filter = ((DateFieldMapper) mapper).rangeFilter(parseContext, from, to, includeLower, includeUpper, timeZone, parseContext, explicitlyCached);
|
||||
} else {
|
||||
if (timeZone != null) {
|
||||
throw new QueryParsingException(parseContext.index(), "[range] time_zone can not be applied to non date field [" + fieldName + "]");
|
||||
}
|
||||
filter = ((NumberFieldMapper) mapper).rangeFilter(parseContext, from, to, includeLower, includeUpper, parseContext);
|
||||
}
|
||||
} else {
|
||||
|
@ -170,4 +187,4 @@ public class RangeFilterParser implements FilterParser {
|
|||
}
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ public class RangeQueryBuilder extends BaseQueryBuilder implements MultiTermQuer
|
|||
private Object from;
|
||||
|
||||
private Object to;
|
||||
private String timeZone;
|
||||
|
||||
private boolean includeLower = true;
|
||||
|
||||
|
@ -398,12 +399,23 @@ public class RangeQueryBuilder extends BaseQueryBuilder implements MultiTermQuer
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* In case of date field, we can adjust the from/to fields using a timezone
|
||||
*/
|
||||
public RangeQueryBuilder timeZone(String preZone) {
|
||||
this.timeZone = preZone;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject(RangeQueryParser.NAME);
|
||||
builder.startObject(name);
|
||||
builder.field("from", from);
|
||||
builder.field("to", to);
|
||||
if (timeZone != null) {
|
||||
builder.field("time_zone", timeZone);
|
||||
}
|
||||
builder.field("include_lower", includeLower);
|
||||
builder.field("include_upper", includeUpper);
|
||||
if (boost != -1) {
|
||||
|
|
|
@ -22,9 +22,13 @@ package org.elasticsearch.index.query;
|
|||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.TermRangeQuery;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.lucene.BytesRefs;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.mapper.FieldMapper;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.mapper.core.DateFieldMapper;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -64,6 +68,7 @@ public class RangeQueryParser implements QueryParser {
|
|||
Object to = null;
|
||||
boolean includeLower = true;
|
||||
boolean includeUpper = true;
|
||||
DateTimeZone timeZone = null;
|
||||
float boost = 1.0f;
|
||||
String queryName = null;
|
||||
|
||||
|
@ -94,6 +99,8 @@ public class RangeQueryParser implements QueryParser {
|
|||
} else if ("lte".equals(currentFieldName) || "le".equals(currentFieldName)) {
|
||||
to = parser.objectBytes();
|
||||
includeUpper = true;
|
||||
} else if ("time_zone".equals(currentFieldName) || "timeZone".equals(currentFieldName)) {
|
||||
timeZone = DateMathParser.parseZone(parser.text());
|
||||
} else if ("_name".equals(currentFieldName)) {
|
||||
queryName = parser.text();
|
||||
} else {
|
||||
|
@ -112,8 +119,20 @@ public class RangeQueryParser implements QueryParser {
|
|||
MapperService.SmartNameFieldMappers smartNameFieldMappers = parseContext.smartFieldMappers(fieldName);
|
||||
if (smartNameFieldMappers != null) {
|
||||
if (smartNameFieldMappers.hasMapper()) {
|
||||
//LUCENE 4 UPGRADE Mapper#rangeQuery should use bytesref as well?
|
||||
query = smartNameFieldMappers.mapper().rangeQuery(from, to, includeLower, includeUpper, parseContext);
|
||||
FieldMapper mapper = smartNameFieldMappers.mapper();
|
||||
if (mapper instanceof DateFieldMapper) {
|
||||
if ((from instanceof Number || to instanceof Number) && timeZone != null) {
|
||||
throw new QueryParsingException(parseContext.index(), "[range] time_zone when using ms since epoch format as it's UTC based can not be applied to [" + fieldName + "]");
|
||||
}
|
||||
query = ((DateFieldMapper) mapper).rangeQuery(from, to, includeLower, includeUpper, timeZone, parseContext);
|
||||
} else {
|
||||
if (timeZone != null) {
|
||||
throw new QueryParsingException(parseContext.index(), "[range] time_zone can not be applied to non date field [" + fieldName + "]");
|
||||
}
|
||||
//LUCENE 4 UPGRADE Mapper#rangeQuery should use bytesref as well?
|
||||
query = mapper.rangeQuery(from, to, includeLower, includeUpper, parseContext);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (query == null) {
|
||||
|
@ -126,4 +145,4 @@ public class RangeQueryParser implements QueryParser {
|
|||
}
|
||||
return query;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.search.aggregations.bucket.histogram;
|
|||
import com.google.common.collect.ImmutableMap;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.rounding.DateTimeUnit;
|
||||
import org.elasticsearch.common.rounding.Rounding;
|
||||
import org.elasticsearch.common.rounding.TimeZoneRounding;
|
||||
|
@ -100,11 +101,11 @@ public class DateHistogramParser implements Aggregator.Parser {
|
|||
continue;
|
||||
} else if (token == XContentParser.Token.VALUE_STRING) {
|
||||
if ("time_zone".equals(currentFieldName) || "timeZone".equals(currentFieldName)) {
|
||||
preZone = parseZone(parser.text());
|
||||
preZone = DateMathParser.parseZone(parser.text());
|
||||
} else if ("pre_zone".equals(currentFieldName) || "preZone".equals(currentFieldName)) {
|
||||
preZone = parseZone(parser.text());
|
||||
preZone = DateMathParser.parseZone(parser.text());
|
||||
} else if ("post_zone".equals(currentFieldName) || "postZone".equals(currentFieldName)) {
|
||||
postZone = parseZone(parser.text());
|
||||
postZone = DateMathParser.parseZone(parser.text());
|
||||
} else if ("pre_offset".equals(currentFieldName) || "preOffset".equals(currentFieldName)) {
|
||||
preOffset = parseOffset(parser.text());
|
||||
} else if ("post_offset".equals(currentFieldName) || "postOffset".equals(currentFieldName)) {
|
||||
|
@ -220,20 +221,4 @@ public class DateHistogramParser implements Aggregator.Parser {
|
|||
int beginIndex = offset.charAt(0) == '+' ? 1 : 0;
|
||||
return TimeValue.parseTimeValue(offset.substring(beginIndex), null).millis();
|
||||
}
|
||||
|
||||
private DateTimeZone parseZone(String text) throws IOException {
|
||||
int index = text.indexOf(':');
|
||||
if (index != -1) {
|
||||
int beginIndex = text.charAt(0) == '+' ? 1 : 0;
|
||||
// format like -02:30
|
||||
return DateTimeZone.forOffsetHoursMinutes(
|
||||
Integer.parseInt(text.substring(beginIndex, index)),
|
||||
Integer.parseInt(text.substring(index + 1))
|
||||
);
|
||||
} else {
|
||||
// id, listed here: http://joda-time.sourceforge.net/timezones.html
|
||||
return DateTimeZone.forID(text);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap;
|
|||
import org.elasticsearch.common.collect.MapBuilder;
|
||||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.joda.DateMathParser;
|
||||
import org.elasticsearch.common.rounding.DateTimeUnit;
|
||||
import org.elasticsearch.common.rounding.Rounding;
|
||||
import org.elasticsearch.common.rounding.TimeZoneRounding;
|
||||
|
@ -214,19 +215,7 @@ public class DateHistogramFacetParser extends AbstractComponent implements Facet
|
|||
if (token == XContentParser.Token.VALUE_NUMBER) {
|
||||
return DateTimeZone.forOffsetHours(parser.intValue());
|
||||
} else {
|
||||
String text = parser.text();
|
||||
int index = text.indexOf(':');
|
||||
if (index != -1) {
|
||||
int beginIndex = text.charAt(0) == '+' ? 1 : 0;
|
||||
// format like -02:30
|
||||
return DateTimeZone.forOffsetHoursMinutes(
|
||||
Integer.parseInt(text.substring(beginIndex, index)),
|
||||
Integer.parseInt(text.substring(index + 1))
|
||||
);
|
||||
} else {
|
||||
// id, listed here: http://joda-time.sourceforge.net/timezones.html
|
||||
return DateTimeZone.forID(text);
|
||||
}
|
||||
return DateMathParser.parseZone(parser.text());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.query;
|
||||
|
||||
|
||||
import org.apache.lucene.search.NumericRangeQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.elasticsearch.common.bytes.BytesArray;
|
||||
import org.elasticsearch.common.compress.CompressedString;
|
||||
import org.elasticsearch.common.inject.Injector;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.service.IndexService;
|
||||
import org.elasticsearch.test.ElasticsearchSingleNodeTest;
|
||||
import org.joda.time.DateTime;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.common.io.Streams.copyToBytesFromClasspath;
|
||||
import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class IndexQueryParserFilterDateRangeTimezoneTests extends ElasticsearchSingleNodeTest {
|
||||
|
||||
private Injector injector;
|
||||
private IndexQueryParserService queryParser;
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
IndexService indexService = createIndex("test");
|
||||
injector = indexService.injector();
|
||||
|
||||
MapperService mapperService = indexService.mapperService();
|
||||
String mapping = copyToStringFromClasspath("/org/elasticsearch/index/query/mapping.json");
|
||||
mapperService.merge("person", new CompressedString(mapping), true);
|
||||
mapperService.documentMapper("person").parse(new BytesArray(copyToBytesFromClasspath("/org/elasticsearch/index/query/data.json")));
|
||||
queryParser = injector.getInstance(IndexQueryParserService.class);
|
||||
}
|
||||
|
||||
private IndexQueryParserService queryParser() throws IOException {
|
||||
return this.queryParser;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateRangeFilterTimezone() throws IOException {
|
||||
IndexQueryParserService queryParser = queryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_filter_timezone.json");
|
||||
queryParser.parse(query).query();
|
||||
// Sadly from NoCacheFilter, we can not access to the delegate filter so we can not check
|
||||
// it's the one we are expecting
|
||||
|
||||
query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_filter_timezone_numeric_field.json");
|
||||
try {
|
||||
queryParser.parse(query).query();
|
||||
fail("A Range Filter on a numeric field with a TimeZone should raise a QueryParsingException");
|
||||
} catch (QueryParsingException e) {
|
||||
// We expect it
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateRangeQueryTimezone() throws IOException {
|
||||
long startDate = System.currentTimeMillis();
|
||||
|
||||
IndexQueryParserService queryParser = queryParser();
|
||||
String query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_query_timezone.json");
|
||||
Query parsedQuery = queryParser.parse(query).query();
|
||||
assertThat(parsedQuery, instanceOf(NumericRangeQuery.class));
|
||||
|
||||
// Min value was 2012-01-01 (UTC) so we need to remove one hour
|
||||
DateTime min = DateTime.parse("2012-01-01T00:00:00.000+01:00");
|
||||
// Max value is when we started the test. So it should be some ms from now
|
||||
DateTime max = new DateTime(startDate);
|
||||
|
||||
assertThat(((NumericRangeQuery) parsedQuery).getMin().longValue(), is(min.getMillis()));
|
||||
|
||||
// We should not have a big difference here (should be some ms)
|
||||
assertThat(((NumericRangeQuery) parsedQuery).getMax().longValue() - max.getMillis(), lessThanOrEqualTo(60000L));
|
||||
|
||||
query = copyToStringFromClasspath("/org/elasticsearch/index/query/date_range_query_timezone_numeric_field.json");
|
||||
try {
|
||||
queryParser.parse(query).query();
|
||||
fail("A Range Query on a numeric field with a TimeZone should raise a QueryParsingException");
|
||||
} catch (QueryParsingException e) {
|
||||
// We expect it
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"constant_score": {
|
||||
"filter": {
|
||||
"range" : {
|
||||
"born" : {
|
||||
"gte": "2012-01-01",
|
||||
"lte": "now",
|
||||
"time_zone": "+1:00"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"constant_score": {
|
||||
"filter": {
|
||||
"range" : {
|
||||
"age" : {
|
||||
"gte": "0",
|
||||
"lte": "100",
|
||||
"time_zone": "-1:00"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"range" : {
|
||||
"born" : {
|
||||
"gte": "2012-01-01",
|
||||
"lte": "now",
|
||||
"time_zone": "+1:00"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"range" : {
|
||||
"age" : {
|
||||
"gte": "0",
|
||||
"lte": "100",
|
||||
"time_zone": "-1:00"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,10 @@ import org.joda.time.format.ISODateTimeFormat;
|
|||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
|
||||
|
@ -2342,6 +2345,200 @@ public class SimpleQueryTests extends ElasticsearchIntegrationTest {
|
|||
assertThat(statsResponse.getIndex("test").getTotal().getFilterCache().getMemorySizeInBytes(), cluster().hasFilterCache() ? greaterThan(filtercacheSize) : is(filtercacheSize));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeFilterWithTimeZone() throws Exception {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("type1", "date", "type=date"));
|
||||
ensureGreen();
|
||||
|
||||
index("test", "type1", "1", "date", "2014-01-01", "num", 1);
|
||||
index("test", "type1", "2", "date", "2013-12-31T23:00:00", "num", 2);
|
||||
index("test", "type1", "3", "date", "2014-01-01T01:00:00", "num", 3);
|
||||
// Now in UTC+1
|
||||
index("test", "type1", "4", "date", DateTime.now(DateTimeZone.forOffsetHours(1)).getMillis(), "num", 4);
|
||||
|
||||
refresh();
|
||||
|
||||
SearchResponse searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2014-01-01T00:00:00").to("2014-01-01T00:59:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("1"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2013-12-31T23:00:00").to("2013-12-31T23:59:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("2"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2014-01-01T01:00:00").to("2014-01-01T01:59:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("3"));
|
||||
|
||||
// We explicitly define a time zone in the from/to dates so whatever the time zone is, it won't be used
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2014-01-01T00:00:00Z").to("2014-01-01T00:59:00Z").timeZone("+10:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("1"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2013-12-31T23:00:00Z").to("2013-12-31T23:59:00Z").timeZone("+10:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("2"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2014-01-01T01:00:00Z").to("2014-01-01T01:59:00Z").timeZone("+10:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("3"));
|
||||
|
||||
// We define a time zone to be applied to the filter and from/to have no time zone
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2014-01-01T03:00:00").to("2014-01-01T03:59:00").timeZone("+3:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("1"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2014-01-01T02:00:00").to("2014-01-01T02:59:00").timeZone("+3:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("2"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2014-01-01T04:00:00").to("2014-01-01T04:59:00").timeZone("+3:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("3"));
|
||||
|
||||
// When we use long values, it means we have ms since epoch UTC based so we don't apply any transformation
|
||||
try {
|
||||
client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from(1388534400000L).to(1388537940999L).timeZone("+1:00")))
|
||||
.get();
|
||||
fail("A Range Filter using ms since epoch with a TimeZone should raise a QueryParsingException");
|
||||
} catch (SearchPhaseExecutionException e) {
|
||||
// We expect it
|
||||
}
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("2014-01-01").to("2014-01-01T00:59:00").timeZone("-1:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("3"));
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("date").from("now/d-1d").timeZone("+1:00")))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("4"));
|
||||
|
||||
// A Range Filter on a numeric field with a TimeZone should raise an exception
|
||||
try {
|
||||
client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.filteredQuery(matchAllQuery(), FilterBuilders.rangeFilter("num").from("0").to("4").timeZone("-1:00")))
|
||||
.get();
|
||||
fail("A Range Filter on a numeric field with a TimeZone should raise a QueryParsingException");
|
||||
} catch (SearchPhaseExecutionException e) {
|
||||
// We expect it
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRangeQueryWithTimeZone() throws Exception {
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("type1", "date", "type=date"));
|
||||
ensureGreen();
|
||||
|
||||
index("test", "type1", "1", "date", "2014-01-01", "num", 1);
|
||||
index("test", "type1", "2", "date", "2013-12-31T23:00:00", "num", 2);
|
||||
index("test", "type1", "3", "date", "2014-01-01T01:00:00", "num", 3);
|
||||
// Now in UTC+1
|
||||
index("test", "type1", "4", "date", DateTime.now(DateTimeZone.forOffsetHours(1)).getMillis(), "num", 4);
|
||||
|
||||
refresh();
|
||||
|
||||
SearchResponse searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T00:00:00").to("2014-01-01T00:59:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("1"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2013-12-31T23:00:00").to("2013-12-31T23:59:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("2"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T01:00:00").to("2014-01-01T01:59:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("3"));
|
||||
|
||||
// We explicitly define a time zone in the from/to dates so whatever the time zone is, it won't be used
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T00:00:00Z").to("2014-01-01T00:59:00Z").timeZone("+10:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("1"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2013-12-31T23:00:00Z").to("2013-12-31T23:59:00Z").timeZone("+10:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("2"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T01:00:00Z").to("2014-01-01T01:59:00Z").timeZone("+10:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("3"));
|
||||
|
||||
// We define a time zone to be applied to the filter and from/to have no time zone
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T03:00:00").to("2014-01-01T03:59:00").timeZone("+3:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("1"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T02:00:00").to("2014-01-01T02:59:00").timeZone("+3:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("2"));
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01T04:00:00").to("2014-01-01T04:59:00").timeZone("+3:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("3"));
|
||||
|
||||
// When we use long values, it means we have ms since epoch UTC based so we don't apply any transformation
|
||||
try {
|
||||
client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from(1388534400000L).to(1388537940999L).timeZone("+1:00"))
|
||||
.get();
|
||||
fail("A Range Filter using ms since epoch with a TimeZone should raise a QueryParsingException");
|
||||
} catch (SearchPhaseExecutionException e) {
|
||||
// We expect it
|
||||
}
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("2014-01-01").to("2014-01-01T00:59:00").timeZone("-1:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("3"));
|
||||
|
||||
searchResponse = client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("date").from("now/d-1d").timeZone("+1:00"))
|
||||
.get();
|
||||
assertHitCount(searchResponse, 1l);
|
||||
assertThat(searchResponse.getHits().getAt(0).getId(), is("4"));
|
||||
|
||||
// A Range Filter on a numeric field with a TimeZone should raise an exception
|
||||
try {
|
||||
client().prepareSearch("test")
|
||||
.setQuery(QueryBuilders.rangeQuery("num").from("0").to("4").timeZone("-1:00"))
|
||||
.get();
|
||||
fail("A Range Filter on a numeric field with a TimeZone should raise a QueryParsingException");
|
||||
} catch (SearchPhaseExecutionException e) {
|
||||
// We expect it
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchEmptyDoc() {
|
||||
assertAcked(prepareCreate("test").setSettings("{\"index.analysis.analyzer.default.type\":\"keyword\"}"));
|
||||
|
|
Loading…
Reference in New Issue