Skip to content

Commit 3870b6f

Browse files
authored
Add a consistent way to parse dates (#61105)
This adds a `parse` method to `date` valued `runtime_script`s that converts any `Object` into a `long` using the same mechanism that indexing a `date` uses. We also use this new method to expand our test that run's core's integration tests against runtime fields to `date` values fields. Previously we couldn't do that because we didn't have a consistent way to parse the field.
1 parent 07bfa51 commit 3870b6f

File tree

9 files changed

+37
-13
lines changed

9 files changed

+37
-13
lines changed

x-pack/plugin/runtime-fields/qa/rest/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ yamlRestTest {
3333
'search/10_source_filtering/docvalue_fields with explicit format',
3434
'search.aggregation/200_top_hits_metric/top_hits aggregation with sequence numbers',
3535
'search.aggregation/200_top_hits_metric/top_hits aggregation with nested documents',
36+
'search/140_pre_filter_search_shards/pre_filter_shard_size with shards that have no hit',
3637
/////// TO FIX ///////
3738

3839
/////// NOT SUPPORTED ///////

x-pack/plugin/runtime-fields/qa/rest/src/yamlRestTest/java/org/elasticsearch/xpack/runtimefields/rest/CoreTestsWithRuntimeFieldsIT.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import org.elasticsearch.common.xcontent.XContentLocation;
1313
import org.elasticsearch.index.mapper.BooleanFieldMapper;
14+
import org.elasticsearch.index.mapper.DateFieldMapper;
1415
import org.elasticsearch.index.mapper.IpFieldMapper;
1516
import org.elasticsearch.index.mapper.KeywordFieldMapper;
1617
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
@@ -183,8 +184,8 @@ private static String painlessToLoadFromSource(String name, String type) {
183184
}
184185

185186
private static final Map<String, String> PAINLESS_TO_EMIT = Map.ofEntries(
186-
// TODO implement dates against the parser
187187
Map.entry(BooleanFieldMapper.CONTENT_TYPE, "value(parse(value));"),
188+
Map.entry(DateFieldMapper.CONTENT_TYPE, "millis(parse(value.toString()));"),
188189
Map.entry(
189190
NumberType.DOUBLE.typeName(),
190191
"value(value instanceof Number ? ((Number) value).doubleValue() : Double.parseDouble(value.toString()));"

x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScript.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.elasticsearch.xpack.runtimefields;
88

99
import org.apache.lucene.index.LeafReaderContext;
10+
import org.elasticsearch.common.time.DateFormatter;
1011
import org.elasticsearch.painless.spi.Whitelist;
1112
import org.elasticsearch.painless.spi.WhitelistLoader;
1213
import org.elasticsearch.script.ScriptContext;
@@ -29,15 +30,18 @@ static List<Whitelist> whitelist() {
2930
public static final String[] PARAMETERS = {};
3031

3132
public interface Factory extends ScriptFactory {
32-
LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup);
33+
LeafFactory newFactory(Map<String, Object> params, SearchLookup searchLookup, DateFormatter formatter);
3334
}
3435

3536
public interface LeafFactory {
3637
DateScriptFieldScript newInstance(LeafReaderContext ctx) throws IOException;
3738
}
3839

39-
public DateScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, LeafReaderContext ctx) {
40+
private final DateFormatter formatter;
41+
42+
public DateScriptFieldScript(Map<String, Object> params, SearchLookup searchLookup, DateFormatter formatter, LeafReaderContext ctx) {
4043
super(params, searchLookup, ctx);
44+
this.formatter = formatter;
4145
}
4246

4347
public static class Millis {
@@ -67,4 +71,15 @@ public void date(TemporalAccessor v) {
6771
}
6872
}
6973

74+
public static class Parse {
75+
private final DateScriptFieldScript script;
76+
77+
public Parse(DateScriptFieldScript script) {
78+
this.script = script;
79+
}
80+
81+
public long parse(Object str) {
82+
return script.formatter.parseMillis(str.toString());
83+
}
84+
}
7085
}

x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public ScriptDateFieldData.Builder fielddataBuilder(String fullyQualifiedIndexNa
8585
}
8686

8787
private DateScriptFieldScript.LeafFactory leafFactory(SearchLookup lookup) {
88-
return scriptFactory.newFactory(script.getParams(), lookup);
88+
return scriptFactory.newFactory(script.getParams(), lookup, dateTimeFormatter);
8989
}
9090

9191
@Override

x-pack/plugin/runtime-fields/src/main/resources/org/elasticsearch/xpack/runtimefields/date_whitelist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class org.elasticsearch.xpack.runtimefields.DateScriptFieldScript @no_import {
1212
static_import {
1313
void millis(org.elasticsearch.xpack.runtimefields.DateScriptFieldScript, long) bound_to org.elasticsearch.xpack.runtimefields.DateScriptFieldScript$Millis
1414
void date(org.elasticsearch.xpack.runtimefields.DateScriptFieldScript, TemporalAccessor) bound_to org.elasticsearch.xpack.runtimefields.DateScriptFieldScript$Date
15+
long parse(org.elasticsearch.xpack.runtimefields.DateScriptFieldScript, def) bound_to org.elasticsearch.xpack.runtimefields.DateScriptFieldScript$Parse
1516
}
1617

1718
# This import is required to make painless happy and it isn't 100% clear why

x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/RuntimeScriptFieldMapperTests.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,12 @@ public void execute() {
349349
};
350350
}
351351
if (context == DateScriptFieldScript.CONTEXT) {
352-
return (DateScriptFieldScript.Factory) (params, lookup) -> ctx -> new DateScriptFieldScript(params, lookup, ctx) {
352+
return (DateScriptFieldScript.Factory) (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
353+
params,
354+
lookup,
355+
formatter,
356+
ctx
357+
) {
353358
@Override
354359
public void execute() {
355360
new DateScriptFieldScript.Millis(this).millis(1595431354874L);

x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/ScriptDateMappedFieldTypeTests.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ protected ScriptDateMappedFieldType simpleMappedFieldType() throws IOException {
389389
}
390390

391391
private ScriptDateMappedFieldType coolFormattedFieldType() throws IOException {
392-
return build(simpleMappedFieldType().script, DateFormatter.forPattern("yyyy-MM-dd(-■_■)HH:mm:ss.SSSz"));
392+
return build(simpleMappedFieldType().script, DateFormatter.forPattern("yyyy-MM-dd(-■_■)HH:mm:ss.SSSz||epoch_millis"));
393393
}
394394

395395
@Override
@@ -435,16 +435,17 @@ public <FactoryType> FactoryType compile(
435435
private DateScriptFieldScript.Factory factory(String code) {
436436
switch (code) {
437437
case "read_timestamp":
438-
return (params, lookup) -> (ctx) -> new DateScriptFieldScript(params, lookup, ctx) {
438+
return (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(params, lookup, formatter, ctx) {
439439
@Override
440440
public void execute() {
441441
for (Object timestamp : (List<?>) getSource().get("timestamp")) {
442-
new DateScriptFieldScript.Millis(this).millis(((Number) timestamp).longValue());
442+
DateScriptFieldScript.Parse parse = new DateScriptFieldScript.Parse(this);
443+
new DateScriptFieldScript.Millis(this).millis(parse.parse(timestamp));
443444
}
444445
}
445446
};
446447
case "add_days":
447-
return (params, lookup) -> (ctx) -> new DateScriptFieldScript(params, lookup, ctx) {
448+
return (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(params, lookup, formatter, ctx) {
448449
@Override
449450
public void execute() {
450451
for (Object timestamp : (List<?>) getSource().get("timestamp")) {

x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/query/LongScriptFieldDistanceFeatureQueryTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public void testMatches() throws IOException {
8787
try (DirectoryReader reader = iw.getReader()) {
8888
IndexSearcher searcher = newSearcher(reader);
8989
CheckedFunction<LeafReaderContext, AbstractLongScriptFieldScript, IOException> leafFactory =
90-
ctx -> new DateScriptFieldScript(Map.of(), new SearchLookup(null, null), ctx) {
90+
ctx -> new DateScriptFieldScript(Map.of(), new SearchLookup(null, null), null, ctx) {
9191
@Override
9292
public void execute() {
9393
for (Object timestamp : (List<?>) getSource().get("timestamp")) {

x-pack/plugin/src/test/resources/rest-api-spec/test/runtime_fields/40_date.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ setup:
2727
}
2828
params:
2929
days: 1
30-
# Test fetching from _source
30+
# Test fetching from _source and parsing
3131
tomorrow_from_source:
3232
type: runtime_script
3333
runtime_type: date
3434
script:
3535
source: |
36-
Instant instant = Instant.ofEpochMilli(source['timestamp']);
36+
Instant instant = Instant.ofEpochMilli(parse(source['timestamp']));
3737
ZonedDateTime dt = ZonedDateTime.ofInstant(instant, ZoneId.of("UTC"));
3838
date(dt.plus(1, ChronoUnit.DAYS));
3939
# Test returning millis
@@ -82,7 +82,7 @@ setup:
8282
{"index":{}}
8383
{"timestamp": 1516383694000, "temperature": 200, "voltage": 4.2, "node": "c"}
8484
{"index":{}}
85-
{"timestamp": 1516297294000, "temperature": 202, "voltage": 4.0, "node": "c"}
85+
{"timestamp": "2018-01-18T17:41:34.000Z", "temperature": 202, "voltage": 4.0, "node": "c"}
8686
8787
---
8888
"get mapping":

0 commit comments

Comments
 (0)