Skip to content

Commit 9c923db

Browse files
[ESQL] Support date_nanos on functions that take "any" type (#114056)
Resolves #109998 For the most part, this is just adding tests. Greater and Least have actual production code changes - notably toEvaluator is modified to map date nanos to the long evaluator. This parallels the work done in #113961. I've added CSV tests and unit tests for all the functions listed in the original ticket. --------- Co-authored-by: Elastic Machine <[email protected]>
1 parent 0782efa commit 9c923db

File tree

17 files changed

+177
-16
lines changed

17 files changed

+177
-16
lines changed

x-pack/plugin/esql/qa/testFixtures/src/main/resources/date_nanos.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ millis:date,nanos:date_nanos,num:long
77
2023-10-23T12:27:28.948Z,2023-10-23T12:27:28.948000000Z,1698064048948000000
88
2023-10-23T12:15:03.360Z,2023-10-23T12:15:03.360103847Z,1698063303360103847
99
1999-10-23T12:15:03.360Z,[2023-03-23T12:15:03.360103847Z, 2023-02-23T13:33:34.937193000Z, 2023-01-23T13:55:01.543123456Z], 0
10+
1999-10-22T12:15:03.360Z,[2023-03-23T12:15:03.360103847Z, 2023-03-23T12:15:03.360103847Z, 2023-03-23T12:15:03.360103847Z], 0

x-pack/plugin/esql/qa/testFixtures/src/main/resources/date_nanos.csv-spec

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ nanos:date_nanos
3838
mv_min on date nanos
3939
required_capability: date_nanos_type
4040

41-
FROM date_nanos | SORT millis ASC | EVAL nanos = MV_MIN(nanos) | KEEP nanos | LIMIT 1;
41+
FROM date_nanos | SORT millis ASC | WHERE millis < "2000-01-01" | EVAL nanos = MV_MIN(nanos) | KEEP nanos;
4242

4343
nanos:date_nanos
44+
2023-03-23T12:15:03.360103847Z
4445
2023-01-23T13:55:01.543123456Z
4546
;
4647

@@ -56,9 +57,10 @@ ct:integer
5657
mv_first on date nanos
5758
required_capability: date_nanos_type
5859

59-
FROM date_nanos | SORT millis ASC | EVAL nanos = MV_FIRST(nanos) | KEEP nanos | LIMIT 1;
60+
FROM date_nanos | SORT millis ASC | WHERE millis < "2000-01-01" | EVAL nanos = MV_FIRST(nanos) | KEEP nanos;
6061

6162
nanos:date_nanos
63+
2023-03-23T12:15:03.360103847Z
6264
2023-01-23T13:55:01.543123456Z
6365
;
6466

@@ -263,3 +265,69 @@ ROW a = TO_DATE_NANOS(null), b = TO_DATE_NANOS(null + 1::long), c = TO_DATE_NANO
263265
a:date_nanos | b:date_nanos | c:date_nanos
264266
null | null | null
265267
;
268+
269+
Coalasce date nanos
270+
required_capability: to_date_nanos
271+
272+
ROW a = COALESCE(null, TO_DATE_NANOS(1698069301543123456));
273+
274+
a:date_nanos
275+
2023-10-23T13:55:01.543123456Z
276+
;
277+
278+
Case date nanos result
279+
required_capability: to_date_nanos
280+
281+
ROW a = CASE(false, TO_DATE_NANOS(0::long), TO_DATE_NANOS(1698069301543123456));
282+
283+
a:date_nanos
284+
2023-10-23T13:55:01.543123456Z
285+
;
286+
287+
Greatest date nanos
288+
required_capability: least_greatest_for_datenanos
289+
290+
ROW a = GREATEST(TO_DATE_NANOS("2023-10-23T13:55:01.543123456"), TO_DATE_NANOS("2023-10-23T13:53:55.832987654"));
291+
292+
a:date_nanos
293+
2023-10-23T13:55:01.543123456Z
294+
;
295+
296+
Least date nanos
297+
required_capability: least_greatest_for_datenanos
298+
299+
ROW a = LEAST(TO_DATE_NANOS("2023-10-23T13:55:01.543123456"), TO_DATE_NANOS("2023-10-23T13:53:55.832987654"));
300+
301+
a:date_nanos
302+
2023-10-23T13:53:55.832987654Z
303+
;
304+
305+
mv_dedup over date nanos
306+
required_capability: date_nanos_type
307+
308+
FROM date_nanos | WHERE millis < "2000-01-01" | EVAL a = MV_DEDUPE(nanos) | SORT millis DESC | KEEP a;
309+
310+
a:date_nanos
311+
[2023-01-23T13:55:01.543123456Z, 2023-02-23T13:33:34.937193000Z, 2023-03-23T12:15:03.360103847Z]
312+
2023-03-23T12:15:03.360103847Z
313+
;
314+
315+
mv_sort over date nanos
316+
required_capability: date_nanos_type
317+
318+
FROM date_nanos | WHERE millis < "2000-01-01" | EVAL a = MV_SORT(nanos, "asc") | SORT millis DESC | KEEP a;
319+
320+
a:date_nanos
321+
[2023-01-23T13:55:01.543123456Z, 2023-02-23T13:33:34.937193000Z, 2023-03-23T12:15:03.360103847Z]
322+
[2023-03-23T12:15:03.360103847Z, 2023-03-23T12:15:03.360103847Z, 2023-03-23T12:15:03.360103847Z]
323+
;
324+
325+
mv_slice over date nanos
326+
required_capability: date_nanos_type
327+
328+
FROM date_nanos | WHERE millis < "2000-01-01" | EVAL a = MV_SLICE(MV_SORT(nanos, "asc"), 1, 2) | SORT millis DESC | KEEP a;
329+
330+
a:date_nanos
331+
[2023-02-23T13:33:34.937193000Z, 2023-03-23T12:15:03.360103847Z]
332+
[2023-03-23T12:15:03.360103847Z, 2023-03-23T12:15:03.360103847Z]
333+
;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,11 @@ public enum Cap {
308308
*/
309309
TO_DATE_NANOS(EsqlCorePlugin.DATE_NANOS_FEATURE_FLAG),
310310

311+
/**
312+
* Support Least and Greatest functions on Date Nanos type
313+
*/
314+
LEAST_GREATEST_FOR_DATENANOS(EsqlCorePlugin.DATE_NANOS_FEATURE_FLAG),
315+
311316
/**
312317
* Support for datetime in least and greatest functions
313318
*/

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Greatest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class Greatest extends EsqlScalarFunction implements OptionalArgument {
4343
private DataType dataType;
4444

4545
@FunctionInfo(
46-
returnType = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
46+
returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
4747
description = "Returns the maximum value from multiple columns. This is similar to <<esql-mv_max>>\n"
4848
+ "except it is intended to run on multiple columns at once.",
4949
note = "When run on `keyword` or `text` fields, this returns the last string in alphabetical order. "
@@ -54,12 +54,12 @@ public Greatest(
5454
Source source,
5555
@Param(
5656
name = "first",
57-
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
57+
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
5858
description = "First of the columns to evaluate."
5959
) Expression first,
6060
@Param(
6161
name = "rest",
62-
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
62+
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
6363
description = "The rest of the columns to evaluate.",
6464
optional = true
6565
) List<Expression> rest
@@ -152,7 +152,7 @@ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
152152
if (dataType == DataType.INTEGER) {
153153
return new GreatestIntEvaluator.Factory(source(), factories);
154154
}
155-
if (dataType == DataType.LONG || dataType == DataType.DATETIME) {
155+
if (dataType == DataType.LONG || dataType == DataType.DATETIME || dataType == DataType.DATE_NANOS) {
156156
return new GreatestLongEvaluator.Factory(source(), factories);
157157
}
158158
if (DataType.isString(dataType) || dataType == DataType.IP || dataType == DataType.VERSION || dataType == DataType.UNSUPPORTED) {

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/conditional/Least.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class Least extends EsqlScalarFunction implements OptionalArgument {
4343
private DataType dataType;
4444

4545
@FunctionInfo(
46-
returnType = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
46+
returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
4747
description = "Returns the minimum value from multiple columns. "
4848
+ "This is similar to <<esql-mv_min>> except it is intended to run on multiple columns at once.",
4949
examples = @Example(file = "math", tag = "least")
@@ -52,12 +52,12 @@ public Least(
5252
Source source,
5353
@Param(
5454
name = "first",
55-
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
55+
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
5656
description = "First of the columns to evaluate."
5757
) Expression first,
5858
@Param(
5959
name = "rest",
60-
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
60+
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
6161
description = "The rest of the columns to evaluate.",
6262
optional = true
6363
) List<Expression> rest
@@ -151,7 +151,7 @@ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
151151
if (dataType == DataType.INTEGER) {
152152
return new LeastIntEvaluator.Factory(source(), factories);
153153
}
154-
if (dataType == DataType.LONG || dataType == DataType.DATETIME) {
154+
if (dataType == DataType.LONG || dataType == DataType.DATETIME || dataType == DataType.DATE_NANOS) {
155155
return new LeastLongEvaluator.Factory(source(), factories);
156156
}
157157
if (DataType.isString(dataType) || dataType == DataType.IP || dataType == DataType.VERSION || dataType == DataType.UNSUPPORTED) {

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvDedupe.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class MvDedupe extends AbstractMultivalueFunction {
3838
"cartesian_point",
3939
"cartesian_shape",
4040
"date",
41+
"date_nanos",
4142
"double",
4243
"geo_point",
4344
"geo_shape",
@@ -60,6 +61,7 @@ public MvDedupe(
6061
"cartesian_point",
6162
"cartesian_shape",
6263
"date",
64+
"date_nanos",
6365
"double",
6466
"geo_point",
6567
"geo_shape",

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSlice.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public class MvSlice extends EsqlScalarFunction implements OptionalArgument, Eva
5959
"cartesian_point",
6060
"cartesian_shape",
6161
"date",
62+
"date_nanos",
6263
"double",
6364
"geo_point",
6465
"geo_shape",
@@ -87,6 +88,7 @@ public MvSlice(
8788
"cartesian_point",
8889
"cartesian_shape",
8990
"date",
91+
"date_nanos",
9092
"double",
9193
"geo_point",
9294
"geo_shape",

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,15 @@ public class MvSort extends EsqlScalarFunction implements OptionalArgument, Vali
6969
private static final String INVALID_ORDER_ERROR = "Invalid order value in [{}], expected one of [{}, {}] but got [{}]";
7070

7171
@FunctionInfo(
72-
returnType = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
72+
returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
7373
description = "Sorts a multivalued field in lexicographical order.",
7474
examples = @Example(file = "ints", tag = "mv_sort")
7575
)
7676
public MvSort(
7777
Source source,
7878
@Param(
7979
name = "field",
80-
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
80+
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
8181
description = "Multivalue expression. If `null`, the function returns `null`."
8282
) Expression field,
8383
@Param(

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/Coalesce.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public class Coalesce extends EsqlScalarFunction implements OptionalArgument {
5353
"boolean",
5454
"cartesian_point",
5555
"cartesian_shape",
56+
"date_nanos",
5657
"date",
5758
"geo_point",
5859
"geo_shape",
@@ -73,6 +74,7 @@ public Coalesce(
7374
"boolean",
7475
"cartesian_point",
7576
"cartesian_shape",
77+
"date_nanos",
7678
"date",
7779
"geo_point",
7880
"geo_shape",
@@ -90,6 +92,7 @@ public Coalesce(
9092
"boolean",
9193
"cartesian_point",
9294
"cartesian_shape",
95+
"date_nanos",
9396
"date",
9497
"geo_point",
9598
"geo_shape",

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -722,17 +722,19 @@ public static void testFunctionInfo() {
722722
for (int i = 0; i < args.size() && i < types.size(); i++) {
723723
typesFromSignature.get(i).add(types.get(i).esNameIfPossible());
724724
}
725-
returnFromSignature.add(entry.getValue().esNameIfPossible());
725+
if (DataType.UNDER_CONSTRUCTION.containsKey(entry.getValue()) == false) {
726+
returnFromSignature.add(entry.getValue().esNameIfPossible());
727+
}
726728
}
727729

728730
for (int i = 0; i < args.size(); i++) {
729731
EsqlFunctionRegistry.ArgSignature arg = args.get(i);
730732
Set<String> annotationTypes = Arrays.stream(arg.type())
731-
.filter(DataType.UNDER_CONSTRUCTION::containsKey)
733+
.filter(t -> DataType.UNDER_CONSTRUCTION.containsKey(DataType.fromNameOrAlias(t)) == false)
732734
.collect(Collectors.toCollection(TreeSet::new));
733735
Set<String> signatureTypes = typesFromSignature.get(i)
734736
.stream()
735-
.filter(DataType.UNDER_CONSTRUCTION::containsKey)
737+
.filter(t -> DataType.UNDER_CONSTRUCTION.containsKey(DataType.fromNameOrAlias(t)) == false)
736738
.collect(Collectors.toCollection(TreeSet::new));
737739
if (signatureTypes.isEmpty()) {
738740
log.info("{}: skipping", arg.name());
@@ -746,7 +748,9 @@ public static void testFunctionInfo() {
746748
);
747749
}
748750

749-
Set<String> returnTypes = Arrays.stream(description.returnType()).collect(Collectors.toCollection(TreeSet::new));
751+
Set<String> returnTypes = Arrays.stream(description.returnType())
752+
.filter(t -> DataType.UNDER_CONSTRUCTION.containsKey(DataType.fromNameOrAlias(t)) == false)
753+
.collect(Collectors.toCollection(TreeSet::new));
750754
assertEquals(returnFromSignature, returnTypes);
751755
}
752756

0 commit comments

Comments
 (0)