Skip to content

Commit f766086

Browse files
authored
Restore single-element array removal in synthetic source (elastic#120844) (elastic#120862)
1 parent 7585952 commit f766086

File tree

2 files changed

+49
-15
lines changed

2 files changed

+49
-15
lines changed

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

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public final class DocumentParser {
5757
static final NodeFeature FIX_PARSING_SUBOBJECTS_FALSE_DYNAMIC_FALSE = new NodeFeature(
5858
"mapper.fix_parsing_subobjects_false_dynamic_false"
5959
);
60+
private static final String NOOP_FIELD_MAPPER_NAME = "no-op";
6061

6162
private final XContentParserConfiguration parserConfiguration;
6263
private final MappingParserContext mappingParserContext;
@@ -706,6 +707,8 @@ private static void parseNonDynamicArray(
706707

707708
canRemoveSingleLeafElement = mapper instanceof FieldMapper
708709
&& mode == Mapper.SourceKeepMode.ARRAYS
710+
&& context.inArrayScope() == false
711+
&& mapper.leafName().equals(NOOP_FIELD_MAPPER_NAME) == false
709712
&& fieldWithFallbackSyntheticSource == false
710713
&& copyToFieldHasValuesInDocument == false;
711714

@@ -729,20 +732,28 @@ private static void parseNonDynamicArray(
729732

730733
XContentParser parser = context.parser();
731734
XContentParser.Token token;
735+
int elements = 0;
732736
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
733737
if (token == XContentParser.Token.START_OBJECT) {
738+
elements = 2;
734739
parseObject(context, lastFieldName);
735740
} else if (token == XContentParser.Token.START_ARRAY) {
741+
elements = 2;
736742
parseArray(context, lastFieldName);
737743
} else if (token == XContentParser.Token.VALUE_NULL) {
744+
elements++;
738745
parseNullValue(context, lastFieldName);
739746
} else if (token == null) {
740747
throwEOFOnParseArray(arrayFieldName, context);
741748
} else {
742749
assert token.isValue();
750+
elements++;
743751
parseValue(context, lastFieldName);
744752
}
745753
}
754+
if (elements <= 1 && canRemoveSingleLeafElement) {
755+
context.removeLastIgnoredField(fullPath);
756+
}
746757
postProcessDynamicArrayMapping(context, lastFieldName);
747758
}
748759

@@ -917,22 +928,26 @@ private static Mapper getLeafMapper(final DocumentParserContext context, String
917928
}
918929

919930
private static FieldMapper noopFieldMapper(String path) {
920-
return new FieldMapper("no-op", new MappedFieldType("no-op", false, false, false, TextSearchInfo.NONE, Collections.emptyMap()) {
921-
@Override
922-
public ValueFetcher valueFetcher(SearchExecutionContext context, String format) {
923-
throw new UnsupportedOperationException();
924-
}
931+
return new FieldMapper(
932+
NOOP_FIELD_MAPPER_NAME,
933+
new MappedFieldType(NOOP_FIELD_MAPPER_NAME, false, false, false, TextSearchInfo.NONE, Collections.emptyMap()) {
934+
@Override
935+
public ValueFetcher valueFetcher(SearchExecutionContext context, String format) {
936+
throw new UnsupportedOperationException();
937+
}
925938

926-
@Override
927-
public String typeName() {
928-
throw new UnsupportedOperationException();
929-
}
939+
@Override
940+
public String typeName() {
941+
throw new UnsupportedOperationException();
942+
}
930943

931-
@Override
932-
public Query termQuery(Object value, SearchExecutionContext context) {
933-
throw new UnsupportedOperationException();
934-
}
935-
}, FieldMapper.BuilderParams.empty()) {
944+
@Override
945+
public Query termQuery(Object value, SearchExecutionContext context) {
946+
throw new UnsupportedOperationException();
947+
}
948+
},
949+
FieldMapper.BuilderParams.empty()
950+
) {
936951

937952
@Override
938953
protected void parseCreateField(DocumentParserContext context) {

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

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,9 @@ public void testIndexStoredArraySourceSingleLeafElement() throws IOException {
743743
b.startObject("int_value").field("type", "integer").endObject();
744744
})).documentMapper();
745745
var syntheticSource = syntheticSource(documentMapper, b -> b.array("int_value", new int[] { 10 }));
746-
assertEquals("{\"int_value\":[10]}", syntheticSource);
746+
assertEquals("{\"int_value\":10}", syntheticSource);
747+
ParsedDocument doc = documentMapper.parse(source(syntheticSource));
748+
assertNull(doc.rootDoc().getField("_ignored_source"));
747749
}
748750

749751
public void testIndexStoredArraySourceSingleLeafElementAndNull() throws IOException {
@@ -754,6 +756,23 @@ public void testIndexStoredArraySourceSingleLeafElementAndNull() throws IOExcept
754756
assertEquals("{\"value\":[\"foo\",null]}", syntheticSource);
755757
}
756758

759+
public void testIndexStoredArraySourceSingleLeafElementInObjectArray() throws IOException {
760+
DocumentMapper documentMapper = createMapperServiceWithStoredArraySource(mapping(b -> {
761+
b.startObject("path").field("synthetic_source_keep", "none").startObject("properties");
762+
{
763+
b.startObject("int_value").field("type", "integer").endObject();
764+
}
765+
b.endObject().endObject();
766+
})).documentMapper();
767+
var syntheticSource = syntheticSource(documentMapper, b -> {
768+
b.startArray("path");
769+
b.startObject().field("int_value", 10).endObject();
770+
b.startObject().array("int_value", new int[] { 20 }).endObject();
771+
b.endArray();
772+
});
773+
assertEquals("{\"path\":{\"int_value\":[10,20]}}", syntheticSource);
774+
}
775+
757776
public void testIndexStoredArraySourceSingleObjectElement() throws IOException {
758777
DocumentMapper documentMapper = createMapperServiceWithStoredArraySource(mapping(b -> {
759778
b.startObject("path").startObject("properties");

0 commit comments

Comments
 (0)