Fix index_prefix sub field name on nested text fields (#43862)
This change fixes the name of the index_prefix sub field when the `index_prefix` option is set on a text field that is nested under an object or a multi-field. We don't use the full path of the parent field to set the index_prefix field name so the field is registered under the wrong name. This doesn't break queries since we always retrieve the prefix field through its parent field but this breaks other APIs like _field_caps which tries to find the parent of the `index_prefix` field in the mapping but fails. Closes #43741
This commit is contained in:
parent
826f38cd70
commit
05c0cff1b6
|
@ -111,7 +111,8 @@ public class TextFieldMapper extends FieldMapper {
|
|||
public static class Builder extends FieldMapper.Builder<Builder, TextFieldMapper> {
|
||||
|
||||
private int positionIncrementGap = POSITION_INCREMENT_GAP_USE_ANALYZER;
|
||||
private PrefixFieldType prefixFieldType;
|
||||
private int minPrefixChars = -1;
|
||||
private int maxPrefixChars = -1;
|
||||
|
||||
public Builder(String name) {
|
||||
super(name, Defaults.FIELD_TYPE, Defaults.FIELD_TYPE);
|
||||
|
@ -162,6 +163,7 @@ public class TextFieldMapper extends FieldMapper {
|
|||
}
|
||||
|
||||
public Builder indexPrefixes(int minChars, int maxChars) {
|
||||
|
||||
if (minChars > maxChars) {
|
||||
throw new IllegalArgumentException("min_chars [" + minChars + "] must be less than max_chars [" + maxChars + "]");
|
||||
}
|
||||
|
@ -171,8 +173,8 @@ public class TextFieldMapper extends FieldMapper {
|
|||
if (maxChars >= 20) {
|
||||
throw new IllegalArgumentException("max_chars [" + maxChars + "] must be less than 20");
|
||||
}
|
||||
this.prefixFieldType = new PrefixFieldType(name(), name() + "._index_prefix", minChars, maxChars);
|
||||
fieldType().setPrefixFieldType(this.prefixFieldType);
|
||||
this.minPrefixChars = minChars;
|
||||
this.maxPrefixChars = maxChars;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -189,7 +191,18 @@ public class TextFieldMapper extends FieldMapper {
|
|||
}
|
||||
setupFieldType(context);
|
||||
PrefixFieldMapper prefixMapper = null;
|
||||
if (prefixFieldType != null) {
|
||||
if (minPrefixChars != -1) {
|
||||
/**
|
||||
* Mappings before v7.2.1 use {@link Builder#name} instead of {@link Builder#fullName}
|
||||
* to build prefix field names so we preserve the name that was used at creation time
|
||||
* even if it is different from the expected one (in case the field is nested under an object
|
||||
* or a multi-field). This way search will continue to work on old indices and new indices
|
||||
* will use the expected full name.
|
||||
**/
|
||||
String fullName = context.indexCreatedVersion().before(Version.V_7_2_1) ? name() : buildFullName(context);
|
||||
PrefixFieldType prefixFieldType =
|
||||
new PrefixFieldType(fullName, fullName + "._index_prefix", minPrefixChars, maxPrefixChars);
|
||||
fieldType().setPrefixFieldType(prefixFieldType);
|
||||
if (fieldType().isSearchable() == false) {
|
||||
throw new IllegalArgumentException("Cannot set index_prefixes on unindexed field [" + name() + "]");
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ package org.elasticsearch.index.mapper;
|
|||
import org.apache.lucene.analysis.MockSynonymAnalyzer;
|
||||
import org.apache.lucene.analysis.TokenStream;
|
||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
|
@ -636,7 +635,8 @@ public class TextFieldMapperTests extends ESSingleNodeTestCase {
|
|||
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
|
||||
FieldType ft = prefix.fieldType;
|
||||
MappedFieldType ft = prefix.fieldType;
|
||||
assertEquals(ft.name(), "field._index_prefix");
|
||||
assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, ft.indexOptions());
|
||||
}
|
||||
|
||||
|
@ -652,7 +652,8 @@ public class TextFieldMapperTests extends ESSingleNodeTestCase {
|
|||
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
|
||||
FieldType ft = prefix.fieldType;
|
||||
MappedFieldType ft = prefix.fieldType;
|
||||
assertEquals(ft.name(), "field._index_prefix");
|
||||
assertEquals(IndexOptions.DOCS, ft.indexOptions());
|
||||
assertFalse(ft.storeTermVectors());
|
||||
}
|
||||
|
@ -669,11 +670,12 @@ public class TextFieldMapperTests extends ESSingleNodeTestCase {
|
|||
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
|
||||
FieldType ft = prefix.fieldType;
|
||||
MappedFieldType ft = prefix.fieldType;
|
||||
assertEquals(ft.name(), "field._index_prefix");
|
||||
if (indexService.getIndexSettings().getIndexVersionCreated().onOrAfter(Version.V_6_4_0)) {
|
||||
assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, ft.indexOptions());
|
||||
assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, ft.indexOptions());
|
||||
} else {
|
||||
assertEquals(IndexOptions.DOCS, ft.indexOptions());
|
||||
assertEquals(IndexOptions.DOCS, ft.indexOptions());
|
||||
}
|
||||
assertFalse(ft.storeTermVectors());
|
||||
}
|
||||
|
@ -690,7 +692,8 @@ public class TextFieldMapperTests extends ESSingleNodeTestCase {
|
|||
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
|
||||
FieldType ft = prefix.fieldType;
|
||||
MappedFieldType ft = prefix.fieldType;
|
||||
assertEquals(ft.name(), "field._index_prefix");
|
||||
if (indexService.getIndexSettings().getIndexVersionCreated().onOrAfter(Version.V_6_4_0)) {
|
||||
assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, ft.indexOptions());
|
||||
} else {
|
||||
|
@ -711,7 +714,8 @@ public class TextFieldMapperTests extends ESSingleNodeTestCase {
|
|||
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
FieldMapper prefix = (FieldMapper) mapper.mappers().getMapper("field._index_prefix");
|
||||
FieldType ft = prefix.fieldType;
|
||||
MappedFieldType ft = prefix.fieldType;
|
||||
assertEquals(ft.name(), "field._index_prefix");
|
||||
if (indexService.getIndexSettings().getIndexVersionCreated().onOrAfter(Version.V_6_4_0)) {
|
||||
assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, ft.indexOptions());
|
||||
} else {
|
||||
|
@ -721,6 +725,60 @@ public class TextFieldMapperTests extends ESSingleNodeTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testNestedIndexPrefixes() throws IOException {
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("properties")
|
||||
.startObject("object")
|
||||
.field("type", "object")
|
||||
.startObject("properties")
|
||||
.startObject("field")
|
||||
.field("type", "text")
|
||||
.startObject("index_prefixes").endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject());
|
||||
|
||||
indexService.mapperService().merge("type", new CompressedXContent(mapping), MergeReason.MAPPING_UPDATE);
|
||||
MappedFieldType textField = indexService.mapperService().fullName("object.field");
|
||||
assertNotNull(textField);
|
||||
assertThat(textField, instanceOf(TextFieldType.class));
|
||||
MappedFieldType prefix = ((TextFieldType) textField).getPrefixFieldType();
|
||||
assertEquals(prefix.name(), "object.field._index_prefix");
|
||||
assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, prefix.indexOptions());
|
||||
assertFalse(prefix.storeTermVectorOffsets());
|
||||
}
|
||||
|
||||
{
|
||||
String mapping = Strings.toString(XContentFactory.jsonBuilder()
|
||||
.startObject()
|
||||
.startObject("properties")
|
||||
.startObject("body")
|
||||
.field("type", "text")
|
||||
.startObject("fields")
|
||||
.startObject("with_prefix")
|
||||
.field("type", "text")
|
||||
.startObject("index_prefixes").endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject()
|
||||
.endObject());
|
||||
|
||||
indexService.mapperService().merge("type", new CompressedXContent(mapping), MergeReason.MAPPING_UPDATE);
|
||||
MappedFieldType textField = indexService.mapperService().fullName("body.with_prefix");
|
||||
assertNotNull(textField);
|
||||
assertThat(textField, instanceOf(TextFieldType.class));
|
||||
MappedFieldType prefix = ((TextFieldType) textField).getPrefixFieldType();
|
||||
assertEquals(prefix.name(), "body.with_prefix._index_prefix");
|
||||
assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS, prefix.indexOptions());
|
||||
assertFalse(prefix.storeTermVectorOffsets());
|
||||
}
|
||||
}
|
||||
|
||||
public void testFastPhraseMapping() throws IOException {
|
||||
|
||||
QueryShardContext queryShardContext = indexService.newQueryShardContext(
|
||||
|
|
Loading…
Reference in New Issue