diff --git a/docs/changelog/83099.yaml b/docs/changelog/83099.yaml new file mode 100644 index 0000000000000..718509f80cd07 --- /dev/null +++ b/docs/changelog/83099.yaml @@ -0,0 +1,5 @@ +pr: 83099 +summary: Improve support for joda datetime to java datetime in Painless +area: Infra/Scripting +type: enhancement +issues: [] diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/api/Augmentation.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/api/Augmentation.java index 2c056d50539ca..9267a8e963045 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/api/Augmentation.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/api/Augmentation.java @@ -708,11 +708,83 @@ public static Matcher matcher(Pattern receiver, int limitFactor, CharSequence in /** * Convert a {@link TemporalAccessor} into millis since epoch like {@link Instant#toEpochMilli()}. */ - public static long toEpochMilli(TemporalAccessor v) { - return v.getLong(ChronoField.INSTANT_SECONDS) * 1_000 + v.get(ChronoField.NANO_OF_SECOND) / 1_000_000; + public static long toEpochMilli(TemporalAccessor receiver) { + return receiver.getLong(ChronoField.INSTANT_SECONDS) * 1_000 + receiver.get(ChronoField.NANO_OF_SECOND) / 1_000_000; + } + + public static long getMillis(TemporalAccessor receiver) { + return toEpochMilli(receiver); } public static DayOfWeek getDayOfWeekEnum(ZonedDateTime receiver) { return receiver.getDayOfWeek(); } + + public static int getCenturyOfEra(ZonedDateTime receiver) { + throw new UnsupportedOperationException( + "[getCenturyOfEra] is no longer available; " + "use [get(ChronoField.YEAR_OF_ERA) / 100] instead" + ); + } + + public static int getEra(ZonedDateTime receiver) { + throw new UnsupportedOperationException("[getEra] is no longer available; use [get(ChronoField.ERA)] instead"); + } + + public static int getHourOfDay(ZonedDateTime receiver) { + throw new UnsupportedOperationException("[getHourOfDay] is no longer available; use [getHour()] instead"); + } + + public static int getMillisOfDay(ZonedDateTime receiver) { + throw new UnsupportedOperationException("[getMillisOfDay] is no longer available; use [get(ChronoField.MILLI_OF_DAY)] instead"); + } + + public static int getMillisOfSecond(ZonedDateTime receiver) { + throw new UnsupportedOperationException( + "[getMillisOfSecond] is no longer available; " + "use [get(ChronoField.MILLI_OF_SECOND)] instead" + ); + } + + public static int getMinuteOfDay(ZonedDateTime receiver) { + throw new UnsupportedOperationException( + "[getMinuteOfDay] is no longer available; " + "use [get(ChronoField.MINUTE_OF_DAY)] instead" + ); + } + + public static int getMinuteOfHour(ZonedDateTime receiver) { + throw new UnsupportedOperationException("[getMinuteOfHour] is no longer available; use [getMinute()] instead"); + } + + public static int getMonthOfYear(ZonedDateTime receiver) { + throw new UnsupportedOperationException("[getMonthOfYear] is no longer available; use [getMonthValue()] instead"); + } + + public static int getSecondOfDay(ZonedDateTime receiver) { + throw new UnsupportedOperationException( + "[getSecondOfDay] is no longer available; " + "use [get(ChronoField.SECOND_OF_DAY)] instead" + ); + } + + public static int getSecondOfMinute(ZonedDateTime receiver) { + throw new UnsupportedOperationException("[getSecondOfMinute] is no longer available; use [getSecond()] instead"); + } + + public static int getWeekOfWeekyear(ZonedDateTime receiver) { + throw new UnsupportedOperationException( + "[getWeekOfWeekyear] is no longer available; " + "use [get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)] instead" + ); + } + + public static int getWeekyear(ZonedDateTime receiver) { + throw new UnsupportedOperationException("[getWeekyear] is no longer available; use [get(IsoFields.WEEK_BASED_YEAR)] instead"); + } + + public static int getYearOfCentury(ZonedDateTime receiver) { + throw new UnsupportedOperationException( + "[getYearOfCentury] is no longer available; " + "use [get(ChronoField.YEAR_OF_ERA) % 100] instead" + ); + } + + public static int getYearOfEra(ZonedDateTime receiver) { + throw new UnsupportedOperationException("[getYearOfEra] is no longer available; use [get(ChronoField.YEAR_OF_ERA)] instead"); + } } diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.time.temporal.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.time.temporal.txt index 70cc075936bd7..de7cd586a3068 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.time.temporal.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.time.temporal.txt @@ -29,8 +29,9 @@ class java.time.temporal.TemporalAccessor { boolean isSupported(TemporalField) def query(TemporalQuery) ValueRange range(TemporalField) - # An easy method to convert temporalAccessors to millis since epoch similar to Instan#toEpochMilli. + # An easy method to convert temporalAccessors to millis since epoch similar to Instant#toEpochMilli. long org.elasticsearch.painless.api.Augmentation toEpochMilli() + long org.elasticsearch.painless.api.Augmentation getMillis() } class java.time.temporal.TemporalAdjuster { diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.time.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.time.txt index de9780e9a6fc3..cd727b1b29bbe 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.time.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/java.time.txt @@ -475,6 +475,20 @@ class java.time.ZonedDateTime { int getDayOfMonth() DayOfWeek getDayOfWeek() DayOfWeek org.elasticsearch.painless.api.Augmentation getDayOfWeekEnum() + int org.elasticsearch.painless.api.Augmentation getCenturyOfEra() + int org.elasticsearch.painless.api.Augmentation getEra() + int org.elasticsearch.painless.api.Augmentation getHourOfDay() + int org.elasticsearch.painless.api.Augmentation getMillisOfDay() + int org.elasticsearch.painless.api.Augmentation getMillisOfSecond() + int org.elasticsearch.painless.api.Augmentation getMinuteOfDay() + int org.elasticsearch.painless.api.Augmentation getMinuteOfHour() + int org.elasticsearch.painless.api.Augmentation getMonthOfYear() + int org.elasticsearch.painless.api.Augmentation getSecondOfDay() + int org.elasticsearch.painless.api.Augmentation getSecondOfMinute() + int org.elasticsearch.painless.api.Augmentation getWeekOfWeekyear() + int org.elasticsearch.painless.api.Augmentation getWeekyear() + int org.elasticsearch.painless.api.Augmentation getYearOfCentury() + int org.elasticsearch.painless.api.Augmentation getYearOfEra() int getDayOfYear() int getHour() LocalDate toLocalDate() diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml index 9ddc05d6675c2..c2880d17361c3 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml @@ -261,6 +261,94 @@ setup: source: "/* avoid yaml stash */ $('date', null)" - match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' } + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "doc.date.get(0).getMillis()" + - match: { hits.hits.0.fields.field.0: 1483272672000 } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "doc.date.value.getMillis()" + - match: { hits.hits.0.fields.field.0: 1483272672000 } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "field('date').get(null).getMillis()" + - match: { hits.hits.0.fields.field.0: 1483272672000 } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid yaml stash */ $('date', null).getMillis()" + - match: { hits.hits.0.fields.field.0: 1483272672000 } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "doc.date.get(0).millis" + - match: { hits.hits.0.fields.field.0: 1483272672000 } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "doc.date.value.millis" + - match: { hits.hits.0.fields.field.0: 1483272672000 } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "field('date').get(null).millis" + - match: { hits.hits.0.fields.field.0: 1483272672000 } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid yaml stash */ $('date', null).millis" + - match: { hits.hits.0.fields.field.0: 1483272672000 } + - do: search: rest_total_hits_as_int: true @@ -1696,3 +1784,357 @@ setup: - match: { hits.hits.1.fields.f_list.0: "dne" } - match: { hits.hits.1.fields.f_list2.0: "789" } - match: { hits.hits.1.fields.all.0: "10111213789876deflmnrstwyz" } + +--- +"unsupported date methods": + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getCenturyOfEra()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getCenturyOfEra] is no longer available; use [get(ChronoField.YEAR_OF_ERA) / 100] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(ChronoField.YEAR_OF_ERA) / 100" + - match: { hits.hits.0.fields.field.0: 20 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getEra()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getEra] is no longer available; use [get(ChronoField.ERA)] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(ChronoField.ERA)" + - match: { hits.hits.0.fields.field.0: 1 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getHourOfDay()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getHourOfDay] is no longer available; use [getHour()] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getHour()" + - match: { hits.hits.0.fields.field.0: 12 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getMillisOfDay()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getMillisOfDay] is no longer available; use [get(ChronoField.MILLI_OF_DAY)] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(ChronoField.MILLI_OF_DAY)" + - match: { hits.hits.0.fields.field.0: 43872000 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getMillisOfSecond()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getMillisOfSecond] is no longer available; use [get(ChronoField.MILLI_OF_SECOND)] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(ChronoField.MILLI_OF_SECOND)" + - match: { hits.hits.0.fields.field.0: 0 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getMinuteOfDay()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getMinuteOfDay] is no longer available; use [get(ChronoField.MINUTE_OF_DAY)] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(ChronoField.MINUTE_OF_DAY)" + - match: { hits.hits.0.fields.field.0: 731 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getMinuteOfHour()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getMinuteOfHour] is no longer available; use [getMinute()] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getMinute()" + - match: { hits.hits.0.fields.field.0: 11 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getMonthOfYear()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getMonthOfYear] is no longer available; use [getMonthValue()] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getMonthValue()" + - match: { hits.hits.0.fields.field.0: 1 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getSecondOfDay()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getSecondOfDay] is no longer available; use [get(ChronoField.SECOND_OF_DAY)] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(ChronoField.SECOND_OF_DAY)" + - match: { hits.hits.0.fields.field.0: 43872 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getSecondOfMinute()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getSecondOfMinute] is no longer available; use [getSecond()] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getSecond()" + - match: { hits.hits.0.fields.field.0: 12 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getWeekOfWeekyear()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getWeekOfWeekyear] is no longer available; use [get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(IsoFields.WEEK_OF_WEEK_BASED_YEAR)" + - match: { hits.hits.0.fields.field.0: 52 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getWeekyear()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getWeekyear] is no longer available; use [get(IsoFields.WEEK_BASED_YEAR)] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(IsoFields.WEEK_BASED_YEAR)" + - match: { hits.hits.0.fields.field.0: 2016 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getYearOfCentury()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getYearOfCentury] is no longer available; use [get(ChronoField.YEAR_OF_ERA) % 100] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(ChronoField.YEAR_OF_ERA) % 100" + - match: { hits.hits.0.fields.field.0: 17 } + + - do: + catch: bad_request + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).getYearOfEra()" + - match: { error.failed_shards.0.reason.caused_by.type: "unsupported_operation_exception" } + - match: { error.failed_shards.0.reason.caused_by.reason: + "[getYearOfEra] is no longer available; use [get(ChronoField.YEAR_OF_ERA)] instead" } + + - do: + search: + rest_total_hits_as_int: true + body: + query: { term: { _id: 1 } } + script_fields: + field: + script: + source: "/* avoid stash */ $('date', null).get(ChronoField.YEAR_OF_ERA)" + - match: { hits.hits.0.fields.field.0: 2017 } +