Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix range query on date fields for number inputs #63692

Merged
merged 17 commits into from
Dec 1, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
field_caps:
index: 'field_caps_index_4,my_remote_cluster:field_*'
fields: [number]
body: { index_filter: { range: { created_at: { lt: 2018 } } } }
body: { index_filter: { range: { created_at: { lt: "2018" } } } }

- match: {indices: ["field_caps_index_4","my_remote_cluster:field_caps_index_1"]}
- length: {fields.number: 1}
Expand All @@ -117,7 +117,7 @@
field_caps:
index: 'field_caps_index_4,my_remote_cluster:field_*'
fields: [number]
body: { index_filter: { range: { created_at: { gt: 2019 } } } }
body: { index_filter: { range: { created_at: { gt: "2019" } } } }

- match: {indices: ["my_remote_cluster:field_caps_index_3"]}
- length: {fields.number: 1}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ setup:
field_caps:
index: test-*
fields: "*"
body: { index_filter: { range: { timestamp: { gte: 2010 }}}}
body: { index_filter: { range: { timestamp: { gte: "2010" }}}}
cbuescher marked this conversation as resolved.
Show resolved Hide resolved

- match: {indices: ["test-1", "test-2", "test-3"]}
- length: {fields.field1: 3}
Expand All @@ -90,7 +90,7 @@ setup:
field_caps:
index: test-*
fields: "*"
body: { index_filter: { range: { timestamp: { gte: 2019 } } } }
body: { index_filter: { range: { timestamp: { gte: "2019" } } } }

- match: {indices: ["test-2", "test-3"]}
- length: {fields.field1: 2}
Expand All @@ -106,7 +106,7 @@ setup:
field_caps:
index: test-*
fields: "*"
body: { index_filter: { range: { timestamp: { lt: 2019 } } } }
body: { index_filter: { range: { timestamp: { lt: "2019" } } } }

- match: {indices: ["test-1"]}
- length: {fields.field1: 1}
Expand Down
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,9 +350,17 @@ 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;
DateMathParser parser;
if (forcedDateParser == null) {
if (lowerTerm instanceof Number || upperTerm instanceof Number) {
// force epoch_millis
parser = EPOCH_MILLIS_PARSER;
cbuescher marked this conversation as resolved.
Show resolved Hide resolved
} else {
parser = dateMathParser;
}
} else {
parser = forcedDateParser;
}
return dateRangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, timeZone, parser, context, resolution, (l, u) -> {
Query query = LongPoint.newRangeQuery(name(), l, u);
if (hasDocValues()) {
Expand Down Expand Up @@ -443,7 +452,12 @@ public Relation isFieldWithinQuery(IndexReader reader,
Object from, Object to, boolean includeLower, boolean includeUpper,
ZoneId timeZone, DateMathParser dateParser, QueryRewriteContext context) throws IOException {
if (dateParser == null) {
dateParser = this.dateMathParser;
if (from instanceof Number || to instanceof Number) {
// force epoch_millis
dateParser = EPOCH_MILLIS_PARSER;
} else {
dateParser = this.dateMathParser;
}
}

long fromInclusive = Long.MIN_VALUE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

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

DateScriptFieldType(
String name,
Expand All @@ -47,6 +48,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 @@ -88,7 +90,7 @@ public Query distanceFeatureQuery(Object origin, String pivot, float boost, Quer
origin,
true,
null,
dateTimeFormatter.toDateMathParser(),
this.dateMathParser,
now,
DateFieldMapper.Resolution.MILLISECONDS
);
Expand Down Expand Up @@ -120,7 +122,7 @@ public Query rangeQuery(
@Nullable DateMathParser parser,
QueryShardContext context
) {
parser = parser == null ? dateTimeFormatter.toDateMathParser() : parser;
parser = parser == null ? this.dateMathParser : parser;
checkAllowExpensiveQueries(context);
return DateFieldType.dateRangeQuery(
lowerTerm,
Expand All @@ -138,14 +140,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, this.dateMathParser, now, DateFieldMapper.Resolution.MILLISECONDS);
checkAllowExpensiveQueries(context);
return new LongScriptFieldTermQuery(script, leafFactory(context)::newInstance, name(), l);
});
Expand All @@ -159,16 +154,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, this.dateMathParser, now, DateFieldMapper.Resolution.MILLISECONDS));
}
checkAllowExpensiveQueries(context);
return new LongScriptFieldTermsQuery(script, leafFactory(context)::newInstance, name(), terms);
Expand Down