Skip to content

Commit e8663a9

Browse files
committed
Script: ulong via fields API
Exposes unsigned long via the fields API. Unsigned longs default to java signed longs. That means the upper range appears negative. Consumers should use `Long.compareUnsigned(long, long)` `Long.divideUnsigned(long, long)` and `Long.remainderUnsigned(long, long)` to correctly work with values known to be unsigned long. Alternatively, users may treat the unsigned long type as `BigInteger` using the field API, `field('ul').as(Field.BigInteger).getValue(BigInteger.ZERO)`. ``` field('ul').as(Field.BigInteger).getValue(BigInteger.valueOf(1000)) field('ul').getValue(1000L) ``` This change also implements the beginning of the converters for the fields API. The following conversions have been added: ``` ulong <-> BigInteger long <-> BigInteger double -> BigInteger String (parsed as long or double) -> BigInteger double -> long String (parsed as long or double) -> long Date (epoch milliseconds) -> long Nano Date (epoch nanoseconds) -> long boolean (1L for true, 0L for false) -> long ``` Fixes: elastic#64361
1 parent 72ba4cd commit e8663a9

File tree

22 files changed

+883
-96
lines changed

22 files changed

+883
-96
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@
1010

1111
# API
1212
class org.elasticsearch.script.Field {
13+
org.elasticsearch.script.Converter BigInteger
14+
org.elasticsearch.script.Converter Long
1315
String getName()
1416
boolean isEmpty()
1517
List getValues()
1618
def getValue(def)
19+
double getDouble(double)
20+
long getLong(long)
21+
org.elasticsearch.script.Field as(org.elasticsearch.script.Converter)
22+
}
23+
24+
class org.elasticsearch.script.Converter {
1725
}
1826

1927
class org.elasticsearch.script.DocBasedScript {

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

Lines changed: 128 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
import org.elasticsearch.common.geo.GeoUtils;
1818
import org.elasticsearch.common.time.DateUtils;
1919
import org.elasticsearch.geometry.utils.Geohash;
20+
import org.elasticsearch.script.Field;
21+
import org.elasticsearch.script.FieldValues;
22+
import org.elasticsearch.script.InvalidConversion;
2023
import org.elasticsearch.script.JodaCompatibleZonedDateTime;
2124

2225
import java.io.IOException;
@@ -25,6 +28,8 @@
2528
import java.util.AbstractList;
2629
import java.util.Arrays;
2730
import java.util.Comparator;
31+
import java.util.List;
32+
import java.util.concurrent.TimeUnit;
2833
import java.util.function.UnaryOperator;
2934

3035
/**
@@ -35,7 +40,7 @@
3540
* return as a single {@link ScriptDocValues} instance can be reused to return
3641
* values form multiple documents.
3742
*/
38-
public abstract class ScriptDocValues<T> extends AbstractList<T> {
43+
public abstract class ScriptDocValues<T> extends AbstractList<T> implements FieldValues<T> {
3944

4045
/**
4146
* Set the current doc ID.
@@ -68,6 +73,31 @@ public final void sort(Comparator<? super T> c) {
6873
throw new UnsupportedOperationException("doc values are unmodifiable");
6974
}
7075

76+
public abstract Field<T> toField(String fieldName);
77+
78+
public List<T> getValues() {
79+
return this;
80+
}
81+
82+
public T getNonPrimitiveValue() {
83+
return get(0);
84+
}
85+
86+
public long getLongValue() {
87+
throw new InvalidConversion(this.getClass(), long.class);
88+
}
89+
90+
public double getDoubleValue() {
91+
throw new InvalidConversion(this.getClass(), double.class);
92+
}
93+
94+
protected void throwIfEmpty() {
95+
if (size() == 0) {
96+
throw new IllegalStateException("A document doesn't have a value for a field! " +
97+
"Use doc[<field>].size()==0 to check if a document is missing a field!");
98+
}
99+
}
100+
71101
public static final class Longs extends ScriptDocValues<Long> {
72102
private final SortedNumericDocValues in;
73103
private long[] values = new long[0];
@@ -107,17 +137,31 @@ public long getValue() {
107137

108138
@Override
109139
public Long get(int index) {
110-
if (count == 0) {
111-
throw new IllegalStateException("A document doesn't have a value for a field! " +
112-
"Use doc[<field>].size()==0 to check if a document is missing a field!");
113-
}
140+
throwIfEmpty();
114141
return values[index];
115142
}
116143

117144
@Override
118145
public int size() {
119146
return count;
120147
}
148+
149+
@Override
150+
public long getLongValue() {
151+
throwIfEmpty();
152+
return values[0];
153+
}
154+
155+
@Override
156+
public double getDoubleValue() {
157+
throwIfEmpty();
158+
return values[0];
159+
}
160+
161+
@Override
162+
public Field<Long> toField(String fieldName) {
163+
return new Field.LongField(fieldName, this);
164+
}
121165
}
122166

123167
public static final class Dates extends ScriptDocValues<JodaCompatibleZonedDateTime> {
@@ -192,6 +236,29 @@ void refreshArray() throws IOException {
192236
}
193237
}
194238
}
239+
240+
@Override
241+
public long getLongValue() {
242+
throwIfEmpty();
243+
Instant dt = dates[0].toInstant();
244+
if (isNanos) {
245+
return TimeUnit.SECONDS.toNanos(dt.getEpochSecond()) + dt.getNano();
246+
}
247+
return dt.toEpochMilli();
248+
}
249+
250+
@Override
251+
public double getDoubleValue() {
252+
return getLongValue();
253+
}
254+
255+
@Override
256+
public Field<JodaCompatibleZonedDateTime> toField(String fieldName) {
257+
if (isNanos) {
258+
return new Field.DateNanosField(fieldName, this);
259+
}
260+
return new Field.DateMillisField(fieldName, this);
261+
}
195262
}
196263

197264
public static final class Doubles extends ScriptDocValues<Double> {
@@ -246,6 +313,22 @@ public Double get(int index) {
246313
public int size() {
247314
return count;
248315
}
316+
317+
@Override
318+
public long getLongValue() {
319+
return (long) getDoubleValue();
320+
}
321+
322+
@Override
323+
public double getDoubleValue() {
324+
throwIfEmpty();
325+
return values[0];
326+
}
327+
328+
@Override
329+
public Field<Double> toField(String fieldName) {
330+
return new Field.DoubleField(fieldName, this);
331+
}
249332
}
250333

251334
public abstract static class Geometry<T> extends ScriptDocValues<T> {
@@ -436,6 +519,11 @@ public double getMercatorHeight() {
436519
public GeoBoundingBox getBoundingBox() {
437520
return size() == 0 ? null : boundingBox;
438521
}
522+
523+
@Override
524+
public Field<GeoPoint> toField(String fieldName) {
525+
return new Field.GeoPointField(fieldName, this);
526+
}
439527
}
440528

441529
public static final class Booleans extends ScriptDocValues<Boolean> {
@@ -496,6 +584,22 @@ private static boolean[] grow(boolean[] array, int minSize) {
496584
return array;
497585
}
498586

587+
@Override
588+
public long getLongValue() {
589+
throwIfEmpty();
590+
return values[0] ? 1L : 0L;
591+
}
592+
593+
@Override
594+
public double getDoubleValue() {
595+
throwIfEmpty();
596+
return values[0] ? 1.0D : 0.0D;
597+
}
598+
599+
@Override
600+
public Field<Boolean> toField(String fieldName) {
601+
return new Field.BooleanField(fieldName, this);
602+
}
499603
}
500604

501605
abstract static class BinaryScriptDocValues<T> extends ScriptDocValues<T> {
@@ -568,6 +672,21 @@ protected String bytesToString(BytesRef bytes) {
568672
public final String getValue() {
569673
return get(0);
570674
}
675+
676+
@Override
677+
public long getLongValue() {
678+
return Long.parseLong(get(0));
679+
}
680+
681+
@Override
682+
public double getDoubleValue() {
683+
return Double.parseDouble(get(0));
684+
}
685+
686+
@Override
687+
public Field<String> toField(String fieldName) {
688+
return new Field.StringField(fieldName, this);
689+
}
571690
}
572691

573692
public static final class BytesRefs extends BinaryScriptDocValues<BytesRef> {
@@ -594,5 +713,9 @@ public BytesRef getValue() {
594713
return get(0);
595714
}
596715

716+
@Override
717+
public Field<BytesRef> toField(String fieldName) {
718+
return new Field.BytesRefField(fieldName, this);
719+
}
597720
}
598721
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,11 @@ public String get(int index) {
355355
public int size() {
356356
return count;
357357
}
358+
359+
@Override
360+
public org.elasticsearch.script.Field<String> toField(String fieldName) {
361+
return new org.elasticsearch.script.Field.IpField(fieldName, this);
362+
}
358363
}
359364

360365
@Override
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.script;
10+
11+
public interface Converter<CT, CF extends Field<CT>> {
12+
CF convert(Field<?> sourceField);
13+
Class<CF> getFieldClass();
14+
Class<CT> getTargetClass();
15+
}

0 commit comments

Comments
 (0)