From ffd5c1b9ff541a3df3d0926e96108182ce810052 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 | 89 +++++++++++-------- .../elasticsearch/script/ScriptModule.java | 16 ++-- .../support/values/ScriptDoubleValues.java | 6 ++ .../support/values/ScriptLongValues.java | 3 + .../fielddata/ScriptDocValuesDatesTests.java | 86 ++++++++++-------- .../fielddata/ScriptDocValuesLongsTests.java | 4 +- .../search/fields/SearchFieldsIT.java | 22 ++--- 15 files changed, 149 insertions(+), 108 deletions(-) diff --git a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy index 1fde16915b03a..98cd6bf7319a5 100644 --- a/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy +++ b/buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy @@ -778,11 +778,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 8ce1a5a1e8c4c..6c06f36ab0070 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 02435b0b0d5f9..02edc583abfa4 100644 --- a/docs/painless/painless-getting-started.asciidoc +++ b/docs/painless/painless-getting-started.asciidoc @@ -211,7 +211,7 @@ POST hockey/player/1/_update ==== Dates Date fields are exposed as -`ReadableDateTime` +`ReadableDateTime` or so they support methods like `getYear`, and `getDayOfWeek`. @@ -233,6 +233,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 5c763df825703..d14a4bcb36776 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 @@ -79,8 +79,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() org.joda.time.ReadableDateTime getDate() List getDates() 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 69b5bb132a5ee..eba821f0ef55a 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 ede2927b992e0..1fe27ffe1d2a9 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' } - do: warnings: 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 7664345bd46e2..d85bc6bb1f164 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; @@ -38,6 +37,9 @@ 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; @@ -45,6 +47,7 @@ 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 @@ -56,6 +59,7 @@ * values form multiple documents. */ public abstract class ScriptDocValues extends AbstractList { + /** * Set the current doc ID. */ @@ -165,20 +169,20 @@ public long getValue() { } @Deprecated - public ReadableDateTime getDate() throws IOException { + public Object getDate() throws IOException { deprecated("getDate on numeric fields is deprecated. Use a date field to get dates."); if (dates == null) { - dates = new Dates(in); + dates = new Dates(in, deprecationCallback, false); dates.setNextDocId(docId); } return dates.getValue(); } @Deprecated - public List getDates() throws IOException { + public List getDates() throws IOException { deprecated("getDates on numeric fields is deprecated. Use a date field to get dates."); if (dates == null) { - dates = new Dates(in); + dates = new Dates(in, deprecationCallback, false); dates.setNextDocId(docId); } return dates; @@ -211,45 +215,57 @@ public Void run() { } } - 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 { + + /** 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 ReadableDateTime EPOCH = new DateTime(0, DateTimeZone.UTC); + private static final DeprecationLogger deprecationLogger = new DeprecationLogger(ESLoggerFactory.getLogger(Dates.class)); + private final SortedNumericDocValues in; + /** - * Callback for deprecated fields. In production this should always point to - * {@link #deprecationLogger} but tests will override it so they can test that - * we use the required permissions when calling it. + * Method call to add deprecation message. Normally this is + * {@link #deprecationLogger} but tests override. */ private final Consumer deprecationCallback; + + /** + * Whether java time or joda time should be used. This is normally {@link #USE_JAVA_TIME} but tests override it. + */ + private final boolean useJavaTime; + /** - * 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. + * 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 MutableDateTime[] dates; + private Object[] dates; private int count; /** * Standard constructor. */ public Dates(SortedNumericDocValues in) { - this(in, deprecationLogger::deprecated); + this(in, message -> deprecationLogger.deprecatedAndMaybeLog("scripting_joda_time_deprecation", message), USE_JAVA_TIME); } /** - * Constructor for testing deprecation logging. + * Constructor for testing with a deprecation callback. */ - Dates(SortedNumericDocValues in, Consumer deprecationCallback) { + 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) { if (ScriptModule.EXCEPTION_FOR_MISSING_VALUE) { throw new IllegalStateException("A document doesn't have a value for a field! " + @@ -264,7 +280,7 @@ public ReadableDateTime getValue() { * Fetch the first value. Added for backwards compatibility with 5.x when date fields were {@link Longs}. */ @Deprecated - public ReadableDateTime getDate() { + public Object getDate() { deprecated("getDate is no longer necessary on date fields as the value is now a date."); return getValue(); } @@ -273,13 +289,13 @@ public ReadableDateTime getDate() { * Fetch all the values. Added for backwards compatibility with 5.x when date fields were {@link Longs}. */ @Deprecated - public List getDates() { + public List getDates() { deprecated("getDates is no longer necessary on date fields as the values are now dates."); return this; } @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 [" @@ -310,29 +326,24 @@ 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); } } diff --git a/server/src/main/java/org/elasticsearch/script/ScriptModule.java b/server/src/main/java/org/elasticsearch/script/ScriptModule.java index bf4bd9c57cef0..61e01957c1633 100644 --- a/server/src/main/java/org/elasticsearch/script/ScriptModule.java +++ b/server/src/main/java/org/elasticsearch/script/ScriptModule.java @@ -19,6 +19,14 @@ package org.elasticsearch.script; +import org.elasticsearch.common.Booleans; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.logging.Loggers; +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,14 +35,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; -import org.elasticsearch.common.Booleans; -import org.elasticsearch.common.logging.DeprecationLogger; -import org.elasticsearch.common.logging.Loggers; - /** * 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 4244bfc564d03..4eb1508921d55 100644 --- a/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesDatesTests.java +++ b/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesDatesTests.java @@ -23,7 +23,6 @@ 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; @@ -32,38 +31,72 @@ 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, "getDate is no longer necessary on date fields as the value is now a date.", + "getDates is no longer necessary on date fields as the values are now dates."); + } + + 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", + "getDate is no longer necessary on date fields as the value is now a date.", + "getDates is no longer necessary on date fields as the values are now dates."); + } + + 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]); } } + 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()); - assertEquals(expectedDates[d][0] , dates.getDate()); + Object dateValue = AccessController.doPrivileged((PrivilegedAction) dates::getValue, noPermissionsAcc); + assertEquals(expectedDates[d][0] , dateValue); + Object bwcDateValue = AccessController.doPrivileged((PrivilegedAction) dates::getDate, noPermissionsAcc); + assertEquals(expectedDates[d][0] , bwcDateValue); + AccessController.doPrivileged((PrivilegedAction) dates::getDates, noPermissionsAcc); } else { Exception e = expectThrows(IllegalStateException.class, () -> dates.getValue()); assertEquals("A document doesn't have a value for a field! " + @@ -71,39 +104,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()); } - /* - * Invoke getDates without any privileges to verify that - * it still works without any. In particularly, this - * verifies that the callback that we've configured - * above works. That callback creates a temporary - * directory which is not possible with "noPermissions". - */ - PermissionCollection noPermissions = new Permissions(); - AccessControlContext noPermissionsAcc = new AccessControlContext( - new ProtectionDomain[] { - new ProtectionDomain(null, noPermissions) - } - ); - AccessController.doPrivileged(new PrivilegedAction() { - public Void run() { - dates.getDates(); - return null; - } - }, noPermissionsAcc); - - assertThat(warnings, containsInAnyOrder( - "getDate is no longer necessary on date fields as the value is now a date.", - "getDates is no longer necessary on date fields as the values are now dates.")); + assertThat(warnings, containsInAnyOrder(expectedWarnings)); } - private Dates wrap(long[][] values, Consumer deprecationHandler) { + private Dates wrap(long[][] values, Consumer deprecationHandler, boolean useJavaTime) { return new Dates(new AbstractSortedNumericDocValues() { long[] current; int i; @@ -122,6 +132,6 @@ public int docValueCount() { public long nextValue() { return current[i++]; } - }, deprecationHandler); + }, deprecationHandler, useJavaTime); } } diff --git a/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java b/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java index 166f3d8ccd272..a3bf7d09b8b98 100644 --- a/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java +++ b/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java @@ -136,7 +136,9 @@ public Void run() { assertThat(warnings, containsInAnyOrder( "getDate on numeric fields is deprecated. Use a date field to get dates.", - "getDates on numeric fields is deprecated. Use a date field to get dates.")); + "getDates on numeric fields is deprecated. Use a date field to get dates.", + "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")); } private Longs wrap(long[][] values, Consumer deprecationCallback) { 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 f1278814b822c..b83f99f7df210 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; @@ -116,7 +112,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")); @@ -827,8 +823,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")); @@ -968,10 +964,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()) @@ -999,7 +995,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)); }