Skip to content

Commit b9600c7

Browse files
authored
Only update DocumentMapper if field type changes (#22165)
Merging mappings ensures that fields are used consistently across mapping types. Disabling norms for a specific field in one mapping type for example also disables norms for the same field in other mapping types of that index. The logic that ensures this while merging mappings currently always creates a fresh document mapper for all existing mapping types, even if no change occurred. Creating such a fresh document mapper does not come for free though as it involves recompressing the source. Making a mapping change to one type of an index with 100 types will thus re-serialize and recompress all 100 types, independent of any changes made to those types. This commit fixes the update logic to only create a new DocumentMapper if a field type actually changes.
1 parent cdd5fbe commit b9600c7

File tree

5 files changed

+64
-20
lines changed

5 files changed

+64
-20
lines changed

core/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,11 @@ public DocumentMapper merge(Mapping mapping, boolean updateAllTypes) {
327327
*/
328328
public DocumentMapper updateFieldType(Map<String, MappedFieldType> fullNameToFieldType) {
329329
Mapping updated = this.mapping.updateFieldType(fullNameToFieldType);
330+
if (updated == this.mapping) {
331+
// no change
332+
return this;
333+
}
334+
assert updated == updated.updateFieldType(fullNameToFieldType) : "updateFieldType operation is not idempotent";
330335
return new DocumentMapper(mapperService, updated);
331336
}
332337

core/src/main/java/org/elasticsearch/index/mapper/FieldTypeLookup.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public FieldTypeLookup copyAndAddAll(String type, Collection<FieldMapper> fieldM
9393
// is the update even legal?
9494
checkCompatibility(type, fieldMapper, updateAllTypes);
9595

96-
if (fieldType != fullNameFieldType) {
96+
if (fieldType.equals(fullNameFieldType) == false) {
9797
fullName = fullName.copyAndPut(fieldType.name(), fieldMapper.fieldType());
9898
}
9999

core/src/main/java/org/elasticsearch/index/mapper/Mapping.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,22 @@ public Mapping merge(Mapping mergeWith, boolean updateAllTypes) {
104104
* Recursively update sub field types.
105105
*/
106106
public Mapping updateFieldType(Map<String, MappedFieldType> fullNameToFieldType) {
107-
final MetadataFieldMapper[] updatedMeta = Arrays.copyOf(metadataMappers, metadataMappers.length);
108-
for (int i = 0; i < updatedMeta.length; ++i) {
109-
updatedMeta[i] = (MetadataFieldMapper) updatedMeta[i].updateFieldType(fullNameToFieldType);
107+
MetadataFieldMapper[] updatedMeta = null;
108+
for (int i = 0; i < metadataMappers.length; ++i) {
109+
MetadataFieldMapper currentFieldMapper = metadataMappers[i];
110+
MetadataFieldMapper updatedFieldMapper = (MetadataFieldMapper) currentFieldMapper.updateFieldType(fullNameToFieldType);
111+
if (updatedFieldMapper != currentFieldMapper) {
112+
if (updatedMeta == null) {
113+
updatedMeta = Arrays.copyOf(metadataMappers, metadataMappers.length);
114+
}
115+
updatedMeta[i] = updatedFieldMapper;
116+
}
110117
}
111118
RootObjectMapper updatedRoot = root.updateFieldType(fullNameToFieldType);
112-
return new Mapping(indexCreated, updatedRoot, updatedMeta, meta);
119+
if (updatedMeta == null && updatedRoot == root) {
120+
return this;
121+
}
122+
return new Mapping(indexCreated, updatedRoot, updatedMeta == null ? metadataMappers : updatedMeta, meta);
113123
}
114124

115125
@Override

core/src/test/java/org/elasticsearch/index/mapper/FieldTypeLookupTests.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,12 @@ public void testAddExistingField() {
7272
MockFieldMapper f = new MockFieldMapper("foo");
7373
MockFieldMapper f2 = new MockFieldMapper("foo");
7474
FieldTypeLookup lookup = new FieldTypeLookup();
75-
lookup = lookup.copyAndAddAll("type1", newList(f), randomBoolean());
76-
FieldTypeLookup lookup2 = lookup.copyAndAddAll("type2", newList(f2), randomBoolean());
75+
lookup = lookup.copyAndAddAll("type1", newList(f), true);
76+
FieldTypeLookup lookup2 = lookup.copyAndAddAll("type2", newList(f2), true);
7777

78-
assertSame(f2.fieldType(), lookup2.get("foo"));
7978
assertEquals(1, size(lookup2.iterator()));
79+
assertSame(f.fieldType(), lookup2.get("foo"));
80+
assertEquals(f2.fieldType(), lookup2.get("foo"));
8081
}
8182

8283
public void testAddExistingIndexName() {

core/src/test/java/org/elasticsearch/index/mapper/MapperServiceTests.java

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,6 @@
1919

2020
package org.elasticsearch.index.mapper;
2121

22-
import java.io.IOException;
23-
import java.io.UncheckedIOException;
24-
import java.util.Arrays;
25-
import java.util.Collections;
26-
import java.util.HashMap;
27-
import java.util.HashSet;
28-
import java.util.Map;
29-
import java.util.concurrent.ExecutionException;
30-
import java.util.function.Function;
31-
3222
import org.elasticsearch.ExceptionsHelper;
3323
import org.elasticsearch.common.compress.CompressedXContent;
3424
import org.elasticsearch.common.settings.Settings;
@@ -39,8 +29,17 @@
3929
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType;
4030
import org.elasticsearch.test.ESSingleNodeTestCase;
4131

32+
import java.io.IOException;
33+
import java.io.UncheckedIOException;
34+
import java.util.Arrays;
35+
import java.util.Collections;
36+
import java.util.HashMap;
37+
import java.util.HashSet;
38+
import java.util.Map;
39+
import java.util.concurrent.ExecutionException;
40+
import java.util.function.Function;
41+
4242
import static org.hamcrest.CoreMatchers.containsString;
43-
import static org.hamcrest.Matchers.hasToString;
4443
import static org.hamcrest.Matchers.instanceOf;
4544
import static org.hamcrest.Matchers.startsWith;
4645

@@ -169,7 +168,6 @@ public void testUnmappedFieldType() {
169168
assertThat(mapperService.unmappedFieldType("string"), instanceOf(KeywordFieldType.class));
170169
}
171170

172-
173171
public void testMergeWithMap() throws Throwable {
174172
IndexService indexService1 = createIndex("index1");
175173
MapperService mapperService = indexService1.mapperService();
@@ -187,4 +185,34 @@ public void testMergeWithMap() throws Throwable {
187185
() -> mapperService.merge(mappings, false));
188186
assertThat(e.getMessage(), startsWith("Failed to parse mapping [type1]: "));
189187
}
188+
189+
public void testOtherDocumentMappersOnlyUpdatedWhenChangingFieldType() throws IOException {
190+
IndexService indexService = createIndex("test");
191+
192+
CompressedXContent simpleMapping = new CompressedXContent(XContentFactory.jsonBuilder().startObject()
193+
.startObject("properties")
194+
.startObject("field")
195+
.field("type", "text")
196+
.endObject()
197+
.endObject().endObject().bytes());
198+
199+
indexService.mapperService().merge("type1", simpleMapping, MergeReason.MAPPING_UPDATE, true);
200+
DocumentMapper documentMapper = indexService.mapperService().documentMapper("type1");
201+
202+
indexService.mapperService().merge("type2", simpleMapping, MergeReason.MAPPING_UPDATE, true);
203+
assertSame(indexService.mapperService().documentMapper("type1"), documentMapper);
204+
205+
CompressedXContent normsDisabledMapping = new CompressedXContent(XContentFactory.jsonBuilder().startObject()
206+
.startObject("properties")
207+
.startObject("field")
208+
.field("type", "text")
209+
.startObject("norms")
210+
.field("enabled", false)
211+
.endObject()
212+
.endObject()
213+
.endObject().endObject().bytes());
214+
215+
indexService.mapperService().merge("type3", normsDisabledMapping, MergeReason.MAPPING_UPDATE, true);
216+
assertNotSame(indexService.mapperService().documentMapper("type1"), documentMapper);
217+
}
190218
}

0 commit comments

Comments
 (0)