Skip to content

Commit b19b82b

Browse files
committed
Simulate ingest API uses existing index mapping when mapping_addition is given
1 parent dc3ee44 commit b19b82b

File tree

2 files changed

+78
-25
lines changed

2 files changed

+78
-25
lines changed

qa/smoke-test-ingest-with-all-dependencies/src/yamlRestTest/resources/rest-api-spec/test/ingest/80_ingest_simulate.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,3 +1740,63 @@ setup:
17401740
- match: { docs.0.doc._source.abc: "sfdsfsfdsfsfdsfsfdsfsfdsfsfdsf" }
17411741
- match: { docs.0.doc.ignored_fields: [ {"field": "abc"} ] }
17421742
- not_exists: docs.0.doc.error
1743+
1744+
---
1745+
"Test mapping addition correctly respects mapping of indices without templates":
1746+
# In this test, we make sure that when we have an index that has mapping but was not built with a template, that the
1747+
# additional_mapping respects the existing mapping for validation.
1748+
1749+
- skip:
1750+
features:
1751+
- headers
1752+
- allowed_warnings
1753+
1754+
# A global match-everything legacy template is added to the cluster sometimes (rarely). We have to get rid of this template if it exists
1755+
# because this test is making sure we get correct behavior when an index matches *no* template:
1756+
- do:
1757+
indices.delete_template:
1758+
name: '*'
1759+
ignore: 404
1760+
1761+
# We create the index no-template-index with an implicit mapping that has a num field with type long:
1762+
- do:
1763+
bulk:
1764+
refresh: true
1765+
body:
1766+
- '{"index": {"_index": "no-template-index"}}'
1767+
- '{"num": 3}'
1768+
1769+
# Now we make sure that the existing mapping is taken into account when we simulate with a mapping_addition. Since
1770+
# the pre-existing mapping has num mapped as a long, this ought to fail with a document_parsing_exception because
1771+
# we are attempting to write a boolean num.
1772+
- do:
1773+
headers:
1774+
Content-Type: application/json
1775+
simulate.ingest:
1776+
index: no-template-index
1777+
body: >
1778+
{
1779+
"docs": [
1780+
{
1781+
"_id": "test-id",
1782+
"_index": "no-template-index",
1783+
"_source": {
1784+
"@timestamp": "2025-07-25T09:06:06.929Z",
1785+
"is_valid": true,
1786+
"num": true
1787+
}
1788+
}
1789+
],
1790+
"mapping_addition": {
1791+
"properties": {
1792+
"is_valid": {
1793+
"type": "boolean"
1794+
}
1795+
}
1796+
}
1797+
}
1798+
- length: { docs: 1 }
1799+
- match: { docs.0.doc._index: "no-template-index" }
1800+
- match: { docs.0.doc._source.num: true }
1801+
- match: { docs.0.doc._source.is_valid: true }
1802+
- match: { docs.0.doc.error.type: "document_parsing_exception" }

server/src/main/java/org/elasticsearch/action/bulk/TransportSimulateBulkAction.java

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -200,30 +200,15 @@ private Tuple<Collection<String>, Exception> validateMappings(
200200
try {
201201
if (indexAbstraction != null
202202
&& componentTemplateSubstitutions.isEmpty()
203-
&& indexTemplateSubstitutions.isEmpty()
204-
&& mappingAddition.isEmpty()) {
203+
&& indexTemplateSubstitutions.isEmpty()) {
205204
/*
206-
* In this case the index exists and we don't have any component template overrides. So we can just use withTempIndexService
207-
* to do the mapping validation, using all the existing logic for validation.
205+
* In this case the index exists and we don't have any template overrides. So we can just merge the mappingAddition (which
206+
* might not exist) into the existing index mapping.
208207
*/
209208
IndexMetadata imd = project.getIndexSafe(indexAbstraction.getWriteIndex(request, project));
210-
indicesService.withTempIndexService(imd, indexService -> {
211-
indexService.mapperService().updateMapping(null, imd);
212-
return IndexShard.prepareIndex(
213-
indexService.mapperService(),
214-
sourceToParse,
215-
SequenceNumbers.UNASSIGNED_SEQ_NO,
216-
-1,
217-
-1,
218-
VersionType.INTERNAL,
219-
Engine.Operation.Origin.PRIMARY,
220-
Long.MIN_VALUE,
221-
false,
222-
request.ifSeqNo(),
223-
request.ifPrimaryTerm(),
224-
0
225-
);
226-
});
209+
CompressedXContent mappings = imd.mapping() == null ? null : imd.mapping().source();
210+
CompressedXContent mergedMappings = mappingAddition == null ? null : mergeMappings(mappings, mappingAddition);
211+
ignoredFields = validateUpdatedMappingsFromIndexMetadata(imd, mergedMappings, request, sourceToParse);
227212
} else {
228213
/*
229214
* The index did not exist, or we have component template substitutions, so we put together the mappings from existing
@@ -332,9 +317,6 @@ private Collection<String> validateUpdatedMappings(
332317
IndexRequest request,
333318
SourceToParse sourceToParse
334319
) throws IOException {
335-
if (updatedMappings == null) {
336-
return List.of(); // no validation to do
337-
}
338320
Settings dummySettings = Settings.builder()
339321
.put(IndexMetadata.SETTING_VERSION_CREATED, IndexVersion.current())
340322
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
@@ -346,8 +328,19 @@ private Collection<String> validateUpdatedMappings(
346328
originalIndexMetadataBuilder.putMapping(new MappingMetadata(originalMappings));
347329
}
348330
final IndexMetadata originalIndexMetadata = originalIndexMetadataBuilder.build();
331+
return validateUpdatedMappingsFromIndexMetadata(originalIndexMetadata, updatedMappings, request, sourceToParse);
332+
}
333+
private Collection<String> validateUpdatedMappingsFromIndexMetadata(
334+
IndexMetadata originalIndexMetadata,
335+
@Nullable CompressedXContent updatedMappings,
336+
IndexRequest request,
337+
SourceToParse sourceToParse
338+
) throws IOException {
339+
if (updatedMappings == null) {
340+
return List.of(); // no validation to do
341+
}
349342
final IndexMetadata updatedIndexMetadata = IndexMetadata.builder(request.index())
350-
.settings(dummySettings)
343+
.settings(originalIndexMetadata.getSettings())
351344
.putMapping(new MappingMetadata(updatedMappings))
352345
.build();
353346
Engine.Index result = indicesService.withTempIndexService(originalIndexMetadata, indexService -> {

0 commit comments

Comments
 (0)