From c0ce480de0520fdab67cdca82396143ad9c4526b Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Thu, 5 Aug 2021 20:46:17 -0500 Subject: [PATCH 1/6] Script: ulong support --- .../spi/org.elasticsearch.script.fields.txt | 29 ++++- .../test/painless/40_fields_api.yml | 10 +- .../index/fielddata/ScriptDocValues.java | 49 ++++++-- .../elasticsearch/script/AbstractField.java | 109 ++++++++++++++++++ .../elasticsearch/script/BigIntegerField.java | 43 +++++++ .../elasticsearch/script/DocBasedScript.java | 3 +- .../script/DocValuesDocReader.java | 7 +- .../elasticsearch/script/DocValuesField.java | 29 ++--- .../org/elasticsearch/script/DoubleField.java | 43 +++++++ .../org/elasticsearch/script/EmptyField.java | 47 +++++++- .../script/EmptyFieldValues.java | 95 +++++++++++++++ .../java/org/elasticsearch/script/Field.java | 30 ++++- .../org/elasticsearch/script/FieldValues.java | 40 +++++++ .../org/elasticsearch/script/LongField.java | 43 +++++++ .../org/elasticsearch/script/ObjectField.java | 42 +++++++ .../DocValuesWhitelistExtension.java | 33 +++++- .../UnsignedLongLeafFieldData.java | 3 +- .../UnsignedLongScriptDocValues.java | 45 +++++++- .../test/unsigned_long/50_script_values.yml | 32 ++--- 19 files changed, 658 insertions(+), 74 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/script/AbstractField.java create mode 100644 server/src/main/java/org/elasticsearch/script/BigIntegerField.java create mode 100644 server/src/main/java/org/elasticsearch/script/DoubleField.java create mode 100644 server/src/main/java/org/elasticsearch/script/EmptyFieldValues.java create mode 100644 server/src/main/java/org/elasticsearch/script/FieldValues.java create mode 100644 server/src/main/java/org/elasticsearch/script/LongField.java create mode 100644 server/src/main/java/org/elasticsearch/script/ObjectField.java diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt index b21437b14a884..7f0bf26c11fed 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt @@ -13,7 +13,34 @@ class org.elasticsearch.script.Field { String getName() boolean isEmpty() List getValues() - def getValue(def) + + org.elasticsearch.script.LongField asLongField() + long asLong(long) + + org.elasticsearch.script.DoubleField asDoubleField() + double asDouble(double) + + org.elasticsearch.script.BigIntegerField asBigIntegerField() + BigInteger asBigInteger(BigInteger) + + org.elasticsearch.script.ObjectField asObjectField() + Object asObject(Object) +} + +class org.elasticsearch.script.LongField { + long getValue(long) +} + +class org.elasticsearch.script.DoubleField { + double getValue(double) +} + +class org.elasticsearch.script.BigIntegerField { + BigInteger getValue(BigInteger) +} + +class org.elasticsearch.script.ObjectField { + Object getValue(Object) } class org.elasticsearch.script.DocBasedScript { diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml index 61749d8333cd4..c33ded617a630 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml @@ -43,7 +43,7 @@ setup: _script: type: number script: - source: "field('dval').getValue(3)" + source: "field('dval').asDoubleField().getValue(3)" - match: { hits.total: 3 } - match: { hits.hits.0._id: d2 } - match: { hits.hits.1._id: d3 } @@ -57,7 +57,7 @@ setup: _script: type: string script: - source: "field('sval.keyword').getValue('g')" + source: "field('sval.keyword').asObjectField().getValue('g')" - match: { hits.total: 3 } - match: { hits.hits.0._id: d3 } - match: { hits.hits.1._id: d1 } @@ -101,7 +101,7 @@ setup: script_score: query: {match_all: {} } script: - source: "field('dval').getValue(3)" + source: "field('dval').asDoubleField().getValue(3)" - match: { hits.total: 3 } - match: { hits.hits.0._id: d1 } - match: { hits.hits.1._id: d3 } @@ -144,7 +144,7 @@ setup: bool: filter: script: - script: "field('dval').getValue(6) > 6" + script: "field('dval').asDoubleField().getValue(6) > 6" - match: { hits.total: 1 } - match: { hits.hits.0._id: d1 } - do: @@ -155,6 +155,6 @@ setup: bool: filter: script: - script: "field('sval.keyword').getValue('b') == 'a'" + script: "field('sval.keyword').asObjectField().getValue('b') == 'a'" - match: { hits.total: 1 } - match: { hits.hits.0._id: d3 } diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java index 86d007072197e..e7b663ceb5c7d 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java @@ -17,14 +17,17 @@ import org.elasticsearch.common.geo.GeoUtils; import org.elasticsearch.common.time.DateUtils; import org.elasticsearch.geometry.utils.Geohash; +import org.elasticsearch.script.FieldValues; import org.elasticsearch.script.JodaCompatibleZonedDateTime; import java.io.IOException; import java.time.Instant; import java.time.ZoneOffset; import java.util.AbstractList; +import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.List; import java.util.function.UnaryOperator; /** @@ -35,7 +38,7 @@ * return as a single {@link ScriptDocValues} instance can be reused to return * values form multiple documents. */ -public abstract class ScriptDocValues extends AbstractList { +public abstract class ScriptDocValues extends AbstractList implements FieldValues.Objects { /** * Set the current doc ID. @@ -68,7 +71,17 @@ public final void sort(Comparator c) { throw new UnsupportedOperationException("doc values are unmodifiable"); } - public static final class Longs extends ScriptDocValues { + @Override + public List getObjects() { + return new ArrayList<>(this); + } + + @Override + public Object getObject(int index) { + return get(index); + } + + public static final class Longs extends ScriptDocValues implements FieldValues.Longs { private final SortedNumericDocValues in; private long[] values = new long[0]; private int count; @@ -102,11 +115,11 @@ protected void resize(int newSize) { } public long getValue() { - return get(0); + return getLong(0); } @Override - public Long get(int index) { + public long getLong(int index) { if (count == 0) { throw new IllegalStateException("A document doesn't have a value for a field! " + "Use doc[].size()==0 to check if a document is missing a field!"); @@ -114,6 +127,16 @@ public Long get(int index) { return values[index]; } + @Override + public Long get(int index) { + return getLong(index); + } + + @Override + public List getLongs() { + return this; + } + @Override public int size() { return count; @@ -152,8 +175,8 @@ public JodaCompatibleZonedDateTime get(int index) { } if (index >= count) { throw new IndexOutOfBoundsException( - "attempted to fetch the [" + index + "] date when there are only [" - + count + "] dates."); + "attempted to fetch the [" + index + "] date when there are only [" + + count + "] dates."); } return dates[index]; } @@ -194,7 +217,7 @@ void refreshArray() throws IOException { } } - public static final class Doubles extends ScriptDocValues { + public static final class Doubles extends ScriptDocValues implements FieldValues.Doubles { private final SortedNumericDoubleValues in; private double[] values = new double[0]; @@ -230,11 +253,16 @@ public SortedNumericDoubleValues getInternalValues() { } public double getValue() { - return get(0); + return getDouble(0); } @Override public Double get(int index) { + return getDouble(index); + } + + @Override + public double getDouble(int index) { if (count == 0) { throw new IllegalStateException("A document doesn't have a value for a field! " + "Use doc[].size()==0 to check if a document is missing a field!"); @@ -242,6 +270,11 @@ public Double get(int index) { return values[index]; } + @Override + public List getDoubles() { + return this; + } + @Override public int size() { return count; diff --git a/server/src/main/java/org/elasticsearch/script/AbstractField.java b/server/src/main/java/org/elasticsearch/script/AbstractField.java new file mode 100644 index 0000000000000..946a7c3b76fef --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/AbstractField.java @@ -0,0 +1,109 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script; + +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; + +/** + * Parent of all Field implementations, handles common actions such as the {@code asField}, {@code as}, {@code getName()} + * API calls. Subclasses should override {@code getFieldValues} for {@code getValues()}, they should also override their + * own {@code asField}, {@code as} to avoid new object creation. + * + * Of course, for duck typing, subclasses must also implement their own {@code getValue}. + * + * @param Type of value held by this field. Boxed for primitive types. + * @param Field values subclass used to implement common methods for Field. + */ +public abstract class AbstractField implements Field { + protected final String name; + protected final V values; + + public AbstractField(String name, V values) { + this.name = name; + this.values = values; + } + + @Override + public String getName() { + return name; + } + + @Override + public List getValues() { + if (isEmpty()) { + return Collections.emptyList(); + } + return getFieldValues(); + } + + protected abstract List getFieldValues(); + + @Override + public boolean isEmpty() { + return values.isEmpty(); + } + + @Override + public LongField asLongField() { + if (values instanceof FieldValues.Longs == false) { + throw new IllegalStateException("This Field cannot be converted to a LongField due to the underlying data"); + } + + return new LongField(name, (FieldValues.Longs) values); + } + + @Override + public long asLong(long defaultValue) { + return asLongField().getValue(defaultValue); + } + + @Override + public DoubleField asDoubleField() { + if (values instanceof FieldValues.Doubles == false) { + throw new IllegalStateException("This Field cannot be converted to a DoubleValues due to the underlying data"); + } + + return new DoubleField(name, (FieldValues.Doubles) values); + } + + @Override + public double asDouble(double defaultValue) { + return asDoubleField().getValue(defaultValue); + } + + @Override + public BigIntegerField asBigIntegerField() { + if (values instanceof FieldValues.BigIntegers == false) { + throw new IllegalStateException("This Field cannot be converted to a BigIntegerField due to the underlying data"); + } + + return new BigIntegerField(name, (FieldValues.BigIntegers) values); + } + + @Override + public BigInteger asBigInteger(BigInteger defaultValue) { + return asBigIntegerField().getValue(defaultValue); + } + + @Override + public ObjectField asObjectField() { + if (values instanceof FieldValues.Objects == false) { + throw new IllegalStateException("This Field cannot be converted to a ObjectField due to the underlying data"); + } + + return new ObjectField(name, (FieldValues.Objects) values); + } + + @Override + public Object asObject(Object defaultValue) { + return asObjectField().getValue(defaultValue); + } +} diff --git a/server/src/main/java/org/elasticsearch/script/BigIntegerField.java b/server/src/main/java/org/elasticsearch/script/BigIntegerField.java new file mode 100644 index 0000000000000..dc723b8a75cc6 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/BigIntegerField.java @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script; + +import java.math.BigInteger; +import java.util.List; + +/** + * Duck-typed {@code BigInteger} field. + */ +public class BigIntegerField extends AbstractField { + public BigIntegerField(String name, FieldValues.BigIntegers values) { + super(name, values); + } + + public BigInteger getValue(BigInteger defaultValue) { + if (values.isEmpty()) { + return defaultValue; + } + return values.getBigInteger(0); + } + + @Override + protected List getFieldValues() { + return values.getBigIntegers(); + } + + @Override + public BigIntegerField asBigIntegerField() { + return this; + } + + @Override + public BigInteger asBigInteger(BigInteger defaultValue) { + return getValue(defaultValue); + } +} diff --git a/server/src/main/java/org/elasticsearch/script/DocBasedScript.java b/server/src/main/java/org/elasticsearch/script/DocBasedScript.java index 9e08228cd7c70..d4a041d52a738 100644 --- a/server/src/main/java/org/elasticsearch/script/DocBasedScript.java +++ b/server/src/main/java/org/elasticsearch/script/DocBasedScript.java @@ -23,7 +23,7 @@ public DocBasedScript(DocReader docReader) { public Field field(String fieldName) { if (docReader == null) { - return new EmptyField<>(fieldName); + return new EmptyField(fieldName); } return docReader.field(fieldName); } @@ -44,6 +44,7 @@ public void setDocument(int docID) { } } + /** Old-style doc access for contexts that map some doc contents in params */ public Map docAsMap() { if (docReader == null) { return Collections.emptyMap(); diff --git a/server/src/main/java/org/elasticsearch/script/DocValuesDocReader.java b/server/src/main/java/org/elasticsearch/script/DocValuesDocReader.java index 3d4d3b591809c..ebf8fc8e596d0 100644 --- a/server/src/main/java/org/elasticsearch/script/DocValuesDocReader.java +++ b/server/src/main/java/org/elasticsearch/script/DocValuesDocReader.java @@ -18,7 +18,7 @@ public class DocValuesDocReader implements DocReader, LeafReaderContextSupplier { /** A leaf lookup for the bound segment this proxy will operate on. */ - protected LeafSearchLookup leafLookup; + protected final LeafSearchLookup leafLookup; // provide access to the leaf context reader for expressions protected final LeafReaderContext leafReaderContext; @@ -37,12 +37,13 @@ public Field field(String fieldName) { Map> doc = leafLookup.doc(); if (doc.containsKey(fieldName) == false) { - return new EmptyField(fieldName); + return new EmptyField(fieldName); } return new DocValuesField<>(fieldName, doc.get(fieldName)); } + // Currently unimplemented @Override public Stream> fields(String fieldGlob) { return Stream.empty(); @@ -63,11 +64,13 @@ public Map> doc() { return leafLookup.doc(); } + // Implemented for backwards compatibility for random score scripts which need a seed @Override public int getDocBase() { return docBase; } + // Implemented for compatibility with expressions @Override public LeafReaderContext getLeafReaderContext() { return leafReaderContext; diff --git a/server/src/main/java/org/elasticsearch/script/DocValuesField.java b/server/src/main/java/org/elasticsearch/script/DocValuesField.java index 7b94988871e12..19cb5c5ea1877 100644 --- a/server/src/main/java/org/elasticsearch/script/DocValuesField.java +++ b/server/src/main/java/org/elasticsearch/script/DocValuesField.java @@ -12,35 +12,26 @@ import java.util.List; -public class DocValuesField implements Field { - protected final String name; +/** + * Fields API accessor for DocValues, users must currently call their own casting methods using + * {@code asField}. This is the initial entry point to that casting system. Could be replaced + * with field type inspection via {@code SearchLookup} in the future. + */ +public class DocValuesField extends AbstractField> { protected final ScriptDocValues scriptDocValues; public DocValuesField(String name, ScriptDocValues scriptDocValues) { - this.name = name; + super(name, scriptDocValues); this.scriptDocValues = scriptDocValues; } @Override - public T getValue(T defaultValue) { - if (scriptDocValues.isEmpty()) { - return defaultValue; - } - return scriptDocValues.get(0); - } - - @Override - public String getName() { - return name; - } - - @Override - public boolean isEmpty() { - return scriptDocValues.isEmpty(); + public List getValues() { + return scriptDocValues; } @Override - public List getValues() { + protected List getFieldValues() { return scriptDocValues; } } diff --git a/server/src/main/java/org/elasticsearch/script/DoubleField.java b/server/src/main/java/org/elasticsearch/script/DoubleField.java new file mode 100644 index 0000000000000..1c73d1eb86e80 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/DoubleField.java @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script; + +import java.util.List; + +/** + * Duck-typed {@code double} field. Allows un-boxed access to the underlying {@code double} via + * {@code getValue}. + */ +public class DoubleField extends AbstractField { + public DoubleField(String name, FieldValues.Doubles values) { + super(name, values); + } + + public double getValue(double defaultValue) { + if (isEmpty()) { + return defaultValue; + } + return values.getDouble(0); + } + + @Override + protected List getFieldValues() { + return values.getDoubles(); + } + + @Override + public DoubleField asDoubleField() { + return this; + } + + @Override + public double asDouble(double defaultValue) { + return getValue(defaultValue); + } +} diff --git a/server/src/main/java/org/elasticsearch/script/EmptyField.java b/server/src/main/java/org/elasticsearch/script/EmptyField.java index c28b8b602f76a..c63de02924e56 100644 --- a/server/src/main/java/org/elasticsearch/script/EmptyField.java +++ b/server/src/main/java/org/elasticsearch/script/EmptyField.java @@ -8,10 +8,14 @@ package org.elasticsearch.script; +import java.math.BigInteger; import java.util.Collections; import java.util.List; -public class EmptyField implements Field { +/** + * An empty field is empty no matter the type, handles all the user-type coercion here. + */ +public class EmptyField implements Field { protected final String name; public EmptyField(String name) { @@ -29,12 +33,47 @@ public boolean isEmpty() { } @Override - public Object getValue(Object defaultValue) { + public List getValues() { + return Collections.emptyList(); + } + + @Override + public LongField asLongField() { + return new LongField(name, EmptyFieldValues.LONG); + } + + @Override + public long asLong(long defaultValue) { return defaultValue; } @Override - public List getValues() { - return Collections.emptyList(); + public DoubleField asDoubleField() { + return new DoubleField(name, EmptyFieldValues.DOUBLE); + } + + @Override + public double asDouble(double defaultValue) { + return defaultValue; + } + + @Override + public BigIntegerField asBigIntegerField() { + return new BigIntegerField(name, EmptyFieldValues.BIGINTEGER); + } + + @Override + public BigInteger asBigInteger(BigInteger defaultValue) { + return defaultValue; + } + + @Override + public ObjectField asObjectField() { + return new ObjectField(name, EmptyFieldValues.OBJECT); + } + + @Override + public Object asObject(Object defaultValue) { + return defaultValue; } } diff --git a/server/src/main/java/org/elasticsearch/script/EmptyFieldValues.java b/server/src/main/java/org/elasticsearch/script/EmptyFieldValues.java new file mode 100644 index 0000000000000..39f927212eb9e --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/EmptyFieldValues.java @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script; + +import java.math.BigInteger; +import java.util.Collections; +import java.util.List; + +/** + * Empty versions of all the duck-typed field values supported. + */ +public class EmptyFieldValues { + public static final FieldValues.Doubles DOUBLE = new Doubles(); + public static final FieldValues.Longs LONG = new Longs(); + public static final FieldValues.BigIntegers BIGINTEGER = new BigIntegers(); + public static final FieldValues.Objects OBJECT = new Objects(); + + protected static void badIndex(int index) { + throw new IllegalStateException("Cannot read index [" + index + "] for empty field"); + } + + private static class Doubles implements FieldValues.Doubles { + @Override + public boolean isEmpty() { + return true; + } + + @Override + public double getDouble(int index) { + throw new IllegalStateException("Cannot read index [" + index + "] for empty field"); + } + + @Override + public List getDoubles() { + return Collections.emptyList(); + } + } + + private static class Longs implements FieldValues.Longs { + @Override + public boolean isEmpty() { + return true; + } + + @Override + public long getLong(int index) { + throw new IllegalStateException("Cannot read index [" + index + "] for empty field"); + } + + @Override + public List getLongs() { + return Collections.emptyList(); + } + } + + private static class Objects implements FieldValues.Objects { + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Object getObject(int index) { + throw new IllegalStateException("Cannot read index [" + index + "] for empty field"); + } + + @Override + public List getObjects() { + return Collections.emptyList(); + } + } + + private static class BigIntegers implements FieldValues.BigIntegers { + @Override + public boolean isEmpty() { + return true; + } + + @Override + public BigInteger getBigInteger(int index) { + throw new IllegalStateException("Cannot read index [" + index + "] for empty field"); + } + + @Override + public List getBigIntegers() { + return Collections.emptyList(); + } + } +} diff --git a/server/src/main/java/org/elasticsearch/script/Field.java b/server/src/main/java/org/elasticsearch/script/Field.java index 2d501c4bea9ef..a3d8877afb85b 100644 --- a/server/src/main/java/org/elasticsearch/script/Field.java +++ b/server/src/main/java/org/elasticsearch/script/Field.java @@ -9,6 +9,7 @@ package org.elasticsearch.script; +import java.math.BigInteger; import java.util.List; /** @@ -22,14 +23,37 @@ * @param */ public interface Field { + /** The field name */ String getName(); /** Does the field have any values? An unmapped field may have values from source */ boolean isEmpty(); - /** Get all values of a multivalued field. If {@code isEmpty()} this returns an empty list */ + /** Get all values of a multivalued field. If {@code isEmpty()} this returns an empty list. */ List getValues(); - /** Get the first value of a field, if {@code isEmpty()} return defaultValue instead */ - T getValue(T defaultValue); + /* This is left to the duck-typed implementing classes */ + // T getValue(T defaultValue); + + /** Treat the current {@code Field} as if it held primitive {@code long}s, throws {@code IllegalStateException} if impossible */ + LongField asLongField(); + long asLong(long defaultValue); + + /** Treat the current {@code Field} as if it held primitive {@code double}s, {@code throws IllegalStateException} if impossible */ + DoubleField asDoubleField(); + double asDouble(double defaultValue); + + /** + * Treat the current {@code Field} as if it held {@code BigInteger}, throws {@code IllegalStateException} if underlying type does not + * naturally contain {@code BigInteger}s. If underlying values fit in a signed {@code long}, {@code asLongField} should be used. + **/ + BigIntegerField asBigIntegerField(); + BigInteger asBigInteger(BigInteger defaultValue); + + /** + * Treat the current Field as if it held {@code Object}. This is a way to break out of the Fields API and + * allow the caller to do their own casting if necessary. + */ + ObjectField asObjectField(); + Object asObject(Object defaultValue); } diff --git a/server/src/main/java/org/elasticsearch/script/FieldValues.java b/server/src/main/java/org/elasticsearch/script/FieldValues.java new file mode 100644 index 0000000000000..b99cfe4473f82 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/FieldValues.java @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script; + +import java.math.BigInteger; +import java.util.List; + +/** + * Codecs for reading underlying field values. A particular ScriptDocValues subclass may + * implement multiple {@code FieldValues} interfaces. + */ +public interface FieldValues { + boolean isEmpty(); + + interface Doubles extends FieldValues { + double getDouble(int index); + List getDoubles(); + } + + interface Longs extends FieldValues { + long getLong(int index); + List getLongs(); + } + + interface Objects extends FieldValues { + Object getObject(int index); + List getObjects(); + } + + interface BigIntegers extends FieldValues { + BigInteger getBigInteger(int index); + List getBigIntegers(); + } +} diff --git a/server/src/main/java/org/elasticsearch/script/LongField.java b/server/src/main/java/org/elasticsearch/script/LongField.java new file mode 100644 index 0000000000000..439b97a78d7c6 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/LongField.java @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script; + +import java.util.List; + +/** + * Duck-typed {@code long} field. Allows un-boxed access to the underlying {@code long} via + * {@code getValue}. + */ +public class LongField extends AbstractField { + public LongField(String name, FieldValues.Longs values) { + super(name, values); + } + + public long getValue(long defaultValue) { + if (isEmpty()) { + return defaultValue; + } + return values.getLong(0); + } + + @Override + protected List getFieldValues() { + return values.getLongs(); + } + + @Override + public LongField asLongField() { + return this; + } + + @Override + public long asLong(long defaultValue) { + return getValue(defaultValue); + } +} diff --git a/server/src/main/java/org/elasticsearch/script/ObjectField.java b/server/src/main/java/org/elasticsearch/script/ObjectField.java new file mode 100644 index 0000000000000..0b170698516ae --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/ObjectField.java @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script; + +import java.util.List; + +/** + * Fields API for a Objects that don't yet have specialized {@code getValue}s implemented yet. + */ +public class ObjectField extends AbstractField { + public ObjectField(String name, FieldValues.Objects values) { + super(name, values); + } + + public Object getValue(Object defaultValue) { + if (isEmpty()) { + return defaultValue; + } + return values.getObject(0); + } + + @Override + protected List getFieldValues() { + return values.getObjects(); + } + + @Override + public ObjectField asObjectField() { + return this; + } + + @Override + public Object asObject(Object defaultValue) { + return getValue(defaultValue); + } +} diff --git a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/DocValuesWhitelistExtension.java b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/DocValuesWhitelistExtension.java index 219149453d64b..1c9722872bd03 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/DocValuesWhitelistExtension.java +++ b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/DocValuesWhitelistExtension.java @@ -9,17 +9,44 @@ import org.elasticsearch.painless.spi.PainlessExtension; import org.elasticsearch.painless.spi.Whitelist; +import org.elasticsearch.painless.spi.WhitelistLoader; +import org.elasticsearch.script.AggregationScript; +import org.elasticsearch.script.BucketAggregationSelectorScript; +import org.elasticsearch.script.FieldScript; +import org.elasticsearch.script.FilterScript; +import org.elasticsearch.script.NumberSortScript; +import org.elasticsearch.script.ScoreScript; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.script.StringSortScript; -import java.util.Collections; import java.util.List; import java.util.Map; +import static java.util.Collections.singletonList; + public class DocValuesWhitelistExtension implements PainlessExtension { + private static final Whitelist WHITELIST = WhitelistLoader.loadFromResourceFiles(DocValuesWhitelistExtension.class, "whitelist.txt"); + @Override public Map, List> getContextWhitelists() { - // TODO: support unsigned_long in scripts - return Collections.emptyMap(); + List whitelist = singletonList(WHITELIST); + Map, List> contexts = Map.of( + FieldScript.CONTEXT, + whitelist, + ScoreScript.CONTEXT, + whitelist, + FilterScript.CONTEXT, + whitelist, + AggregationScript.CONTEXT, + whitelist, + NumberSortScript.CONTEXT, + whitelist, + StringSortScript.CONTEXT, + whitelist, + BucketAggregationSelectorScript.CONTEXT, + whitelist + ); + return contexts; } } diff --git a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongLeafFieldData.java b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongLeafFieldData.java index 0121cd62cc035..c898d2e147c9e 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongLeafFieldData.java +++ b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongLeafFieldData.java @@ -74,8 +74,7 @@ public int docValueCount() { @Override public ScriptDocValues getScriptValues() { - // TODO: add support for scripts - throw new UnsupportedOperationException("Using unsigned_long in scripts is currently not supported!"); + return new UnsignedLongScriptDocValues(getLongValues()); } @Override diff --git a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongScriptDocValues.java b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongScriptDocValues.java index f5b8025cfc974..aa4afe3b8861a 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongScriptDocValues.java +++ b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/UnsignedLongScriptDocValues.java @@ -10,11 +10,16 @@ import org.apache.lucene.index.SortedNumericDocValues; import org.apache.lucene.util.ArrayUtil; import org.elasticsearch.index.fielddata.ScriptDocValues; +import org.elasticsearch.script.FieldValues; import org.elasticsearch.search.DocValueFormat; import java.io.IOException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; -public class UnsignedLongScriptDocValues extends ScriptDocValues { +public class UnsignedLongScriptDocValues extends ScriptDocValues implements FieldValues.BigIntegers, FieldValues.Longs { private final SortedNumericDocValues in; private long[] values = new long[0]; private int count; @@ -65,4 +70,42 @@ public Number get(int index) { public int size() { return count; } + + @Override + public long getLong(int index) { + return get(index).longValue(); + } + + @Override + public List getLongs() { + return this.stream().map(Number::longValue).collect(Collectors.toList()); + } + + @Override + public List getObjects() { + // Default to longs instead of the mixed BigInteger/Long from DocValueFormat.UNSIGNED_LONG_SHIFTED.format + return new ArrayList<>(getLongs()); + } + + @Override + public BigInteger getBigInteger(int index) { + return numberToBigInteger(get(index)); + } + + private static BigInteger numberToBigInteger(Number number) { + if (number instanceof BigInteger) { + return (BigInteger) number; + } + return BigInteger.valueOf(number.longValue()); + } + + @Override + public List getBigIntegers() { + return this.stream().map(UnsignedLongScriptDocValues::numberToBigInteger).collect(Collectors.toList()); + } + + @Override + public Number getObject(int index) { + return getBigInteger(index); + } } diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/unsigned_long/50_script_values.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/unsigned_long/50_script_values.yml index 56929f56f4742..7f7a72d2ad51a 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/unsigned_long/50_script_values.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/unsigned_long/50_script_values.yml @@ -1,8 +1,8 @@ setup: - skip: - version: "all" - reason: "AwaitsFix https://github.com/elastic/elasticsearch/issues/64361" + version: " - 7.14.99" + reason: "unsigned long script fields api was added in 7.15.0" - do: indices.create: @@ -25,28 +25,10 @@ setup: { "index": {"_id" : "3"} } { "ul": 9223372036854775808 } { "index": {"_id" : "4"} } - { "ul": 18446744073709551614 } + { "ul": 18446744073709551613 } { "index": {"_id" : "5"} } { "ul": 18446744073709551615 } ---- -"Scripted fields values return BigInteger or Long": - - do: - search: - index: test1 - body: - sort: [ { ul: desc } ] - script_fields: - scripted_ul: - script: - source: "doc['ul'].value" - - - match: { hits.hits.0.fields.scripted_ul.0: 18446744073709551615 } - - match: { hits.hits.1.fields.scripted_ul.0: 18446744073709551614 } - - match: { hits.hits.2.fields.scripted_ul.0: 9223372036854775808 } - - match: { hits.hits.3.fields.scripted_ul.0: 9223372036854775807 } - - match: { hits.hits.4.fields.scripted_ul.0: 0 } - --- "Scripted sort values": - do: @@ -58,7 +40,7 @@ setup: order: desc type: number script: - source: "doc['ul'].value" + source: "field('ul').asBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" - match: { hits.hits.0.sort: [1.8446744073709552E19] } - match: { hits.hits.1.sort: [1.8446744073709552E19] } @@ -77,7 +59,7 @@ setup: filter: script: script: - source: "doc['ul'].value.doubleValue() > 10E18" + source: "field('ul').asBigIntegerField().getValue(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue() > 10E18" sort: [ { ul: asc } ] - match: { hits.total.value: 2 } - match: { hits.hits.0._id: "4" } @@ -93,7 +75,7 @@ setup: filter: script: script: - source: "doc['ul'].size() > 0" + source: "field('ul').isEmpty() == false" - match: { hits.total.value: 5 } --- @@ -106,6 +88,6 @@ setup: script_score: query: {match_all: {}} script: - source: "doc['ul'].value" + source: "field('ul').asBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" - match: { hits.total.value: 5 } From 06ab04f101e8f0394071f57bd7986dedaf7e05df Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Fri, 6 Aug 2021 07:44:11 -0500 Subject: [PATCH 2/6] Script: Add StringField --- .../spi/org.elasticsearch.script.fields.txt | 7 ++++ .../test/painless/40_fields_api.yml | 2 +- .../index/fielddata/ScriptDocValues.java | 12 +++++- .../elasticsearch/script/AbstractField.java | 14 +++++++ .../org/elasticsearch/script/EmptyField.java | 10 +++++ .../script/EmptyFieldValues.java | 30 +++++++++++--- .../java/org/elasticsearch/script/Field.java | 4 ++ .../org/elasticsearch/script/FieldValues.java | 5 +++ .../org/elasticsearch/script/StringField.java | 39 +++++++++++++++++++ 9 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 server/src/main/java/org/elasticsearch/script/StringField.java diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt index 7f0bf26c11fed..24e2245187cd2 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt @@ -23,6 +23,9 @@ class org.elasticsearch.script.Field { org.elasticsearch.script.BigIntegerField asBigIntegerField() BigInteger asBigInteger(BigInteger) + org.elasticsearch.script.StringField asStringField() + String asString(String) + org.elasticsearch.script.ObjectField asObjectField() Object asObject(Object) } @@ -39,6 +42,10 @@ class org.elasticsearch.script.BigIntegerField { BigInteger getValue(BigInteger) } +class org.elasticsearch.script.StringField { + String getValue(String) +} + class org.elasticsearch.script.ObjectField { Object getValue(Object) } diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml index c33ded617a630..51e69a32cc202 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml @@ -57,7 +57,7 @@ setup: _script: type: string script: - source: "field('sval.keyword').asObjectField().getValue('g')" + source: "field('sval.keyword').asStringField().getValue('g')" - match: { hits.total: 3 } - match: { hits.hits.0._id: d3 } - match: { hits.hits.1._id: d1 } diff --git a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java index e7b663ceb5c7d..34fc3f0aded62 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java @@ -577,13 +577,18 @@ public int size() { } } - public static class Strings extends BinaryScriptDocValues { + public static class Strings extends BinaryScriptDocValues implements FieldValues.Strings { public Strings(SortedBinaryDocValues in) { super(in); } @Override public final String get(int index) { + return getString(index); + } + + @Override + public String getString(int index) { if (count == 0) { throw new IllegalStateException("A document doesn't have a value for a field! " + "Use doc[].size()==0 to check if a document is missing a field!"); @@ -591,6 +596,11 @@ public final String get(int index) { return bytesToString(values[index].get()); } + @Override + public List getStrings() { + return this; + } + /** * Convert the stored bytes to a String. */ diff --git a/server/src/main/java/org/elasticsearch/script/AbstractField.java b/server/src/main/java/org/elasticsearch/script/AbstractField.java index 946a7c3b76fef..a79d84c95c522 100644 --- a/server/src/main/java/org/elasticsearch/script/AbstractField.java +++ b/server/src/main/java/org/elasticsearch/script/AbstractField.java @@ -93,6 +93,20 @@ public BigInteger asBigInteger(BigInteger defaultValue) { return asBigIntegerField().getValue(defaultValue); } + @Override + public StringField asStringField() { + if (values instanceof FieldValues.Strings == false) { + throw new IllegalStateException("This Field cannot be converted to a StringField due to the underlying data"); + } + + return new StringField(name, (FieldValues.Strings) values); + } + + @Override + public String asString(String defaultValue) { + return asStringField().getValue(defaultValue); + } + @Override public ObjectField asObjectField() { if (values instanceof FieldValues.Objects == false) { diff --git a/server/src/main/java/org/elasticsearch/script/EmptyField.java b/server/src/main/java/org/elasticsearch/script/EmptyField.java index c63de02924e56..c653e215ea2b4 100644 --- a/server/src/main/java/org/elasticsearch/script/EmptyField.java +++ b/server/src/main/java/org/elasticsearch/script/EmptyField.java @@ -67,6 +67,16 @@ public BigInteger asBigInteger(BigInteger defaultValue) { return defaultValue; } + @Override + public StringField asStringField() { + return new StringField(name, EmptyFieldValues.STRING); + } + + @Override + public String asString(String defaultValue) { + return defaultValue; + } + @Override public ObjectField asObjectField() { return new ObjectField(name, EmptyFieldValues.OBJECT); diff --git a/server/src/main/java/org/elasticsearch/script/EmptyFieldValues.java b/server/src/main/java/org/elasticsearch/script/EmptyFieldValues.java index 39f927212eb9e..78c2d2a877849 100644 --- a/server/src/main/java/org/elasticsearch/script/EmptyFieldValues.java +++ b/server/src/main/java/org/elasticsearch/script/EmptyFieldValues.java @@ -19,6 +19,7 @@ public class EmptyFieldValues { public static final FieldValues.Doubles DOUBLE = new Doubles(); public static final FieldValues.Longs LONG = new Longs(); public static final FieldValues.BigIntegers BIGINTEGER = new BigIntegers(); + public static final FieldValues.Strings STRING = new Strings(); public static final FieldValues.Objects OBJECT = new Objects(); protected static void badIndex(int index) { @@ -59,36 +60,53 @@ public List getLongs() { } } - private static class Objects implements FieldValues.Objects { + private static class BigIntegers implements FieldValues.BigIntegers { @Override public boolean isEmpty() { return true; } @Override - public Object getObject(int index) { + public BigInteger getBigInteger(int index) { throw new IllegalStateException("Cannot read index [" + index + "] for empty field"); } @Override - public List getObjects() { + public List getBigIntegers() { return Collections.emptyList(); } } - private static class BigIntegers implements FieldValues.BigIntegers { + private static class Strings implements FieldValues.Strings { @Override public boolean isEmpty() { return true; } @Override - public BigInteger getBigInteger(int index) { + public String getString(int index) { throw new IllegalStateException("Cannot read index [" + index + "] for empty field"); } @Override - public List getBigIntegers() { + public List getStrings() { + return Collections.emptyList(); + } + } + + private static class Objects implements FieldValues.Objects { + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Object getObject(int index) { + throw new IllegalStateException("Cannot read index [" + index + "] for empty field"); + } + + @Override + public List getObjects() { return Collections.emptyList(); } } diff --git a/server/src/main/java/org/elasticsearch/script/Field.java b/server/src/main/java/org/elasticsearch/script/Field.java index a3d8877afb85b..a19ca5b550048 100644 --- a/server/src/main/java/org/elasticsearch/script/Field.java +++ b/server/src/main/java/org/elasticsearch/script/Field.java @@ -50,6 +50,10 @@ public interface Field { BigIntegerField asBigIntegerField(); BigInteger asBigInteger(BigInteger defaultValue); + /** Treat the current {@code Field} as if it held {@code Strings}s, throws {@code IllegalStateException} if impossible */ + StringField asStringField(); + String asString(String defaultValue); + /** * Treat the current Field as if it held {@code Object}. This is a way to break out of the Fields API and * allow the caller to do their own casting if necessary. diff --git a/server/src/main/java/org/elasticsearch/script/FieldValues.java b/server/src/main/java/org/elasticsearch/script/FieldValues.java index b99cfe4473f82..9847e7ed6ebed 100644 --- a/server/src/main/java/org/elasticsearch/script/FieldValues.java +++ b/server/src/main/java/org/elasticsearch/script/FieldValues.java @@ -37,4 +37,9 @@ interface BigIntegers extends FieldValues { BigInteger getBigInteger(int index); List getBigIntegers(); } + + interface Strings extends FieldValues { + String getString (int index); + List getStrings(); + } } diff --git a/server/src/main/java/org/elasticsearch/script/StringField.java b/server/src/main/java/org/elasticsearch/script/StringField.java new file mode 100644 index 0000000000000..68264e6467f92 --- /dev/null +++ b/server/src/main/java/org/elasticsearch/script/StringField.java @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +package org.elasticsearch.script; + +import java.util.List; + +public class StringField extends AbstractField { + public StringField(String name, FieldValues.Strings values) { + super(name, values); + } + + public String getValue(String defaultValue) { + if (values.isEmpty()) { + return defaultValue; + } + return values.getString(0); + } + + @Override + protected List getFieldValues() { + return values.getStrings(); + } + + @Override + public StringField asStringField() { + return this; + } + + @Override + public String asString(String defaultValue) { + return getValue(defaultValue); + } +} From a4fd2e7ecf2484c7ec3cff9e87301ded66872e7e Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Mon, 9 Aug 2021 12:58:21 -0500 Subject: [PATCH 3/6] Script: ObjectField -> DefField --- .../painless/spi/org.elasticsearch.script.fields.txt | 8 ++++---- .../rest-api-spec/test/painless/40_fields_api.yml | 2 +- .../main/java/org/elasticsearch/script/AbstractField.java | 8 ++++---- .../script/{ObjectField.java => DefField.java} | 8 ++++---- .../main/java/org/elasticsearch/script/EmptyField.java | 6 +++--- server/src/main/java/org/elasticsearch/script/Field.java | 4 ++-- 6 files changed, 18 insertions(+), 18 deletions(-) rename server/src/main/java/org/elasticsearch/script/{ObjectField.java => DefField.java} (79%) diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt index 24e2245187cd2..7803976783d2f 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt @@ -26,8 +26,8 @@ class org.elasticsearch.script.Field { org.elasticsearch.script.StringField asStringField() String asString(String) - org.elasticsearch.script.ObjectField asObjectField() - Object asObject(Object) + org.elasticsearch.script.DefField asDefField() + def asDef(def) } class org.elasticsearch.script.LongField { @@ -46,8 +46,8 @@ class org.elasticsearch.script.StringField { String getValue(String) } -class org.elasticsearch.script.ObjectField { - Object getValue(Object) +class org.elasticsearch.script.DefField { + def getValue(def) } class org.elasticsearch.script.DocBasedScript { diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml index 51e69a32cc202..29f03a9122d11 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml @@ -155,6 +155,6 @@ setup: bool: filter: script: - script: "field('sval.keyword').asObjectField().getValue('b') == 'a'" + script: "field('sval.keyword').asDefField().getValue('b') == 'a'" - match: { hits.total: 1 } - match: { hits.hits.0._id: d3 } diff --git a/server/src/main/java/org/elasticsearch/script/AbstractField.java b/server/src/main/java/org/elasticsearch/script/AbstractField.java index a79d84c95c522..d4bf1d3302360 100644 --- a/server/src/main/java/org/elasticsearch/script/AbstractField.java +++ b/server/src/main/java/org/elasticsearch/script/AbstractField.java @@ -108,16 +108,16 @@ public String asString(String defaultValue) { } @Override - public ObjectField asObjectField() { + public DefField asDefField() { if (values instanceof FieldValues.Objects == false) { throw new IllegalStateException("This Field cannot be converted to a ObjectField due to the underlying data"); } - return new ObjectField(name, (FieldValues.Objects) values); + return new DefField(name, (FieldValues.Objects) values); } @Override - public Object asObject(Object defaultValue) { - return asObjectField().getValue(defaultValue); + public Object asDef(Object defaultValue) { + return asDefField().getValue(defaultValue); } } diff --git a/server/src/main/java/org/elasticsearch/script/ObjectField.java b/server/src/main/java/org/elasticsearch/script/DefField.java similarity index 79% rename from server/src/main/java/org/elasticsearch/script/ObjectField.java rename to server/src/main/java/org/elasticsearch/script/DefField.java index 0b170698516ae..572cfb76ac600 100644 --- a/server/src/main/java/org/elasticsearch/script/ObjectField.java +++ b/server/src/main/java/org/elasticsearch/script/DefField.java @@ -13,8 +13,8 @@ /** * Fields API for a Objects that don't yet have specialized {@code getValue}s implemented yet. */ -public class ObjectField extends AbstractField { - public ObjectField(String name, FieldValues.Objects values) { +public class DefField extends AbstractField { + public DefField(String name, FieldValues.Objects values) { super(name, values); } @@ -31,12 +31,12 @@ protected List getFieldValues() { } @Override - public ObjectField asObjectField() { + public DefField asDefField() { return this; } @Override - public Object asObject(Object defaultValue) { + public Object asDef(Object defaultValue) { return getValue(defaultValue); } } diff --git a/server/src/main/java/org/elasticsearch/script/EmptyField.java b/server/src/main/java/org/elasticsearch/script/EmptyField.java index c653e215ea2b4..496df67facd5d 100644 --- a/server/src/main/java/org/elasticsearch/script/EmptyField.java +++ b/server/src/main/java/org/elasticsearch/script/EmptyField.java @@ -78,12 +78,12 @@ public String asString(String defaultValue) { } @Override - public ObjectField asObjectField() { - return new ObjectField(name, EmptyFieldValues.OBJECT); + public DefField asDefField() { + return new DefField(name, EmptyFieldValues.OBJECT); } @Override - public Object asObject(Object defaultValue) { + public Object asDef(Object defaultValue) { return defaultValue; } } diff --git a/server/src/main/java/org/elasticsearch/script/Field.java b/server/src/main/java/org/elasticsearch/script/Field.java index a19ca5b550048..b45d4dacab524 100644 --- a/server/src/main/java/org/elasticsearch/script/Field.java +++ b/server/src/main/java/org/elasticsearch/script/Field.java @@ -58,6 +58,6 @@ public interface Field { * Treat the current Field as if it held {@code Object}. This is a way to break out of the Fields API and * allow the caller to do their own casting if necessary. */ - ObjectField asObjectField(); - Object asObject(Object defaultValue); + DefField asDefField(); + Object asDef(Object defaultValue); } From 6852c8e843a51bedb4e4a078e59fe25ae4467f94 Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Mon, 9 Aug 2021 14:26:35 -0500 Subject: [PATCH 4/6] Scripting: get(default) --- .../spi/org.elasticsearch.script.fields.txt | 19 +- .../test/painless/40_fields_api.yml | 182 ++++++++++++++++++ .../elasticsearch/script/AbstractField.java | 18 +- .../elasticsearch/script/BigIntegerField.java | 18 +- .../org/elasticsearch/script/DefField.java | 11 +- .../script/DocValuesDocReader.java | 2 +- .../elasticsearch/script/DocValuesField.java | 19 +- .../org/elasticsearch/script/DoubleField.java | 16 +- .../org/elasticsearch/script/EmptyField.java | 15 +- .../java/org/elasticsearch/script/Field.java | 16 +- .../org/elasticsearch/script/LongField.java | 16 +- .../org/elasticsearch/script/StringField.java | 18 +- .../test/unsigned_long/50_script_values.yml | 86 ++++++++- 13 files changed, 367 insertions(+), 69 deletions(-) diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt index 7803976783d2f..dec53199a0d83 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.script.fields.txt @@ -13,37 +13,38 @@ class org.elasticsearch.script.Field { String getName() boolean isEmpty() List getValues() + def getValue(def) org.elasticsearch.script.LongField asLongField() - long asLong(long) + long getLong(long) org.elasticsearch.script.DoubleField asDoubleField() - double asDouble(double) + double getDouble(double) org.elasticsearch.script.BigIntegerField asBigIntegerField() - BigInteger asBigInteger(BigInteger) + BigInteger getBigInteger(BigInteger) org.elasticsearch.script.StringField asStringField() - String asString(String) + String getString(String) org.elasticsearch.script.DefField asDefField() - def asDef(def) + def getDef(def) } class org.elasticsearch.script.LongField { - long getValue(long) + long getLong(long) } class org.elasticsearch.script.DoubleField { - double getValue(double) + double getDouble(double) } class org.elasticsearch.script.BigIntegerField { - BigInteger getValue(BigInteger) + BigInteger getBigInteger(BigInteger) } class org.elasticsearch.script.StringField { - String getValue(String) + String getString(String) } class org.elasticsearch.script.DefField { diff --git a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml index 29f03a9122d11..7a75ae369672d 100644 --- a/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml +++ b/modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/40_fields_api.yml @@ -48,6 +48,48 @@ setup: - match: { hits.hits.0._id: d2 } - match: { hits.hits.1._id: d3 } - match: { hits.hits.2._id: d1 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + sort: + _script: + type: number + script: + source: "field('dval').asDoubleField().getDouble(3)" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d2 } + - match: { hits.hits.1._id: d3 } + - match: { hits.hits.2._id: d1 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + sort: + _script: + type: number + script: + source: "field('dval').getDouble(3)" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d2 } + - match: { hits.hits.1._id: d3 } + - match: { hits.hits.2._id: d1 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + sort: + _script: + type: number + script: + source: "field('dval').getValue(3)" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d2 } + - match: { hits.hits.1._id: d3 } + - match: { hits.hits.2._id: d1 } - do: search: rest_total_hits_as_int: true @@ -62,6 +104,48 @@ setup: - match: { hits.hits.0._id: d3 } - match: { hits.hits.1._id: d1 } - match: { hits.hits.2._id: d2 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + sort: + _script: + type: string + script: + source: "field('sval.keyword').asStringField().getString('g')" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d3 } + - match: { hits.hits.1._id: d1 } + - match: { hits.hits.2._id: d2 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + sort: + _script: + type: string + script: + source: "field('sval.keyword').getString('g')" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d3 } + - match: { hits.hits.1._id: d1 } + - match: { hits.hits.2._id: d2 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + sort: + _script: + type: string + script: + source: "field('sval.keyword').getValue('g')" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d3 } + - match: { hits.hits.1._id: d1 } + - match: { hits.hits.2._id: d2 } --- "script score fields api": @@ -106,6 +190,48 @@ setup: - match: { hits.hits.0._id: d1 } - match: { hits.hits.1._id: d3 } - match: { hits.hits.2._id: d2 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + script_score: + query: {match_all: {} } + script: + source: "field('dval').asDoubleField().getDouble(3)" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d1 } + - match: { hits.hits.1._id: d3 } + - match: { hits.hits.2._id: d2 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + script_score: + query: {match_all: {} } + script: + source: "field('dval').getDouble(3)" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d1 } + - match: { hits.hits.1._id: d3 } + - match: { hits.hits.2._id: d2 } + - do: + search: + rest_total_hits_as_int: true + index: test + body: + query: + script_score: + query: {match_all: {} } + script: + source: "field('dval').getValue(3)" + - match: { hits.total: 3 } + - match: { hits.hits.0._id: d1 } + - match: { hits.hits.1._id: d3 } + - match: { hits.hits.2._id: d2 } --- "filter script fields api": @@ -147,6 +273,29 @@ setup: script: "field('dval').asDoubleField().getValue(6) > 6" - match: { hits.total: 1 } - match: { hits.hits.0._id: d1 } + - do: + search: + rest_total_hits_as_int: true + body: + query: + bool: + filter: + script: + script: "field('dval').getDouble(6) > 6" + - match: { hits.total: 1 } + - match: { hits.hits.0._id: d1 } + - match: { hits.hits.0._id: d1 } + - do: + search: + rest_total_hits_as_int: true + body: + query: + bool: + filter: + script: + script: "field('dval').getValue(6) > 6" + - match: { hits.total: 1 } + - match: { hits.hits.0._id: d1 } - do: search: rest_total_hits_as_int: true @@ -158,3 +307,36 @@ setup: script: "field('sval.keyword').asDefField().getValue('b') == 'a'" - match: { hits.total: 1 } - match: { hits.hits.0._id: d3 } + - do: + search: + rest_total_hits_as_int: true + body: + query: + bool: + filter: + script: + script: "field('sval.keyword').getDef('b') == 'a'" + - match: { hits.total: 1 } + - match: { hits.hits.0._id: d3 } + - do: + search: + rest_total_hits_as_int: true + body: + query: + bool: + filter: + script: + script: "field('sval.keyword').getValue('b') == 'a'" + - match: { hits.total: 1 } + - match: { hits.hits.0._id: d3 } + - do: + search: + rest_total_hits_as_int: true + body: + query: + bool: + filter: + script: + script: "field('sval.keyword').getString('b') == 'a'" + - match: { hits.total: 1 } + - match: { hits.hits.0._id: d3 } diff --git a/server/src/main/java/org/elasticsearch/script/AbstractField.java b/server/src/main/java/org/elasticsearch/script/AbstractField.java index d4bf1d3302360..74850f599b138 100644 --- a/server/src/main/java/org/elasticsearch/script/AbstractField.java +++ b/server/src/main/java/org/elasticsearch/script/AbstractField.java @@ -61,8 +61,8 @@ public LongField asLongField() { } @Override - public long asLong(long defaultValue) { - return asLongField().getValue(defaultValue); + public long getLong(long defaultValue) { + return asLongField().getLong(defaultValue); } @Override @@ -75,8 +75,8 @@ public DoubleField asDoubleField() { } @Override - public double asDouble(double defaultValue) { - return asDoubleField().getValue(defaultValue); + public double getDouble(double defaultValue) { + return asDoubleField().getDouble(defaultValue); } @Override @@ -89,8 +89,8 @@ public BigIntegerField asBigIntegerField() { } @Override - public BigInteger asBigInteger(BigInteger defaultValue) { - return asBigIntegerField().getValue(defaultValue); + public BigInteger getBigInteger(BigInteger defaultValue) { + return asBigIntegerField().getBigInteger(defaultValue); } @Override @@ -103,8 +103,8 @@ public StringField asStringField() { } @Override - public String asString(String defaultValue) { - return asStringField().getValue(defaultValue); + public String getString(String defaultValue) { + return asStringField().getString(defaultValue); } @Override @@ -117,7 +117,7 @@ public DefField asDefField() { } @Override - public Object asDef(Object defaultValue) { + public Object getDef(Object defaultValue) { return asDefField().getValue(defaultValue); } } diff --git a/server/src/main/java/org/elasticsearch/script/BigIntegerField.java b/server/src/main/java/org/elasticsearch/script/BigIntegerField.java index dc723b8a75cc6..871a0881730fa 100644 --- a/server/src/main/java/org/elasticsearch/script/BigIntegerField.java +++ b/server/src/main/java/org/elasticsearch/script/BigIntegerField.java @@ -19,8 +19,17 @@ public BigIntegerField(String name, FieldValues.BigIntegers values) { super(name, values); } - public BigInteger getValue(BigInteger defaultValue) { - if (values.isEmpty()) { + @Override + public Object getValue(Object defaultValue) { + if (isEmpty()) { + return defaultValue; + } + return values.getBigInteger(0); + } + + @Override + public BigInteger getBigInteger(BigInteger defaultValue) { + if (isEmpty()) { return defaultValue; } return values.getBigInteger(0); @@ -35,9 +44,4 @@ protected List getFieldValues() { public BigIntegerField asBigIntegerField() { return this; } - - @Override - public BigInteger asBigInteger(BigInteger defaultValue) { - return getValue(defaultValue); - } } diff --git a/server/src/main/java/org/elasticsearch/script/DefField.java b/server/src/main/java/org/elasticsearch/script/DefField.java index 572cfb76ac600..aadf76bd1f9a8 100644 --- a/server/src/main/java/org/elasticsearch/script/DefField.java +++ b/server/src/main/java/org/elasticsearch/script/DefField.java @@ -18,6 +18,7 @@ public DefField(String name, FieldValues.Objects values) { super(name, values); } + @Override public Object getValue(Object defaultValue) { if (isEmpty()) { return defaultValue; @@ -25,6 +26,11 @@ public Object getValue(Object defaultValue) { return values.getObject(0); } + @Override + public Object getDef(Object defaultValue) { + return getValue(defaultValue); + } + @Override protected List getFieldValues() { return values.getObjects(); @@ -34,9 +40,4 @@ protected List getFieldValues() { public DefField asDefField() { return this; } - - @Override - public Object asDef(Object defaultValue) { - return getValue(defaultValue); - } } diff --git a/server/src/main/java/org/elasticsearch/script/DocValuesDocReader.java b/server/src/main/java/org/elasticsearch/script/DocValuesDocReader.java index ebf8fc8e596d0..0cdd07ea81c6e 100644 --- a/server/src/main/java/org/elasticsearch/script/DocValuesDocReader.java +++ b/server/src/main/java/org/elasticsearch/script/DocValuesDocReader.java @@ -39,7 +39,7 @@ public Field field(String fieldName) { if (doc.containsKey(fieldName) == false) { return new EmptyField(fieldName); } - return new DocValuesField<>(fieldName, doc.get(fieldName)); + return new DocValuesField(fieldName, (ScriptDocValues) doc.get(fieldName)); } diff --git a/server/src/main/java/org/elasticsearch/script/DocValuesField.java b/server/src/main/java/org/elasticsearch/script/DocValuesField.java index 19cb5c5ea1877..31b5db591660d 100644 --- a/server/src/main/java/org/elasticsearch/script/DocValuesField.java +++ b/server/src/main/java/org/elasticsearch/script/DocValuesField.java @@ -11,27 +11,36 @@ import org.elasticsearch.index.fielddata.ScriptDocValues; import java.util.List; +import java.util.Objects; /** * Fields API accessor for DocValues, users must currently call their own casting methods using * {@code asField}. This is the initial entry point to that casting system. Could be replaced * with field type inspection via {@code SearchLookup} in the future. */ -public class DocValuesField extends AbstractField> { - protected final ScriptDocValues scriptDocValues; +public class DocValuesField extends AbstractField { + protected final ScriptDocValues scriptDocValues; - public DocValuesField(String name, ScriptDocValues scriptDocValues) { + public DocValuesField(String name, ScriptDocValues scriptDocValues) { super(name, scriptDocValues); this.scriptDocValues = scriptDocValues; } @Override - public List getValues() { + public Object getValue(Object defaultValue) { + if (values.isEmpty()) { + return defaultValue; + } + return scriptDocValues.getObject(0); + } + + @Override + public List getValues() { return scriptDocValues; } @Override - protected List getFieldValues() { + protected List getFieldValues() { return scriptDocValues; } } diff --git a/server/src/main/java/org/elasticsearch/script/DoubleField.java b/server/src/main/java/org/elasticsearch/script/DoubleField.java index 1c73d1eb86e80..3d0e937233682 100644 --- a/server/src/main/java/org/elasticsearch/script/DoubleField.java +++ b/server/src/main/java/org/elasticsearch/script/DoubleField.java @@ -19,7 +19,16 @@ public DoubleField(String name, FieldValues.Doubles values) { super(name, values); } - public double getValue(double defaultValue) { + @Override + public Object getValue(Object defaultValue) { + if (isEmpty()) { + return defaultValue; + } + return values.getDouble(0); + } + + @Override + public double getDouble(double defaultValue) { if (isEmpty()) { return defaultValue; } @@ -35,9 +44,4 @@ protected List getFieldValues() { public DoubleField asDoubleField() { return this; } - - @Override - public double asDouble(double defaultValue) { - return getValue(defaultValue); - } } diff --git a/server/src/main/java/org/elasticsearch/script/EmptyField.java b/server/src/main/java/org/elasticsearch/script/EmptyField.java index 496df67facd5d..a43587393082b 100644 --- a/server/src/main/java/org/elasticsearch/script/EmptyField.java +++ b/server/src/main/java/org/elasticsearch/script/EmptyField.java @@ -37,13 +37,18 @@ public List getValues() { return Collections.emptyList(); } + @Override + public Object getValue(Object defaultValue) { + return defaultValue; + } + @Override public LongField asLongField() { return new LongField(name, EmptyFieldValues.LONG); } @Override - public long asLong(long defaultValue) { + public long getLong(long defaultValue) { return defaultValue; } @@ -53,7 +58,7 @@ public DoubleField asDoubleField() { } @Override - public double asDouble(double defaultValue) { + public double getDouble(double defaultValue) { return defaultValue; } @@ -63,7 +68,7 @@ public BigIntegerField asBigIntegerField() { } @Override - public BigInteger asBigInteger(BigInteger defaultValue) { + public BigInteger getBigInteger(BigInteger defaultValue) { return defaultValue; } @@ -73,7 +78,7 @@ public StringField asStringField() { } @Override - public String asString(String defaultValue) { + public String getString(String defaultValue) { return defaultValue; } @@ -83,7 +88,7 @@ public DefField asDefField() { } @Override - public Object asDef(Object defaultValue) { + public Object getDef(Object defaultValue) { return defaultValue; } } diff --git a/server/src/main/java/org/elasticsearch/script/Field.java b/server/src/main/java/org/elasticsearch/script/Field.java index b45d4dacab524..b8f540a9f4339 100644 --- a/server/src/main/java/org/elasticsearch/script/Field.java +++ b/server/src/main/java/org/elasticsearch/script/Field.java @@ -32,32 +32,34 @@ public interface Field { /** Get all values of a multivalued field. If {@code isEmpty()} this returns an empty list. */ List getValues(); - /* This is left to the duck-typed implementing classes */ - // T getValue(T defaultValue); + /** Get first value, if it exists, or return {@code defaultValue}. Returns def type, use the {@code get} + * to avoid boxing + */ + Object getValue(Object defaultValue); /** Treat the current {@code Field} as if it held primitive {@code long}s, throws {@code IllegalStateException} if impossible */ LongField asLongField(); - long asLong(long defaultValue); + long getLong(long defaultValue); /** Treat the current {@code Field} as if it held primitive {@code double}s, {@code throws IllegalStateException} if impossible */ DoubleField asDoubleField(); - double asDouble(double defaultValue); + double getDouble(double defaultValue); /** * Treat the current {@code Field} as if it held {@code BigInteger}, throws {@code IllegalStateException} if underlying type does not * naturally contain {@code BigInteger}s. If underlying values fit in a signed {@code long}, {@code asLongField} should be used. **/ BigIntegerField asBigIntegerField(); - BigInteger asBigInteger(BigInteger defaultValue); + BigInteger getBigInteger(BigInteger defaultValue); /** Treat the current {@code Field} as if it held {@code Strings}s, throws {@code IllegalStateException} if impossible */ StringField asStringField(); - String asString(String defaultValue); + String getString(String defaultValue); /** * Treat the current Field as if it held {@code Object}. This is a way to break out of the Fields API and * allow the caller to do their own casting if necessary. */ DefField asDefField(); - Object asDef(Object defaultValue); + Object getDef(Object defaultValue); } diff --git a/server/src/main/java/org/elasticsearch/script/LongField.java b/server/src/main/java/org/elasticsearch/script/LongField.java index 439b97a78d7c6..6752e3afd0266 100644 --- a/server/src/main/java/org/elasticsearch/script/LongField.java +++ b/server/src/main/java/org/elasticsearch/script/LongField.java @@ -19,7 +19,16 @@ public LongField(String name, FieldValues.Longs values) { super(name, values); } - public long getValue(long defaultValue) { + @Override + public Object getValue(Object defaultValue) { + if (isEmpty()) { + return defaultValue; + } + return values.getLong(0); + } + + @Override + public long getLong(long defaultValue) { if (isEmpty()) { return defaultValue; } @@ -35,9 +44,4 @@ protected List getFieldValues() { public LongField asLongField() { return this; } - - @Override - public long asLong(long defaultValue) { - return getValue(defaultValue); - } } diff --git a/server/src/main/java/org/elasticsearch/script/StringField.java b/server/src/main/java/org/elasticsearch/script/StringField.java index 68264e6467f92..1bbab0d9ca511 100644 --- a/server/src/main/java/org/elasticsearch/script/StringField.java +++ b/server/src/main/java/org/elasticsearch/script/StringField.java @@ -15,8 +15,17 @@ public StringField(String name, FieldValues.Strings values) { super(name, values); } - public String getValue(String defaultValue) { - if (values.isEmpty()) { + @Override + public Object getValue(Object defaultValue) { + if (isEmpty()) { + return defaultValue; + } + return values.getString(0); + } + + @Override + public String getString(String defaultValue) { + if (isEmpty()) { return defaultValue; } return values.getString(0); @@ -31,9 +40,4 @@ protected List getFieldValues() { public StringField asStringField() { return this; } - - @Override - public String asString(String defaultValue) { - return getValue(defaultValue); - } } diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/unsigned_long/50_script_values.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/unsigned_long/50_script_values.yml index 7f7a72d2ad51a..1d5a35986b705 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/unsigned_long/50_script_values.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/unsigned_long/50_script_values.yml @@ -40,7 +40,39 @@ setup: order: desc type: number script: - source: "field('ul').asBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" + source: "field('ul').getBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" + + - match: { hits.hits.0.sort: [1.8446744073709552E19] } + - match: { hits.hits.1.sort: [1.8446744073709552E19] } + - match: { hits.hits.2.sort: [9.223372036854776E18] } + - match: { hits.hits.3.sort: [9.223372036854776E18] } + - match: { hits.hits.4.sort: [0.0] } + - do: + search: + index: test1 + body: + sort: + _script: + order: desc + type: number + script: + source: "field('ul').getValue(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" + + - match: { hits.hits.0.sort: [1.8446744073709552E19] } + - match: { hits.hits.1.sort: [1.8446744073709552E19] } + - match: { hits.hits.2.sort: [9.223372036854776E18] } + - match: { hits.hits.3.sort: [9.223372036854776E18] } + - match: { hits.hits.4.sort: [0.0] } + - do: + search: + index: test1 + body: + sort: + _script: + order: desc + type: number + script: + source: "field('ul').asBigIntegerField().getValue(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" - match: { hits.hits.0.sort: [1.8446744073709552E19] } - match: { hits.hits.1.sort: [1.8446744073709552E19] } @@ -64,6 +96,34 @@ setup: - match: { hits.total.value: 2 } - match: { hits.hits.0._id: "4" } - match: { hits.hits.1._id: "5" } + - do: + search: + index: test1 + body: + query: + bool: + filter: + script: + script: + source: "field('ul').getBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue() > 10E18" + sort: [ { ul: asc } ] + - match: { hits.total.value: 2 } + - match: { hits.hits.0._id: "4" } + - match: { hits.hits.1._id: "5" } + - do: + search: + index: test1 + body: + query: + bool: + filter: + script: + script: + source: "field('ul').getValue(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue() > 10E18" + sort: [ { ul: asc } ] + - match: { hits.total.value: 2 } + - match: { hits.hits.0._id: "4" } + - match: { hits.hits.1._id: "5" } - do: search: @@ -88,6 +148,28 @@ setup: script_score: query: {match_all: {}} script: - source: "field('ul').asBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" + source: "field('ul').getBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" + + - match: { hits.total.value: 5 } + - do: + search: + index: test1 + body: + query: + script_score: + query: {match_all: {}} + script: + source: "field('ul').asBigIntegerField().getBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" + + - match: { hits.total.value: 5 } + - do: + search: + index: test1 + body: + query: + script_score: + query: {match_all: {}} + script: + source: "field('ul').getValue(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" - match: { hits.total.value: 5 } From c698d49a1a423107e1786e472b707b5711a59e79 Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Tue, 10 Aug 2021 12:07:15 -0500 Subject: [PATCH 5/6] Script: unused import --- .../src/main/java/org/elasticsearch/script/DocValuesField.java | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/java/org/elasticsearch/script/DocValuesField.java b/server/src/main/java/org/elasticsearch/script/DocValuesField.java index 31b5db591660d..1a92d1c8a7ad0 100644 --- a/server/src/main/java/org/elasticsearch/script/DocValuesField.java +++ b/server/src/main/java/org/elasticsearch/script/DocValuesField.java @@ -11,7 +11,6 @@ import org.elasticsearch.index.fielddata.ScriptDocValues; import java.util.List; -import java.util.Objects; /** * Fields API accessor for DocValues, users must currently call their own casting methods using From fe2b4ee88b7cdfece64062ab47e38fe65f3f1b5d Mon Sep 17 00:00:00 2001 From: Stuart Tettemer Date: Tue, 10 Aug 2021 12:43:53 -0500 Subject: [PATCH 6/6] Script: indent --- .../org/elasticsearch/script/AbstractField.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/script/AbstractField.java b/server/src/main/java/org/elasticsearch/script/AbstractField.java index 74850f599b138..c21b1ffaa70e5 100644 --- a/server/src/main/java/org/elasticsearch/script/AbstractField.java +++ b/server/src/main/java/org/elasticsearch/script/AbstractField.java @@ -54,7 +54,8 @@ public boolean isEmpty() { @Override public LongField asLongField() { if (values instanceof FieldValues.Longs == false) { - throw new IllegalStateException("This Field cannot be converted to a LongField due to the underlying data"); + throw new IllegalStateException("This Field cannot be converted to a LongField due to the underlying data [" + + values.getClass().getSimpleName() + "]"); } return new LongField(name, (FieldValues.Longs) values); @@ -68,7 +69,8 @@ public long getLong(long defaultValue) { @Override public DoubleField asDoubleField() { if (values instanceof FieldValues.Doubles == false) { - throw new IllegalStateException("This Field cannot be converted to a DoubleValues due to the underlying data"); + throw new IllegalStateException("This Field cannot be converted to a DoubleValues due to the underlying data [" + + values.getClass().getSimpleName() + "]"); } return new DoubleField(name, (FieldValues.Doubles) values); @@ -82,7 +84,8 @@ public double getDouble(double defaultValue) { @Override public BigIntegerField asBigIntegerField() { if (values instanceof FieldValues.BigIntegers == false) { - throw new IllegalStateException("This Field cannot be converted to a BigIntegerField due to the underlying data"); + throw new IllegalStateException("This Field cannot be converted to a BigIntegerField due to the underlying data [" + + values.getClass().getSimpleName() + "]"); } return new BigIntegerField(name, (FieldValues.BigIntegers) values); @@ -96,7 +99,8 @@ public BigInteger getBigInteger(BigInteger defaultValue) { @Override public StringField asStringField() { if (values instanceof FieldValues.Strings == false) { - throw new IllegalStateException("This Field cannot be converted to a StringField due to the underlying data"); + throw new IllegalStateException("This Field cannot be converted to a StringField due to the underlying data [" + + values.getClass().getSimpleName() + "]"); } return new StringField(name, (FieldValues.Strings) values); @@ -110,7 +114,8 @@ public String getString(String defaultValue) { @Override public DefField asDefField() { if (values instanceof FieldValues.Objects == false) { - throw new IllegalStateException("This Field cannot be converted to a ObjectField due to the underlying data"); + throw new IllegalStateException("This Field cannot be converted to a ObjectField due to the underlying data [" + + values.getClass().getSimpleName() + "]"); } return new DefField(name, (FieldValues.Objects) values);