Sfoglia il codice sorgente

Improve support for joda datetime to java datetime transition in Painless (#83099)

Within Painless, this change augments TemporalAccessor with the method getMillis to make 
milliseconds easier to access directly from a datetime within a document.

This change also adds several methods from Joda datetimes augmented onto ZonedDateTime that 
throw an UnsupportedOperationException with a suggestion of how to implement that Joda method 
using ZonedDateTime instead.
Jack Conradson 3 anni fa
parent
commit
ab7f002792

+ 5 - 0
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: []

+ 74 - 2
modules/lang-painless/src/main/java/org/elasticsearch/painless/api/Augmentation.java

@@ -708,11 +708,83 @@ public class Augmentation {
     /**
      * 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");
+    }
 }

+ 2 - 1
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 {

+ 14 - 0
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()

+ 442 - 0
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 }
+