Skip to content

Commit

Permalink
Fix index_prefix sub field name on nested text fields (#43862)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jimczi committed Jul 3, 2019
1 parent 6c323e8 commit 2ace8d8
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ public static class Defaults {
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);
Expand Down Expand Up @@ -160,6 +161,7 @@ public Builder fielddataFrequencyFilter(double minFreq, double maxFreq, int minS
}

public Builder indexPrefixes(int minChars, int maxChars) {

if (minChars > maxChars) {
throw new IllegalArgumentException("min_chars [" + minChars + "] must be less than max_chars [" + maxChars + "]");
}
Expand All @@ -169,8 +171,8 @@ public Builder indexPrefixes(int minChars, int maxChars) {
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;
}

Expand All @@ -187,7 +189,18 @@ public TextFieldMapper build(BuilderContext context) {
}
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() + "]");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
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;
Expand Down Expand Up @@ -636,7 +635,8 @@ public void testIndexPrefixIndexTypes() throws IOException {
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());
}

Expand All @@ -652,7 +652,8 @@ public void testIndexPrefixIndexTypes() throws IOException {
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());
}
Expand All @@ -669,11 +670,12 @@ public void testIndexPrefixIndexTypes() throws IOException {
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());
}
Expand All @@ -690,7 +692,8 @@ public void testIndexPrefixIndexTypes() throws IOException {
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 {
Expand All @@ -711,7 +714,8 @@ public void testIndexPrefixIndexTypes() throws IOException {
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 {
Expand All @@ -721,6 +725,60 @@ public void testIndexPrefixIndexTypes() throws IOException {
}
}

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(
Expand Down

0 comments on commit 2ace8d8

Please sign in to comment.