From 22bbf10da16240b1f162356f81c3280c2416f704 Mon Sep 17 00:00:00 2001 From: Mayya Sharipova Date: Wed, 18 Apr 2018 15:28:05 -0400 Subject: [PATCH] Handle missing and multiple values in script Previously in script for numeric fields, there was no way to check if a document is missing a value. Also certain operations on multiple- values fields were missing. This PR adds the following: - add the following functions for multiple-valued numeric fields: doc['field'].min returns the minumum amoung values doc['field'].max returns the maximum amoung values doc['field'].sum returns the sum of amoung values doc['field'].avg returns the average of values - return null for doc['field'] if a document is missing a 'field1': Now we can do this: if (doc['field'] == null) {return -1;} return doc['field'].value; or doc['field']?.value ?: -1 This new behaviour will only work is the following system property is set: `export ES_JAVA_OPTS="-Des.script.null_for_missing_value=true"' Closes #29286 --- docs/CHANGELOG.asciidoc | 4 + .../painless-getting-started.asciidoc | 26 ++++ modules/lang-painless/build.gradle | 1 + .../painless/spi/org.elasticsearch.txt | 8 ++ .../test/painless/20_scriptfield.yml | 2 +- .../70_missing_and_multiple_values.yml | 117 ++++++++++++++++++ .../index/fielddata/ScriptDocValues.java | 101 +++++++++++++-- .../index/mapper/IpFieldMapper.java | 6 +- .../java/org/elasticsearch/node/Node.java | 4 + .../elasticsearch/script/ScriptModule.java | 3 + .../search/lookup/LeafDocLookup.java | 7 +- .../fielddata/ScriptDocValuesLongsTests.java | 33 +++++ .../aggregations/bucket/MinDocCountIT.java | 6 +- .../aggregations/metrics/CardinalityIT.java | 7 +- .../search/functionscore/FunctionScoreIT.java | 2 +- 15 files changed, 303 insertions(+), 24 deletions(-) create mode 100644 modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_missing_and_multiple_values.yml diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index 0a9312ce9f331..fda5bbe7e473a 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -41,6 +41,10 @@ written to by an older Elasticsearch after writing to it with a newer Elasticsea Do not ignore request analysis/similarity settings on index resize operations when the source index already contains such settings ({pull}30216[#30216]) +=== Deprecations + +Returning 0 for missing numeric fields in script is deprecated. PR: + === Regressions === Known Issues diff --git a/docs/painless/painless-getting-started.asciidoc b/docs/painless/painless-getting-started.asciidoc index b47b417c793e5..e87ca5b9dcad0 100644 --- a/docs/painless/painless-getting-started.asciidoc +++ b/docs/painless/painless-getting-started.asciidoc @@ -68,6 +68,7 @@ GET hockey/_search ---------------------------------------------------------------- // CONSOLE + Alternatively, you could do the same thing using a script field instead of a function score: [source,js] @@ -119,6 +120,31 @@ GET hockey/_search ---------------------------------------------------------------- // CONSOLE +[float] +===== Missing values + +Currently by default, if a document is missing a numeric field `field`, +`doc['field'].value` returns `0` for this document. This default behaviour +will be changed in the next major version of elasticsearch. Starting from 7.0, +if a document is missing a field `field`, `doc['field']` for this document +will return `null`. From 6.4 version, you can set a system property +`export ES_JAVA_OPTS="-Des.script.null_for_missing_value=true"' on a node +to make this node's behaviour compatible with the future major version. +Otherwise, every time the node starts a deprecation warning will remind you +about this forthcoming change in 7.x. + + +===== Multiple values + +There is a number of operations designed for numeric fields, +if a document has multiple values in such a field: + +- `doc['field'].min` - gets the minimum value among values +- `doc['field'].max` - gets the maximum value among values +- `doc['field'].sum` - gets the sum of all values +- `doc['field'].avg` - gets the average of all values + + [float] ==== Updating Fields with Painless diff --git a/modules/lang-painless/build.gradle b/modules/lang-painless/build.gradle index d287d7ee02378..6d759a497f5b2 100644 --- a/modules/lang-painless/build.gradle +++ b/modules/lang-painless/build.gradle @@ -25,6 +25,7 @@ esplugin { } integTestCluster { + systemProperty 'es.script.null_for_missing_value', 'true' module project.project(':modules:mapper-extras') } diff --git a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt index 51a1b7cecb3f8..9ae832579f9f4 100644 --- a/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt +++ b/modules/lang-painless/src/main/resources/org/elasticsearch/painless/spi/org.elasticsearch.txt @@ -73,6 +73,10 @@ class org.elasticsearch.index.fielddata.ScriptDocValues$Strings { class org.elasticsearch.index.fielddata.ScriptDocValues$Longs { Long get(int) long getValue() + long getMin() + long getMax() + long getSum() + double getAvg() List getValues() org.joda.time.ReadableDateTime getDate() List getDates() @@ -89,6 +93,10 @@ class org.elasticsearch.index.fielddata.ScriptDocValues$Dates { class org.elasticsearch.index.fielddata.ScriptDocValues$Doubles { Double get(int) double getValue() + double getMin() + double getMax() + double getSum() + double getAvg() List getValues() } diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml index e498a1737576e..27f6678e31ed3 100644 --- a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/20_scriptfield.yml @@ -95,7 +95,7 @@ setup: script_fields: bar: script: - source: "(doc['missing'].value?.length() ?: 0) + params.x;" + source: "(doc['missing']?.value?.length() ?: 0) + params.x;" params: x: 5 diff --git a/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_missing_and_multiple_values.yml b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_missing_and_multiple_values.yml new file mode 100644 index 0000000000000..11233f0b9d58a --- /dev/null +++ b/modules/lang-painless/src/test/resources/rest-api-spec/test/painless/70_missing_and_multiple_values.yml @@ -0,0 +1,117 @@ +setup: + - do: + indices.create: + index: test + body: + settings: + number_of_shards: 1 + mappings: + _doc: + properties: + dval: + type: double + lval: + type: long + + - do: + index: + index: test + type: _doc + id: 1 + body: { "dval": 5.5, "lval": 5 } + + - do: + index: + index: test + type: _doc + id: 2 + body: { "dval": [5.5, 3.5, 4.5] } + + + - do: + index: + index: test + type: _doc + id: 3 + body: { "lval": [5, 3, 4] } + + - do: + indices.refresh: {} + +--- +"check double and long values: missing values and operations on multiple values": + - skip: + version: " - 6.3.99" + reason: Handling missing values and operations on multiple values were added from 6.4 + + - do: + search: + body: + script_fields: + val_dval: + script: + source: "doc['dval']?.value ?: -1.0" + min_dval: + script: + source: "doc['dval']?.min ?: -1.0" + max_dval: + script: + source: "doc['dval']?.max ?: -1.0" + sum_dval: + script: + source: "doc['dval']?.sum ?: -1.0" + avg_dval: + script: + source: "doc['dval']?.avg ?: -1.0" + val_lval: + script: + source: "doc['lval']?.value ?: -1" + min_lval: + script: + source: "doc['lval']?.min ?: -1" + max_lval: + script: + source: "doc['lval']?.max ?: -1" + sum_lval: + script: + source: "doc['lval']?.sum ?: -1" + avg_lval: + script: + source: "doc['lval']?.avg ?: -1" + + - match: { hits.hits.0.fields.val_dval.0: 5.5} + - match: { hits.hits.0.fields.min_dval.0: 5.5} + - match: { hits.hits.0.fields.max_dval.0: 5.5} + - match: { hits.hits.0.fields.sum_dval.0: 5.5} + - match: { hits.hits.0.fields.avg_dval.0: 5.5} + + - match: { hits.hits.0.fields.val_lval.0: 5} + - match: { hits.hits.0.fields.min_lval.0: 5} + - match: { hits.hits.0.fields.max_lval.0: 5} + - match: { hits.hits.0.fields.sum_lval.0: 5} + - match: { hits.hits.0.fields.avg_lval.0: 5} + + - match: { hits.hits.1.fields.val_dval.0: 3.5} + - match: { hits.hits.1.fields.min_dval.0: 3.5} + - match: { hits.hits.1.fields.max_dval.0: 5.5} + - match: { hits.hits.1.fields.sum_dval.0: 13.5} + - match: { hits.hits.1.fields.avg_dval.0: 4.5} + + - match: { hits.hits.1.fields.val_lval.0: -1} + - match: { hits.hits.1.fields.min_lval.0: -1} + - match: { hits.hits.1.fields.max_lval.0: -1} + - match: { hits.hits.1.fields.sum_lval.0: -1} + - match: { hits.hits.1.fields.avg_lval.0: -1} + + - match: { hits.hits.2.fields.val_dval.0: -1.0} + - match: { hits.hits.2.fields.min_dval.0: -1.0} + - match: { hits.hits.2.fields.max_dval.0: -1.0} + - match: { hits.hits.2.fields.sum_dval.0: -1.0} + - match: { hits.hits.2.fields.avg_dval.0: -1.0} + + - match: { hits.hits.2.fields.val_lval.0: 3} + - match: { hits.hits.2.fields.min_lval.0: 3} + - match: { hits.hits.2.fields.max_lval.0: 5} + - match: { hits.hits.2.fields.sum_lval.0: 12} + - match: { hits.hits.2.fields.avg_lval.0: 4} + 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 3d07a0f87aa5e..a2d841d376ecb 100644 --- a/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java +++ b/server/src/main/java/org/elasticsearch/index/fielddata/ScriptDocValues.java @@ -58,7 +58,7 @@ public abstract class ScriptDocValues extends AbstractList { /** * Set the current doc ID. */ - public abstract void setNextDocId(int docId) throws IOException; + public abstract boolean setNextDocId(int docId) throws IOException; /** * Return a copy of the list of the values for the current document. @@ -124,9 +124,10 @@ public Longs(SortedNumericDocValues in) { } @Override - public void setNextDocId(int docId) throws IOException { + public boolean setNextDocId(int docId) throws IOException { this.docId = docId; - if (in.advanceExact(docId)) { + boolean docHasValues = in.advanceExact(docId); + if (docHasValues) { resize(in.docValueCount()); for (int i = 0; i < count; i++) { values[i] = in.nextValue(); @@ -137,6 +138,7 @@ public void setNextDocId(int docId) throws IOException { if (dates != null) { dates.setNextDocId(docId); } + return docHasValues; } /** @@ -159,6 +161,37 @@ public long getValue() { return values[0]; } + public long getMin() { + if (count == 0) { + return 0L; + }; + return values[0]; + } + + public long getMax() { + if (count == 0) { + return 0L; + }; + return values[count - 1]; + } + + public long getSum() { + if (count == 0) { + return 0L; + }; + long sum = 0L; + for (int i = 0; i < count; i++) + sum += values[i]; + return sum; + } + + public double getAvg() { + if (count == 0) { + return 0d; + }; + return getSum() * 1.0/count; + } + @Deprecated public ReadableDateTime getDate() throws IOException { deprecated("getDate on numeric fields is deprecated. Use a date field to get dates."); @@ -285,13 +318,15 @@ public int size() { } @Override - public void setNextDocId(int docId) throws IOException { - if (in.advanceExact(docId)) { + public boolean setNextDocId(int docId) throws IOException { + boolean docHasValues = in.advanceExact(docId); + if (docHasValues) { count = in.docValueCount(); } else { count = 0; } refreshArray(); + return docHasValues; } /** @@ -355,8 +390,9 @@ public Doubles(SortedNumericDoubleValues in) { } @Override - public void setNextDocId(int docId) throws IOException { - if (in.advanceExact(docId)) { + public boolean setNextDocId(int docId) throws IOException { + boolean docHasValues = in.advanceExact(docId); + if (docHasValues) { resize(in.docValueCount()); for (int i = 0; i < count; i++) { values[i] = in.nextValue(); @@ -364,6 +400,7 @@ public void setNextDocId(int docId) throws IOException { } else { resize(0); } + return docHasValues; } /** @@ -386,6 +423,38 @@ public double getValue() { return values[0]; } + public double getMin() { + if (count == 0) { + return 0d; + }; + return values[0]; + } + + public double getMax() { + if (count == 0) { + return 0d; + }; + return values[count - 1]; + } + + public double getSum() { + if (count == 0) { + return 0d; + }; + double sum = 0d; + for (int i = 0; i < count; i++) + sum += values[i]; + return sum; + } + + public double getAvg() { + if (count == 0) { + return 0d; + }; + return getSum() / count; + } + + @Override public Double get(int index) { return values[index]; @@ -408,8 +477,9 @@ public GeoPoints(MultiGeoPointValues in) { } @Override - public void setNextDocId(int docId) throws IOException { - if (in.advanceExact(docId)) { + public boolean setNextDocId(int docId) throws IOException { + boolean docHasValues = in.advanceExact(docId); + if (docHasValues) { resize(in.docValueCount()); for (int i = 0; i < count; i++) { GeoPoint point = in.nextValue(); @@ -418,6 +488,7 @@ public void setNextDocId(int docId) throws IOException { } else { resize(0); } + return docHasValues; } /** @@ -528,8 +599,9 @@ public Booleans(SortedNumericDocValues in) { } @Override - public void setNextDocId(int docId) throws IOException { - if (in.advanceExact(docId)) { + public boolean setNextDocId(int docId) throws IOException { + boolean docHasValues = in.advanceExact(docId); + if (docHasValues) { resize(in.docValueCount()); for (int i = 0; i < count; i++) { values[i] = in.nextValue() == 1; @@ -537,6 +609,7 @@ public void setNextDocId(int docId) throws IOException { } else { resize(0); } + return docHasValues; } /** @@ -584,8 +657,9 @@ abstract static class BinaryScriptDocValues extends ScriptDocValues { } @Override - public void setNextDocId(int docId) throws IOException { - if (in.advanceExact(docId)) { + public boolean setNextDocId(int docId) throws IOException { + boolean docHasValues = in.advanceExact(docId); + if (docHasValues) { resize(in.docValueCount()); for (int i = 0; i < count; i++) { // We need to make a copy here, because BytesBinaryDVAtomicFieldData's SortedBinaryDocValues @@ -596,6 +670,7 @@ public void setNextDocId(int docId) throws IOException { } else { resize(0); } + return docHasValues; } /** diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java index 13e1cfb75f1dd..b5c27fc869a78 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java @@ -251,14 +251,16 @@ public IpScriptDocValues(SortedSetDocValues in) { } @Override - public void setNextDocId(int docId) throws IOException { + public boolean setNextDocId(int docId) throws IOException { count = 0; - if (in.advanceExact(docId)) { + boolean docHasValues = in.advanceExact(docId); + if (docHasValues) { for (long ord = in.nextOrd(); ord != SortedSetDocValues.NO_MORE_ORDS; ord = in.nextOrd()) { ords = ArrayUtil.grow(ords, count + 1); ords[count++] = ord; } } + return docHasValues; } public String getValue() { diff --git a/server/src/main/java/org/elasticsearch/node/Node.java b/server/src/main/java/org/elasticsearch/node/Node.java index ecf5dfc6b24f9..58942e9fb0204 100644 --- a/server/src/main/java/org/elasticsearch/node/Node.java +++ b/server/src/main/java/org/elasticsearch/node/Node.java @@ -719,6 +719,10 @@ public void onTimeout(TimeValue timeout) { writePortsFile("transport", transport.boundAddress()); } + if (!ScriptModule.NULL_FOR_MISSING_VALUE) + logger.warn("Script: returning 0 for missing numeric values is deprecated. " + + "Set system property '-Des.script.null_for_missing_value=true' to make behaviour compatible with future major versions."); + logger.info("started"); pluginsService.filterPlugins(ClusterPlugin.class).forEach(ClusterPlugin::onNodeStarted); diff --git a/server/src/main/java/org/elasticsearch/script/ScriptModule.java b/server/src/main/java/org/elasticsearch/script/ScriptModule.java index 727651be6a565..75290553d6dd4 100644 --- a/server/src/main/java/org/elasticsearch/script/ScriptModule.java +++ b/server/src/main/java/org/elasticsearch/script/ScriptModule.java @@ -30,6 +30,7 @@ import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.ScriptPlugin; +import org.elasticsearch.common.Booleans; /** * Manages building {@link ScriptService}. @@ -51,6 +52,8 @@ public class ScriptModule { TemplateScript.CONTEXT ).collect(Collectors.toMap(c -> c.name, Function.identity())); } + public static final boolean NULL_FOR_MISSING_VALUE = + Booleans.parseBoolean(System.getProperty("es.script.null_for_missing_value", "false")); private final ScriptService scriptService; diff --git a/server/src/main/java/org/elasticsearch/search/lookup/LeafDocLookup.java b/server/src/main/java/org/elasticsearch/search/lookup/LeafDocLookup.java index 33645340c7802..c17f3e4e76529 100644 --- a/server/src/main/java/org/elasticsearch/search/lookup/LeafDocLookup.java +++ b/server/src/main/java/org/elasticsearch/search/lookup/LeafDocLookup.java @@ -25,6 +25,7 @@ import org.elasticsearch.index.fielddata.ScriptDocValues; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.script.ScriptModule; import java.io.IOException; import java.security.AccessController; @@ -91,7 +92,11 @@ public ScriptDocValues run() { localCacheFieldData.put(fieldName, scriptValues); } try { - scriptValues.setNextDocId(docId); + boolean docHasValues = scriptValues.setNextDocId(docId); + if (!docHasValues) return null; + if (ScriptModule.NULL_FOR_MISSING_VALUE) { + if (!docHasValues) return null; + } } catch (IOException e) { throw ExceptionsHelper.convertToElastic(e); } diff --git a/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java b/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java index 8b20e9a9f3a71..a5104b46a8bdb 100644 --- a/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java +++ b/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java @@ -32,6 +32,7 @@ import java.security.Permissions; import java.security.PrivilegedAction; import java.security.ProtectionDomain; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; @@ -128,6 +129,38 @@ public Void run() { "getDates on numeric fields is deprecated. Use a date field to get dates.")); } + public void testLongsMinMaxSumAvg() throws IOException { + long[][] values = new long[between(3, 10)][]; + long[] mins = new long[values.length]; + long[] maxs = new long[values.length]; + long[] sums = new long[values.length]; + double[] avgs = new double[values.length]; + for (int d = 0; d < values.length; d++) { + values[d] = new long[randomBoolean() ? randomBoolean() ? 0 : 1 : between(2, 100)]; + mins[d] = values[d].length > 0 ? Long.MAX_VALUE : 0L; + maxs[d] = values[d].length > 0 ? Long.MIN_VALUE : 0L; + sums[d] = 0L; + for (int i = 0; i < values[d].length; i++) { + values[d][i] = randomLong(); + mins[d] = mins[d] > values[d][i] ? values[d][i] : mins[d]; + maxs[d] = maxs[d] < values[d][i] ? values[d][i] : maxs[d]; + sums[d] += values[d][i]; + } + avgs[d] = values[d].length > 0 ? sums[d] * 1.0/ values[d].length : 0L; + Arrays.sort(values[d]); + } + Longs longs = wrap(values, deprecationMessage -> {fail("unexpected deprecation: " + deprecationMessage);}); + + for (int round = 0; round < 10; round++) { + int d = between(0, values.length - 1); + longs.setNextDocId(d); + assertEquals(mins[d], longs.getMin()); + assertEquals(maxs[d], longs.getMax()); + assertEquals(sums[d], longs.getSum()); + assertEquals(avgs[d], longs.getAvg(), 0.0); + } + } + private Longs wrap(long[][] values, Consumer deprecationCallback) { return new Longs(new AbstractSortedNumericDocValues() { long[] current; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java index 015664109cdfe..84128d120cdd2 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/MinDocCountIT.java @@ -85,19 +85,19 @@ protected Map, Object>> pluginScripts() { scripts.put("doc['d'].values", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Doubles value = (ScriptDocValues.Doubles) doc.get("d"); - return value.getValues(); + if (value != null) { return value.getValues(); } else return null; }); scripts.put("doc['l'].values", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Longs value = (ScriptDocValues.Longs) doc.get("l"); - return value.getValues(); + if (value != null) { return value.getValues(); } else return null; }); scripts.put("doc['s'].values", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Strings value = (ScriptDocValues.Strings) doc.get("s"); - return value.getValues(); + if (value != null) { return value.getValues(); } else return null; }); return scripts; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityIT.java index b8b33b97e4d00..96ac161c52230 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/CardinalityIT.java @@ -76,7 +76,7 @@ protected Map, Object>> pluginScripts() { scripts.put("doc['str_values'].values", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Strings strValue = (ScriptDocValues.Strings) doc.get("str_values"); - return strValue.getValues(); + if (strValue != null) { return strValue.getValues(); } else return null; }); scripts.put("doc[' + singleNumericField() + '].value", vars -> { @@ -86,7 +86,8 @@ protected Map, Object>> pluginScripts() { scripts.put("doc[' + multiNumericField(false) + '].values", vars -> { Map doc = (Map) vars.get("doc"); - return ((ScriptDocValues) doc.get(multiNumericField(false))).getValues(); + ScriptDocValues numValue = (ScriptDocValues) doc.get(multiNumericField(false)); + if (numValue != null) { return numValue.getValues(); } else return null; }); return scripts; @@ -129,7 +130,7 @@ public void setupSuiteScopeCluster() throws Exception { .endObject() .endObject().endObject().endObject()).execute().actionGet(); - numDocs = randomIntBetween(2, 100); + numDocs = randomIntBetween(2, 3); precisionThreshold = randomIntBetween(0, 1 << randomInt(20)); IndexRequestBuilder[] builders = new IndexRequestBuilder[(int) numDocs]; for (int i = 0; i < numDocs; ++i) { diff --git a/server/src/test/java/org/elasticsearch/search/functionscore/FunctionScoreIT.java b/server/src/test/java/org/elasticsearch/search/functionscore/FunctionScoreIT.java index 0e92aba2a8552..c476bbb6efc0a 100644 --- a/server/src/test/java/org/elasticsearch/search/functionscore/FunctionScoreIT.java +++ b/server/src/test/java/org/elasticsearch/search/functionscore/FunctionScoreIT.java @@ -83,7 +83,7 @@ protected Map, Object>> pluginScripts() { scripts.put("doc['random_score']", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Doubles randomScore = (ScriptDocValues.Doubles) doc.get("random_score"); - return randomScore.getValue(); + if (randomScore != null) {return randomScore.getValue();} else return 0; }); return scripts; }