2525import org .elasticsearch .common .geo .GeoHashUtils ;
2626import org .elasticsearch .common .geo .GeoPoint ;
2727import org .elasticsearch .common .geo .GeoUtils ;
28+ import org .joda .time .DateTime ;
2829import org .joda .time .DateTimeZone ;
2930import org .joda .time .MutableDateTime ;
3031import org .joda .time .ReadableDateTime ;
3132
3233import java .util .AbstractList ;
33- import java .util .Collections ;
34+ import java .util .Comparator ;
3435import java .util .List ;
36+ import java .util .function .UnaryOperator ;
3537
3638
3739/**
3840 * Script level doc values, the assumption is that any implementation will implement a <code>getValue</code>
3941 * and a <code>getValues</code> that return the relevant type that then can be used in scripts.
4042 */
41- public interface ScriptDocValues <T > extends List <T > {
43+ public abstract class ScriptDocValues <T > extends AbstractList <T > {
4244
4345 /**
4446 * Set the current doc ID.
4547 */
46- void setNextDocId (int docId );
48+ public abstract void setNextDocId (int docId );
4749
4850 /**
4951 * Return a copy of the list of the values for the current document.
5052 */
51- List <T > getValues ();
53+ public final List <T > getValues () {
54+ return this ;
55+ }
56+
57+ // Throw meaningful exceptions if someone tries to modify the ScriptDocValues.
58+ @ Override
59+ public final void add (int index , T element ) {
60+ throw new UnsupportedOperationException ("doc values are unmodifiable" );
61+ }
62+
63+ @ Override
64+ public final boolean remove (Object o ) {
65+ throw new UnsupportedOperationException ("doc values are unmodifiable" );
66+ }
67+
68+ @ Override
69+ public final void replaceAll (UnaryOperator <T > operator ) {
70+ throw new UnsupportedOperationException ("doc values are unmodifiable" );
71+ }
72+
73+ @ Override
74+ public final T set (int index , T element ) {
75+ throw new UnsupportedOperationException ("doc values are unmodifiable" );
76+ }
77+
78+ @ Override
79+ public final void sort (Comparator <? super T > c ) {
80+ throw new UnsupportedOperationException ("doc values are unmodifiable" );
81+ }
5282
53- public static final class Strings extends AbstractList < String > implements ScriptDocValues <String > {
83+ public static final class Strings extends ScriptDocValues <String > {
5484
5585 private final SortedBinaryDocValues values ;
5686
@@ -84,11 +114,6 @@ public String getValue() {
84114 }
85115 }
86116
87- @ Override
88- public List <String > getValues () {
89- return Collections .unmodifiableList (this );
90- }
91-
92117 @ Override
93118 public String get (int index ) {
94119 return values .valueAt (index ).utf8ToString ();
@@ -101,10 +126,10 @@ public int size() {
101126
102127 }
103128
104- public static class Longs extends AbstractList < Long > implements ScriptDocValues <Long > {
129+ public static final class Longs extends ScriptDocValues <Long > {
105130
106131 private final SortedNumericDocValues values ;
107- private final MutableDateTime date = new MutableDateTime ( 0 , DateTimeZone . UTC ) ;
132+ private Dates dates ;
108133
109134 public Longs (SortedNumericDocValues values ) {
110135 this .values = values ;
@@ -113,6 +138,9 @@ public Longs(SortedNumericDocValues values) {
113138 @ Override
114139 public void setNextDocId (int docId ) {
115140 values .setDocument (docId );
141+ if (dates != null ) {
142+ dates .refreshArray ();
143+ }
116144 }
117145
118146 public SortedNumericDocValues getInternalValues () {
@@ -127,14 +155,20 @@ public long getValue() {
127155 return values .valueAt (0 );
128156 }
129157
130- @ Override
131- public List <Long > getValues () {
132- return Collections .unmodifiableList (this );
158+ public ReadableDateTime getDate () {
159+ if (dates == null ) {
160+ dates = new Dates (values );
161+ dates .refreshArray ();
162+ }
163+ return dates .getValue ();
133164 }
134165
135- public ReadableDateTime getDate () {
136- date .setMillis (getValue ());
137- return date ;
166+ public List <ReadableDateTime > getDates () {
167+ if (dates == null ) {
168+ dates = new Dates (values );
169+ dates .refreshArray ();
170+ }
171+ return dates ;
138172 }
139173
140174 @ Override
@@ -146,10 +180,87 @@ public Long get(int index) {
146180 public int size () {
147181 return values .count ();
148182 }
183+ }
184+
185+ public static final class Dates extends ScriptDocValues <ReadableDateTime > {
186+ private static final ReadableDateTime EPOCH = new DateTime (0 , DateTimeZone .UTC );
187+
188+ private final SortedNumericDocValues values ;
189+ /**
190+ * Values wrapped in {@link MutableDateTime}. Null by default an allocated on first usage so we allocate a reasonably size. We keep
191+ * this array so we don't have allocate new {@link MutableDateTime}s on every usage. Instead we reuse them for every document.
192+ */
193+ private MutableDateTime [] dates ;
194+
195+ public Dates (SortedNumericDocValues values ) {
196+ this .values = values ;
197+ }
198+
199+ /**
200+ * Fetch the first field value or 0 millis after epoch if there are no values.
201+ */
202+ public ReadableDateTime getValue () {
203+ if (values .count () == 0 ) {
204+ return EPOCH ;
205+ }
206+ return get (0 );
207+ }
208+
209+ @ Override
210+ public ReadableDateTime get (int index ) {
211+ if (index >= values .count ()) {
212+ throw new IndexOutOfBoundsException (
213+ "attempted to fetch the [" + index + "] date when there are only [" + values .count () + "] dates." );
214+ }
215+ return dates [index ];
216+ }
217+
218+ @ Override
219+ public int size () {
220+ return values .count ();
221+ }
149222
223+ @ Override
224+ public void setNextDocId (int docId ) {
225+ values .setDocument (docId );
226+ refreshArray ();
227+ }
228+
229+ /**
230+ * Refresh the backing array. Package private so it can be called when {@link Longs} loads dates.
231+ */
232+ void refreshArray () {
233+ if (values .count () == 0 ) {
234+ return ;
235+ }
236+ if (dates == null ) {
237+ // Happens for the document. We delay allocating dates so we can allocate it with a reasonable size.
238+ dates = new MutableDateTime [values .count ()];
239+ for (int i = 0 ; i < dates .length ; i ++) {
240+ dates [i ] = new MutableDateTime (values .valueAt (i ), DateTimeZone .UTC );
241+ }
242+ return ;
243+ }
244+ if (values .count () > dates .length ) {
245+ // Happens when we move to a new document and it has more dates than any documents before it.
246+ MutableDateTime [] backup = dates ;
247+ dates = new MutableDateTime [values .count ()];
248+ System .arraycopy (backup , 0 , dates , 0 , backup .length );
249+ for (int i = 0 ; i < backup .length ; i ++) {
250+ dates [i ].setMillis (values .valueAt (i ));
251+ }
252+ for (int i = backup .length ; i < dates .length ; i ++) {
253+ dates [i ] = new MutableDateTime (values .valueAt (i ), DateTimeZone .UTC );
254+ }
255+ return ;
256+ }
257+ for (int i = 0 ; i < values .count (); i ++) {
258+ dates [i ].setMillis (values .valueAt (i ));
259+ }
260+ }
150261 }
151262
152- public static class Doubles extends AbstractList < Double > implements ScriptDocValues <Double > {
263+ public static final class Doubles extends ScriptDocValues <Double > {
153264
154265 private final SortedNumericDoubleValues values ;
155266
@@ -174,11 +285,6 @@ public double getValue() {
174285 return values .valueAt (0 );
175286 }
176287
177- @ Override
178- public List <Double > getValues () {
179- return Collections .unmodifiableList (this );
180- }
181-
182288 @ Override
183289 public Double get (int index ) {
184290 return values .valueAt (index );
@@ -190,7 +296,7 @@ public int size() {
190296 }
191297 }
192298
193- class GeoPoints extends AbstractList < GeoPoint > implements ScriptDocValues <GeoPoint > {
299+ public static final class GeoPoints extends ScriptDocValues <GeoPoint > {
194300
195301 private final MultiGeoPointValues values ;
196302
@@ -237,11 +343,6 @@ public double getLon() {
237343 return getValue ().lon ();
238344 }
239345
240- @ Override
241- public List <GeoPoint > getValues () {
242- return Collections .unmodifiableList (this );
243- }
244-
245346 @ Override
246347 public GeoPoint get (int index ) {
247348 final GeoPoint point = values .valueAt (index );
@@ -291,7 +392,7 @@ public double geohashDistanceWithDefault(String geohash, double defaultValue) {
291392 }
292393 }
293394
294- final class Booleans extends AbstractList < Boolean > implements ScriptDocValues <Boolean > {
395+ public static final class Booleans extends ScriptDocValues <Boolean > {
295396
296397 private final SortedNumericDocValues values ;
297398
@@ -304,11 +405,6 @@ public void setNextDocId(int docId) {
304405 values .setDocument (docId );
305406 }
306407
307- @ Override
308- public List <Boolean > getValues () {
309- return this ;
310- }
311-
312408 public boolean getValue () {
313409 return values .count () != 0 && values .valueAt (0 ) == 1 ;
314410 }
@@ -325,7 +421,7 @@ public int size() {
325421
326422 }
327423
328- public static class BytesRefs extends AbstractList < BytesRef > implements ScriptDocValues <BytesRef > {
424+ public static final class BytesRefs extends ScriptDocValues <BytesRef > {
329425
330426 private final SortedBinaryDocValues values ;
331427
@@ -350,11 +446,6 @@ public BytesRef getValue() {
350446 return values .valueAt (0 );
351447 }
352448
353- @ Override
354- public List <BytesRef > getValues () {
355- return Collections .unmodifiableList (this );
356- }
357-
358449 @ Override
359450 public BytesRef get (int index ) {
360451 return values .valueAt (index );
@@ -365,5 +456,4 @@ public int size() {
365456 return values .count ();
366457 }
367458 }
368-
369459}
0 commit comments