From 3bcadb0eb14458118a9003765a8536d2435c8ed3 Mon Sep 17 00:00:00 2001 From: Luca Cavanna Date: Fri, 8 Jul 2022 14:07:38 +0200 Subject: [PATCH] Enforce max values limit only when running a script (#88295) Runtime fields scripts have a hard maximum number of values that they can emit. This was accidentally inherited by some doc_value based queries that reuse the existing runtime fields infrastructure (in absence of a corresponding already existing lucene doc_value based query) as well as by script-less runtime fields that load from _source. This limit was introduced to prevent mistakes when writing a script hence when we are loading from either _source or doc_values the same limit should not be enforced. This commit addresses this, and applies the same to the max chars limit for string fields. --- docs/changelog/88295.yaml | 5 + .../script/AbstractFieldScript.java | 6 +- .../script/AbstractLongFieldScript.java | 1 - .../script/CompositeFieldScript.java | 1 + .../elasticsearch/script/DateFieldScript.java | 1 + .../script/DoubleFieldScript.java | 2 +- .../script/GeoPointFieldScript.java | 1 + .../elasticsearch/script/IpFieldScript.java | 2 +- .../elasticsearch/script/LongFieldScript.java | 1 + .../script/StringFieldScript.java | 17 +++- .../index/mapper/BooleanFieldScriptTests.java | 2 +- .../index/mapper/DateFieldScriptTests.java | 34 ++++++- .../index/mapper/DoubleFieldScriptTests.java | 33 ++++++- .../mapper/GeoPointFieldScriptTests.java | 2 +- .../index/mapper/IpFieldScriptTests.java | 34 ++++++- .../index/mapper/LongFieldScriptTests.java | 33 ++++++- .../index/mapper/StringFieldScriptTests.java | 62 +++++++++++- .../script/CompositeFieldScriptTests.java | 98 +++++++++++++++++++ ...dNumericDocValuesLongFieldScriptTests.java | 50 ++++++++++ ...tedSetDocValuesStringFieldScriptTests.java | 51 ++++++++++ 20 files changed, 422 insertions(+), 14 deletions(-) create mode 100644 docs/changelog/88295.yaml create mode 100644 server/src/test/java/org/elasticsearch/script/CompositeFieldScriptTests.java create mode 100644 server/src/test/java/org/elasticsearch/script/field/SortedNumericDocValuesLongFieldScriptTests.java create mode 100644 server/src/test/java/org/elasticsearch/script/field/SortedSetDocValuesStringFieldScriptTests.java diff --git a/docs/changelog/88295.yaml b/docs/changelog/88295.yaml new file mode 100644 index 0000000000000..93bea72f1da07 --- /dev/null +++ b/docs/changelog/88295.yaml @@ -0,0 +1,5 @@ +pr: 88295 +summary: Enforce max values limit only when running a script +area: Mapping +type: bug +issues: [] diff --git a/server/src/main/java/org/elasticsearch/script/AbstractFieldScript.java b/server/src/main/java/org/elasticsearch/script/AbstractFieldScript.java index d940141bdf198..c31e5d3db352b 100644 --- a/server/src/main/java/org/elasticsearch/script/AbstractFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/AbstractFieldScript.java @@ -99,10 +99,14 @@ protected final void emitFromCompositeScript(CompositeFieldScript compositeField return; } for (Object value : values) { - emitFromObject(value); + emitValueFromCompositeScript(value); } } + protected void emitValueFromCompositeScript(Object value) { + emitFromObject(value); + } + protected abstract void emitFromObject(Object v); protected final void emitFromSource() { diff --git a/server/src/main/java/org/elasticsearch/script/AbstractLongFieldScript.java b/server/src/main/java/org/elasticsearch/script/AbstractLongFieldScript.java index 826f0ae2a743a..10cb90607b5a1 100644 --- a/server/src/main/java/org/elasticsearch/script/AbstractLongFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/AbstractLongFieldScript.java @@ -63,7 +63,6 @@ public final int count() { } public final void emit(long v) { - checkMaxSize(count); if (values.length < count + 1) { values = ArrayUtil.grow(values, count + 1); } diff --git a/server/src/main/java/org/elasticsearch/script/CompositeFieldScript.java b/server/src/main/java/org/elasticsearch/script/CompositeFieldScript.java index 8469d3edd2eb7..253d83fd2596a 100644 --- a/server/src/main/java/org/elasticsearch/script/CompositeFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/CompositeFieldScript.java @@ -64,6 +64,7 @@ public final Map> runForDoc(int doc) { protected final void emit(String field, Object value) { // fields will be emitted without the prefix, yet they will be looked up using their full name, hence we store the full name List values = this.fieldValues.computeIfAbsent(fieldName + "." + field, s -> new ArrayList<>()); + checkMaxSize(values.size()); values.add(value); } diff --git a/server/src/main/java/org/elasticsearch/script/DateFieldScript.java b/server/src/main/java/org/elasticsearch/script/DateFieldScript.java index c8006eb486922..bf4ce0b047c1c 100644 --- a/server/src/main/java/org/elasticsearch/script/DateFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/DateFieldScript.java @@ -97,6 +97,7 @@ public Emit(DateFieldScript script) { } public void emit(long v) { + script.checkMaxSize(script.count()); script.emit(v); } } diff --git a/server/src/main/java/org/elasticsearch/script/DoubleFieldScript.java b/server/src/main/java/org/elasticsearch/script/DoubleFieldScript.java index b18d6942a7309..f59759e65bdd9 100644 --- a/server/src/main/java/org/elasticsearch/script/DoubleFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/DoubleFieldScript.java @@ -124,7 +124,6 @@ protected void emitFromObject(Object v) { } public final void emit(double v) { - checkMaxSize(count); if (values.length < count + 1) { values = ArrayUtil.grow(values, count + 1); } @@ -139,6 +138,7 @@ public Emit(DoubleFieldScript script) { } public void emit(double v) { + script.checkMaxSize(script.count()); script.emit(v); } } diff --git a/server/src/main/java/org/elasticsearch/script/GeoPointFieldScript.java b/server/src/main/java/org/elasticsearch/script/GeoPointFieldScript.java index fbe1fc7480697..5fa1d5c95873a 100644 --- a/server/src/main/java/org/elasticsearch/script/GeoPointFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/GeoPointFieldScript.java @@ -155,6 +155,7 @@ public Emit(GeoPointFieldScript script) { } public void emit(double lat, double lon) { + script.checkMaxSize(script.count()); script.emit(lat, lon); } } diff --git a/server/src/main/java/org/elasticsearch/script/IpFieldScript.java b/server/src/main/java/org/elasticsearch/script/IpFieldScript.java index cf49b7eaed23b..665eaee24cedb 100644 --- a/server/src/main/java/org/elasticsearch/script/IpFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/IpFieldScript.java @@ -143,7 +143,6 @@ protected void emitFromObject(Object v) { } public final void emit(String v) { - checkMaxSize(count); if (values.length < count + 1) { values = ArrayUtil.grow(values, count + 1); } @@ -161,6 +160,7 @@ public Emit(IpFieldScript script) { } public void emit(String v) { + script.checkMaxSize(script.count()); script.emit(v); } } diff --git a/server/src/main/java/org/elasticsearch/script/LongFieldScript.java b/server/src/main/java/org/elasticsearch/script/LongFieldScript.java index db1a27a07db35..bfecad9a68690 100644 --- a/server/src/main/java/org/elasticsearch/script/LongFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/LongFieldScript.java @@ -87,6 +87,7 @@ public Emit(LongFieldScript script) { } public void emit(long v) { + script.checkMaxSize(script.count()); script.emit(v); } } diff --git a/server/src/main/java/org/elasticsearch/script/StringFieldScript.java b/server/src/main/java/org/elasticsearch/script/StringFieldScript.java index 0907234e5a119..7e366f4b72b18 100644 --- a/server/src/main/java/org/elasticsearch/script/StringFieldScript.java +++ b/server/src/main/java/org/elasticsearch/script/StringFieldScript.java @@ -99,6 +99,15 @@ public final void runForDoc(int docId, Consumer consumer) { resultsForDoc(docId).forEach(consumer); } + @Override + protected void emitValueFromCompositeScript(Object value) { + if (value != null) { + String string = value.toString(); + checkMaxChars(string); + emit(string); + } + } + @Override protected void emitFromObject(Object v) { if (v != null) { @@ -107,7 +116,10 @@ protected void emitFromObject(Object v) { } public final void emit(String v) { - checkMaxSize(results.size()); + results.add(v); + } + + private void checkMaxChars(String v) { chars += v.length(); if (chars > MAX_CHARS) { throw new IllegalArgumentException( @@ -120,7 +132,6 @@ public final void emit(String v) { ) ); } - results.add(v); } public static class Emit { @@ -131,6 +142,8 @@ public Emit(StringFieldScript script) { } public void emit(String v) { + script.checkMaxSize(script.results.size()); + script.checkMaxChars(v); script.emit(v); } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldScriptTests.java b/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldScriptTests.java index 67c15548fcf13..8e1d25abfbcfc 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldScriptTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/BooleanFieldScriptTests.java @@ -63,7 +63,7 @@ public void testTooManyValues() throws IOException { @Override public void execute() { for (int i = 0; i <= AbstractFieldScript.MAX_VALUES * 1000; i++) { - emit(i % 2 == 0); + new Emit(this).value(i % 2 == 0); } } }; diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DateFieldScriptTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DateFieldScriptTests.java index 48bf8a465a878..d4f8f43db2941 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DateFieldScriptTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DateFieldScriptTests.java @@ -13,13 +13,18 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.time.DateFormatter; import org.elasticsearch.script.AbstractFieldScript; import org.elasticsearch.script.DateFieldScript; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.json.JsonXContent; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -68,7 +73,7 @@ public void testTooManyValues() throws IOException { @Override public void execute() { for (int i = 0; i <= AbstractFieldScript.MAX_VALUES; i++) { - emit(0); + new Emit(this).emit(0); } } }; @@ -80,4 +85,31 @@ public void execute() { } } } + + public final void testFromSourceDoesNotEnforceValuesLimit() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + int numValues = AbstractFieldScript.MAX_VALUES + randomIntBetween(1, 100); + XContentBuilder builder = JsonXContent.contentBuilder(); + builder.startObject(); + builder.startArray("field"); + for (int i = 0; i < numValues; i++) { + builder.value(i); + } + builder.endArray(); + builder.endObject(); + iw.addDocument(List.of(new StoredField("_source", new BytesRef(Strings.toString(builder))))); + try (DirectoryReader reader = iw.getReader()) { + DateFieldScript.LeafFactory leafFactory = fromSource().newFactory( + "field", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null), + DateFormatter.forPattern("epoch_millis") + ); + DateFieldScript dateFieldScript = leafFactory.newInstance(reader.leaves().get(0)); + List results = new ArrayList<>(); + dateFieldScript.runForDoc(0, results::add); + assertEquals(numValues, results.size()); + } + } + } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DoubleFieldScriptTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DoubleFieldScriptTests.java index 4235770f4ba41..6dbb109709a73 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DoubleFieldScriptTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DoubleFieldScriptTests.java @@ -13,12 +13,17 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.Strings; import org.elasticsearch.script.AbstractFieldScript; import org.elasticsearch.script.DoubleFieldScript; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.json.JsonXContent; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -65,7 +70,7 @@ public void testTooManyValues() throws IOException { @Override public void execute() { for (int i = 0; i <= AbstractFieldScript.MAX_VALUES; i++) { - emit(1.0); + new Emit(this).emit(1.0); } } }; @@ -77,4 +82,30 @@ public void execute() { } } } + + public final void testFromSourceDoesNotEnforceValuesLimit() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + int numValues = AbstractFieldScript.MAX_VALUES + randomIntBetween(1, 100); + XContentBuilder builder = JsonXContent.contentBuilder(); + builder.startObject(); + builder.startArray("field"); + for (int i = 0; i < numValues; i++) { + builder.value(i + 0.1); + } + builder.endArray(); + builder.endObject(); + iw.addDocument(List.of(new StoredField("_source", new BytesRef(Strings.toString(builder))))); + try (DirectoryReader reader = iw.getReader()) { + DoubleFieldScript.LeafFactory leafFactory = fromSource().newFactory( + "field", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null) + ); + DoubleFieldScript doubleFieldScript = leafFactory.newInstance(reader.leaves().get(0)); + List results = new ArrayList<>(); + doubleFieldScript.runForDoc(0, results::add); + assertEquals(numValues, results.size()); + } + } + } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldScriptTests.java b/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldScriptTests.java index 2cd554eda0f30..76ebd29e762b1 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldScriptTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/GeoPointFieldScriptTests.java @@ -65,7 +65,7 @@ public void testTooManyValues() throws IOException { @Override public void execute() { for (int i = 0; i <= AbstractFieldScript.MAX_VALUES; i++) { - emit(0, 0); + new Emit(this).emit(0, 0); } } }; diff --git a/server/src/test/java/org/elasticsearch/index/mapper/IpFieldScriptTests.java b/server/src/test/java/org/elasticsearch/index/mapper/IpFieldScriptTests.java index 640378f3fd06d..86ea18b746814 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/IpFieldScriptTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/IpFieldScriptTests.java @@ -13,12 +13,18 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.Strings; import org.elasticsearch.script.AbstractFieldScript; import org.elasticsearch.script.IpFieldScript; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.json.JsonXContent; import java.io.IOException; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -65,7 +71,7 @@ public void testTooManyValues() throws IOException { @Override public void execute() { for (int i = 0; i <= AbstractFieldScript.MAX_VALUES; i++) { - emit("192.168.0.1"); + new Emit(this).emit("192.168.0.1"); } } }; @@ -77,4 +83,30 @@ public void execute() { } } } + + public final void testFromSourceDoesNotEnforceValuesLimit() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + int numValues = AbstractFieldScript.MAX_VALUES + randomIntBetween(1, 100); + XContentBuilder builder = JsonXContent.contentBuilder(); + builder.startObject(); + builder.startArray("field"); + for (int i = 0; i < numValues; i++) { + builder.value("192.168.0." + i); + } + builder.endArray(); + builder.endObject(); + iw.addDocument(List.of(new StoredField("_source", new BytesRef(Strings.toString(builder))))); + try (DirectoryReader reader = iw.getReader()) { + IpFieldScript.LeafFactory leafFactory = fromSource().newFactory( + "field", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null) + ); + IpFieldScript ipFieldScript = leafFactory.newInstance(reader.leaves().get(0)); + List results = new ArrayList<>(); + ipFieldScript.runForDoc(0, results::add); + assertEquals(numValues, results.size()); + } + } + } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/LongFieldScriptTests.java b/server/src/test/java/org/elasticsearch/index/mapper/LongFieldScriptTests.java index 06f148fbc92c0..4c657e14e8412 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/LongFieldScriptTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/LongFieldScriptTests.java @@ -13,12 +13,17 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.Strings; import org.elasticsearch.script.AbstractFieldScript; import org.elasticsearch.script.LongFieldScript; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.json.JsonXContent; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -65,7 +70,7 @@ public void testTooManyValues() throws IOException { @Override public void execute() { for (int i = 0; i <= AbstractFieldScript.MAX_VALUES; i++) { - emit(0); + new Emit(this).emit(0); } } }; @@ -77,4 +82,30 @@ public void execute() { } } } + + public final void testFromSourceDoesNotEnforceValuesLimit() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + int numValues = AbstractFieldScript.MAX_VALUES + randomIntBetween(1, 100); + XContentBuilder builder = JsonXContent.contentBuilder(); + builder.startObject(); + builder.startArray("field"); + for (int i = 0; i < numValues; i++) { + builder.value(i); + } + builder.endArray(); + builder.endObject(); + iw.addDocument(List.of(new StoredField("_source", new BytesRef(Strings.toString(builder))))); + try (DirectoryReader reader = iw.getReader()) { + LongFieldScript.LeafFactory leafFactory = fromSource().newFactory( + "field", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null) + ); + LongFieldScript longFieldScript = leafFactory.newInstance(reader.leaves().get(0)); + List results = new ArrayList<>(); + longFieldScript.runForDoc(0, results::add); + assertEquals(numValues, results.size()); + } + } + } } diff --git a/server/src/test/java/org/elasticsearch/index/mapper/StringFieldScriptTests.java b/server/src/test/java/org/elasticsearch/index/mapper/StringFieldScriptTests.java index c7e8e9482d6bd..4f00772f20a3b 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/StringFieldScriptTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/StringFieldScriptTests.java @@ -13,12 +13,16 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.tests.index.RandomIndexWriter; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.Strings; import org.elasticsearch.script.AbstractFieldScript; import org.elasticsearch.script.ScriptContext; import org.elasticsearch.script.StringFieldScript; import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.xcontent.XContentBuilder; +import org.elasticsearch.xcontent.json.JsonXContent; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -65,7 +69,7 @@ public void testTooManyValues() throws IOException { @Override public void execute() { for (int i = 0; i <= AbstractFieldScript.MAX_VALUES; i++) { - emit("test"); + new Emit(this).emit("test"); } } }; @@ -96,7 +100,7 @@ public void execute() { } String bigString = big.toString(); for (int i = 0; i <= 4; i++) { - emit(bigString); + new Emit(this).emit(bigString); } } }; @@ -108,4 +112,58 @@ public void execute() { } } } + + public final void testFromSourceDoesNotEnforceValuesLimit() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + int numValues = AbstractFieldScript.MAX_VALUES + randomIntBetween(1, 100); + XContentBuilder builder = JsonXContent.contentBuilder(); + builder.startObject(); + builder.startArray("field"); + for (int i = 0; i < numValues; i++) { + builder.value("value" + i); + } + builder.endArray(); + builder.endObject(); + iw.addDocument(List.of(new StoredField("_source", new BytesRef(Strings.toString(builder))))); + try (DirectoryReader reader = iw.getReader()) { + StringFieldScript.LeafFactory leafFactory = fromSource().newFactory( + "field", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null) + ); + StringFieldScript stringFieldScript = leafFactory.newInstance(reader.leaves().get(0)); + List results = stringFieldScript.resultsForDoc(0); + assertEquals(numValues, results.size()); + } + } + } + + public final void testFromSourceDoesNotEnforceCharsLimit() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + StringBuilder big = new StringBuilder(); + while (big.length() < StringFieldScript.MAX_CHARS / 4) { + big.append("test"); + } + String bigString = big.toString(); + XContentBuilder builder = JsonXContent.contentBuilder(); + builder.startObject(); + builder.startArray("field"); + for (int i = 0; i <= 4; i++) { + builder.value(bigString); + } + builder.endArray(); + builder.endObject(); + iw.addDocument(List.of(new StoredField("_source", new BytesRef(Strings.toString(builder))))); + try (DirectoryReader reader = iw.getReader()) { + StringFieldScript.LeafFactory leafFactory = fromSource().newFactory( + "field", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null) + ); + StringFieldScript stringFieldScript = leafFactory.newInstance(reader.leaves().get(0)); + List results = stringFieldScript.resultsForDoc(0); + assertEquals(5, results.size()); + } + } + } } diff --git a/server/src/test/java/org/elasticsearch/script/CompositeFieldScriptTests.java b/server/src/test/java/org/elasticsearch/script/CompositeFieldScriptTests.java new file mode 100644 index 0000000000000..7c477cbcbe27a --- /dev/null +++ b/server/src/test/java/org/elasticsearch/script/CompositeFieldScriptTests.java @@ -0,0 +1,98 @@ +/* + * 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 org.apache.lucene.document.StoredField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.Matchers.equalTo; + +public class CompositeFieldScriptTests extends ESTestCase { + + public void testTooManyValues() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}")))); + try (DirectoryReader reader = iw.getReader()) { + CompositeFieldScript script = new CompositeFieldScript( + "composite", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + for (int i = 0; i <= AbstractFieldScript.MAX_VALUES; i++) { + emit("leaf", "value" + i); + } + } + }; + Exception e = expectThrows(IllegalArgumentException.class, script::execute); + assertThat( + e.getMessage(), + equalTo("Runtime field [composite] is emitting [101] values while the maximum number of values allowed is [100]") + ); + } + } + } + + public void testTooManyChars() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + iw.addDocument(List.of(new StoredField("_source", new BytesRef("{}")))); + try (DirectoryReader reader = iw.getReader()) { + StringBuilder big = new StringBuilder(); + while (big.length() < StringFieldScript.MAX_CHARS / 4) { + big.append("test"); + } + String bigString = big.toString(); + CompositeFieldScript script = new CompositeFieldScript( + "composite", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + for (int i = 0; i <= 4; i++) { + emit("leaf", bigString); + } + } + }; + StringFieldScript stringFieldScript = new StringFieldScript( + "composite.leaf", + Collections.emptyMap(), + new SearchLookup(field -> null, (ft, lookup) -> null), + reader.leaves().get(0) + ) { + @Override + public void execute() { + emitFromCompositeScript(script); + } + }; + + Exception e = expectThrows(IllegalArgumentException.class, stringFieldScript::execute); + assertThat( + e.getMessage(), + equalTo( + "Runtime field [composite.leaf] is emitting [1310720] characters " + + "while the maximum number of values allowed is [1048576]" + ) + ); + } + } + } +} diff --git a/server/src/test/java/org/elasticsearch/script/field/SortedNumericDocValuesLongFieldScriptTests.java b/server/src/test/java/org/elasticsearch/script/field/SortedNumericDocValuesLongFieldScriptTests.java new file mode 100644 index 0000000000000..0cc14dcfa23c4 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/script/field/SortedNumericDocValuesLongFieldScriptTests.java @@ -0,0 +1,50 @@ +/* + * 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.field; + +import org.apache.lucene.document.FieldType; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.elasticsearch.script.AbstractFieldScript; +import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class SortedNumericDocValuesLongFieldScriptTests extends ESTestCase { + + public void testValuesLimitIsNotEnforced() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + FieldType fieldType = new FieldType(); + fieldType.setDocValuesType(DocValuesType.BINARY); + List fields = new ArrayList<>(); + int numValues = AbstractFieldScript.MAX_VALUES + randomIntBetween(1, 100); + for (int i = 0; i < numValues; i++) { + fields.add(new SortedNumericDocValuesField("test", i)); + } + iw.addDocument(fields); + try (DirectoryReader reader = iw.getReader()) { + SortedNumericDocValuesLongFieldScript docValues = new SortedNumericDocValuesLongFieldScript( + "test", + new SearchLookup(field -> null, (ft, lookup) -> null), + reader.leaves().get(0) + ); + List values = new ArrayList<>(); + docValues.runForDoc(0, values::add); + assertEquals(numValues, values.size()); + } + } + } +} diff --git a/server/src/test/java/org/elasticsearch/script/field/SortedSetDocValuesStringFieldScriptTests.java b/server/src/test/java/org/elasticsearch/script/field/SortedSetDocValuesStringFieldScriptTests.java new file mode 100644 index 0000000000000..09b86887c54dc --- /dev/null +++ b/server/src/test/java/org/elasticsearch/script/field/SortedSetDocValuesStringFieldScriptTests.java @@ -0,0 +1,51 @@ +/* + * 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.field; + +import org.apache.lucene.document.FieldType; +import org.apache.lucene.document.SortedSetDocValuesField; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.store.Directory; +import org.apache.lucene.tests.index.RandomIndexWriter; +import org.apache.lucene.util.BytesRef; +import org.elasticsearch.script.AbstractFieldScript; +import org.elasticsearch.search.lookup.SearchLookup; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class SortedSetDocValuesStringFieldScriptTests extends ESTestCase { + + public void testValuesLimitIsNotEnforced() throws IOException { + try (Directory directory = newDirectory(); RandomIndexWriter iw = new RandomIndexWriter(random(), directory)) { + FieldType fieldType = new FieldType(); + fieldType.setDocValuesType(DocValuesType.BINARY); + List fields = new ArrayList<>(); + int numValues = AbstractFieldScript.MAX_VALUES + randomIntBetween(1, 100); + for (int i = 0; i < numValues; i++) { + fields.add(new SortedSetDocValuesField("test", new BytesRef("term" + i))); + } + iw.addDocument(fields); + try (DirectoryReader reader = iw.getReader()) { + SortedSetDocValuesStringFieldScript docValues = new SortedSetDocValuesStringFieldScript( + "test", + new SearchLookup(field -> null, (ft, lookup) -> null), + reader.leaves().get(0) + ); + List values = new ArrayList<>(); + docValues.runForDoc(0, values::add); + assertEquals(numValues, values.size()); + } + } + } +}