Skip to content

Commit ac3eae6

Browse files
authored
Fix handling of object arrays FallbackSyntheticSourceBlockLoader (elastic#122528) (elastic#122560)
1 parent 557dae6 commit ac3eae6

File tree

2 files changed

+56
-13
lines changed

2 files changed

+56
-13
lines changed

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

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Map;
2424
import java.util.Optional;
2525
import java.util.Set;
26+
import java.util.Stack;
2627

2728
/**
2829
* Block loader for fields that use fallback synthetic source implementation.
@@ -191,18 +192,45 @@ private void parseFieldFromParent(IgnoredSourceFieldMapper.NameValue nameValue,
191192
.createParser(filterParserConfig, nameValue.value().bytes, nameValue.value().offset + 1, nameValue.value().length - 1)
192193
) {
193194
parser.nextToken();
194-
var fieldNameInParser = new StringBuilder(nameValue.name());
195-
while (true) {
196-
if (parser.currentToken() == XContentParser.Token.FIELD_NAME) {
197-
fieldNameInParser.append('.').append(parser.currentName());
198-
if (fieldNameInParser.toString().equals(fieldName)) {
199-
parser.nextToken();
200-
break;
195+
var fieldNames = new Stack<String>() {
196+
{
197+
push(nameValue.name());
198+
}
199+
};
200+
201+
while (parser.currentToken() != null) {
202+
// We are descending into an object/array hierarchy of arbitrary depth
203+
// until we find the field that we need.
204+
while (true) {
205+
if (parser.currentToken() == XContentParser.Token.FIELD_NAME) {
206+
fieldNames.push(parser.currentName());
207+
var nameInParser = String.join(".", fieldNames);
208+
if (nameInParser.equals(fieldName)) {
209+
parser.nextToken();
210+
break;
211+
}
212+
} else {
213+
assert parser.currentToken() == XContentParser.Token.START_OBJECT
214+
|| parser.currentToken() == XContentParser.Token.START_ARRAY;
201215
}
216+
217+
parser.nextToken();
202218
}
219+
parseWithReader(parser, blockValues);
203220
parser.nextToken();
221+
222+
// We are coming back up in object/array hierarchy.
223+
// If arrays are present we will explore all array items by going back down again.
224+
while (parser.currentToken() == XContentParser.Token.END_OBJECT
225+
|| parser.currentToken() == XContentParser.Token.END_ARRAY) {
226+
// When exiting an object arrays we'll see END_OBJECT followed by END_ARRAY, but we only need to pop the object name
227+
// once.
228+
if (parser.currentToken() == XContentParser.Token.END_OBJECT) {
229+
fieldNames.pop();
230+
}
231+
parser.nextToken();
232+
}
204233
}
205-
parseWithReader(parser, blockValues);
206234
}
207235
}
208236

test/framework/src/main/java/org/elasticsearch/index/mapper/BlockLoaderTestCase.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.logsdb.datageneration.DataGeneratorSpecification;
2020
import org.elasticsearch.logsdb.datageneration.DocumentGenerator;
2121
import org.elasticsearch.logsdb.datageneration.FieldType;
22+
import org.elasticsearch.logsdb.datageneration.Mapping;
2223
import org.elasticsearch.logsdb.datageneration.MappingGenerator;
2324
import org.elasticsearch.logsdb.datageneration.Template;
2425
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceHandler;
@@ -72,9 +73,13 @@ public DataSourceResponse.ObjectMappingParametersGenerator handle(
7273

7374
public void testBlockLoader() throws IOException {
7475
var template = new Template(Map.of(fieldName, new Template.Leaf(fieldName, fieldType)));
75-
runTest(template, fieldName);
76+
var syntheticSource = randomBoolean();
77+
var mapping = mappingGenerator.generate(template);
78+
79+
runTest(template, mapping, syntheticSource, fieldName);
7680
}
7781

82+
@SuppressWarnings("unchecked")
7883
public void testBlockLoaderForFieldInObject() throws IOException {
7984
int depth = randomIntBetween(0, 3);
8085

@@ -94,14 +99,24 @@ public void testBlockLoaderForFieldInObject() throws IOException {
9499
fullFieldName.append('.').append(fieldName);
95100
currentLevel.put(fieldName, new Template.Leaf(fieldName, fieldType));
96101
var template = new Template(top);
97-
runTest(template, fullFieldName.toString());
98-
}
99102

100-
private void runTest(Template template, String fieldName) throws IOException {
103+
var syntheticSource = randomBoolean();
104+
101105
var mapping = mappingGenerator.generate(template);
106+
107+
if (syntheticSource && randomBoolean()) {
108+
// force fallback synthetic source in the hierarchy
109+
var docMapping = (Map<String, Object>) mapping.raw().get("_doc");
110+
var topLevelMapping = (Map<String, Object>) ((Map<String, Object>) docMapping.get("properties")).get("top");
111+
topLevelMapping.put("synthetic_source_keep", "all");
112+
}
113+
114+
runTest(template, mapping, syntheticSource, fullFieldName.toString());
115+
}
116+
117+
private void runTest(Template template, Mapping mapping, boolean syntheticSource, String fieldName) throws IOException {
102118
var mappingXContent = XContentBuilder.builder(XContentType.JSON.xContent()).map(mapping.raw());
103119

104-
var syntheticSource = randomBoolean();
105120
var mapperService = syntheticSource ? createSytheticSourceMapperService(mappingXContent) : createMapperService(mappingXContent);
106121

107122
var document = documentGenerator.generate(template, mapping);

0 commit comments

Comments
 (0)