Skip to content

Commit 55b0903

Browse files
authored
Add date fields to the scripting fields api (#81272)
This change adds support for date millisecond and date nanoseconds fields to the scripting fields api.
1 parent 4ed6e8a commit 55b0903

File tree

9 files changed

+446
-74
lines changed

9 files changed

+446
-74
lines changed

docs/changelog/81272.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 81272
2+
summary: Add date fields to the scripting fields api
3+
area: Infra/Scripting
4+
type: enhancement
5+
issues: []

modules/lang-painless/src/main/resources/org/elasticsearch/painless/org.elasticsearch.script.fields.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ class org.elasticsearch.script.field.ShortDocValuesField @dynamic_type {
6868
short get(int, int)
6969
}
7070

71+
class org.elasticsearch.script.field.DateMillisDocValuesField @dynamic_type {
72+
ZonedDateTime get(ZonedDateTime)
73+
ZonedDateTime get(int, ZonedDateTime)
74+
}
75+
76+
class org.elasticsearch.script.field.DateNanosDocValuesField @dynamic_type {
77+
ZonedDateTime get(ZonedDateTime)
78+
ZonedDateTime get(int, ZonedDateTime)
79+
}
80+
7181
class org.elasticsearch.script.field.KeywordDocValuesField @dynamic_type {
7282
String get(String)
7383
String get(int, String)

modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/50_script_doc_values.yml

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ setup:
1111
type: boolean
1212
date:
1313
type: date
14+
nanos:
15+
type: date_nanos
1416
geo_point:
1517
type: geo_point
1618
ip:
@@ -49,6 +51,7 @@ setup:
4951
rank: 1
5052
boolean: true
5153
date: 2017-01-01T12:11:12
54+
nanos: 2015-01-01T12:10:30.123456789Z
5255
geo_point: 41.12,-71.34
5356
ip: 192.168.0.1
5457
keyword: not split at all
@@ -76,6 +79,8 @@ setup:
7679
body:
7780
rank: 3
7881
boolean: [true, false, true]
82+
date: [2017-01-01T12:11:12, 2018-01-01T12:11:12]
83+
nanos: [2015-01-01T12:10:30.123456789Z, 2015-01-01T12:10:30.987654321Z]
7984
keyword: ["one string", "another string"]
8085
long: [1152921504606846976, 576460752303423488]
8186
integer: [5, 17, 29]
@@ -228,6 +233,193 @@ setup:
228233
source: "doc.date.value"
229234
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' }
230235

236+
- do:
237+
search:
238+
rest_total_hits_as_int: true
239+
body:
240+
query: { term: { _id: 1 } }
241+
script_fields:
242+
field:
243+
script:
244+
source: "field('date').get(null)"
245+
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' }
246+
247+
- do:
248+
search:
249+
rest_total_hits_as_int: true
250+
body:
251+
query: { term: { _id: 1 } }
252+
script_fields:
253+
field:
254+
script:
255+
source: "/* avoid yaml stash */ $('date', null)"
256+
- match: { hits.hits.0.fields.field.0: '2017-01-01T12:11:12.000Z' }
257+
258+
- do:
259+
search:
260+
rest_total_hits_as_int: true
261+
body:
262+
query: { term: { _id: 2 } }
263+
script_fields:
264+
field:
265+
script:
266+
source: "field('date').get(ZonedDateTime.parse('2018-01-01T12:11:12.000Z'))"
267+
- match: { hits.hits.0.fields.field.0: '2018-01-01T12:11:12.000Z' }
268+
269+
- do:
270+
search:
271+
rest_total_hits_as_int: true
272+
body:
273+
query: { term: { _id: 2 } }
274+
script_fields:
275+
field:
276+
script:
277+
source: "/* avoid yaml stash */ $('date', ZonedDateTime.parse('2018-01-01T12:11:12.000Z'))"
278+
- match: { hits.hits.0.fields.field.0: '2018-01-01T12:11:12.000Z' }
279+
280+
- do:
281+
search:
282+
rest_total_hits_as_int: true
283+
body:
284+
query: { term: { _id: 1 } }
285+
script_fields:
286+
field:
287+
script:
288+
source: "doc['nanos'].value"
289+
- match: { hits.hits.0.fields.field.0: '2015-01-01T12:10:30.123456789Z' }
290+
291+
- do:
292+
search:
293+
rest_total_hits_as_int: true
294+
body:
295+
query: { term: { _id: 1 } }
296+
script_fields:
297+
field:
298+
script:
299+
source: "field('nanos').get(null)"
300+
- match: { hits.hits.0.fields.field.0: '2015-01-01T12:10:30.123456789Z' }
301+
302+
- do:
303+
search:
304+
rest_total_hits_as_int: true
305+
body:
306+
query: { term: { _id: 1 } }
307+
script_fields:
308+
field:
309+
script:
310+
source: "/* avoid yaml stash */ $('nanos', null)"
311+
- match: { hits.hits.0.fields.field.0: '2015-01-01T12:10:30.123456789Z' }
312+
313+
- do:
314+
search:
315+
rest_total_hits_as_int: true
316+
body:
317+
query: { term: { _id: 2 } }
318+
script_fields:
319+
field:
320+
script:
321+
source: "field('nanos').get(ZonedDateTime.parse('2016-01-01T12:10:30.123Z'))"
322+
- match: { hits.hits.0.fields.field.0: '2016-01-01T12:10:30.123Z' }
323+
324+
- do:
325+
search:
326+
rest_total_hits_as_int: true
327+
body:
328+
query: { term: { _id: 2 } }
329+
script_fields:
330+
field:
331+
script:
332+
source: "/* avoid yaml stash */ $('nanos', ZonedDateTime.parse('2016-01-01T12:10:30.123Z'))"
333+
- match: { hits.hits.0.fields.field.0: '2016-01-01T12:10:30.123Z' }
334+
335+
- do:
336+
search:
337+
rest_total_hits_as_int: true
338+
body:
339+
query: { term: { _id: 1 } }
340+
script_fields:
341+
field:
342+
script:
343+
source: "doc['nanos'].value.getNano()"
344+
- match: { hits.hits.0.fields.field.0: 123456789 }
345+
346+
- do:
347+
search:
348+
rest_total_hits_as_int: true
349+
body:
350+
query: { term: { _id: 1 } }
351+
script_fields:
352+
field:
353+
script:
354+
source: "field('nanos').get(null).getNano()"
355+
- match: { hits.hits.0.fields.field.0: 123456789 }
356+
357+
- do:
358+
search:
359+
rest_total_hits_as_int: true
360+
body:
361+
query: { term: { _id: 1 } }
362+
script_fields:
363+
field:
364+
script:
365+
source: "/* avoid yaml stash */ $('nanos', null).getNano()"
366+
- match: { hits.hits.0.fields.field.0: 123456789 }
367+
368+
- do:
369+
search:
370+
rest_total_hits_as_int: true
371+
body:
372+
query: { term: { _id: 2 } }
373+
script_fields:
374+
field:
375+
script:
376+
source: "field('nanos').get(ZonedDateTime.parse('2016-01-01T12:10:30.123Z')).getNano()"
377+
- match: { hits.hits.0.fields.field.0: 123000000 }
378+
379+
- do:
380+
search:
381+
rest_total_hits_as_int: true
382+
body:
383+
query: { term: { _id: 3 } }
384+
script_fields:
385+
field:
386+
script:
387+
source: "field('date').get(1, null)"
388+
- match: { hits.hits.0.fields.field.0: "2018-01-01T12:11:12.000Z" }
389+
390+
- do:
391+
search:
392+
rest_total_hits_as_int: true
393+
body:
394+
query: { term: { _id: 3 } }
395+
script_fields:
396+
field:
397+
script:
398+
source: "field('nanos').get(1, null)"
399+
- match: { hits.hits.0.fields.field.0: "2015-01-01T12:10:30.987654321Z" }
400+
401+
- do:
402+
search:
403+
rest_total_hits_as_int: true
404+
body:
405+
query: { term: { _id: 3 } }
406+
script_fields:
407+
field:
408+
script:
409+
source: "List times = new ArrayList(); for (ZonedDateTime zdt : field('date')) times.add(zdt); times"
410+
- match: { hits.hits.0.fields.field: ["2017-01-01T12:11:12.000Z", "2018-01-01T12:11:12.000Z"] }
411+
412+
- do:
413+
search:
414+
rest_total_hits_as_int: true
415+
body:
416+
query: { term: { _id: 3 } }
417+
script_fields:
418+
field:
419+
script:
420+
source: "List times = new ArrayList(); for (ZonedDateTime zdt : field('nanos')) times.add(zdt); times"
421+
- match: { hits.hits.0.fields.field: ["2015-01-01T12:10:30.123456789Z", "2015-01-01T12:10:30.987654321Z"] }
422+
231423
---
232424
"geo_point":
233425
- do:

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

Lines changed: 0 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@
1515
import org.elasticsearch.common.geo.GeoBoundingBox;
1616
import org.elasticsearch.common.geo.GeoPoint;
1717
import org.elasticsearch.common.geo.GeoUtils;
18-
import org.elasticsearch.common.time.DateUtils;
1918
import org.elasticsearch.geometry.utils.Geohash;
2019
import org.elasticsearch.script.field.DocValuesField;
2120

2221
import java.io.IOException;
23-
import java.time.Instant;
24-
import java.time.ZoneOffset;
2522
import java.time.ZonedDateTime;
2623
import java.util.AbstractList;
2724
import java.util.Comparator;
@@ -161,63 +158,6 @@ public int size() {
161158
}
162159
}
163160

164-
public static class DatesSupplier implements Supplier<ZonedDateTime> {
165-
166-
private final SortedNumericDocValues in;
167-
private final boolean isNanos;
168-
169-
/**
170-
* Values wrapped in {@link java.time.ZonedDateTime} objects.
171-
*/
172-
private ZonedDateTime[] dates;
173-
private int count;
174-
175-
public DatesSupplier(SortedNumericDocValues in, boolean isNanos) {
176-
this.in = in;
177-
this.isNanos = isNanos;
178-
}
179-
180-
@Override
181-
public ZonedDateTime getInternal(int index) {
182-
return dates[index];
183-
}
184-
185-
@Override
186-
public int size() {
187-
return count;
188-
}
189-
190-
@Override
191-
public void setNextDocId(int docId) throws IOException {
192-
if (in.advanceExact(docId)) {
193-
count = in.docValueCount();
194-
} else {
195-
count = 0;
196-
}
197-
refreshArray();
198-
}
199-
200-
/**
201-
* Refresh the backing array. Package private so it can be called when {@link Longs} loads dates.
202-
*/
203-
private void refreshArray() throws IOException {
204-
if (count == 0) {
205-
return;
206-
}
207-
if (dates == null || count > dates.length) {
208-
// Happens for the document. We delay allocating dates so we can allocate it with a reasonable size.
209-
dates = new ZonedDateTime[count];
210-
}
211-
for (int i = 0; i < count; ++i) {
212-
if (isNanos) {
213-
dates[i] = ZonedDateTime.ofInstant(DateUtils.toInstant(in.nextValue()), ZoneOffset.UTC);
214-
} else {
215-
dates[i] = ZonedDateTime.ofInstant(Instant.ofEpochMilli(in.nextValue()), ZoneOffset.UTC);
216-
}
217-
}
218-
}
219-
}
220-
221161
public static class Dates extends ScriptDocValues<ZonedDateTime> {
222162

223163
public Dates(Supplier<ZonedDateTime> supplier) {

server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,15 @@
3333
import org.elasticsearch.core.TimeValue;
3434
import org.elasticsearch.index.fielddata.IndexFieldData;
3535
import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
36-
import org.elasticsearch.index.fielddata.ScriptDocValues.Dates;
37-
import org.elasticsearch.index.fielddata.ScriptDocValues.DatesSupplier;
3836
import org.elasticsearch.index.fielddata.plain.SortedNumericIndexFieldData;
3937
import org.elasticsearch.index.query.DateRangeIncludingNowQuery;
4038
import org.elasticsearch.index.query.QueryRewriteContext;
4139
import org.elasticsearch.index.query.SearchExecutionContext;
4240
import org.elasticsearch.script.DateFieldScript;
4341
import org.elasticsearch.script.Script;
4442
import org.elasticsearch.script.ScriptCompiler;
45-
import org.elasticsearch.script.field.DelegateDocValuesField;
43+
import org.elasticsearch.script.field.DateMillisDocValuesField;
44+
import org.elasticsearch.script.field.DateNanosDocValuesField;
4645
import org.elasticsearch.script.field.ToScriptField;
4746
import org.elasticsearch.search.DocValueFormat;
4847
import org.elasticsearch.search.lookup.FieldValues;
@@ -81,7 +80,7 @@ public final class DateFieldMapper extends FieldMapper {
8180
private static final DateMathParser EPOCH_MILLIS_PARSER = DateFormatter.forPattern("epoch_millis").toDateMathParser();
8281

8382
public enum Resolution {
84-
MILLISECONDS(CONTENT_TYPE, NumericType.DATE, (dv, n) -> new DelegateDocValuesField(new Dates(new DatesSupplier(dv, false)), n)) {
83+
MILLISECONDS(CONTENT_TYPE, NumericType.DATE, DateMillisDocValuesField::new) {
8584
@Override
8685
public long convert(Instant instant) {
8786
return instant.toEpochMilli();
@@ -112,11 +111,7 @@ protected Query distanceFeatureQuery(String field, float boost, long origin, Tim
112111
return LongPoint.newDistanceFeatureQuery(field, boost, origin, pivot.getMillis());
113112
}
114113
},
115-
NANOSECONDS(
116-
DATE_NANOS_CONTENT_TYPE,
117-
NumericType.DATE_NANOSECONDS,
118-
(dv, n) -> new DelegateDocValuesField(new Dates(new DatesSupplier(dv, true)), n)
119-
) {
114+
NANOSECONDS(DATE_NANOS_CONTENT_TYPE, NumericType.DATE_NANOSECONDS, DateNanosDocValuesField::new) {
120115
@Override
121116
public long convert(Instant instant) {
122117
return toLong(instant);

0 commit comments

Comments
 (0)