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

[ESQL] Support date_nanos on functions that take "any" type #114056

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ millis:date,nanos:date_nanos,num:long
2023-10-23T12:27:28.948Z,2023-10-23T12:27:28.948000000Z,1698064048948000000
2023-10-23T12:15:03.360Z,2023-10-23T12:15:03.360103847Z,1698063303360103847
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
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
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ nanos:date_nanos
mv_min on date nanos
required_capability: date_nanos_type

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

nanos:date_nanos
2023-03-23T12:15:03.360103847Z
2023-01-23T13:55:01.543123456Z
;

Expand All @@ -56,9 +57,10 @@ ct:integer
mv_first on date nanos
required_capability: date_nanos_type

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

nanos:date_nanos
2023-03-23T12:15:03.360103847Z
2023-01-23T13:55:01.543123456Z
;

Expand Down Expand Up @@ -263,3 +265,69 @@ ROW a = TO_DATE_NANOS(null), b = TO_DATE_NANOS(null + 1::long), c = TO_DATE_NANO
a:date_nanos | b:date_nanos | c:date_nanos
null | null | null
;

Coalasce date nanos
required_capability: to_date_nanos

ROW a = COALESCE(null, TO_DATE_NANOS(1698069301543123456));

a:date_nanos
2023-10-23T13:55:01.543123456Z
;

Case date nanos result
required_capability: to_date_nanos

ROW a = CASE(false, TO_DATE_NANOS(0::long), TO_DATE_NANOS(1698069301543123456));

a:date_nanos
2023-10-23T13:55:01.543123456Z
;

Greatest date nanos
required_capability: least_greatest_for_datenanos

ROW a = GREATEST(TO_DATE_NANOS("2023-10-23T13:55:01.543123456"), TO_DATE_NANOS("2023-10-23T13:53:55.832987654"));

a:date_nanos
2023-10-23T13:55:01.543123456Z
;

Least date nanos
required_capability: least_greatest_for_datenanos

ROW a = LEAST(TO_DATE_NANOS("2023-10-23T13:55:01.543123456"), TO_DATE_NANOS("2023-10-23T13:53:55.832987654"));

a:date_nanos
2023-10-23T13:53:55.832987654Z
;

mv_dedup over date nanos
required_capability: date_nanos_type

FROM date_nanos | WHERE millis < "2000-01-01" | EVAL a = MV_DEDUPE(nanos) | SORT millis DESC | KEEP a;

a:date_nanos
[2023-01-23T13:55:01.543123456Z, 2023-02-23T13:33:34.937193000Z, 2023-03-23T12:15:03.360103847Z]
2023-03-23T12:15:03.360103847Z
;

mv_sort over date nanos
required_capability: date_nanos_type

FROM date_nanos | WHERE millis < "2000-01-01" | EVAL a = MV_SORT(nanos, "asc") | SORT millis DESC | KEEP a;

a:date_nanos
[2023-01-23T13:55:01.543123456Z, 2023-02-23T13:33:34.937193000Z, 2023-03-23T12:15:03.360103847Z]
[2023-03-23T12:15:03.360103847Z, 2023-03-23T12:15:03.360103847Z, 2023-03-23T12:15:03.360103847Z]
;

mv_slice over date nanos
required_capability: date_nanos_type

FROM date_nanos | WHERE millis < "2000-01-01" | EVAL a = MV_SLICE(MV_SORT(nanos, "asc"), 1, 2) | SORT millis DESC | KEEP a;

a:date_nanos
[2023-02-23T13:33:34.937193000Z, 2023-03-23T12:15:03.360103847Z]
[2023-03-23T12:15:03.360103847Z, 2023-03-23T12:15:03.360103847Z]
;
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,11 @@ public enum Cap {
*/
TO_DATE_NANOS(EsqlCorePlugin.DATE_NANOS_FEATURE_FLAG),

/**
* Support Least and Greatest functions on Date Nanos type
*/
LEAST_GREATEST_FOR_DATENANOS(EsqlCorePlugin.DATE_NANOS_FEATURE_FLAG),

/**
* Support for datetime in least and greatest functions
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class Greatest extends EsqlScalarFunction implements OptionalArgument {
private DataType dataType;

@FunctionInfo(
returnType = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
description = "Returns the maximum value from multiple columns. This is similar to <<esql-mv_max>>\n"
+ "except it is intended to run on multiple columns at once.",
note = "When run on `keyword` or `text` fields, this returns the last string in alphabetical order. "
Expand All @@ -54,12 +54,12 @@ public Greatest(
Source source,
@Param(
name = "first",
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
description = "First of the columns to evaluate."
) Expression first,
@Param(
name = "rest",
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
description = "The rest of the columns to evaluate.",
optional = true
) List<Expression> rest
Expand Down Expand Up @@ -152,7 +152,7 @@ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
if (dataType == DataType.INTEGER) {
return new GreatestIntEvaluator.Factory(source(), factories);
}
if (dataType == DataType.LONG || dataType == DataType.DATETIME) {
if (dataType == DataType.LONG || dataType == DataType.DATETIME || dataType == DataType.DATE_NANOS) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that having a DataType method to check on the two date types would be useful (a la isDateTimeOrNanos()).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like adding more of those "helper" functions. In my opinion, it's not any more readable than just having another case in the switching logic, and in fact may be less readable. And I'd like to refactor this to use a map anyway, which we could reuse in the type checker.

return new GreatestLongEvaluator.Factory(source(), factories);
}
if (DataType.isString(dataType) || dataType == DataType.IP || dataType == DataType.VERSION || dataType == DataType.UNSUPPORTED) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class Least extends EsqlScalarFunction implements OptionalArgument {
private DataType dataType;

@FunctionInfo(
returnType = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
description = "Returns the minimum value from multiple columns. "
+ "This is similar to <<esql-mv_min>> except it is intended to run on multiple columns at once.",
examples = @Example(file = "math", tag = "least")
Expand All @@ -52,12 +52,12 @@ public Least(
Source source,
@Param(
name = "first",
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
description = "First of the columns to evaluate."
) Expression first,
@Param(
name = "rest",
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
description = "The rest of the columns to evaluate.",
optional = true
) List<Expression> rest
Expand Down Expand Up @@ -151,7 +151,7 @@ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
if (dataType == DataType.INTEGER) {
return new LeastIntEvaluator.Factory(source(), factories);
}
if (dataType == DataType.LONG || dataType == DataType.DATETIME) {
if (dataType == DataType.LONG || dataType == DataType.DATETIME || dataType == DataType.DATE_NANOS) {
return new LeastLongEvaluator.Factory(source(), factories);
}
if (DataType.isString(dataType) || dataType == DataType.IP || dataType == DataType.VERSION || dataType == DataType.UNSUPPORTED) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class MvDedupe extends AbstractMultivalueFunction {
"cartesian_point",
"cartesian_shape",
"date",
"date_nanos",
"double",
"geo_point",
"geo_shape",
Expand All @@ -60,6 +61,7 @@ public MvDedupe(
"cartesian_point",
"cartesian_shape",
"date",
"date_nanos",
"double",
"geo_point",
"geo_shape",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public class MvSlice extends EsqlScalarFunction implements OptionalArgument, Eva
"cartesian_point",
"cartesian_shape",
"date",
"date_nanos",
"double",
"geo_point",
"geo_shape",
Expand Down Expand Up @@ -87,6 +88,7 @@ public MvSlice(
"cartesian_point",
"cartesian_shape",
"date",
"date_nanos",
"double",
"geo_point",
"geo_shape",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,15 @@ public class MvSort extends EsqlScalarFunction implements OptionalArgument, Vali
private static final String INVALID_ORDER_ERROR = "Invalid order value in [{}], expected one of [{}, {}] but got [{}]";

@FunctionInfo(
returnType = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
returnType = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
description = "Sorts a multivalued field in lexicographical order.",
examples = @Example(file = "ints", tag = "mv_sort")
)
public MvSort(
Source source,
@Param(
name = "field",
type = { "boolean", "date", "double", "integer", "ip", "keyword", "long", "text", "version" },
type = { "boolean", "date", "date_nanos", "double", "integer", "ip", "keyword", "long", "text", "version" },
description = "Multivalue expression. If `null`, the function returns `null`."
) Expression field,
@Param(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class Coalesce extends EsqlScalarFunction implements OptionalArgument {
"boolean",
"cartesian_point",
"cartesian_shape",
"date_nanos",
"date",
"geo_point",
"geo_shape",
Expand All @@ -73,6 +74,7 @@ public Coalesce(
"boolean",
"cartesian_point",
"cartesian_shape",
"date_nanos",
"date",
"geo_point",
"geo_shape",
Expand All @@ -90,6 +92,7 @@ public Coalesce(
"boolean",
"cartesian_point",
"cartesian_shape",
"date_nanos",
"date",
"geo_point",
"geo_shape",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -722,17 +722,19 @@ public static void testFunctionInfo() {
for (int i = 0; i < args.size() && i < types.size(); i++) {
typesFromSignature.get(i).add(types.get(i).esNameIfPossible());
}
returnFromSignature.add(entry.getValue().esNameIfPossible());
if (DataType.UNDER_CONSTRUCTION.containsKey(entry.getValue()) == false) {
returnFromSignature.add(entry.getValue().esNameIfPossible());
}
}

for (int i = 0; i < args.size(); i++) {
EsqlFunctionRegistry.ArgSignature arg = args.get(i);
Set<String> annotationTypes = Arrays.stream(arg.type())
.filter(DataType.UNDER_CONSTRUCTION::containsKey)
.filter(t -> DataType.UNDER_CONSTRUCTION.containsKey(DataType.fromNameOrAlias(t)) == false)
.collect(Collectors.toCollection(TreeSet::new));
Set<String> signatureTypes = typesFromSignature.get(i)
.stream()
.filter(DataType.UNDER_CONSTRUCTION::containsKey)
.filter(t -> DataType.UNDER_CONSTRUCTION.containsKey(DataType.fromNameOrAlias(t)) == false)
.collect(Collectors.toCollection(TreeSet::new));
if (signatureTypes.isEmpty()) {
log.info("{}: skipping", arg.name());
Expand All @@ -746,7 +748,9 @@ public static void testFunctionInfo() {
);
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class CaseTests extends AbstractScalarFunctionTestCase {
DataType.TEXT,
DataType.BOOLEAN,
DataType.DATETIME,
DataType.DATE_NANOS,
DataType.DOUBLE,
DataType.INTEGER,
DataType.LONG,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,21 @@ public static Iterable<Object[]> parameters() {
)
)
);
suppliers.add(
new TestCaseSupplier(
"(a, b)",
List.of(DataType.DATE_NANOS, DataType.DATE_NANOS),
() -> new TestCaseSupplier.TestCase(
List.of(
new TestCaseSupplier.TypedData(1727877348000123456L, DataType.DATE_NANOS, "a"),
new TestCaseSupplier.TypedData(1727790948000987654L, DataType.DATE_NANOS, "b")
),
"GreatestLongEvaluator[values=[MvMax[field=Attribute[channel=0]], MvMax[field=Attribute[channel=1]]]]",
DataType.DATE_NANOS,
equalTo(1727877348000123456L)
)
)
);
return parameterSuppliersFromTypedData(anyNullIsNull(false, suppliers));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ public static Iterable<Object[]> parameters() {
)
)
);
suppliers.add(
new TestCaseSupplier(
"(a, b)",
List.of(DataType.DATE_NANOS, DataType.DATE_NANOS),
() -> new TestCaseSupplier.TestCase(
List.of(
new TestCaseSupplier.TypedData(1727877348000123456L, DataType.DATE_NANOS, "a"),
new TestCaseSupplier.TypedData(1727790948000987654L, DataType.DATE_NANOS, "b")
),
"LeastLongEvaluator[values=[MvMin[field=Attribute[channel=0]], MvMin[field=Attribute[channel=1]]]]",
DataType.DATE_NANOS,
equalTo(1727790948000987654L)
)
)
);
return parameterSuppliersFromTypedData(anyNullIsNull(false, suppliers));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public static Iterable<Object[]> parameters() {
booleans(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values));
bytesRefs(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values));
dateTimes(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values.mapToObj(Long::valueOf)));
dateNanos(cases, "mv_dedupe", "MvDedupe", DataType.DATE_NANOS, (size, values) -> getMatcher(values.mapToObj(Long::valueOf)));
doubles(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values.mapToObj(Double::valueOf)));
ints(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values.mapToObj(Integer::valueOf)));
longs(cases, "mv_dedupe", "MvDedupe", (size, values) -> getMatcher(values.mapToObj(Long::valueOf)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,23 @@ private static void longs(List<TestCaseSupplier> suppliers) {
equalTo(start == end ? field.get(start) : field.subList(start, end + 1))
);
}));

suppliers.add(new TestCaseSupplier(List.of(DataType.DATE_NANOS, DataType.INTEGER, DataType.INTEGER), () -> {
List<Long> field = randomList(1, 10, () -> randomLong());
int length = field.size();
int start = randomIntBetween(0, length - 1);
int end = randomIntBetween(start, length - 1);
return new TestCaseSupplier.TestCase(
List.of(
new TestCaseSupplier.TypedData(field, DataType.DATE_NANOS, "field"),
new TestCaseSupplier.TypedData(start, DataType.INTEGER, "start"),
new TestCaseSupplier.TypedData(end, DataType.INTEGER, "end")
),
"MvSliceLongEvaluator[field=Attribute[channel=0], start=Attribute[channel=1], end=Attribute[channel=2]]",
DataType.DATE_NANOS,
equalTo(start == end ? field.get(start) : field.subList(start, end + 1))
);
}));
}

private static void doubles(List<TestCaseSupplier> suppliers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,20 @@ private static void longs(List<TestCaseSupplier> suppliers) {
equalTo(field.size() == 1 ? field.iterator().next() : field.stream().sorted(Collections.reverseOrder()).toList())
);
}));

suppliers.add(new TestCaseSupplier(List.of(DataType.DATE_NANOS, DataType.KEYWORD), () -> {
List<Long> field = randomList(1, 10, () -> randomLong());
BytesRef order = new BytesRef("DESC");
return new TestCaseSupplier.TestCase(
List.of(
new TestCaseSupplier.TypedData(field, DataType.DATE_NANOS, "field"),
new TestCaseSupplier.TypedData(order, DataType.KEYWORD, "order").forceLiteral()
),
"MvSortLong[field=Attribute[channel=0], order=false]",
DataType.DATE_NANOS,
equalTo(field.size() == 1 ? field.iterator().next() : field.stream().sorted(Collections.reverseOrder()).toList())
);
}));
}

private static void doubles(List<TestCaseSupplier> suppliers) {
Expand Down
Loading