Skip to content

Commit 8c590ea

Browse files
committed
Expose multi-valued dates to scripts and document painless's date functions (#22875)
Implemented by wrapping an array of reused `ModuleDateTime`s that we grow when needed. The `ModuleDateTime`s are reused when we move to the next document. Also improves the error message returned when attempting to modify the `ScriptdocValues`, removes a couple of allocations, and documents that the date functions are available in Painless. Relates to #22162
1 parent cb048f2 commit 8c590ea

File tree

9 files changed

+404
-65
lines changed

9 files changed

+404
-65
lines changed

core/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java

Lines changed: 133 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,32 +25,62 @@
2525
import org.elasticsearch.common.geo.GeoHashUtils;
2626
import org.elasticsearch.common.geo.GeoPoint;
2727
import org.elasticsearch.common.geo.GeoUtils;
28+
import org.joda.time.DateTime;
2829
import org.joda.time.DateTimeZone;
2930
import org.joda.time.MutableDateTime;
3031
import org.joda.time.ReadableDateTime;
3132

3233
import java.util.AbstractList;
33-
import java.util.Collections;
34+
import java.util.Comparator;
3435
import 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
}

core/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,7 @@
4848

4949
import java.io.IOException;
5050
import java.net.InetAddress;
51-
import java.util.AbstractList;
5251
import java.util.Arrays;
53-
import java.util.Collections;
5452
import java.util.Iterator;
5553
import java.util.List;
5654
import java.util.Map;
@@ -234,11 +232,11 @@ public FieldStats.Ip stats(IndexReader reader) throws IOException {
234232
InetAddressPoint.decode(min), InetAddressPoint.decode(max));
235233
}
236234

237-
public static final class IpScriptDocValues extends AbstractList<String> implements ScriptDocValues<String> {
235+
public static final class IpScriptDocValues extends ScriptDocValues<String> {
238236

239237
private final RandomAccessOrds values;
240238

241-
IpScriptDocValues(RandomAccessOrds values) {
239+
public IpScriptDocValues(RandomAccessOrds values) {
242240
this.values = values;
243241
}
244242

@@ -255,11 +253,6 @@ public String getValue() {
255253
}
256254
}
257255

258-
@Override
259-
public List<String> getValues() {
260-
return Collections.unmodifiableList(this);
261-
}
262-
263256
@Override
264257
public String get(int index) {
265258
BytesRef encoded = values.lookupOrd(values.ordAt(0));

0 commit comments

Comments
 (0)