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: Add ability to perform date math #98870

Merged
merged 6 commits into from
Aug 30, 2023

Conversation

bpintea
Copy link
Contributor

@bpintea bpintea commented Aug 25, 2023

This adds the ability to perform date math: allow date types be updated with a time span (a duration or a period).

Ex. : row n = now() | eval then = n - 1 year + 2 minutes

Closes #98402.

This adds the ability to perform date math: allow date types be updated
with a time span (a duration or a period).

Ex. : `row n = now() | eval then = n - 1 year + 2 minutes`
@elasticsearchmachine
Copy link
Collaborator

Hi @bpintea, I've created a changelog YAML for you.

@elasticsearchmachine elasticsearchmachine added the Team:QL (Deprecated) Meta label for query languages team label Aug 25, 2023
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-ql (Team:QL)

@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/elasticsearch-esql (:Query Languages/ES|QL)

Copy link
Contributor

@alex-spies alex-spies left a comment

Choose a reason for hiding this comment

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

LGTM, I only have minor remarks which are all meant to be optional.

Additionally, maybe it would make sense to add the following tests:

  • unit tests tests for period + datetime and duration + datetime
  • unit tests for period - datetime and duration - datetime
  • unit or csv tests for weird edge cases, like date math for leap years, adding days or hours past the end of a month, etc. Although this may be less important since we know that we can trust the date math functions from the ql package.

if (EsqlDataTypes.isDateTime(leftType) || EsqlDataTypes.isDateTime(rightType)) {
Expression dateTime = argumentOfType(dt -> dt == DataTypes.DATETIME);
Expression nonDateTime = argumentOfType(dt -> dt != DataTypes.DATETIME);
if (dateTime == null || nonDateTime == null || EsqlDataTypes.isTemporalAmount(nonDateTime.dataType()) == false) {
Copy link
Contributor

Choose a reason for hiding this comment

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

question: instead of using nonDateTime together with isTemporalAmount, wouldn't it be easier to just use argumentOfType(EsqlDataTypes::isTemporalAmount, like below in line 73?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

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

The main comment I have is around the use of timezones.
Date functions need to be time zone aware yet here the UTC tz is used - that makes sense only if the dates are considered to be coming from ES and thus they originate in UTC but the output is converted into the user TZ.
If that is not the case, this should be clarified.

Lastly some docs help - I would reuse the structure and content from SQL since its fairly similar.


@Evaluator(extraName = "Datetimes", warnExceptions = { ArithmeticException.class, DateTimeException.class })
static long processDatetimes(long datetime, @Fixed TemporalAmount temporalAmount) {
return asMillis(asDateTime(datetime, DEFAULT_TZ).plus(temporalAmount));
Copy link
Member

Choose a reason for hiding this comment

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

Why use UTC and not the request timezone?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added a comment.
Both timestamps that come from ES as well as those converted by our functions are in UTC.

@@ -52,6 +52,14 @@ public static ZonedDateTime asDateTime(long millis) {
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), UTC);
}

public static ZonedDateTime asDateTime(long millis, ZoneId zoneId) {
Copy link
Member

Choose a reason for hiding this comment

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

Since you always pass UTC, why not use the above?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remanent from a first attempt to use session's zoneID. Updated to use the existing one.

@costin costin added the ES|QL-ui Impacts ES|QL UI label Aug 25, 2023
Add Add/Sub unit tests
Make temporal units negatable.
Rename data type tests methods.
More tests.
@bpintea
Copy link
Contributor Author

bpintea commented Aug 28, 2023

The main comment I have is around the use of timezones.
If that is not the case, this should be clarified.

I've added a comment.

Lastly some docs help - I would reuse the structure and content from SQL since its fairly similar.

Will follow with a subsequent PR.

@bpintea
Copy link
Contributor Author

bpintea commented Aug 28, 2023

unit tests tests for period + datetime and duration + datetime

added.

unit tests for period - datetime and duration - datetime

Good point, thanks. Increased the Sub type resolution checks to prevent this.

unit or csv tests for weird edge cases, like date math for leap years, adding days or hours past the end of a month, etc. Although this may be less important since we know that we can trust the date math functions from the ql package.

Indeed, left this out.

Copy link
Member

@costin costin left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@alex-spies alex-spies left a comment

Choose a reason for hiding this comment

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

Still LGTM, only added very minor remarks (all supposed to be completely optional). Did not look too closely into what appeared to be refactors of the test that probably became necessary due to #98890.

@@ -85,6 +85,7 @@ static double processDoubles(double lhs, double rhs) {

@Evaluator(extraName = "Datetimes", warnExceptions = { ArithmeticException.class, DateTimeException.class })
static long processDatetimes(long datetime, @Fixed TemporalAmount temporalAmount) {
return asMillis(asDateTime(datetime, DEFAULT_TZ).plus(temporalAmount));
// using a UTC conversion since `datetime` is always a UTC-Epoch timestamp, either read from ES or converted through a function
Copy link
Contributor

Choose a reason for hiding this comment

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

praise: very helpful comment.

@@ -112,14 +137,47 @@ public void testEdgeCases() {

return;
}
if (testCaseType == EsqlDataTypes.DATE_PERIOD) {
Period minPeriod = Period.of(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
Copy link
Contributor

Choose a reason for hiding this comment

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

praise: good call testing the Neg edge cases :)

Comment on lines 78 to 81
if (false == (lhsType == rhsType || lhsType.isNumeric() && rhsType.isNumeric())) {
return false;
}
return super.supportsTypes(lhsType, rhsType);
Copy link
Contributor

Choose a reason for hiding this comment

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

question (bikeshed-y, so not meant as a suggestion in any way): couldn't this just be

Suggested change
if (false == (lhsType == rhsType || lhsType.isNumeric() && rhsType.isNumeric())) {
return false;
}
return super.supportsTypes(lhsType, rhsType);
return (lhsType == rhsType || lhsType.isNumeric() && rhsType.isNumeric()))
&& super.supportsTypes(lhsType, rhsType);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
:Analytics/ES|QL AKA ESQL >enhancement ES|QL-ui Impacts ES|QL UI Team:QL (Deprecated) Meta label for query languages team v8.11.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ESQL: allow math with timespan literals
4 participants