diff --git a/docs/painless/painless-getting-started.asciidoc b/docs/painless/painless-getting-started.asciidoc index 2cf91666ba48d..47f8bf03c5f0a 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,27 @@ GET hockey/_search ---------------------------------------------------------------- // CONSOLE + +[float] +===== Missing values + +If you request the value from a field `field` that isn’t in +the document, `doc['field'].value` for this document returns: + +- `0` if a `field` has a numeric datatype (long, double etc.) +- `false` is a `field` has a boolean datatype +- epoch date if a `field` has a date datatype +- `null` if a `field` has a string datatype +- `null` if a `field` has a geo datatype +- `""` if a `field` has a binary datatype + +IMPORTANT: Starting in 7.0, `doc['field']` returns a value of null if +the field is missing in a document. To enable this behavior now, +set a <> +`-Des.script.null_for_missing_value=true` on a node. If you do not enable +this behavior, a deprecation warning is logged on start up. + + [float] ==== Updating Fields with Painless @@ -357,6 +379,16 @@ Note: all of the `_update_by_query` examples above could really do with a as using any other query because script queries aren't able to use the inverted index to limit the documents that they have to check. +[[multi-value-field-operations]] +=== Multi-value field operations + +Painless supports the following operations for multi-value numeric fields: + +- `doc['field'].min` - gets the minimum value +- `doc['field'].max` - gets the maximum value +- `doc['field'].sum` - gets the sum of the values +- `doc['field'].avg` - gets the average of the values + [[modules-scripting-painless-dispatch]] === How painless dispatches functions diff --git a/modules/lang-painless/build.gradle b/modules/lang-painless/build.gradle index d287d7ee02378..a7d927b6215f5 100644 --- a/modules/lang-painless/build.gradle +++ b/modules/lang-painless/build.gradle @@ -17,7 +17,7 @@ * under the License. */ -import org.apache.tools.ant.types.Path +import org.elasticsearch.gradle.test.RestIntegTestTask esplugin { description 'An easy, safe and fast scripting language for Elasticsearch' @@ -28,6 +28,15 @@ integTestCluster { module project.project(':modules:mapper-extras') } +Task additionalClusterTest = tasks.create( + name: "additionalClusterTest", type: RestIntegTestTask) +additionalClusterTestCluster { + systemProperty 'es.script.null_for_missing_value', 'true' + distribution = 'integ-test-zip' + module project // add the lang-painless module itself + module project.project(':modules:mapper-extras') +} + dependencies { compile 'org.antlr:antlr4-runtime:4.5.3' compile 'org.ow2.asm:asm-debug-all:5.1' 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 6495659d9cdc0..fe2df7428d700 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() } @@ -85,6 +89,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..ce5e3ada60eaf --- /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.99.99" + reason: Handling missing values and operations on multiple values were added after these versions + + - do: + search: + body: + script_fields: + val_dval: + script: + source: "doc['dval']?.value ?: 0" + min_dval: + script: + source: "doc['dval']?.min ?: 0" + max_dval: + script: + source: "doc['dval']?.max ?: 0" + sum_dval: + script: + source: "doc['dval']?.sum ?: 0" + avg_dval: + script: + source: "doc['dval']?.avg ?: 0" + val_lval: + script: + source: "doc['lval']?.value ?: 0" + min_lval: + script: + source: "doc['lval']?.min ?: 0" + max_lval: + script: + source: "doc['lval']?.max ?: 0" + sum_lval: + script: + source: "doc['lval']?.sum ?: 0" + avg_lval: + script: + source: "doc['lval']?.avg ?: 0" + + - 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: 0} + - match: { hits.hits.1.fields.min_lval.0: 0} + - match: { hits.hits.1.fields.max_lval.0: 0} + - match: { hits.hits.1.fields.sum_lval.0: 0} + - match: { hits.hits.1.fields.avg_lval.0: 0} + + - match: { hits.hits.2.fields.val_dval.0: 0} + - match: { hits.hits.2.fields.min_dval.0: 0} + - match: { hits.hits.2.fields.max_dval.0: 0} + - match: { hits.hits.2.fields.sum_dval.0: 0} + - match: { hits.hits.2.fields.avg_dval.0: 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/modules/parent-join/build.gradle b/modules/parent-join/build.gradle index 67bcc9d54e8e7..38563e467914b 100644 --- a/modules/parent-join/build.gradle +++ b/modules/parent-join/build.gradle @@ -22,3 +22,6 @@ esplugin { classname 'org.elasticsearch.join.ParentJoinPlugin' hasClientJar = true } +test.configure { + systemProperty 'es.script.null_for_missing_value', 'true' +} diff --git a/modules/percolator/build.gradle b/modules/percolator/build.gradle index db4a716af6513..7be888d377dd3 100644 --- a/modules/percolator/build.gradle +++ b/modules/percolator/build.gradle @@ -36,3 +36,7 @@ dependencyLicenses { compileJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes" compileTestJava.options.compilerArgs << "-Xlint:-deprecation,-rawtypes" + +test.configure { + systemProperty 'es.script.null_for_missing_value', 'true' +} diff --git a/server/build.gradle b/server/build.gradle index 7e880e0dae4d2..2c3ae6e6573e3 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -338,3 +338,7 @@ if (isEclipse == false || project.path == ":server-tests") { check.dependsOn integTest integTest.mustRunAfter test } + +test.configure { + systemProperty 'es.script.null_for_missing_value', 'true' +} 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 dd85e921e4ac5..264498fe20f2a 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. @@ -106,8 +106,9 @@ public Longs(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(); @@ -115,6 +116,7 @@ public void setNextDocId(int docId) throws IOException { } else { resize(0); } + return docHasValues; } /** @@ -133,6 +135,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; + } + @Override public Long get(int index) { return values[index]; @@ -191,13 +224,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; } /** @@ -245,8 +280,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(); @@ -254,6 +290,7 @@ public void setNextDocId(int docId) throws IOException { } else { resize(0); } + return docHasValues; } /** @@ -276,6 +313,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]; @@ -298,8 +367,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(); @@ -308,6 +378,7 @@ public void setNextDocId(int docId) throws IOException { } else { resize(0); } + return docHasValues; } /** @@ -418,8 +489,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; @@ -427,6 +499,7 @@ public void setNextDocId(int docId) throws IOException { } else { resize(0); } + return docHasValues; } /** @@ -474,8 +547,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 @@ -486,6 +560,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 a8ef46b93060e..31b1aaf3ba879 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java @@ -252,14 +252,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/script/ScriptModule.java b/server/src/main/java/org/elasticsearch/script/ScriptModule.java index 583421be8e581..5f29a0a821e23 100644 --- a/server/src/main/java/org/elasticsearch/script/ScriptModule.java +++ b/server/src/main/java/org/elasticsearch/script/ScriptModule.java @@ -27,6 +27,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.elasticsearch.common.Booleans; +import org.elasticsearch.common.logging.DeprecationLogger; +import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.ClusterSettings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.plugins.ScriptPlugin; @@ -57,6 +60,11 @@ public class ScriptModule { ).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 static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(ScriptModule.class)); + private final ScriptService scriptService; public ScriptModule(Settings settings, List scriptPlugins) { @@ -80,6 +88,10 @@ public ScriptModule(Settings settings, List scriptPlugins) { } } } + if (NULL_FOR_MISSING_VALUE == false) + DEPRECATION_LOGGER.deprecated("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."); + scriptService = new ScriptService(settings, Collections.unmodifiableMap(engines), Collections.unmodifiableMap(contexts)); } 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..573aec7e7851c 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,8 @@ public ScriptDocValues run() { localCacheFieldData.put(fieldName, scriptValues); } try { - scriptValues.setNextDocId(docId); + boolean docHasValues = scriptValues.setNextDocId(docId); + if ((docHasValues == false) && ScriptModule.NULL_FOR_MISSING_VALUE) 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 5fd33da27e399..9c02e0acdd4bf 100644 --- a/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java +++ b/server/src/test/java/org/elasticsearch/index/fielddata/ScriptDocValuesLongsTests.java @@ -18,25 +18,11 @@ */ package org.elasticsearch.index.fielddata; - import org.elasticsearch.index.fielddata.ScriptDocValues.Longs; import org.elasticsearch.test.ESTestCase; -import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; -import org.joda.time.ReadableDateTime; import java.io.IOException; -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.PermissionCollection; -import java.security.Permissions; -import java.security.PrivilegedAction; -import java.security.ProtectionDomain; -import java.util.HashSet; -import java.util.Set; -import java.util.function.Consumer; - -import static org.hamcrest.Matchers.containsInAnyOrder; +import java.util.Arrays; public class ScriptDocValuesLongsTests extends ESTestCase { public void testLongs() throws IOException { @@ -66,6 +52,39 @@ public void testLongs() throws IOException { } } + 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); + + 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) { 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..da77f5fc9e82e 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(); + return value == null ? null : value.getValues(); }); scripts.put("doc['l'].values", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Longs value = (ScriptDocValues.Longs) doc.get("l"); - return value.getValues(); + return value == null ? null : value.getValues(); }); scripts.put("doc['s'].values", vars -> { Map doc = (Map) vars.get("doc"); ScriptDocValues.Strings value = (ScriptDocValues.Strings) doc.get("s"); - return value.getValues(); + return value == null ? null : value.getValues(); }); 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..11cca5d57f385 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(); + return strValue == null ? null : strValue.getValues(); }); 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)); + return numValue == null ? null : numValue.getValues(); }); 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..6368933a4289d 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(); + return randomScore == null ? 0 : randomScore.getValue(); }); return scripts; } diff --git a/test/framework/build.gradle b/test/framework/build.gradle index 193fcb30988c6..a543fd81e7c75 100644 --- a/test/framework/build.gradle +++ b/test/framework/build.gradle @@ -73,4 +73,5 @@ precommit.dependsOn namingConventionsMain test.configure { systemProperty 'tests.gradle_index_compat_versions', bwcVersions.indexCompatible.join(',') systemProperty 'tests.gradle_wire_compat_versions', bwcVersions.wireCompatible.join(',') + systemProperty 'es.script.null_for_missing_value', 'true' } diff --git a/x-pack/plugin/security/build.gradle b/x-pack/plugin/security/build.gradle index 12533a389b5f1..e4b24343c4a37 100644 --- a/x-pack/plugin/security/build.gradle +++ b/x-pack/plugin/security/build.gradle @@ -225,6 +225,7 @@ test { * other if we allow them to set the number of available processors as it's set-once in Netty. */ systemProperty 'es.set.netty.runtime.available.processors', 'false' + systemProperty 'es.script.null_for_missing_value', 'true' } // xpack modules are installed in real clusters as the meta plugin, so