19
19
20
20
package org .elasticsearch .index .fielddata ;
21
21
22
-
23
22
import org .apache .lucene .index .SortedNumericDocValues ;
24
23
import org .apache .lucene .util .ArrayUtil ;
25
24
import org .apache .lucene .util .BytesRef ;
29
28
import org .elasticsearch .common .geo .GeoUtils ;
30
29
import org .elasticsearch .common .logging .DeprecationLogger ;
31
30
import org .elasticsearch .common .logging .ESLoggerFactory ;
32
- import org .joda .time .DateTime ;
33
31
import org .joda .time .DateTimeZone ;
34
32
import org .joda .time .MutableDateTime ;
35
- import org .joda .time .ReadableDateTime ;
36
33
37
34
import java .io .IOException ;
35
+ import java .security .AccessController ;
36
+ import java .security .PrivilegedAction ;
37
+ import java .time .Instant ;
38
+ import java .time .ZoneOffset ;
39
+ import java .time .ZonedDateTime ;
38
40
import java .util .AbstractList ;
39
41
import java .util .Arrays ;
40
42
import java .util .Comparator ;
41
43
import java .util .List ;
44
+ import java .util .function .Consumer ;
42
45
import java .util .function .UnaryOperator ;
43
46
47
+ import static org .elasticsearch .common .Booleans .parseBoolean ;
44
48
45
49
/**
46
50
* Script level doc values, the assumption is that any implementation will
52
56
* values form multiple documents.
53
57
*/
54
58
public abstract class ScriptDocValues <T > extends AbstractList <T > {
59
+
55
60
/**
56
61
* Set the current doc ID.
57
62
*/
@@ -142,31 +147,55 @@ public int size() {
142
147
}
143
148
}
144
149
145
- public static final class Dates extends ScriptDocValues <ReadableDateTime > {
146
- protected static final DeprecationLogger deprecationLogger = new DeprecationLogger (ESLoggerFactory .getLogger (Dates .class ));
150
+ public static final class Dates extends ScriptDocValues <Object > {
147
151
148
- private static final ReadableDateTime EPOCH = new DateTime (0 , DateTimeZone .UTC );
152
+ /** Whether scripts should expose dates as java time objects instead of joda time. */
153
+ private static final boolean USE_JAVA_TIME = parseBoolean (System .getProperty ("es.scripting.use_java_time" ), false );
154
+
155
+ private static final DeprecationLogger deprecationLogger = new DeprecationLogger (ESLoggerFactory .getLogger (Dates .class ));
149
156
150
157
private final SortedNumericDocValues in ;
158
+
159
+ /**
160
+ * Method call to add deprecation message. Normally this is
161
+ * {@link #deprecationLogger} but tests override.
162
+ */
163
+ private final Consumer <String > deprecationCallback ;
164
+
151
165
/**
152
- * Values wrapped in {@link MutableDateTime}. Null by default an allocated on first usage so we allocate a reasonably size. We keep
153
- * this array so we don't have allocate new {@link MutableDateTime}s on every usage. Instead we reuse them for every document.
166
+ * Whether java time or joda time should be used. This is normally {@link #USE_JAVA_TIME} but tests override it.
154
167
*/
155
- private MutableDateTime [] dates ;
168
+ private final boolean useJavaTime ;
169
+
170
+ /**
171
+ * Values wrapped in a date time object. The concrete type depends on the system property {@code es.scripting.use_java_time}.
172
+ * When that system property is {@code false}, the date time objects are of type {@link MutableDateTime}. When the system
173
+ * property is {@code true}, the date time objects are of type {@link java.time.ZonedDateTime}.
174
+ */
175
+ private Object [] dates ;
156
176
private int count ;
157
177
158
178
/**
159
179
* Standard constructor.
160
180
*/
161
181
public Dates (SortedNumericDocValues in ) {
182
+ this (in , message -> deprecationLogger .deprecatedAndMaybeLog ("scripting_joda_time_deprecation" , message ), USE_JAVA_TIME );
183
+ }
184
+
185
+ /**
186
+ * Constructor for testing with a deprecation callback.
187
+ */
188
+ Dates (SortedNumericDocValues in , Consumer <String > deprecationCallback , boolean useJavaTime ) {
162
189
this .in = in ;
190
+ this .deprecationCallback = deprecationCallback ;
191
+ this .useJavaTime = useJavaTime ;
163
192
}
164
193
165
194
/**
166
195
* Fetch the first field value or 0 millis after epoch if there are no
167
196
* in.
168
197
*/
169
- public ReadableDateTime getValue () {
198
+ public Object getValue () {
170
199
if (count == 0 ) {
171
200
throw new IllegalStateException ("A document doesn't have a value for a field! " +
172
201
"Use doc[<field>].size()==0 to check if a document is missing a field!" );
@@ -175,7 +204,7 @@ public ReadableDateTime getValue() {
175
204
}
176
205
177
206
@ Override
178
- public ReadableDateTime get (int index ) {
207
+ public Object get (int index ) {
179
208
if (index >= count ) {
180
209
throw new IndexOutOfBoundsException (
181
210
"attempted to fetch the [" + index + "] date when there are only ["
@@ -206,31 +235,42 @@ void refreshArray() throws IOException {
206
235
if (count == 0 ) {
207
236
return ;
208
237
}
209
- if (dates == null ) {
210
- // Happens for the document. We delay allocating dates so we can allocate it with a reasonable size.
211
- dates = new MutableDateTime [count ];
212
- for (int i = 0 ; i < dates .length ; i ++) {
213
- dates [i ] = new MutableDateTime (in .nextValue (), DateTimeZone .UTC );
238
+ if (useJavaTime ) {
239
+ if (dates == null || count > dates .length ) {
240
+ // Happens for the document. We delay allocating dates so we can allocate it with a reasonable size.
241
+ dates = new ZonedDateTime [count ];
214
242
}
215
- return ;
216
- }
217
- if (count > dates .length ) {
218
- // Happens when we move to a new document and it has more dates than any documents before it.
219
- MutableDateTime [] backup = dates ;
220
- dates = new MutableDateTime [count ];
221
- System .arraycopy (backup , 0 , dates , 0 , backup .length );
222
- for (int i = 0 ; i < backup .length ; i ++) {
223
- dates [i ].setMillis (in .nextValue ());
243
+ for (int i = 0 ; i < count ; ++i ) {
244
+ dates [i ] = ZonedDateTime .ofInstant (Instant .ofEpochMilli (in .nextValue ()), ZoneOffset .UTC );
224
245
}
225
- for (int i = backup .length ; i < dates .length ; i ++) {
246
+ } else {
247
+ deprecated ("The joda time api for doc values is deprecated. Use -Des.scripting.use_java_time=true" +
248
+ " to use the java time api for date field doc values" );
249
+ if (dates == null || count > dates .length ) {
250
+ // Happens for the document. We delay allocating dates so we can allocate it with a reasonable size.
251
+ dates = new MutableDateTime [count ];
252
+ }
253
+ for (int i = 0 ; i < count ; i ++) {
226
254
dates [i ] = new MutableDateTime (in .nextValue (), DateTimeZone .UTC );
227
255
}
228
- return ;
229
- }
230
- for (int i = 0 ; i < count ; i ++) {
231
- dates [i ] = new MutableDateTime (in .nextValue (), DateTimeZone .UTC );
232
256
}
233
257
}
258
+
259
+ /**
260
+ * Log a deprecation log, with the server's permissions, not the permissions of the
261
+ * script calling this method. We need to do this to prevent errors when rolling
262
+ * the log file.
263
+ */
264
+ private void deprecated (String message ) {
265
+ // Intentionally not calling SpecialPermission.check because this is supposed to be called by scripts
266
+ AccessController .doPrivileged (new PrivilegedAction <Void >() {
267
+ @ Override
268
+ public Void run () {
269
+ deprecationCallback .accept (message );
270
+ return null ;
271
+ }
272
+ });
273
+ }
234
274
}
235
275
236
276
public static final class Doubles extends ScriptDocValues <Double > {
0 commit comments