Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.oneOf;

public class SimpleSearchIT extends ESIntegTestCase {

Expand Down Expand Up @@ -198,6 +200,7 @@ public void testSimpleDateRange() throws Exception {
createIndex("test");
client().prepareIndex("test").setId("1").setSource("field", "2010-01-05T02:00").get();
client().prepareIndex("test").setId("2").setSource("field", "2010-01-06T02:00").get();
client().prepareIndex("test").setId("3").setSource("field", "1967-01-01T00:00").get();
ensureGreen();
refresh();
SearchResponse searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-03||+2d")
Expand All @@ -223,6 +226,23 @@ public void testSimpleDateRange() throws Exception {
searchResponse = client().prepareSearch("test").setQuery(
QueryBuilders.queryStringQuery("field:[2010-01-03||+2d TO 2010-01-04||+2d/d]")).get();
assertHitCount(searchResponse, 2L);

// a string value of "1000" should be parsed as the year 1000 and return all three docs
searchResponse = client().prepareSearch("test")
.setQuery(QueryBuilders.rangeQuery("field").gt("1000"))
.get();
assertNoFailures(searchResponse);
assertHitCount(searchResponse, 3L);

// a numeric value of 1000 should be parsed as 1000 millis since epoch and return only docs after 1970
searchResponse = client().prepareSearch("test")
.setQuery(QueryBuilders.rangeQuery("field").gt(1000))
.get();
assertNoFailures(searchResponse);
assertHitCount(searchResponse, 2L);
String[] expectedIds = new String[] {"1", "2"};
assertThat(searchResponse.getHits().getHits()[0].getId(), is(oneOf(expectedIds)));
assertThat(searchResponse.getHits().getHits()[1].getId(), is(oneOf(expectedIds)));
}

public void testSimpleTerminateAfterCount() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public final class DateFieldMapper extends ParametrizedFieldMapper {
public static final String CONTENT_TYPE = "date";
public static final String DATE_NANOS_CONTENT_TYPE = "date_nanos";
public static final DateFormatter DEFAULT_DATE_TIME_FORMATTER = DateFormatter.forPattern("strict_date_optional_time||epoch_millis");
private static final DateMathParser EPOCH_MILLIS_PARSER = DateFormatter.forPattern("epoch_millis").toDateMathParser();

public enum Resolution {
MILLISECONDS(CONTENT_TYPE, NumericType.DATE) {
Expand Down Expand Up @@ -349,21 +350,29 @@ public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower
throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() +
"] does not support DISJOINT ranges");
}
DateMathParser parser = forcedDateParser == null
? dateMathParser
: forcedDateParser;
return dateRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, parser, context, resolution, (l, u) -> {
Query query = LongPoint.newRangeQuery(name(), l, u);
if (hasDocValues()) {
Query dvQuery = SortedNumericDocValuesField.newSlowRangeQuery(name(), l, u);
query = new IndexOrDocValuesQuery(query, dvQuery);

if (context.indexSortedOnField(name())) {
query = new IndexSortSortedNumericDocValuesRangeQuery(name(), l, u, query);
return dateRangeQuery(
lowerTerm,
upperTerm,
includeLower,
includeUpper,
timeZone,
forcedDateParser,
this.dateMathParser,
context,
resolution,
(l, u) -> {
Query query = LongPoint.newRangeQuery(name(), l, u);
if (hasDocValues()) {
Query dvQuery = SortedNumericDocValuesField.newSlowRangeQuery(name(), l, u);
query = new IndexOrDocValuesQuery(query, dvQuery);

if (context.indexSortedOnField(name())) {
query = new IndexSortSortedNumericDocValuesRangeQuery(name(), l, u, query);
}
}
return query;
}
return query;
});
);
}

public static Query dateRangeQuery(
Expand All @@ -372,25 +381,37 @@ public static Query dateRangeQuery(
boolean includeLower,
boolean includeUpper,
@Nullable ZoneId timeZone,
DateMathParser parser,
DateMathParser forcedDateParser,
DateMathParser fallbackParser,
QueryShardContext context,
Resolution resolution,
BiFunction<Long, Long, Query> builder
) {
return handleNow(context, nowSupplier -> {
long l, u;
DateMathParser parser = forcedDateParser == null ? fallbackParser : forcedDateParser;
if (lowerTerm == null) {
l = Long.MIN_VALUE;
} else {
l = parseToLong(lowerTerm, !includeLower, timeZone, parser, nowSupplier, resolution);
if (lowerTerm instanceof Number && forcedDateParser == null) {
// force epoch_millis
l = resolution.convert(Instant.ofEpochMilli(((Number) lowerTerm).longValue()));
} else {
l = parseToLong(lowerTerm, !includeLower, timeZone, parser, nowSupplier, resolution);
}
if (includeLower == false) {
++l;
}
}
if (upperTerm == null) {
u = Long.MAX_VALUE;
} else {
u = parseToLong(upperTerm, includeUpper, timeZone, parser, nowSupplier, resolution);
if (upperTerm instanceof Number && forcedDateParser == null) {
// force epoch_millis
u = resolution.convert(Instant.ofEpochMilli(((Number) upperTerm).longValue()));
} else {
u = parseToLong(upperTerm, includeUpper, timeZone, parser, nowSupplier, resolution);
}
if (includeUpper == false) {
--u;
}
Expand Down Expand Up @@ -442,13 +463,18 @@ public Query distanceFeatureQuery(Object origin, String pivot, float boost, Quer
public Relation isFieldWithinQuery(IndexReader reader,
Object from, Object to, boolean includeLower, boolean includeUpper,
ZoneId timeZone, DateMathParser dateParser, QueryRewriteContext context) throws IOException {
DateMathParser forcedDateParser = dateParser;
if (dateParser == null) {
dateParser = this.dateMathParser;
forcedDateParser = this.dateMathParser;
}

long fromInclusive = Long.MIN_VALUE;
if (from != null) {
fromInclusive = parseToLong(from, !includeLower, timeZone, dateParser, context::nowInMillis, resolution);
if (dateParser == null && from instanceof Number) {
fromInclusive = resolution.convert(Instant.ofEpochMilli(((Number) from).longValue()));
} else {
fromInclusive = parseToLong(from, !includeLower, timeZone, forcedDateParser, context::nowInMillis, resolution);
}
if (includeLower == false) {
if (fromInclusive == Long.MAX_VALUE) {
return Relation.DISJOINT;
Expand All @@ -459,7 +485,11 @@ public Relation isFieldWithinQuery(IndexReader reader,

long toInclusive = Long.MAX_VALUE;
if (to != null) {
toInclusive = parseToLong(to, includeUpper, timeZone, dateParser, context::nowInMillis, resolution);
if (dateParser == null && to instanceof Number) {
toInclusive = resolution.convert(Instant.ofEpochMilli(((Number) to).longValue()));
} else {
toInclusive = parseToLong(to, includeUpper, timeZone, forcedDateParser, context::nowInMillis, resolution);
}
if (includeUpper == false) {
if (toInclusive == Long.MIN_VALUE) {
return Relation.DISJOINT;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.carrotsearch.hppc.LongHashSet;
import com.carrotsearch.hppc.LongSet;

import org.apache.lucene.search.Query;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.lucene.search.Queries;
Expand Down Expand Up @@ -37,6 +38,7 @@

public class DateScriptFieldType extends AbstractScriptFieldType<DateFieldScript.LeafFactory> {
private final DateFormatter dateTimeFormatter;
private final DateMathParser dateMathParser;

DateScriptFieldType(
String name,
Expand All @@ -47,6 +49,7 @@ public class DateScriptFieldType extends AbstractScriptFieldType<DateFieldScript
) {
super(name, script, (n, params, ctx) -> scriptFactory.newFactory(n, params, ctx, dateTimeFormatter), meta);
this.dateTimeFormatter = dateTimeFormatter;
this.dateMathParser = dateTimeFormatter.toDateMathParser();
}

@Override
Expand Down Expand Up @@ -84,14 +87,7 @@ public DateScriptFieldData.Builder fielddataBuilder(String fullyQualifiedIndexNa
public Query distanceFeatureQuery(Object origin, String pivot, float boost, QueryShardContext context) {
checkAllowExpensiveQueries(context);
return DateFieldType.handleNow(context, now -> {
long originLong = DateFieldType.parseToLong(
origin,
true,
null,
dateTimeFormatter.toDateMathParser(),
now,
DateFieldMapper.Resolution.MILLISECONDS
);
long originLong = DateFieldType.parseToLong(origin, true, null, dateMathParser, now, DateFieldMapper.Resolution.MILLISECONDS);
TimeValue pivotTime = TimeValue.parseTimeValue(pivot, "distance_feature.pivot");
return new LongScriptFieldDistanceFeatureQuery(
script,
Expand Down Expand Up @@ -120,7 +116,6 @@ public Query rangeQuery(
@Nullable DateMathParser parser,
QueryShardContext context
) {
parser = parser == null ? dateTimeFormatter.toDateMathParser() : parser;
checkAllowExpensiveQueries(context);
return DateFieldType.dateRangeQuery(
lowerTerm,
Expand All @@ -129,6 +124,7 @@ public Query rangeQuery(
includeUpper,
timeZone,
parser,
dateMathParser,
context,
DateFieldMapper.Resolution.MILLISECONDS,
(l, u) -> new LongScriptFieldRangeQuery(script, leafFactory(context)::newInstance, name(), l, u)
Expand All @@ -138,14 +134,7 @@ public Query rangeQuery(
@Override
public Query termQuery(Object value, QueryShardContext context) {
return DateFieldType.handleNow(context, now -> {
long l = DateFieldType.parseToLong(
value,
false,
null,
dateTimeFormatter.toDateMathParser(),
now,
DateFieldMapper.Resolution.MILLISECONDS
);
long l = DateFieldType.parseToLong(value, false, null, dateMathParser, now, DateFieldMapper.Resolution.MILLISECONDS);
checkAllowExpensiveQueries(context);
return new LongScriptFieldTermQuery(script, leafFactory(context)::newInstance, name(), l);
});
Expand All @@ -159,16 +148,7 @@ public Query termsQuery(List<?> values, QueryShardContext context) {
return DateFieldType.handleNow(context, now -> {
LongSet terms = new LongHashSet(values.size());
for (Object value : values) {
terms.add(
DateFieldType.parseToLong(
value,
false,
null,
dateTimeFormatter.toDateMathParser(),
now,
DateFieldMapper.Resolution.MILLISECONDS
)
);
terms.add(DateFieldType.parseToLong(value, false, null, dateMathParser, now, DateFieldMapper.Resolution.MILLISECONDS));
}
checkAllowExpensiveQueries(context);
return new LongScriptFieldTermsQuery(script, leafFactory(context)::newInstance, name(), terms);
Expand Down