Skip to content

Commit 5def107

Browse files
authored
[8.x] Second parsing pass tracks array scopes properly (#114621) (#114646)
* Second parsing pass tracks array scopes properly (#114621) (cherry picked from commit f3cd890) # Conflicts: # muted-tests.yml # server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java * fix merge issues * fix merge issues
1 parent 1aed889 commit 5def107

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ private static List<IgnoredSourceFieldMapper.NameValue> parseDocForMissingValues
208208
XContentParser parser = context.parser();
209209
XContentParser.Token currentToken = parser.nextToken();
210210
List<String> path = new ArrayList<>();
211+
List<Boolean> isObjectInPath = new ArrayList<>(); // Tracks if path components correspond to an object or an array.
211212
String fieldName = null;
212213
while (currentToken != null) {
213214
while (currentToken != XContentParser.Token.FIELD_NAME) {
@@ -218,11 +219,16 @@ private static List<IgnoredSourceFieldMapper.NameValue> parseDocForMissingValues
218219
parser.skipChildren();
219220
} else {
220221
path.add(fieldName);
222+
isObjectInPath.add(currentToken == XContentParser.Token.START_OBJECT);
221223
}
222224
fieldName = null;
223225
} else if (currentToken == XContentParser.Token.END_OBJECT || currentToken == XContentParser.Token.END_ARRAY) {
224-
if (currentToken == XContentParser.Token.END_OBJECT && path.isEmpty() == false) {
226+
// Remove the path, if the scope type matches the one when the path was added.
227+
if (isObjectInPath.isEmpty() == false
228+
&& (isObjectInPath.get(isObjectInPath.size() - 1) && currentToken == XContentParser.Token.END_OBJECT
229+
|| isObjectInPath.get(isObjectInPath.size() - 1) == false && currentToken == XContentParser.Token.END_ARRAY)) {
225230
path.remove(path.size() - 1);
231+
isObjectInPath.remove(isObjectInPath.size() - 1);
226232
}
227233
fieldName = null;
228234
}
@@ -237,7 +243,6 @@ private static List<IgnoredSourceFieldMapper.NameValue> parseDocForMissingValues
237243
if (leaf != null) {
238244
parser.nextToken(); // Advance the parser to the value to be read.
239245
result.add(leaf.cloneWithValue(context.encodeFlattenedToken()));
240-
parser.nextToken(); // Skip the token ending the value.
241246
fieldName = null;
242247
}
243248
currentToken = parser.nextToken();

server/src/test/java/org/elasticsearch/index/mapper/IgnoredSourceFieldMapperTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,37 @@ public void testNestedArray() throws IOException {
901901
);
902902
}
903903

904+
public void testConflictingFieldNameAfterArray() throws IOException {
905+
DocumentMapper documentMapper = createMapperService(syntheticSourceMapping(b -> {
906+
b.startObject("path").startObject("properties");
907+
{
908+
b.startObject("to").startObject("properties");
909+
{
910+
b.startObject("id").field("type", "integer").field("synthetic_source_keep", "arrays").endObject();
911+
}
912+
b.endObject().endObject();
913+
b.startObject("id").field("type", "float").endObject();
914+
}
915+
b.endObject().endObject();
916+
})).documentMapper();
917+
918+
var syntheticSource = syntheticSource(documentMapper, b -> {
919+
b.startObject("path");
920+
{
921+
b.startArray("to");
922+
{
923+
b.startObject().array("id", 1, 20, 3).endObject();
924+
b.startObject().field("id", 10).endObject();
925+
}
926+
b.endArray();
927+
b.field("id", "0.1");
928+
}
929+
b.endObject();
930+
});
931+
assertEquals("""
932+
{"path":{"id":0.1,"to":{"id":[1,20,3,10]}}}""", syntheticSource);
933+
}
934+
904935
public void testArrayWithinArray() throws IOException {
905936
DocumentMapper documentMapper = createMapperService(syntheticSourceMapping(b -> {
906937
b.startObject("path");

0 commit comments

Comments
 (0)