Skip to content

Commit a93be8d

Browse files
authored
Handle nested arrays in field retrieval. (#60981)
We accept _source values with multiple levels of arrays, such as `"field": [[[1, 2]]]`. This PR ensures that field retrieval can handle nested arrays by unwrapping the arrays before parsing the values.
1 parent 929f1cc commit a93be8d

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.elasticsearch.search.lookup.SourceLookup;
3838

3939
import java.io.IOException;
40+
import java.util.ArrayDeque;
4041
import java.util.ArrayList;
4142
import java.util.Arrays;
4243
import java.util.Collections;
@@ -46,6 +47,7 @@
4647
import java.util.List;
4748
import java.util.Map;
4849
import java.util.Objects;
50+
import java.util.Queue;
4951
import java.util.TreeMap;
5052
import java.util.stream.StreamSupport;
5153

@@ -307,11 +309,17 @@ public List<?> lookupValues(SourceLookup lookup, @Nullable String format) {
307309
List<Object> values = new ArrayList<>();
308310
if (parsesArrayValue()) {
309311
return (List<?>) parseSourceValue(sourceValue, format);
310-
} else {
311-
List<?> sourceValues = sourceValue instanceof List
312-
? (List<?>) sourceValue
313-
: org.elasticsearch.common.collect.List.of(sourceValue);
314-
for (Object value : sourceValues) {
312+
}
313+
314+
// We allow source values to contain multiple levels of arrays, such as `"field": [[1, 2]]`.
315+
// So we need to unwrap these arrays before passing them on to be parsed.
316+
Queue<Object> queue = new ArrayDeque<>();
317+
queue.add(sourceValue);
318+
while (queue.isEmpty() == false) {
319+
Object value = queue.poll();
320+
if (value instanceof List) {
321+
queue.addAll((List<?>) value);
322+
} else {
315323
Object parsedValue = parseSourceValue(value, format);
316324
if (parsedValue != null) {
317325
values.add(parsedValue);

server/src/test/java/org/elasticsearch/search/fetch/subphase/FieldValueRetrieverTests.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,35 @@ public void testRetrieveAllFields() throws IOException {
115115
assertThat(fields.size(), equalTo(2));
116116
}
117117

118+
public void testNestedArrays() throws IOException {
119+
MapperService mapperService = createMapperService();
120+
XContentBuilder source = XContentFactory.jsonBuilder().startObject()
121+
.startArray("field")
122+
.startArray().value("first").value("second").endArray()
123+
.endArray()
124+
.endObject();
125+
126+
Map<String, DocumentField> fields = retrieveFields(mapperService, source, "field");
127+
DocumentField field = fields.get("field");
128+
assertNotNull(field);
129+
assertThat(field.getValues().size(), equalTo(2));
130+
assertThat(field.getValues(), hasItems("first", "second"));
131+
132+
source = XContentFactory.jsonBuilder().startObject()
133+
.startArray("object")
134+
.startObject().array("field", "first", "second").endObject()
135+
.startObject().array("field", "third").endObject()
136+
.startObject().field("field", "fourth").endObject()
137+
.endArray()
138+
.endObject();
139+
140+
fields = retrieveFields(mapperService, source, "object.field");
141+
field = fields.get("object.field");
142+
assertNotNull(field);
143+
assertThat(field.getValues().size(), equalTo(4));
144+
assertThat(field.getValues(), hasItems("first", "second", "third", "fourth"));
145+
}
146+
118147
public void testArrayValueMappers() throws IOException {
119148
MapperService mapperService = createMapperService();
120149

0 commit comments

Comments
 (0)