Skip to content

Commit 093e36c

Browse files
authored
Introduce DocumentParsingException (#92646)
Document parsing methods currently throw MapperParsingException. This isn't very helpful, as it doesn't contain any information about where the parse error happened - it is designed for parsing mappings, which are realised into java maps before being examined. This commit introduces a new exception specifically for document parsing that extends XContentException, so that it reports the current position of the parser as part of its error message. Fixes #85083
1 parent 4ae4e3a commit 093e36c

File tree

77 files changed

+707
-500
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+707
-500
lines changed

docs/changelog/92646.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 92646
2+
summary: Introduce `DocumentParsingException`
3+
area: Search
4+
type: enhancement
5+
issues:
6+
- 85083

modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/DataStreamIT.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@
8080
import org.elasticsearch.index.IndexNotFoundException;
8181
import org.elasticsearch.index.mapper.DataStreamTimestampFieldMapper;
8282
import org.elasticsearch.index.mapper.DateFieldMapper;
83-
import org.elasticsearch.index.mapper.MapperParsingException;
8483
import org.elasticsearch.index.query.TermQueryBuilder;
8584
import org.elasticsearch.index.shard.IndexingStats;
8685
import org.elasticsearch.indices.InvalidAliasNameException;
@@ -1320,7 +1319,7 @@ public void testNoTimestampInDocument() throws Exception {
13201319
client().execute(CreateDataStreamAction.INSTANCE, createDataStreamRequest).get();
13211320

13221321
IndexRequest indexRequest = new IndexRequest(dataStreamName).opType("create").source("{}", XContentType.JSON);
1323-
Exception e = expectThrows(MapperParsingException.class, () -> client().index(indexRequest).actionGet());
1322+
Exception e = expectThrows(Exception.class, () -> client().index(indexRequest).actionGet());
13241323
assertThat(e.getCause().getMessage(), equalTo("data stream timestamp field [@timestamp] is missing"));
13251324
}
13261325

@@ -1332,7 +1331,7 @@ public void testMultipleTimestampValuesInDocument() throws Exception {
13321331

13331332
IndexRequest indexRequest = new IndexRequest(dataStreamName).opType("create")
13341333
.source("{\"@timestamp\": [\"2020-12-12\",\"2022-12-12\"]}", XContentType.JSON);
1335-
Exception e = expectThrows(MapperParsingException.class, () -> client().index(indexRequest).actionGet());
1334+
Exception e = expectThrows(Exception.class, () -> client().index(indexRequest).actionGet());
13361335
assertThat(e.getCause().getMessage(), equalTo("data stream timestamp field [@timestamp] encountered multiple values"));
13371336
}
13381337

modules/data-streams/src/test/java/org/elasticsearch/datastreams/mapper/DataStreamTimestampFieldMapperTests.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
import org.elasticsearch.index.mapper.DataStreamTimestampFieldMapper;
1515
import org.elasticsearch.index.mapper.DateFieldMapper;
1616
import org.elasticsearch.index.mapper.DocumentMapper;
17+
import org.elasticsearch.index.mapper.DocumentParsingException;
1718
import org.elasticsearch.index.mapper.FieldMapper;
18-
import org.elasticsearch.index.mapper.MapperException;
1919
import org.elasticsearch.index.mapper.MapperService;
2020
import org.elasticsearch.index.mapper.MetadataMapperTestCase;
2121
import org.elasticsearch.index.mapper.ParsedDocument;
@@ -81,10 +81,16 @@ public void testPostParse() throws IOException {
8181
ParsedDocument doc = docMapper.parse(source(b -> b.field("@timestamp", "2020-12-12")));
8282
assertThat(doc.rootDoc().getFields("@timestamp").size(), equalTo(1));
8383

84-
Exception e = expectThrows(MapperException.class, () -> docMapper.parse(source(b -> b.field("@timestamp1", "2020-12-12"))));
84+
Exception e = expectThrows(
85+
DocumentParsingException.class,
86+
() -> docMapper.parse(source(b -> b.field("@timestamp1", "2020-12-12")))
87+
);
8588
assertThat(e.getCause().getMessage(), equalTo("data stream timestamp field [@timestamp] is missing"));
8689

87-
e = expectThrows(MapperException.class, () -> docMapper.parse(source(b -> b.array("@timestamp", "2020-12-12", "2020-12-13"))));
90+
e = expectThrows(
91+
DocumentParsingException.class,
92+
() -> docMapper.parse(source(b -> b.array("@timestamp", "2020-12-12", "2020-12-13")))
93+
);
8894
assertThat(e.getCause().getMessage(), equalTo("data stream timestamp field [@timestamp] encountered multiple values"));
8995
}
9096

modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/160_dense_vector_special_cases.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ setup:
2626

2727
---
2828
"Indexing of Dense vectors should error when dims don't match defined in the mapping":
29+
- skip:
30+
version: " - 8.7.99"
31+
reason: "exception changed in 8.8"
2932

3033
- do:
3134
catch: bad_request
@@ -34,7 +37,7 @@ setup:
3437
id: "1"
3538
body:
3639
vector: [10, 2]
37-
- match: { error.type: "mapper_parsing_exception" }
40+
- match: { error.type: "document_parsing_exception" }
3841

3942
- do:
4043
catch: bad_request
@@ -43,7 +46,7 @@ setup:
4346
id: "1"
4447
body:
4548
indexed_vector: [10, 2]
46-
- match: { error.type: "mapper_parsing_exception" }
49+
- match: { error.type: "document_parsing_exception" }
4750

4851
---
4952
"Vectors of mixed integers and floats":

modules/lang-painless/src/yamlRestTest/resources/rest-api-spec/test/painless/165_dense_vector_byte_special_cases.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ setup:
3030

3131
---
3232
"Indexing of Dense vectors should error when dims don't match defined in the mapping":
33+
- skip:
34+
version: " - 8.7.99"
35+
reason: "exception changed in 8.8"
3336

3437
- do:
3538
catch: bad_request
@@ -38,7 +41,7 @@ setup:
3841
id: "1"
3942
body:
4043
vector: [10, 2]
41-
- match: { error.type: "mapper_parsing_exception" }
44+
- match: { error.type: "document_parsing_exception" }
4245

4346
- do:
4447
catch: bad_request
@@ -47,7 +50,7 @@ setup:
4750
id: "1"
4851
body:
4952
indexed_vector: [10, 2]
50-
- match: { error.type: "mapper_parsing_exception" }
53+
- match: { error.type: "document_parsing_exception" }
5154

5255
---
5356
"Vectors of mixed integers and floats":

modules/legacy-geo/src/main/java/org/elasticsearch/legacygeo/mapper/LegacyGeoShapeFieldMapper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@
3232
import org.elasticsearch.index.analysis.NamedAnalyzer;
3333
import org.elasticsearch.index.mapper.AbstractShapeGeometryFieldMapper;
3434
import org.elasticsearch.index.mapper.DocumentParserContext;
35+
import org.elasticsearch.index.mapper.DocumentParsingException;
3536
import org.elasticsearch.index.mapper.FieldMapper;
3637
import org.elasticsearch.index.mapper.GeoShapeQueryable;
3738
import org.elasticsearch.index.mapper.MappedFieldType;
3839
import org.elasticsearch.index.mapper.Mapper;
3940
import org.elasticsearch.index.mapper.MapperBuilderContext;
40-
import org.elasticsearch.index.mapper.MapperParsingException;
4141
import org.elasticsearch.index.query.SearchExecutionContext;
4242
import org.elasticsearch.legacygeo.ShapesAvailability;
4343
import org.elasticsearch.legacygeo.XShapeCollection;
@@ -599,7 +599,8 @@ protected void index(DocumentParserContext context, ShapeBuilder<?, ?, ?> shapeB
599599
}
600600
return;
601601
} else if (shape instanceof Point == false) {
602-
throw new MapperParsingException(
602+
throw new DocumentParsingException(
603+
context.parser().getTokenLocation(),
603604
"[{"
604605
+ fieldType().name()
605606
+ "}] is configured for points only but a "

modules/legacy-geo/src/test/java/org/elasticsearch/legacygeo/search/LegacyGeoShapeQueryTests.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import org.elasticsearch.geometry.Geometry;
1717
import org.elasticsearch.geometry.MultiPoint;
1818
import org.elasticsearch.geometry.Point;
19-
import org.elasticsearch.index.mapper.MapperParsingException;
2019
import org.elasticsearch.legacygeo.mapper.LegacyGeoShapeFieldMapper;
2120
import org.elasticsearch.legacygeo.test.TestLegacyGeoShapeFieldMapperPlugin;
2221
import org.elasticsearch.plugins.Plugin;
@@ -133,7 +132,7 @@ public void testPointsOnly() throws Exception {
133132
.setSource(GeoJson.toXContent(geometry, jsonBuilder().startObject().field(defaultFieldName), null).endObject())
134133
.setRefreshPolicy(IMMEDIATE)
135134
.get();
136-
} catch (MapperParsingException e) {
135+
} catch (Exception e) {
137136
// Random geometry generator created something other than a POINT type, verify the correct exception is thrown
138137
assertThat(e.getMessage(), containsString("is configured for points only"));
139138
return;

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/RankFeatureFieldMapperTests.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
import org.apache.lucene.search.TermQuery;
1717
import org.elasticsearch.common.Strings;
1818
import org.elasticsearch.index.mapper.DocumentMapper;
19+
import org.elasticsearch.index.mapper.DocumentParsingException;
1920
import org.elasticsearch.index.mapper.LuceneDocument;
2021
import org.elasticsearch.index.mapper.MappedFieldType;
21-
import org.elasticsearch.index.mapper.MapperParsingException;
2222
import org.elasticsearch.index.mapper.MapperTestCase;
2323
import org.elasticsearch.index.mapper.ParsedDocument;
2424
import org.elasticsearch.plugins.Plugin;
@@ -125,7 +125,7 @@ public void testNegativeScoreImpact() throws Exception {
125125
assertTrue(freq1 > freq2);
126126
}
127127

128-
public void testRejectMultiValuedFields() throws MapperParsingException, IOException {
128+
public void testRejectMultiValuedFields() throws IOException {
129129
DocumentMapper mapper = createDocumentMapper(mapping(b -> {
130130
b.startObject("field").field("type", "rank_feature").endObject();
131131
b.startObject("foo").startObject("properties");
@@ -135,16 +135,16 @@ public void testRejectMultiValuedFields() throws MapperParsingException, IOExcep
135135
b.endObject().endObject();
136136
}));
137137

138-
MapperParsingException e = expectThrows(
139-
MapperParsingException.class,
138+
DocumentParsingException e = expectThrows(
139+
DocumentParsingException.class,
140140
() -> mapper.parse(source(b -> b.field("field", Arrays.asList(10, 20))))
141141
);
142142
assertEquals(
143143
"[rank_feature] fields do not support indexing multiple values for the same field [field] in the same document",
144144
e.getCause().getMessage()
145145
);
146146

147-
e = expectThrows(MapperParsingException.class, () -> mapper.parse(source(b -> {
147+
e = expectThrows(DocumentParsingException.class, () -> mapper.parse(source(b -> {
148148
b.startArray("foo");
149149
{
150150
b.startObject().field("field", 10).endObject();

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/RankFeatureMetaFieldMapperTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import org.elasticsearch.common.bytes.BytesReference;
1313
import org.elasticsearch.common.compress.CompressedXContent;
1414
import org.elasticsearch.index.mapper.DocumentMapper;
15-
import org.elasticsearch.index.mapper.MapperParsingException;
15+
import org.elasticsearch.index.mapper.DocumentParsingException;
1616
import org.elasticsearch.index.mapper.MapperService;
1717
import org.elasticsearch.index.mapper.MapperServiceTestCase;
1818
import org.elasticsearch.index.mapper.Mapping;
@@ -64,8 +64,8 @@ public void testDocumentParsingFailsOnMetaField() throws Exception {
6464
);
6565
String rfMetaField = RankFeatureMetaFieldMapper.CONTENT_TYPE;
6666
BytesReference bytes = BytesReference.bytes(XContentFactory.jsonBuilder().startObject().field(rfMetaField, 0).endObject());
67-
MapperParsingException e = expectThrows(
68-
MapperParsingException.class,
67+
DocumentParsingException e = expectThrows(
68+
DocumentParsingException.class,
6969
() -> mapper.parse(new SourceToParse("1", bytes, XContentType.JSON))
7070
);
7171
assertThat(

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/extras/RankFeaturesFieldMapperTests.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.apache.lucene.index.IndexableField;
1313
import org.elasticsearch.common.Strings;
1414
import org.elasticsearch.index.mapper.DocumentMapper;
15+
import org.elasticsearch.index.mapper.DocumentParsingException;
1516
import org.elasticsearch.index.mapper.MappedFieldType;
1617
import org.elasticsearch.index.mapper.MapperParsingException;
1718
import org.elasticsearch.index.mapper.MapperService;
@@ -128,8 +129,8 @@ public void testNegativeScoreImpact() throws Exception {
128129

129130
public void testDotinFieldname() throws Exception {
130131
DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping));
131-
MapperParsingException ex = expectThrows(
132-
MapperParsingException.class,
132+
DocumentParsingException ex = expectThrows(
133+
DocumentParsingException.class,
133134
() -> mapper.parse(source(b -> b.field("field", Map.of("politi.cs", 10, "sports", 20))))
134135
);
135136
assertThat(ex.getCause().getMessage(), containsString("do not support dots in feature names"));
@@ -146,16 +147,16 @@ public void testRejectMultiValuedFields() throws MapperParsingException, IOExcep
146147
b.endObject().endObject();
147148
}));
148149

149-
MapperParsingException e = expectThrows(
150-
MapperParsingException.class,
150+
DocumentParsingException e = expectThrows(
151+
DocumentParsingException.class,
151152
() -> mapper.parse(source(b -> b.startObject("field").field("foo", Arrays.asList(10, 20)).endObject()))
152153
);
153154
assertEquals(
154155
"[rank_features] fields take hashes that map a feature to a strictly positive float, but got unexpected token " + "START_ARRAY",
155156
e.getCause().getMessage()
156157
);
157158

158-
e = expectThrows(MapperParsingException.class, () -> mapper.parse(source(b -> {
159+
e = expectThrows(DocumentParsingException.class, () -> mapper.parse(source(b -> {
159160
b.startArray("foo");
160161
{
161162
b.startObject().startObject("field").field("bar", 10).endObject().endObject();

0 commit comments

Comments
 (0)