From 478f6d6cf17c52b09af5f56663746a61190d6c0d Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Wed, 1 Aug 2018 08:58:49 -0700 Subject: [PATCH] Scripting: Conditionally use java time api in scripting (#31441) This commit adds a boolean system property, `es.scripting.use_java_time`, which controls the concrete return type used by doc values within scripts. The return type of accessing doc values for a date field is changed to Object, essentially duck typing the type to allow co-existence during the transition from joda time to java time. --- .../elasticsearch/gradle/BuildPlugin.groovy | 5 + docs/build.gradle | 3 + .../painless-getting-started.asciidoc | 7 +- .../bucket/datehistogram-aggregation.asciidoc | 1 + modules/lang-painless/build.gradle | 3 +- .../painless/spi/org.elasticsearch.txt | 4 +- .../test/painless/20_scriptfield.yml | 4 +- .../test/painless/50_script_doc_values.yml | 4 +- .../index/fielddata/ScriptDocValues.java | 100 ++++++++++++------ .../elasticsearch/script/ScriptModule.java | 11 +- .../support/values/ScriptDoubleValues.java | 6 ++ .../support/values/ScriptLongValues.java | 3 + .../fielddata/ScriptDocValuesDatesTests.java | 74 ++++++++++--- .../search/fields/SearchFieldsIT.java | 22 ++-- 14 files changed, 175 insertions(+), 72 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy index 5a962a5138b5f..702d0f4020ea6 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy @@ -777,11 +777,16 @@ class BuildPlugin implements Plugin { systemProperty property.getKey(), property.getValue() } } + + // TODO: remove this once joda time is removed from scriptin in 7.0 + systemProperty 'es.scripting.use_java_time', 'true' + // Set the system keystore/truststore password if we're running tests in a FIPS-140 JVM if (project.inFipsJvm) { systemProperty 'javax.net.ssl.trustStorePassword', 'password' systemProperty 'javax.net.ssl.keyStorePassword', 'password' } + boolean assertionsEnabled = Boolean.parseBoolean(System.getProperty('tests.asserts', 'true')) enableSystemAssertions assertionsEnabled enableAssertions assertionsEnabled diff --git a/docs/build.gradle b/docs/build.gradle index a67c0217490b3..4c0502a0e06fc 100644 --- a/docs/build.gradle +++ b/docs/build.gradle @@ -37,6 +37,9 @@ integTestCluster { extraConfigFile 'hunspell/en_US/en_US.dic', '../server/src/test/resources/indices/analyze/conf_dir/hunspell/en_US/en_US.dic' // Whitelist reindexing from the local node so we can test it. setting 'reindex.remote.whitelist', '127.0.0.1:*' + + // TODO: remove this for 7.0, this exists to allow the doc examples in 6.x to continue using the defaults + systemProperty 'es.scripting.use_java_time', 'false' } // remove when https://github.com/elastic/elasticsearch/issues/31305 is fixed diff --git a/docs/painless/painless-getting-started.asciidoc b/docs/painless/painless-getting-started.asciidoc index 1dec4a33bb583..8cff207ab04d5 100644 --- a/docs/painless/painless-getting-started.asciidoc +++ b/docs/painless/painless-getting-started.asciidoc @@ -198,7 +198,7 @@ POST hockey/player/1/_update ==== Dates Date fields are exposed as -`ReadableDateTime` +`ReadableDateTime` or so they support methods like `getYear`, and `getDayOfWeek`. @@ -220,6 +220,11 @@ GET hockey/_search } ---------------------------------------------------------------- // CONSOLE +// TEST[warning:The joda time api for doc values is deprecated. Use -Des.scripting.use_java_time=true to use the java time api for date field doc values] + +NOTE: Date fields are changing in 7.0 to be exposed as `ZonedDateTime` +from Java 8's time API. To switch to this functionality early, +add `-Des.scripting.use_java_time=true` to `jvm.options`. [float] [[modules-scripting-painless-regex]] diff --git a/docs/reference/aggregations/bucket/datehistogram-aggregation.asciidoc b/docs/reference/aggregations/bucket/datehistogram-aggregation.asciidoc index efbd8ef7389bb..e19ecac462d14 100644 --- a/docs/reference/aggregations/bucket/datehistogram-aggregation.asciidoc +++ b/docs/reference/aggregations/bucket/datehistogram-aggregation.asciidoc @@ -425,6 +425,7 @@ POST /sales/_search?size=0 -------------------------------------------------- // CONSOLE // TEST[setup:sales] +// TEST[warning:The joda time api for doc values is deprecated. Use -Des.scripting.use_java_time=true to use the java time api for date field doc values] Response: diff --git a/modules/lang-painless/build.gradle b/modules/lang-painless/build.gradle index fb1ea441a9dd5..e3a7ccecae2e5 100644 --- a/modules/lang-painless/build.gradle +++ b/modules/lang-painless/build.gradle @@ -17,8 +17,6 @@ * under the License. */ - - esplugin { description 'An easy, safe and fast scripting language for Elasticsearch' classname 'org.elasticsearch.painless.PainlessPlugin' @@ -26,6 +24,7 @@ esplugin { integTestCluster { module project.project(':modules:mapper-extras') + systemProperty 'es.scripting.use_java_time', 'true' } dependencies { diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt index 8491d15c27eb9..f424f6acf25aa 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt @@ -77,8 +77,8 @@ class org.elasticsearch.index.fielddata.ScriptDocValues$Longs { } class org.elasticsearch.index.fielddata.ScriptDocValues$Dates { - org.joda.time.ReadableDateTime get(int) - org.joda.time.ReadableDateTime getValue() + Object get(int) + Object getValue() List getValues() } diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml index 2914e8a916ec6..3be6601521e87 100644 --- a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml @@ -108,7 +108,7 @@ setup: script_fields: bar: script: - source: "doc.date.value.dayOfWeek" + source: "doc.date.value.dayOfWeek.value" - match: { hits.hits.0.fields.bar.0: 7} @@ -123,7 +123,7 @@ setup: source: > StringBuilder b = new StringBuilder(); for (def date : doc.dates) { - b.append(" ").append(date.getDayOfWeek()); + b.append(" ").append(date.getDayOfWeek().value); } return b.toString().trim() diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/50_script_doc_values.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/50_script_doc_values.yml index 617b8df61b6bd..4c3c204d2d9ca 100644 --- a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/50_script_doc_values.yml +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/50_script_doc_values.yml @@ -95,7 +95,7 @@ setup: field: script: source: "doc.date.get(0)" - - match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' } + - match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12Z' } - do: search: @@ -104,7 +104,7 @@ setup: field: script: source: "doc.date.value" - - match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' } + - match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12Z' } --- "geo_point": diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java index fedad6e134b46..35284cb655d98 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java @@ -19,7 +19,6 @@ package org.elasticsearch.index.fielddata; - import org.apache.lucene.index.SortedNumericDocValues; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.BytesRef; @@ -29,18 +28,23 @@ import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.common.logging.DeprecationLogger; import org.elasticsearch.common.logging.ESLoggerFactory; -import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.joda.time.MutableDateTime; -import org.joda.time.ReadableDateTime; import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; import java.util.AbstractList; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.function.Consumer; import java.util.function.UnaryOperator; +import static org.elasticsearch.common.Booleans.parseBoolean; /** * Script level doc values, the assumption is that any implementation will @@ -52,6 +56,7 @@ * values form multiple documents. */ public abstract class ScriptDocValues extends AbstractList { + /** * Set the current doc ID. */ @@ -142,31 +147,55 @@ public int size() { } } - public static final class Dates extends ScriptDocValues { - protected static final DeprecationLogger deprecationLogger = new DeprecationLogger(ESLoggerFactory.getLogger(Dates.class)); + public static final class Dates extends ScriptDocValues { - private static final ReadableDateTime EPOCH = new DateTime(0, DateTimeZone.UTC); + /** Whether scripts should expose dates as java time objects instead of joda time. */ + private static final boolean USE_JAVA_TIME = parseBoolean(System.getProperty("es.scripting.use_java_time"), false); + + private static final DeprecationLogger deprecationLogger = new DeprecationLogger(ESLoggerFactory.getLogger(Dates.class)); private final SortedNumericDocValues in; + + /** + * Method call to add deprecation message. Normally this is + * {@link #deprecationLogger} but tests override. + */ + private final Consumer deprecationCallback; + /** - * Values wrapped in {@link MutableDateTime}. Null by default an allocated on first usage so we allocate a reasonably size. We keep - * this array so we don't have allocate new {@link MutableDateTime}s on every usage. Instead we reuse them for every document. + * Whether java time or joda time should be used. This is normally {@link #USE_JAVA_TIME} but tests override it. */ - private MutableDateTime[] dates; + private final boolean useJavaTime; + + /** + * Values wrapped in a date time object. The concrete type depends on the system property {@code es.scripting.use_java_time}. + * When that system property is {@code false}, the date time objects are of type {@link MutableDateTime}. When the system + * property is {@code true}, the date time objects are of type {@link java.time.ZonedDateTime}. + */ + private Object[] dates; private int count; /** * Standard constructor. */ public Dates(SortedNumericDocValues in) { + this(in, message -> deprecationLogger.deprecatedAndMaybeLog("scripting_joda_time_deprecation", message), USE_JAVA_TIME); + } + + /** + * Constructor for testing with a deprecation callback. + */ + Dates(SortedNumericDocValues in, Consumer deprecationCallback, boolean useJavaTime) { this.in = in; + this.deprecationCallback = deprecationCallback; + this.useJavaTime = useJavaTime; } /** * Fetch the first field value or 0 millis after epoch if there are no * in. */ - public ReadableDateTime getValue() { + public Object getValue() { if (count == 0) { throw new IllegalStateException("A document doesn't have a value for a field! " + "Use doc[].size()==0 to check if a document is missing a field!"); @@ -175,7 +204,7 @@ public ReadableDateTime getValue() { } @Override - public ReadableDateTime get(int index) { + public Object get(int index) { if (index >= count) { throw new IndexOutOfBoundsException( "attempted to fetch the [" + index + "] date when there are only [" @@ -206,31 +235,42 @@ void refreshArray() throws IOException { if (count == 0) { return; } - if (dates == null) { - // Happens for the document. We delay allocating dates so we can allocate it with a reasonable size. - dates = new MutableDateTime[count]; - for (int i = 0; i < dates.length; i++) { - dates[i] = new MutableDateTime(in.nextValue(), DateTimeZone.UTC); + if (useJavaTime) { + if (dates == null || count > dates.length) { + // Happens for the document. We delay allocating dates so we can allocate it with a reasonable size. + dates = new ZonedDateTime[count]; } - return; - } - if (count > dates.length) { - // Happens when we move to a new document and it has more dates than any documents before it. - MutableDateTime[] backup = dates; - dates = new MutableDateTime[count]; - System.arraycopy(backup, 0, dates, 0, backup.length); - for (int i = 0; i < backup.length; i++) { - dates[i].setMillis(in.nextValue()); + for (int i = 0; i < count; ++i) { + dates[i] = ZonedDateTime.ofInstant(Instant.ofEpochMilli(in.nextValue()), ZoneOffset.UTC); } - for (int i = backup.length; i < dates.length; i++) { + } else { + deprecated("The joda time api for doc values is deprecated. Use -Des.scripting.use_java_time=true" + + " to use the java time api for date field doc values"); + if (dates == null || count > dates.length) { + // Happens for the document. We delay allocating dates so we can allocate it with a reasonable size. + dates = new MutableDateTime[count]; + } + for (int i = 0; i < count; i++) { dates[i] = new MutableDateTime(in.nextValue(), DateTimeZone.UTC); } - return; - } - for (int i = 0; i < count; i++) { - dates[i] = new MutableDateTime(in.nextValue(), DateTimeZone.UTC); } } + + /** + * Log a deprecation log, with the server's permissions, not the permissions of the + * script calling this method. We need to do this to prevent errors when rolling + * the log file. + */ + private void deprecated(String message) { + // Intentionally not calling SpecialPermission.check because this is supposed to be called by scripts + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + deprecationCallback.accept(message); + return null; + } + }); + } } public static final class Doubles extends ScriptDocValues { diff --git a/server/src/main/java/org/elasticsearch/script/ScriptModule.java b/server/src/main/java/org/elasticsearch/script/ScriptModule.java index a3da1dafe48d7..4e4c0a5a3c60f 100644 --- a/server/src/main/java/org/elasticsearch/script/ScriptModule.java +++ b/server/src/main/java/org/elasticsearch/script/ScriptModule.java @@ -19,6 +19,11 @@ package org.elasticsearch.script; +import org.elasticsearch.common.settings.ClusterSettings; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.plugins.ScriptPlugin; +import org.elasticsearch.search.aggregations.pipeline.movfn.MovingFunctionScript; + import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -27,12 +32,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.elasticsearch.common.settings.ClusterSettings; -import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.plugins.ScriptPlugin; -import org.elasticsearch.search.aggregations.pipeline.movfn.MovingFunctionScript; - - /** * Manages building {@link ScriptService}. */ diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/values/ScriptDoubleValues.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/values/ScriptDoubleValues.java index ac3c8f682ba62..1227efb5ea0af 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/values/ScriptDoubleValues.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/values/ScriptDoubleValues.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.lang.reflect.Array; +import java.time.ZonedDateTime; import java.util.Collection; /** @@ -54,6 +55,9 @@ public boolean advanceExact(int target) throws IOException { } else if (value instanceof ReadableInstant) { resize(1); values[0] = ((ReadableInstant) value).getMillis(); + } else if (value instanceof ZonedDateTime) { + resize(1); + values[0] = ((ZonedDateTime) value).toInstant().toEpochMilli(); } else if (value.getClass().isArray()) { int length = Array.getLength(value); if (length == 0) { @@ -89,6 +93,8 @@ private static double toDoubleValue(Object o) { } else if (o instanceof ReadableInstant) { // Dates are exposed in scripts as ReadableDateTimes but aggregations want them to be numeric return ((ReadableInstant) o).getMillis(); + } else if (o instanceof ZonedDateTime) { + return ((ZonedDateTime) o).toInstant().toEpochMilli(); } else if (o instanceof Boolean) { // We do expose boolean fields as boolean in scripts, however aggregations still expect // that scripts return the same internal representation as regular fields, so boolean diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/values/ScriptLongValues.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/values/ScriptLongValues.java index 818a9d9fd8ddf..cdc448bd04130 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/values/ScriptLongValues.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/values/ScriptLongValues.java @@ -28,6 +28,7 @@ import java.io.IOException; import java.lang.reflect.Array; +import java.time.ZonedDateTime; import java.util.Collection; import java.util.Iterator; @@ -91,6 +92,8 @@ private static long toLongValue(Object o) { } else if (o instanceof ReadableInstant) { // Dates are exposed in scripts as ReadableDateTimes but aggregations want them to be numeric return ((ReadableInstant) o).getMillis(); + } else if (o instanceof ZonedDateTime) { + return ((ZonedDateTime) o).toInstant().toEpochMilli(); } else if (o instanceof Boolean) { // We do expose boolean fields as boolean in scripts, however aggregations still expect // that scripts return the same internal representation as regular fields, so boolean diff --git a/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesDatesTests.java b/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesDatesTests.java index 43b9a01560ca8..9f80a45fa8e74 100644 --- a/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesDatesTests.java +++ b/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesDatesTests.java @@ -23,29 +23,74 @@ import org.elasticsearch.test.ESTestCase; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; -import org.joda.time.ReadableDateTime; import java.io.IOException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.PrivilegedAction; +import java.security.ProtectionDomain; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; +import static org.hamcrest.Matchers.containsInAnyOrder; public class ScriptDocValuesDatesTests extends ESTestCase { - public void test() throws IOException { + + public void testJavaTime() throws IOException { + assertDateDocValues(true); + } + + public void testJodaTimeBwc() throws IOException { + assertDateDocValues(false, "The joda time api for doc values is deprecated." + + " Use -Des.scripting.use_java_time=true to use the java time api for date field doc values"); + } + + public void assertDateDocValues(boolean useJavaTime, String... expectedWarnings) throws IOException { + final Function datetimeCtor; + if (useJavaTime) { + datetimeCtor = millis -> ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneOffset.UTC); + } else { + datetimeCtor = millis -> new DateTime(millis, DateTimeZone.UTC); + } long[][] values = new long[between(3, 10)][]; - ReadableDateTime[][] expectedDates = new ReadableDateTime[values.length][]; + Object[][] expectedDates = new Object[values.length][]; for (int d = 0; d < values.length; d++) { values[d] = new long[randomBoolean() ? randomBoolean() ? 0 : 1 : between(2, 100)]; - expectedDates[d] = new ReadableDateTime[values[d].length]; + expectedDates[d] = new Object[values[d].length]; for (int i = 0; i < values[d].length; i++) { - expectedDates[d][i] = new DateTime(randomNonNegativeLong(), DateTimeZone.UTC); - values[d][i] = expectedDates[d][i].getMillis(); + values[d][i] = randomNonNegativeLong(); + expectedDates[d][i] = datetimeCtor.apply(values[d][i]); } } - Dates dates = wrap(values); + + Set warnings = new HashSet<>(); + Dates dates = wrap(values, deprecationMessage -> { + warnings.add(deprecationMessage); + /* Create a temporary directory to prove we are running with the + * server's permissions. */ + createTempDir(); + }, useJavaTime); + // each call to get or getValue will be run with limited permissions, just as they are in scripts + PermissionCollection noPermissions = new Permissions(); + AccessControlContext noPermissionsAcc = new AccessControlContext( + new ProtectionDomain[] { + new ProtectionDomain(null, noPermissions) + } + ); + for (int round = 0; round < 10; round++) { int d = between(0, values.length - 1); dates.setNextDocId(d); if (expectedDates[d].length > 0) { - assertEquals(expectedDates[d][0] , dates.getValue()); + Object dateValue = AccessController.doPrivileged((PrivilegedAction) dates::getValue, noPermissionsAcc); + assertEquals(expectedDates[d][0] , dateValue); } else { Exception e = expectThrows(IllegalStateException.class, () -> dates.getValue()); assertEquals("A document doesn't have a value for a field! " + @@ -54,15 +99,16 @@ public void test() throws IOException { assertEquals(values[d].length, dates.size()); for (int i = 0; i < values[d].length; i++) { - assertEquals(expectedDates[d][i], dates.get(i)); + final int ndx = i; + Object dateValue = AccessController.doPrivileged((PrivilegedAction) () -> dates.get(ndx), noPermissionsAcc); + assertEquals(expectedDates[d][i], dateValue); } - - Exception e = expectThrows(UnsupportedOperationException.class, () -> dates.add(new DateTime())); - assertEquals("doc values are unmodifiable", e.getMessage()); } + + assertThat(warnings, containsInAnyOrder(expectedWarnings)); } - private Dates wrap(long[][] values) { + private Dates wrap(long[][] values, Consumer deprecationHandler, boolean useJavaTime) { return new Dates(new AbstractSortedNumericDocValues() { long[] current; int i; @@ -81,6 +127,6 @@ public int docValueCount() { public long nextValue() { return current[i++]; } - }); + }, deprecationHandler, useJavaTime); } } diff --git a/server/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java b/server/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java index 31fa4f838dfff..aea0243a399d4 100644 --- a/server/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java +++ b/server/src/test/java/org/elasticsearch/search/fields/SearchFieldsIT.java @@ -47,15 +47,10 @@ import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.test.ESIntegTestCase; import org.elasticsearch.test.InternalSettingsPlugin; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.ReadableDateTime; -import org.joda.time.base.BaseDateTime; -import org.joda.time.format.DateTimeFormat; -import org.joda.time.format.DateTimeFormatter; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -64,6 +59,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -115,7 +111,7 @@ protected Map, Object>> pluginScripts() { scripts.put("doc['date'].date.millis", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Dates dates = (ScriptDocValues.Dates) doc.get("date"); - return dates.getValue().getMillis(); + return ((ZonedDateTime) dates.getValue()).toInstant().toEpochMilli(); }); scripts.put("_fields['num1'].value", vars -> fieldsScript(vars, "num1")); @@ -805,8 +801,8 @@ public void testDocValueFields() throws Exception { assertThat(searchResponse.getHits().getAt(0).getFields().get("long_field").getValue(), equalTo((Object) 4L)); assertThat(searchResponse.getHits().getAt(0).getFields().get("float_field").getValue(), equalTo((Object) 5.0)); assertThat(searchResponse.getHits().getAt(0).getFields().get("double_field").getValue(), equalTo((Object) 6.0d)); - BaseDateTime dateField = searchResponse.getHits().getAt(0).getFields().get("date_field").getValue(); - assertThat(dateField.getMillis(), equalTo(date.toInstant().toEpochMilli())); + ZonedDateTime dateField = searchResponse.getHits().getAt(0).getFields().get("date_field").getValue(); + assertThat(dateField.toInstant().toEpochMilli(), equalTo(date.toInstant().toEpochMilli())); assertThat(searchResponse.getHits().getAt(0).getFields().get("boolean_field").getValue(), equalTo((Object) true)); assertThat(searchResponse.getHits().getAt(0).getFields().get("text_field").getValue(), equalTo("foo")); assertThat(searchResponse.getHits().getAt(0).getFields().get("keyword_field").getValue(), equalTo("foo")); @@ -946,10 +942,10 @@ public void testDocValueFieldsWithFieldAlias() throws Exception { assertAcked(prepareCreate("test").addMapping("type", mapping)); ensureGreen("test"); - DateTime date = new DateTime(1990, 12, 29, 0, 0, DateTimeZone.UTC); - DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd"); + ZonedDateTime date = ZonedDateTime.of(1990, 12, 29, 0, 0, 0, 0, ZoneOffset.UTC); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ROOT); - index("test", "type", "1", "text_field", "foo", "date_field", formatter.print(date)); + index("test", "type", "1", "text_field", "foo", "date_field", formatter.format(date)); refresh("test"); SearchRequestBuilder builder = client().prepareSearch().setQuery(matchAllQuery()) @@ -977,7 +973,7 @@ public void testDocValueFieldsWithFieldAlias() throws Exception { DocumentField dateField = fields.get("date_field"); assertThat(dateField.getName(), equalTo("date_field")); - ReadableDateTime fetchedDate = dateField.getValue(); + ZonedDateTime fetchedDate = dateField.getValue(); assertThat(fetchedDate, equalTo(date)); }