Skip to content

Commit 033cd0e

Browse files
author
Christoph Büscher
committed
Fix range quers on date fields for number inputs
Currently, if you write a date range query with numeric 'to' or 'from' bounds, they can be interpreted as years if no format is provided. We use "strict_date_optional_time||epoch_millis" in this case that can interpret inputs like 1000 as the year 1000 for example. We should change this to always interpret and parse numbers in this case with the second option "epoch_millis" if no other formatter was provided. Closes elastic#63680
1 parent b44a03d commit 033cd0e

File tree

3 files changed

+52
-5
lines changed

3 files changed

+52
-5
lines changed

server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ public void testSimpleDateRange() throws Exception {
198198
createIndex("test");
199199
client().prepareIndex("test").setId("1").setSource("field", "2010-01-05T02:00").get();
200200
client().prepareIndex("test").setId("2").setSource("field", "2010-01-06T02:00").get();
201+
client().prepareIndex("test").setId("3").setSource("field", "1967-01-01T00:00").get();
201202
ensureGreen();
202203
refresh();
203204
SearchResponse searchResponse = client().prepareSearch("test").setQuery(QueryBuilders.rangeQuery("field").gte("2010-01-03||+2d")
@@ -223,6 +224,22 @@ public void testSimpleDateRange() throws Exception {
223224
searchResponse = client().prepareSearch("test").setQuery(
224225
QueryBuilders.queryStringQuery("field:[2010-01-03||+2d TO 2010-01-04||+2d/d]")).get();
225226
assertHitCount(searchResponse, 2L);
227+
228+
// a string value of "1000" should be parsed as the year 1000 and return all three docs
229+
searchResponse = client().prepareSearch("test")
230+
.setQuery(QueryBuilders.rangeQuery("field").gt("1000"))
231+
.get();
232+
assertNoFailures(searchResponse);
233+
assertHitCount(searchResponse, 3L);
234+
235+
// a numeric value of 1000 should be parsed as 1000 millis since epoch and return only docs after 1970
236+
searchResponse = client().prepareSearch("test")
237+
.setQuery(QueryBuilders.rangeQuery("field").gt(1000))
238+
.get();
239+
assertNoFailures(searchResponse);
240+
assertHitCount(searchResponse, 2L);
241+
assertEquals("1", searchResponse.getHits().getHits()[0].getId());
242+
assertEquals("2", searchResponse.getHits().getHits()[1].getId());
226243
}
227244

228245
public void testSimpleTerminateAfterCount() throws Exception {

server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public final class DateFieldMapper extends ParametrizedFieldMapper {
7373
public static final String CONTENT_TYPE = "date";
7474
public static final String DATE_NANOS_CONTENT_TYPE = "date_nanos";
7575
public static final DateFormatter DEFAULT_DATE_TIME_FORMATTER = DateFormatter.forPattern("strict_date_optional_time||epoch_millis");
76+
private static final DateMathParser EPOCH_MILLIS_PARSER = DateFormatter.forPattern("epoch_millis").toDateMathParser();
7677

7778
public enum Resolution {
7879
MILLISECONDS(CONTENT_TYPE, NumericType.DATE) {
@@ -349,9 +350,17 @@ public Query rangeQuery(Object lowerTerm, Object upperTerm, boolean includeLower
349350
throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() +
350351
"] does not support DISJOINT ranges");
351352
}
352-
DateMathParser parser = forcedDateParser == null
353-
? dateMathParser
354-
: forcedDateParser;
353+
DateMathParser parser;
354+
if (forcedDateParser == null) {
355+
if (lowerTerm instanceof Integer || upperTerm instanceof Integer) {
356+
// force epoch_millis
357+
parser = EPOCH_MILLIS_PARSER;
358+
} else {
359+
parser = dateMathParser;
360+
}
361+
} else {
362+
parser = forcedDateParser;
363+
}
355364
return dateRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, parser, context, resolution, (l, u) -> {
356365
Query query = LongPoint.newRangeQuery(name(), l, u);
357366
if (hasDocValues()) {
@@ -443,7 +452,12 @@ public Relation isFieldWithinQuery(IndexReader reader,
443452
Object from, Object to, boolean includeLower, boolean includeUpper,
444453
ZoneId timeZone, DateMathParser dateParser, QueryRewriteContext context) throws IOException {
445454
if (dateParser == null) {
446-
dateParser = this.dateMathParser;
455+
if (from instanceof Integer || to instanceof Integer) {
456+
// force epoch_millis
457+
dateParser = EPOCH_MILLIS_PARSER;
458+
} else {
459+
dateParser = this.dateMathParser;
460+
}
447461
}
448462

449463
long fromInclusive = Long.MIN_VALUE;

server/src/test/java/org/elasticsearch/index/query/IdsQueryBuilderTests.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import org.apache.lucene.search.Query;
2525
import org.apache.lucene.search.TermInSetQuery;
2626
import org.elasticsearch.common.ParsingException;
27+
import org.elasticsearch.common.time.DateFormatter;
28+
import org.elasticsearch.common.time.DateMathParser;
2729
import org.elasticsearch.common.xcontent.XContentParser;
2830
import org.elasticsearch.test.AbstractQueryTestCase;
2931

@@ -97,7 +99,7 @@ public void testFromJson() throws IOException {
9799
protected QueryBuilder parseQuery(XContentParser parser) throws IOException {
98100
QueryBuilder query = super.parseQuery(parser);
99101
assertThat(query, instanceOf(IdsQueryBuilder.class));
100-
return (IdsQueryBuilder) query;
102+
return query;
101103
}
102104

103105
@Override
@@ -109,4 +111,18 @@ public void testMustRewrite() throws IOException {
109111
() -> queryBuilder.toQuery(context));
110112
assertEquals("Rewrite first", e.getMessage());
111113
}
114+
115+
public void testFoo() {
116+
DateMathParser dateMathParser = DateFormatter.forPattern("strict_date_optional_time||epoch_millis").toDateMathParser();
117+
System.out.println(dateMathParser.parse("10", null).toEpochMilli());
118+
System.out.println(dateMathParser.parse("100", null).toEpochMilli());
119+
System.out.println(dateMathParser.parse("999", null).toEpochMilli());
120+
System.out.println(dateMathParser.parse("1000", null).toEpochMilli());
121+
System.out.println(dateMathParser.parse("1500", null).toEpochMilli());
122+
System.out.println(dateMathParser.parse("1968", null).toEpochMilli());
123+
System.out.println(dateMathParser.parse("1969", null).toEpochMilli());
124+
System.out.println(dateMathParser.parse("1970", null).toEpochMilli());
125+
System.out.println(dateMathParser.parse("1990", null).toEpochMilli());
126+
System.out.println(dateMathParser.parse("10000", null).toEpochMilli());
127+
}
112128
}

0 commit comments

Comments
 (0)