mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-23 05:15:04 +00:00
Scripting: Rework joda time backcompat (#33486)
This commit switches the joda time backcompat in scripting to use augmentation over ZonedDateTime. The augmentation methods provide compatibility with the missing methods between joda's DateTime and java's ZonedDateTime. Due to getDayOfWeek returning an enum in the java API, ZonedDateTime is wrapped so that the method can return int like the joda time does. The java time api version is renamed to getDayOfWeekEnum, which will be kept through 7.x for compatibility while users switch back to getDayOfWeek once joda compatibility is removed.
This commit is contained in:
parent
e5d82c3dea
commit
3046656ab1
@ -825,9 +825,6 @@ class BuildPlugin implements Plugin<Project> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this once joda time is removed from scripting in 7.0
|
||||
systemProperty 'es.scripting.use_java_time', 'true'
|
||||
|
||||
// TODO: remove this once ctx isn't added to update script params in 7.0
|
||||
systemProperty 'es.scripting.update.ctx_in_params', 'false'
|
||||
|
||||
|
@ -55,7 +55,6 @@ integTestCluster {
|
||||
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'
|
||||
systemProperty 'es.scripting.update.ctx_in_params', 'false'
|
||||
//TODO: remove this once the cname is prepended to the address by default in 7.0
|
||||
systemProperty 'es.http.cname_in_publish_address', 'true'
|
||||
|
@ -220,11 +220,6 @@ 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]]
|
||||
|
@ -416,7 +416,7 @@ POST /sales/_search?size=0
|
||||
"terms": {
|
||||
"script": {
|
||||
"lang": "painless",
|
||||
"source": "doc['date'].value.dayOfWeek"
|
||||
"source": "doc['date'].value.dayOfWeekEnum.value"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -425,7 +425,6 @@ 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:
|
||||
|
||||
|
@ -24,7 +24,6 @@ esplugin {
|
||||
|
||||
integTestCluster {
|
||||
module project.project(':modules:mapper-extras')
|
||||
systemProperty 'es.scripting.use_java_time', 'true'
|
||||
systemProperty 'es.scripting.update.ctx_in_params', 'false'
|
||||
systemProperty 'es.http.cname_in_publish_address', 'true'
|
||||
}
|
||||
|
@ -48,8 +48,7 @@ public final class Whitelist {
|
||||
"java.util.txt",
|
||||
"java.util.function.txt",
|
||||
"java.util.regex.txt",
|
||||
"java.util.stream.txt",
|
||||
"joda.time.txt"
|
||||
"java.util.stream.txt"
|
||||
};
|
||||
|
||||
public static final List<Whitelist> BASE_WHITELISTS =
|
||||
|
@ -1,60 +0,0 @@
|
||||
#
|
||||
# Licensed to Elasticsearch under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright
|
||||
# ownership. Elasticsearch licenses this file to you under
|
||||
# the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
#
|
||||
# Painless definition file. This defines the hierarchy of classes,
|
||||
# what methods and fields they have, etc.
|
||||
#
|
||||
|
||||
# NOTE: this just minimal whitelisting of joda time, just to provide
|
||||
# convenient access via the scripting API. classes are fully qualified to avoid
|
||||
# any confusion with java.time
|
||||
|
||||
class org.joda.time.ReadableInstant {
|
||||
boolean equals(Object)
|
||||
long getMillis()
|
||||
int hashCode()
|
||||
boolean isAfter(org.joda.time.ReadableInstant)
|
||||
boolean isBefore(org.joda.time.ReadableInstant)
|
||||
boolean isEqual(org.joda.time.ReadableInstant)
|
||||
String toString()
|
||||
}
|
||||
|
||||
class org.joda.time.ReadableDateTime {
|
||||
int getCenturyOfEra()
|
||||
int getDayOfMonth()
|
||||
int getDayOfWeek()
|
||||
int getDayOfYear()
|
||||
int getEra()
|
||||
int getHourOfDay()
|
||||
int getMillisOfDay()
|
||||
int getMillisOfSecond()
|
||||
int getMinuteOfDay()
|
||||
int getMinuteOfHour()
|
||||
int getMonthOfYear()
|
||||
int getSecondOfDay()
|
||||
int getSecondOfMinute()
|
||||
int getWeekOfWeekyear()
|
||||
int getWeekyear()
|
||||
int getYear()
|
||||
int getYearOfCentury()
|
||||
int getYearOfEra()
|
||||
String toString(String)
|
||||
String toString(String,Locale)
|
||||
}
|
@ -76,9 +76,93 @@ class org.elasticsearch.index.fielddata.ScriptDocValues$Longs {
|
||||
List getValues()
|
||||
}
|
||||
|
||||
class org.elasticsearch.script.JodaCompatibleZonedDateTime {
|
||||
##### ZonedDateTime methods
|
||||
int getDayOfMonth()
|
||||
int getDayOfYear()
|
||||
int getHour()
|
||||
LocalDate toLocalDate()
|
||||
LocalDateTime toLocalDateTime()
|
||||
int getMinute()
|
||||
Month getMonth()
|
||||
int getMonthValue()
|
||||
int getNano()
|
||||
int getSecond()
|
||||
int getYear()
|
||||
ZonedDateTime minus(TemporalAmount)
|
||||
ZonedDateTime minus(long,TemporalUnit)
|
||||
ZonedDateTime minusYears(long)
|
||||
ZonedDateTime minusMonths(long)
|
||||
ZonedDateTime minusWeeks(long)
|
||||
ZonedDateTime minusDays(long)
|
||||
ZonedDateTime minusHours(long)
|
||||
ZonedDateTime minusMinutes(long)
|
||||
ZonedDateTime minusSeconds(long)
|
||||
ZonedDateTime minusNanos(long)
|
||||
ZonedDateTime plus(TemporalAmount)
|
||||
ZonedDateTime plus(long,TemporalUnit)
|
||||
ZonedDateTime plusDays(long)
|
||||
ZonedDateTime plusHours(long)
|
||||
ZonedDateTime plusMinutes(long)
|
||||
ZonedDateTime plusMonths(long)
|
||||
ZonedDateTime plusNanos(long)
|
||||
ZonedDateTime plusSeconds(long)
|
||||
ZonedDateTime plusWeeks(long)
|
||||
ZonedDateTime plusYears(long)
|
||||
Instant toInstant()
|
||||
OffsetDateTime toOffsetDateTime()
|
||||
ZonedDateTime truncatedTo(TemporalUnit)
|
||||
ZonedDateTime with(TemporalAdjuster)
|
||||
ZonedDateTime with(TemporalField,long)
|
||||
ZonedDateTime withDayOfMonth(int)
|
||||
ZonedDateTime withDayOfYear(int)
|
||||
ZonedDateTime withEarlierOffsetAtOverlap()
|
||||
ZonedDateTime withFixedOffsetZone()
|
||||
ZonedDateTime withHour(int)
|
||||
ZonedDateTime withLaterOffsetAtOverlap()
|
||||
ZonedDateTime withMinute(int)
|
||||
ZonedDateTime withMonth(int)
|
||||
ZonedDateTime withNano(int)
|
||||
ZonedDateTime withSecond(int)
|
||||
ZonedDateTime withYear(int)
|
||||
ZonedDateTime withZoneSameLocal(ZoneId)
|
||||
ZonedDateTime withZoneSameInstant(ZoneId)
|
||||
|
||||
#### Joda methods that exist in java time
|
||||
boolean equals(Object)
|
||||
int hashCode()
|
||||
boolean isAfter(ZonedDateTime)
|
||||
boolean isBefore(ZonedDateTime)
|
||||
boolean isEqual(ZonedDateTime)
|
||||
String toString()
|
||||
|
||||
#### Joda time methods
|
||||
long getMillis()
|
||||
int getCenturyOfEra()
|
||||
int getEra()
|
||||
int getHourOfDay()
|
||||
int getMillisOfDay()
|
||||
int getMillisOfSecond()
|
||||
int getMinuteOfDay()
|
||||
int getMinuteOfHour()
|
||||
int getMonthOfYear()
|
||||
int getSecondOfDay()
|
||||
int getSecondOfMinute()
|
||||
int getWeekOfWeekyear()
|
||||
int getWeekyear()
|
||||
int getYearOfCentury()
|
||||
int getYearOfEra()
|
||||
String toString(String)
|
||||
String toString(String,Locale)
|
||||
|
||||
# conflicting methods
|
||||
DayOfWeek getDayOfWeekEnum()
|
||||
int getDayOfWeek()
|
||||
}
|
||||
|
||||
class org.elasticsearch.index.fielddata.ScriptDocValues$Dates {
|
||||
Object get(int)
|
||||
Object getValue()
|
||||
JodaCompatibleZonedDateTime get(int)
|
||||
JodaCompatibleZonedDateTime getValue()
|
||||
List getValues()
|
||||
}
|
||||
|
||||
|
@ -19,10 +19,8 @@
|
||||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.lang.invoke.LambdaConversionException;
|
||||
import java.time.Instant;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
@ -59,15 +57,15 @@ public class FunctionRefTests extends ScriptTestCase {
|
||||
public void testQualifiedVirtualMethodReference() {
|
||||
long instant = randomLong();
|
||||
assertEquals(instant, exec(
|
||||
"List l = [params.d]; return l.stream().mapToLong(org.joda.time.ReadableDateTime::getMillis).sum()",
|
||||
singletonMap("d", new DateTime(instant, DateTimeZone.UTC)), true));
|
||||
"List l = [params.d]; return l.stream().mapToLong(Instant::toEpochMilli).sum()",
|
||||
singletonMap("d", Instant.ofEpochMilli(instant)), true));
|
||||
}
|
||||
|
||||
public void testQualifiedVirtualMethodReferenceDef() {
|
||||
long instant = randomLong();
|
||||
assertEquals(instant, exec(
|
||||
"def l = [params.d]; return l.stream().mapToLong(org.joda.time.ReadableDateTime::getMillis).sum()",
|
||||
singletonMap("d", new DateTime(instant, DateTimeZone.UTC)), true));
|
||||
"def l = [params.d]; return l.stream().mapToLong(Instant::toEpochMilli).sum()",
|
||||
singletonMap("d", Instant.ofEpochMilli(instant)), true));
|
||||
}
|
||||
|
||||
public void testCtorMethodReference() {
|
||||
@ -197,10 +195,10 @@ public class FunctionRefTests extends ScriptTestCase {
|
||||
|
||||
public void testQualifiedMethodMissing() {
|
||||
Exception e = expectScriptThrows(IllegalArgumentException.class, () -> {
|
||||
exec("List l = [2, 1]; l.sort(org.joda.time.ReadableDateTime::bogus); return l.get(0);", false);
|
||||
exec("List l = [2, 1]; l.sort(java.time.Instant::bogus); return l.get(0);", false);
|
||||
});
|
||||
assertThat(e.getMessage(),
|
||||
containsString("function reference [org.joda.time.ReadableDateTime::bogus/2] matching [java.util.Comparator"));
|
||||
containsString("function reference [java.time.Instant::bogus/2] matching [java.util.Comparator, compare/2"));
|
||||
}
|
||||
|
||||
public void testClassMissing() {
|
||||
|
@ -108,7 +108,7 @@ setup:
|
||||
script_fields:
|
||||
bar:
|
||||
script:
|
||||
source: "doc.date.value.dayOfWeek.value"
|
||||
source: "doc.date.value.dayOfWeekEnum.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().value);
|
||||
b.append(" ").append(date.getDayOfWeekEnum().value);
|
||||
}
|
||||
return b.toString().trim()
|
||||
|
||||
|
@ -39,6 +39,7 @@ import org.elasticsearch.common.io.stream.Writeable.Writer;
|
||||
import org.elasticsearch.common.text.Text;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.util.concurrent.EsRejectedExecutionException;
|
||||
import org.elasticsearch.script.JodaCompatibleZonedDateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.ReadableInstant;
|
||||
|
||||
@ -680,6 +681,15 @@ public abstract class StreamOutput extends OutputStream {
|
||||
o.writeString(zonedDateTime.getZone().getId());
|
||||
o.writeLong(zonedDateTime.toInstant().toEpochMilli());
|
||||
});
|
||||
writers.put(JodaCompatibleZonedDateTime.class, (o, v) -> {
|
||||
// write the joda compatibility datetime as joda datetime
|
||||
o.writeByte((byte) 13);
|
||||
final JodaCompatibleZonedDateTime zonedDateTime = (JodaCompatibleZonedDateTime) v;
|
||||
String zoneId = zonedDateTime.getZonedDateTime().getZone().getId();
|
||||
// joda does not understand "Z" for utc, so we must special case
|
||||
o.writeString(zoneId.equals("Z") ? DateTimeZone.UTC.getID() : zoneId);
|
||||
o.writeLong(zonedDateTime.toInstant().toEpochMilli());
|
||||
});
|
||||
WRITERS = Collections.unmodifiableMap(writers);
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ import org.elasticsearch.common.time.DateFormatter;
|
||||
import org.elasticsearch.common.time.DateFormatters;
|
||||
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.script.JodaCompatibleZonedDateTime;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.Instant;
|
||||
@ -93,6 +94,7 @@ public class XContentElasticsearchExtension implements XContentBuilderExtension
|
||||
writers.put(Year.class, (b, v) -> b.value(v.toString()));
|
||||
writers.put(Duration.class, (b, v) -> b.value(v.toString()));
|
||||
writers.put(Period.class, (b, v) -> b.value(v.toString()));
|
||||
writers.put(JodaCompatibleZonedDateTime.class, XContentBuilder::timeValue);
|
||||
|
||||
writers.put(BytesReference.class, (b, v) -> {
|
||||
if (v == null) {
|
||||
@ -141,6 +143,8 @@ public class XContentElasticsearchExtension implements XContentBuilderExtension
|
||||
d -> DEFAULT_FORMATTER.format(ZonedDateTime.ofInstant((java.time.Instant) d, ZoneOffset.UTC)));
|
||||
transformers.put(LocalDate.class, d -> ((LocalDate) d).toString());
|
||||
transformers.put(LocalTime.class, d -> LOCAL_TIME_FORMATTER.format((LocalTime) d));
|
||||
transformers.put(JodaCompatibleZonedDateTime.class,
|
||||
d -> DEFAULT_FORMATTER.format(((JodaCompatibleZonedDateTime) d).getZonedDateTime()));
|
||||
return transformers;
|
||||
}
|
||||
}
|
||||
|
@ -28,10 +28,17 @@ import org.elasticsearch.common.lease.Releasable;
|
||||
public interface AtomicFieldData extends Accountable, Releasable {
|
||||
|
||||
/**
|
||||
* Returns a "scripting" based values.
|
||||
* Returns field values for use in scripting.
|
||||
*/
|
||||
ScriptDocValues<?> getScriptValues();
|
||||
|
||||
/**
|
||||
* Returns field values for use by returned hits.
|
||||
*/
|
||||
default ScriptDocValues<?> getLegacyFieldValues() {
|
||||
return getScriptValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a String representation of the values.
|
||||
*/
|
||||
|
@ -26,26 +26,17 @@ import org.apache.lucene.util.BytesRefBuilder;
|
||||
import org.elasticsearch.common.geo.GeoHashUtils;
|
||||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.common.geo.GeoUtils;
|
||||
import org.elasticsearch.common.logging.DeprecationLogger;
|
||||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.joda.time.MutableDateTime;
|
||||
import org.elasticsearch.script.JodaCompatibleZonedDateTime;
|
||||
|
||||
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
|
||||
* implement a <code>getValue</code> and a <code>getValues</code> that return
|
||||
@ -147,55 +138,28 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Dates extends ScriptDocValues<Object> {
|
||||
|
||||
/** 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));
|
||||
public static final class Dates extends ScriptDocValues<JodaCompatibleZonedDateTime> {
|
||||
|
||||
private final SortedNumericDocValues in;
|
||||
|
||||
/**
|
||||
* Method call to add deprecation message. Normally this is
|
||||
* {@link #deprecationLogger} but tests override.
|
||||
* Values wrapped in {@link java.time.ZonedDateTime} objects.
|
||||
*/
|
||||
private final Consumer<String> 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 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 JodaCompatibleZonedDateTime[] 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<String> 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 Object getValue() {
|
||||
public JodaCompatibleZonedDateTime getValue() {
|
||||
if (count == 0) {
|
||||
throw new IllegalStateException("A document doesn't have a value for a field! " +
|
||||
"Use doc[<field>].size()==0 to check if a document is missing a field!");
|
||||
@ -204,7 +168,7 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(int index) {
|
||||
public JodaCompatibleZonedDateTime get(int index) {
|
||||
if (index >= count) {
|
||||
throw new IndexOutOfBoundsException(
|
||||
"attempted to fetch the [" + index + "] date when there are only ["
|
||||
@ -235,41 +199,13 @@ public abstract class ScriptDocValues<T> extends AbstractList<T> {
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
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];
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dates[i] = ZonedDateTime.ofInstant(Instant.ofEpochMilli(in.nextValue()), ZoneOffset.UTC);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
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 JodaCompatibleZonedDateTime[count];
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
dates[i] = new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(in.nextValue()), ZoneOffset.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<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
deprecationCallback.accept(message);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,11 @@ import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
|
||||
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
||||
import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
|
||||
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
|
||||
import org.elasticsearch.script.JodaCompatibleZonedDateTime;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Specialization of {@link AtomicNumericFieldData} for integers.
|
||||
@ -47,6 +52,34 @@ abstract class AtomicLongFieldData implements AtomicNumericFieldData {
|
||||
return ramBytesUsed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ScriptDocValues<?> getLegacyFieldValues() {
|
||||
switch (numericType) {
|
||||
case DATE:
|
||||
final ScriptDocValues.Dates realDV = new ScriptDocValues.Dates(getLongValues());
|
||||
return new ScriptDocValues<DateTime>() {
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return realDV.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DateTime get(int index) {
|
||||
JodaCompatibleZonedDateTime dt = realDV.get(index);
|
||||
return new DateTime(dt.toInstant().toEpochMilli(), DateTimeZone.UTC);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNextDocId(int docId) throws IOException {
|
||||
realDV.setNextDocId(docId);
|
||||
}
|
||||
};
|
||||
default:
|
||||
return getScriptValues();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ScriptDocValues<?> getScriptValues() {
|
||||
switch (numericType) {
|
||||
|
@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.script;
|
||||
|
||||
import org.elasticsearch.common.SuppressForbidden;
|
||||
import org.elasticsearch.common.logging.DeprecationLogger;
|
||||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Month;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
import java.time.temporal.TemporalAmount;
|
||||
import java.time.temporal.TemporalField;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.time.temporal.WeekFields;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A wrapper around ZonedDateTime that exposes joda methods for backcompat.
|
||||
*/
|
||||
public class JodaCompatibleZonedDateTime {
|
||||
private static final DeprecationLogger DEPRECATION_LOGGER =
|
||||
new DeprecationLogger(ESLoggerFactory.getLogger(JodaCompatibleZonedDateTime.class));
|
||||
|
||||
private static void logDeprecated(String key, String message, Object... params) {
|
||||
// NOTE: we don't check SpecialPermission because this will be called (indirectly) from scripts
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
DEPRECATION_LOGGER.deprecatedAndMaybeLog(key, message, params);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private static void logDeprecatedMethod(String oldMethod, String newMethod) {
|
||||
logDeprecated(oldMethod, "Use of the joda time method [{}] is deprecated. Use [{}] instead.", oldMethod, newMethod);
|
||||
}
|
||||
|
||||
private ZonedDateTime dt;
|
||||
|
||||
public JodaCompatibleZonedDateTime(Instant instant, ZoneId zone) {
|
||||
this.dt = ZonedDateTime.ofInstant(instant, zone);
|
||||
}
|
||||
|
||||
// access the underlying ZonedDateTime
|
||||
public ZonedDateTime getZonedDateTime() {
|
||||
return dt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return dt.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return dt.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return dt.toString();
|
||||
}
|
||||
|
||||
public boolean isAfter(ZonedDateTime o) {
|
||||
return dt.isAfter(o);
|
||||
}
|
||||
|
||||
public boolean isBefore(ZonedDateTime o) {
|
||||
return dt.isBefore(o);
|
||||
}
|
||||
|
||||
public boolean isEqual(ZonedDateTime o) {
|
||||
return dt.isEqual(o);
|
||||
}
|
||||
|
||||
public int getDayOfMonth() {
|
||||
return dt.getDayOfMonth();
|
||||
}
|
||||
|
||||
public int getDayOfYear() {
|
||||
return dt.getDayOfYear();
|
||||
}
|
||||
|
||||
public int getHour() {
|
||||
return dt.getHour();
|
||||
}
|
||||
|
||||
public LocalDate toLocalDate() {
|
||||
return dt.toLocalDate();
|
||||
}
|
||||
|
||||
public LocalDateTime toLocalDateTime() {
|
||||
return dt.toLocalDateTime();
|
||||
}
|
||||
|
||||
public int getMinute() {
|
||||
return dt.getMinute();
|
||||
}
|
||||
|
||||
public Month getMonth() {
|
||||
return dt.getMonth();
|
||||
}
|
||||
|
||||
public int getMonthValue() {
|
||||
return dt.getMonthValue();
|
||||
}
|
||||
|
||||
public int getNano() {
|
||||
return dt.getNano();
|
||||
}
|
||||
|
||||
public int getSecond() {
|
||||
return dt.getSecond();
|
||||
}
|
||||
|
||||
public int getYear() {
|
||||
return dt.getYear();
|
||||
}
|
||||
|
||||
public ZonedDateTime minus(TemporalAmount delta) {
|
||||
return dt.minus(delta);
|
||||
}
|
||||
|
||||
public ZonedDateTime minus(long amount, TemporalUnit unit) {
|
||||
return dt.minus(amount, unit);
|
||||
}
|
||||
|
||||
public ZonedDateTime minusYears(long amount) {
|
||||
return dt.minusYears(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime minusMonths(long amount) {
|
||||
return dt.minusMonths(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime minusWeeks(long amount) {
|
||||
return dt.minusWeeks(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime minusDays(long amount) {
|
||||
return dt.minusDays(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime minusHours(long amount) {
|
||||
return dt.minusHours(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime minusMinutes(long amount) {
|
||||
return dt.minusMinutes(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime minusSeconds(long amount) {
|
||||
return dt.minusSeconds(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime minusNanos(long amount) {
|
||||
return dt.minusNanos(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plus(TemporalAmount amount) {
|
||||
return dt.plus(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plus(long amount,TemporalUnit unit) {
|
||||
return dt.plus(amount, unit);
|
||||
}
|
||||
|
||||
public ZonedDateTime plusDays(long amount) {
|
||||
return dt.plusDays(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plusHours(long amount) {
|
||||
return dt.plusHours(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plusMinutes(long amount) {
|
||||
return dt.plusMinutes(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plusMonths(long amount) {
|
||||
return dt.plusMonths(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plusNanos(long amount) {
|
||||
return dt.plusNanos(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plusSeconds(long amount) {
|
||||
return dt.plusSeconds(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plusWeeks(long amount) {
|
||||
return dt.plusWeeks(amount);
|
||||
}
|
||||
|
||||
public ZonedDateTime plusYears(long amount) {
|
||||
return dt.plusYears(amount);
|
||||
}
|
||||
|
||||
public Instant toInstant() {
|
||||
return dt.toInstant();
|
||||
}
|
||||
|
||||
public OffsetDateTime toOffsetDateTime() {
|
||||
return dt.toOffsetDateTime();
|
||||
}
|
||||
|
||||
@SuppressForbidden(reason = "only exposing the method as a passthrough")
|
||||
public ZonedDateTime truncatedTo(TemporalUnit unit) {
|
||||
return dt.truncatedTo(unit);
|
||||
}
|
||||
|
||||
public ZonedDateTime with(TemporalAdjuster adjuster) {
|
||||
return dt.with(adjuster);
|
||||
}
|
||||
|
||||
public ZonedDateTime with(TemporalField field, long newValue) {
|
||||
return dt.with(field, newValue);
|
||||
}
|
||||
|
||||
public ZonedDateTime withDayOfMonth(int value) {
|
||||
return dt.withDayOfMonth(value);
|
||||
}
|
||||
|
||||
public ZonedDateTime withDayOfYear(int value) {
|
||||
return dt.withDayOfYear(value);
|
||||
}
|
||||
|
||||
public ZonedDateTime withEarlierOffsetAtOverlap() {
|
||||
return dt.withEarlierOffsetAtOverlap();
|
||||
}
|
||||
|
||||
public ZonedDateTime withFixedOffsetZone() {
|
||||
return dt.withFixedOffsetZone();
|
||||
}
|
||||
|
||||
public ZonedDateTime withHour(int value) {
|
||||
return dt.withHour(value);
|
||||
}
|
||||
|
||||
public ZonedDateTime withLaterOffsetAtOverlap() {
|
||||
return dt.withLaterOffsetAtOverlap();
|
||||
}
|
||||
|
||||
public ZonedDateTime withMinute(int value) {
|
||||
return dt.withMinute(value);
|
||||
}
|
||||
|
||||
public ZonedDateTime withMonth(int value) {
|
||||
return dt.withMonth(value);
|
||||
}
|
||||
|
||||
public ZonedDateTime withNano(int value) {
|
||||
return dt.withNano(value);
|
||||
}
|
||||
|
||||
public ZonedDateTime withSecond(int value) {
|
||||
return dt.withSecond(value);
|
||||
}
|
||||
|
||||
public ZonedDateTime withYear(int value) {
|
||||
return dt.withYear(value);
|
||||
}
|
||||
|
||||
public ZonedDateTime withZoneSameLocal(ZoneId zone) {
|
||||
return dt.withZoneSameLocal(zone);
|
||||
}
|
||||
|
||||
public ZonedDateTime withZoneSameInstant(ZoneId zone) {
|
||||
return dt.withZoneSameInstant(zone);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public long getMillis() {
|
||||
logDeprecatedMethod("getMillis()", "toInstant().toEpochMilli()");
|
||||
return dt.toInstant().toEpochMilli();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getCenturyOfEra() {
|
||||
logDeprecatedMethod("getCenturyOfEra()", "get(ChronoField.YEAR_OF_ERA) / 100");
|
||||
return dt.get(ChronoField.YEAR_OF_ERA) / 100;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getEra() {
|
||||
logDeprecatedMethod("getEra()", "get(ChronoField.ERA)");
|
||||
return dt.get(ChronoField.ERA);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getHourOfDay() {
|
||||
logDeprecatedMethod("getHourOfDay()", "getHour()");
|
||||
return dt.getHour();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getMillisOfDay() {
|
||||
logDeprecatedMethod("getMillisOfDay()", "get(ChronoField.MILLI_OF_DAY)");
|
||||
return dt.get(ChronoField.MILLI_OF_DAY);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getMillisOfSecond() {
|
||||
logDeprecatedMethod("getMillisOfSecond()", "get(ChronoField.MILLI_OF_SECOND)");
|
||||
return dt.get(ChronoField.MILLI_OF_SECOND);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getMinuteOfDay() {
|
||||
logDeprecatedMethod("getMinuteOfDay()", "get(ChronoField.MINUTE_OF_DAY)");
|
||||
return dt.get(ChronoField.MINUTE_OF_DAY);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getMinuteOfHour() {
|
||||
logDeprecatedMethod("getMinuteOfHour()", "getMinute()");
|
||||
return dt.getMinute();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getMonthOfYear() {
|
||||
logDeprecatedMethod("getMonthOfYear()", "getMonthValue()");
|
||||
return dt.getMonthValue();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getSecondOfDay() {
|
||||
logDeprecatedMethod("getSecondOfDay()", "get(ChronoField.SECOND_OF_DAY)");
|
||||
return dt.get(ChronoField.SECOND_OF_DAY);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getSecondOfMinute() {
|
||||
logDeprecatedMethod("getSecondOfMinute()", "getSecond()");
|
||||
return dt.getSecond();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getWeekOfWeekyear() {
|
||||
logDeprecatedMethod("getWeekOfWeekyear()", "get(WeekFields.ISO.weekOfWeekBasedYear())");
|
||||
return dt.get(WeekFields.ISO.weekOfWeekBasedYear());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getWeekyear() {
|
||||
logDeprecatedMethod("getWeekyear()", "get(WeekFields.ISO.weekBasedYear())");
|
||||
return dt.get(WeekFields.ISO.weekBasedYear());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getYearOfCentury() {
|
||||
logDeprecatedMethod("getYearOfCentury()", "get(ChronoField.YEAR_OF_ERA) % 100");
|
||||
return dt.get(ChronoField.YEAR_OF_ERA) % 100;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getYearOfEra() {
|
||||
logDeprecatedMethod("getYearOfEra()", "get(ChronoField.YEAR_OF_ERA)");
|
||||
return dt.get(ChronoField.YEAR_OF_ERA);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String toString(String format) {
|
||||
logDeprecatedMethod("toString(String)", "a DateTimeFormatter");
|
||||
// TODO: replace with bwc formatter
|
||||
return new DateTime(dt.toInstant().toEpochMilli(), DateTimeZone.forID(dt.getZone().getId())).toString(format);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public String toString(String format, Locale locale) {
|
||||
logDeprecatedMethod("toString(String,Locale)", "a DateTimeFormatter");
|
||||
// TODO: replace with bwc formatter
|
||||
return new DateTime(dt.toInstant().toEpochMilli(), DateTimeZone.forID(dt.getZone().getId())).toString(format, locale);
|
||||
}
|
||||
|
||||
public DayOfWeek getDayOfWeekEnum() {
|
||||
return dt.getDayOfWeek();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getDayOfWeek() {
|
||||
logDeprecated("getDayOfWeek()",
|
||||
"The return type of [getDayOfWeek()] will change to an enum in 7.0. Use getDayOfWeekEnum().getValue().");
|
||||
return dt.getDayOfWeek().getValue();
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ package org.elasticsearch.search.aggregations.support.values;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
import org.elasticsearch.common.lucene.ScorerAware;
|
||||
import org.elasticsearch.index.fielddata.SortingNumericDoubleValues;
|
||||
import org.elasticsearch.script.JodaCompatibleZonedDateTime;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.aggregations.AggregationExecutionException;
|
||||
import org.joda.time.ReadableInstant;
|
||||
@ -95,6 +96,8 @@ public class ScriptDoubleValues extends SortingNumericDoubleValues implements Sc
|
||||
return ((ReadableInstant) o).getMillis();
|
||||
} else if (o instanceof ZonedDateTime) {
|
||||
return ((ZonedDateTime) o).toInstant().toEpochMilli();
|
||||
} else if (o instanceof JodaCompatibleZonedDateTime) {
|
||||
return ((JodaCompatibleZonedDateTime) 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
|
||||
|
@ -22,6 +22,7 @@ import org.apache.lucene.search.Scorable;
|
||||
import org.apache.lucene.util.LongValues;
|
||||
import org.elasticsearch.common.lucene.ScorerAware;
|
||||
import org.elasticsearch.index.fielddata.AbstractSortingNumericDocValues;
|
||||
import org.elasticsearch.script.JodaCompatibleZonedDateTime;
|
||||
import org.elasticsearch.script.SearchScript;
|
||||
import org.elasticsearch.search.aggregations.AggregationExecutionException;
|
||||
import org.joda.time.ReadableInstant;
|
||||
@ -94,6 +95,8 @@ public class ScriptLongValues extends AbstractSortingNumericDocValues implements
|
||||
return ((ReadableInstant) o).getMillis();
|
||||
} else if (o instanceof ZonedDateTime) {
|
||||
return ((ZonedDateTime) o).toInstant().toEpochMilli();
|
||||
} else if (o instanceof JodaCompatibleZonedDateTime) {
|
||||
return ((JodaCompatibleZonedDateTime) 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
|
||||
|
@ -115,7 +115,7 @@ public final class DocValueFieldsFetchSubPhase implements FetchSubPhase {
|
||||
subReaderContext = context.searcher().getIndexReader().leaves().get(readerIndex);
|
||||
data = indexFieldData.load(subReaderContext);
|
||||
if (format == null) {
|
||||
scriptValues = data.getScriptValues();
|
||||
scriptValues = data.getLegacyFieldValues();
|
||||
} else if (indexFieldData instanceof IndexNumericFieldData) {
|
||||
if (((IndexNumericFieldData) indexFieldData).getNumericType().isFloatingPoint()) {
|
||||
doubleValues = ((AtomicNumericFieldData) data).getDoubleValues();
|
||||
|
@ -1,137 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.fielddata;
|
||||
|
||||
import org.elasticsearch.index.fielddata.ScriptDocValues.Dates;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
|
||||
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 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<Long, Object> 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)][];
|
||||
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 Object[values[d].length];
|
||||
for (int i = 0; i < values[d].length; i++) {
|
||||
values[d][i] = randomNonNegativeLong();
|
||||
expectedDates[d][i] = datetimeCtor.apply(values[d][i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Set<String> 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)
|
||||
}
|
||||
);
|
||||
|
||||
boolean valuesExist = false;
|
||||
for (int round = 0; round < 10; round++) {
|
||||
int d = between(0, values.length - 1);
|
||||
dates.setNextDocId(d);
|
||||
if (expectedDates[d].length > 0) {
|
||||
Object dateValue = AccessController.doPrivileged((PrivilegedAction<Object>) dates::getValue, noPermissionsAcc);
|
||||
assertEquals(expectedDates[d][0] , dateValue);
|
||||
valuesExist = true;
|
||||
} else {
|
||||
Exception e = expectThrows(IllegalStateException.class, () -> dates.getValue());
|
||||
assertEquals("A document doesn't have a value for a field! " +
|
||||
"Use doc[<field>].size()==0 to check if a document is missing a field!", e.getMessage());
|
||||
}
|
||||
|
||||
assertEquals(values[d].length, dates.size());
|
||||
for (int i = 0; i < values[d].length; i++) {
|
||||
final int ndx = i;
|
||||
Object dateValue = AccessController.doPrivileged((PrivilegedAction<Object>) () -> dates.get(ndx), noPermissionsAcc);
|
||||
assertEquals(expectedDates[d][i], dateValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (valuesExist) {
|
||||
assertThat(warnings, containsInAnyOrder(expectedWarnings));
|
||||
}
|
||||
}
|
||||
|
||||
private Dates wrap(long[][] values, Consumer<String> deprecationHandler, boolean useJavaTime) {
|
||||
return new Dates(new AbstractSortedNumericDocValues() {
|
||||
long[] current;
|
||||
int i;
|
||||
|
||||
@Override
|
||||
public boolean advanceExact(int doc) {
|
||||
current = values[doc];
|
||||
i = 0;
|
||||
return current.length > 0;
|
||||
}
|
||||
@Override
|
||||
public int docValueCount() {
|
||||
return current.length;
|
||||
}
|
||||
@Override
|
||||
public long nextValue() {
|
||||
return current[i++];
|
||||
}
|
||||
}, deprecationHandler, useJavaTime);
|
||||
}
|
||||
}
|
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.script;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.Appender;
|
||||
import org.apache.logging.log4j.core.LogEvent;
|
||||
import org.apache.logging.log4j.core.appender.AbstractAppender;
|
||||
import org.elasticsearch.common.logging.Loggers;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.joda.time.DateTime;
|
||||
import org.joda.time.DateTimeZone;
|
||||
import org.junit.Before;
|
||||
|
||||
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.DayOfWeek;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Locale;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class JodaCompatibleZonedDateTimeTests extends ESTestCase {
|
||||
private static final Logger DEPRECATION_LOGGER =
|
||||
LogManager.getLogger("org.elasticsearch.deprecation.script.JodaCompatibleZonedDateTime");
|
||||
|
||||
// each call to get or getValue will be run with limited permissions, just as they are in scripts
|
||||
private static PermissionCollection NO_PERMISSIONS = new Permissions();
|
||||
private static AccessControlContext NO_PERMISSIONS_ACC = new AccessControlContext(
|
||||
new ProtectionDomain[] {
|
||||
new ProtectionDomain(null, NO_PERMISSIONS)
|
||||
}
|
||||
);
|
||||
|
||||
private JodaCompatibleZonedDateTime javaTime;
|
||||
private DateTime jodaTime;
|
||||
|
||||
@Before
|
||||
public void setupTime() {
|
||||
long millis = randomIntBetween(0, Integer.MAX_VALUE);
|
||||
javaTime = new JodaCompatibleZonedDateTime(Instant.ofEpochMilli(millis), ZoneOffset.ofHours(-7));
|
||||
jodaTime = new DateTime(millis, DateTimeZone.forOffsetHours(-7));
|
||||
}
|
||||
|
||||
void assertDeprecation(Runnable assertions, String message) {
|
||||
Appender appender = new AbstractAppender("test", null, null) {
|
||||
@Override
|
||||
public void append(LogEvent event) {
|
||||
/* Create a temporary directory to prove we are running with the
|
||||
* server's permissions. */
|
||||
createTempDir();
|
||||
}
|
||||
};
|
||||
appender.start();
|
||||
Loggers.addAppender(DEPRECATION_LOGGER, appender);
|
||||
try {
|
||||
// the assertions are run with the same reduced privileges scripts run with
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
assertions.run();
|
||||
return null;
|
||||
}, NO_PERMISSIONS_ACC);
|
||||
} finally {
|
||||
appender.stop();
|
||||
Loggers.removeAppender(DEPRECATION_LOGGER, appender);
|
||||
}
|
||||
|
||||
assertWarnings(message);
|
||||
}
|
||||
|
||||
void assertMethodDeprecation(Runnable assertions, String oldMethod, String newMethod) {
|
||||
assertDeprecation(assertions, "Use of the joda time method [" + oldMethod + "] is deprecated. Use [" + newMethod + "] instead.");
|
||||
}
|
||||
|
||||
public void testDayOfMonth() {
|
||||
assertThat(javaTime.getDayOfMonth(), equalTo(jodaTime.getDayOfMonth()));
|
||||
}
|
||||
|
||||
public void testDayOfYear() {
|
||||
assertThat(javaTime.getDayOfYear(), equalTo(jodaTime.getDayOfYear()));
|
||||
}
|
||||
|
||||
public void testHour() {
|
||||
assertThat(javaTime.getHour(), equalTo(jodaTime.getHourOfDay()));
|
||||
}
|
||||
|
||||
public void testLocalDate() {
|
||||
assertThat(javaTime.toLocalDate(), equalTo(LocalDate.of(jodaTime.getYear(), jodaTime.getMonthOfYear(), jodaTime.getDayOfMonth())));
|
||||
}
|
||||
|
||||
public void testLocalDateTime() {
|
||||
LocalDateTime dt = LocalDateTime.of(jodaTime.getYear(), jodaTime.getMonthOfYear(), jodaTime.getDayOfMonth(),
|
||||
jodaTime.getHourOfDay(), jodaTime.getMinuteOfHour(), jodaTime.getSecondOfMinute(),
|
||||
jodaTime.getMillisOfSecond() * 1000000);
|
||||
assertThat(javaTime.toLocalDateTime(), equalTo(dt));
|
||||
}
|
||||
|
||||
public void testMinute() {
|
||||
assertThat(javaTime.getMinute(), equalTo(jodaTime.getMinuteOfHour()));
|
||||
}
|
||||
|
||||
public void testMonth() {
|
||||
assertThat(javaTime.getMonth(), equalTo(Month.of(jodaTime.getMonthOfYear())));
|
||||
}
|
||||
|
||||
public void testMonthValue() {
|
||||
assertThat(javaTime.getMonthValue(), equalTo(jodaTime.getMonthOfYear()));
|
||||
}
|
||||
|
||||
public void testNano() {
|
||||
assertThat(javaTime.getNano(), equalTo(jodaTime.getMillisOfSecond() * 1000000));
|
||||
}
|
||||
|
||||
public void testSecond() {
|
||||
assertThat(javaTime.getSecond(), equalTo(jodaTime.getSecondOfMinute()));
|
||||
}
|
||||
|
||||
public void testYear() {
|
||||
assertThat(javaTime.getYear(), equalTo(jodaTime.getYear()));
|
||||
}
|
||||
|
||||
public void testMillis() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getMillis(), equalTo(jodaTime.getMillis())),
|
||||
"getMillis()", "toInstant().toEpochMilli()");
|
||||
}
|
||||
|
||||
public void testCenturyOfEra() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getCenturyOfEra(), equalTo(jodaTime.getCenturyOfEra())),
|
||||
"getCenturyOfEra()", "get(ChronoField.YEAR_OF_ERA) / 100");
|
||||
}
|
||||
|
||||
public void testEra() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getEra(), equalTo(jodaTime.getEra())),
|
||||
"getEra()", "get(ChronoField.ERA)");
|
||||
}
|
||||
|
||||
public void testHourOfDay() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getHourOfDay(), equalTo(jodaTime.getHourOfDay())),
|
||||
"getHourOfDay()", "getHour()");
|
||||
}
|
||||
|
||||
public void testMillisOfDay() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getMillisOfDay(), equalTo(jodaTime.getMillisOfDay())),
|
||||
"getMillisOfDay()", "get(ChronoField.MILLI_OF_DAY)");
|
||||
}
|
||||
|
||||
public void testMillisOfSecond() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getMillisOfSecond(), equalTo(jodaTime.getMillisOfSecond())),
|
||||
"getMillisOfSecond()", "get(ChronoField.MILLI_OF_SECOND)");
|
||||
}
|
||||
|
||||
public void testMinuteOfDay() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getMinuteOfDay(), equalTo(jodaTime.getMinuteOfDay())),
|
||||
"getMinuteOfDay()", "get(ChronoField.MINUTE_OF_DAY)");
|
||||
}
|
||||
|
||||
public void testMinuteOfHour() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getMinuteOfHour(), equalTo(jodaTime.getMinuteOfHour())),
|
||||
"getMinuteOfHour()", "getMinute()");
|
||||
}
|
||||
|
||||
public void testMonthOfYear() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getMonthOfYear(), equalTo(jodaTime.getMonthOfYear())),
|
||||
"getMonthOfYear()", "getMonthValue()");
|
||||
}
|
||||
|
||||
public void testSecondOfDay() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getSecondOfDay(), equalTo(jodaTime.getSecondOfDay())),
|
||||
"getSecondOfDay()", "get(ChronoField.SECOND_OF_DAY)");
|
||||
}
|
||||
|
||||
public void testSecondOfMinute() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getSecondOfMinute(), equalTo(jodaTime.getSecondOfMinute())),
|
||||
"getSecondOfMinute()", "getSecond()");
|
||||
}
|
||||
|
||||
public void testWeekOfWeekyear() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getWeekOfWeekyear(), equalTo(jodaTime.getWeekOfWeekyear())),
|
||||
"getWeekOfWeekyear()", "get(WeekFields.ISO.weekOfWeekBasedYear())");
|
||||
}
|
||||
|
||||
public void testWeekyear() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getWeekyear(), equalTo(jodaTime.getWeekyear())),
|
||||
"getWeekyear()", "get(WeekFields.ISO.weekBasedYear())");
|
||||
}
|
||||
|
||||
public void testYearOfCentury() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getYearOfCentury(), equalTo(jodaTime.getYearOfCentury())),
|
||||
"getYearOfCentury()", "get(ChronoField.YEAR_OF_ERA) % 100");
|
||||
}
|
||||
|
||||
public void testYearOfEra() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.getYearOfEra(), equalTo(jodaTime.getYearOfEra())),
|
||||
"getYearOfEra()", "get(ChronoField.YEAR_OF_ERA)");
|
||||
}
|
||||
|
||||
public void testToString1() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.toString("YYYY/MM/dd HH:mm:ss.SSS"),
|
||||
equalTo(jodaTime.toString("YYYY/MM/dd HH:mm:ss.SSS"))), "toString(String)", "a DateTimeFormatter");
|
||||
}
|
||||
|
||||
public void testToString2() {
|
||||
assertMethodDeprecation(() -> assertThat(javaTime.toString("EEE", Locale.GERMANY),
|
||||
equalTo(jodaTime.toString("EEE", Locale.GERMANY))), "toString(String,Locale)", "a DateTimeFormatter");
|
||||
}
|
||||
|
||||
public void testDayOfWeek() {
|
||||
assertDeprecation(() -> assertThat(javaTime.getDayOfWeek(), equalTo(jodaTime.getDayOfWeek())),
|
||||
"The return type of [getDayOfWeek()] will change to an enum in 7.0. Use getDayOfWeekEnum().getValue().");
|
||||
}
|
||||
|
||||
public void testDayOfWeekEnum() {
|
||||
assertThat(javaTime.getDayOfWeekEnum(), equalTo(DayOfWeek.of(jodaTime.getDayOfWeek())));
|
||||
}
|
||||
}
|
@ -47,10 +47,13 @@ import org.elasticsearch.search.lookup.FieldLookup;
|
||||
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.format.DateTimeFormat;
|
||||
|
||||
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;
|
||||
@ -59,7 +62,6 @@ import java.util.Collections;
|
||||
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;
|
||||
@ -111,7 +113,7 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||
scripts.put("doc['date'].date.millis", vars -> {
|
||||
Map<?, ?> doc = (Map) vars.get("doc");
|
||||
ScriptDocValues.Dates dates = (ScriptDocValues.Dates) doc.get("date");
|
||||
return ((ZonedDateTime) dates.getValue()).toInstant().toEpochMilli();
|
||||
return dates.getValue().toInstant().toEpochMilli();
|
||||
});
|
||||
|
||||
scripts.put("_fields['num1'].value", vars -> fieldsScript(vars, "num1"));
|
||||
@ -801,8 +803,8 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||
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));
|
||||
ZonedDateTime dateField = searchResponse.getHits().getAt(0).getFields().get("date_field").getValue();
|
||||
assertThat(dateField.toInstant().toEpochMilli(), equalTo(date.toInstant().toEpochMilli()));
|
||||
DateTime dateField = searchResponse.getHits().getAt(0).getFields().get("date_field").getValue();
|
||||
assertThat(dateField.getMillis(), 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"));
|
||||
@ -828,7 +830,7 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||
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));
|
||||
dateField = searchResponse.getHits().getAt(0).getFields().get("date_field").getValue();
|
||||
assertThat(dateField.toInstant().toEpochMilli(), equalTo(date.toInstant().toEpochMilli()));
|
||||
assertThat(dateField.getMillis(), 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 +970,10 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||
assertAcked(prepareCreate("test").addMapping("type", mapping));
|
||||
ensureGreen("test");
|
||||
|
||||
ZonedDateTime date = ZonedDateTime.of(1990, 12, 29, 0, 0, 0, 0, ZoneOffset.UTC);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ROOT);
|
||||
DateTime date = new DateTime(1990, 12, 29, 0, 0, DateTimeZone.UTC);
|
||||
org.joda.time.format.DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
|
||||
|
||||
index("test", "type", "1", "text_field", "foo", "date_field", formatter.format(date));
|
||||
index("test", "type", "1", "text_field", "foo", "date_field", formatter.print(date));
|
||||
refresh("test");
|
||||
|
||||
SearchRequestBuilder builder = client().prepareSearch().setQuery(matchAllQuery())
|
||||
@ -999,8 +1001,8 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||
DocumentField dateField = fields.get("date_field");
|
||||
assertThat(dateField.getName(), equalTo("date_field"));
|
||||
|
||||
ZonedDateTime fetchedDate = dateField.getValue();
|
||||
assertThat(fetchedDate, equalTo(date));
|
||||
ReadableDateTime fetchedDate = dateField.getValue();
|
||||
assertThat(fetchedDate.getMillis(), equalTo(date.toInstant().getMillis()));
|
||||
}
|
||||
|
||||
public void testWildcardDocValueFieldsWithFieldAlias() throws Exception {
|
||||
@ -1033,10 +1035,10 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||
assertAcked(prepareCreate("test").addMapping("type", mapping));
|
||||
ensureGreen("test");
|
||||
|
||||
ZonedDateTime date = ZonedDateTime.of(1990, 12, 29, 0, 0, 0, 0, ZoneOffset.UTC);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd", Locale.ROOT);
|
||||
DateTime date = new DateTime(1990, 12, 29, 0, 0, DateTimeZone.UTC);
|
||||
org.joda.time.format.DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
|
||||
|
||||
index("test", "type", "1", "text_field", "foo", "date_field", formatter.format(date));
|
||||
index("test", "type", "1", "text_field", "foo", "date_field", formatter.print(date));
|
||||
refresh("test");
|
||||
|
||||
SearchRequestBuilder builder = client().prepareSearch().setQuery(matchAllQuery())
|
||||
@ -1063,8 +1065,8 @@ public class SearchFieldsIT extends ESIntegTestCase {
|
||||
DocumentField dateField = fields.get("date_field");
|
||||
assertThat(dateField.getName(), equalTo("date_field"));
|
||||
|
||||
ZonedDateTime fetchedDate = dateField.getValue();
|
||||
assertThat(fetchedDate, equalTo(date));
|
||||
ReadableDateTime fetchedDate = dateField.getValue();
|
||||
assertThat(fetchedDate.getMillis(), equalTo(date.toInstant().getMillis()));
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user