Skip to content

Commit bbe90ed

Browse files
committed
Store offsets in index prefix fields when stored in the parent field (#29067)
The index prefix field is normally indexed as docs-only, given that it cannot be used in phrases. However, in the case that the parent field has been indexed with offsets, or has term-vector offsets, we should also store this in the index prefix field for highlighting. Note that this commit does not implement highlighting on prefix fields, but rather ensures that future work can implement this without a backwards-break in index data. Closes #28994
1 parent e497df6 commit bbe90ed

File tree

2 files changed

+89
-4
lines changed

2 files changed

+89
-4
lines changed

server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.apache.lucene.analysis.TokenFilter;
2525
import org.apache.lucene.analysis.ngram.EdgeNGramTokenFilter;
2626
import org.apache.lucene.document.Field;
27+
import org.apache.lucene.document.FieldType;
2728
import org.apache.lucene.index.IndexOptions;
2829
import org.apache.lucene.index.IndexableField;
2930
import org.apache.lucene.index.Term;
@@ -152,11 +153,20 @@ public TextFieldMapper build(BuilderContext context) {
152153
fieldType.setSearchQuoteAnalyzer(new NamedAnalyzer(fieldType.searchQuoteAnalyzer(), positionIncrementGap));
153154
}
154155
setupFieldType(context);
155-
if (prefixFieldType != null && fieldType().isSearchable() == false) {
156-
throw new IllegalArgumentException("Cannot set index_prefix on unindexed field [" + name() + "]");
156+
PrefixFieldMapper prefixMapper = null;
157+
if (prefixFieldType != null) {
158+
if (fieldType().isSearchable() == false) {
159+
throw new IllegalArgumentException("Cannot set index_prefix on unindexed field [" + name() + "]");
160+
}
161+
if (fieldType.indexOptions() == IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) {
162+
prefixFieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
163+
}
164+
if (fieldType.storeTermVectorOffsets()) {
165+
prefixFieldType.setStoreTermVectorOffsets(true);
166+
}
167+
prefixFieldType.setAnalyzer(fieldType.indexAnalyzer());
168+
prefixMapper = new PrefixFieldMapper(prefixFieldType, context.indexSettings());
157169
}
158-
PrefixFieldMapper prefixMapper = prefixFieldType == null ? null
159-
: new PrefixFieldMapper(prefixFieldType.setAnalyzer(fieldType.indexAnalyzer()), context.indexSettings());
160170
return new TextFieldMapper(
161171
name, fieldType, defaultFieldType, positionIncrementGap, includeInAll, prefixMapper,
162172
context.indexSettings(), multiFieldsBuilder.build(this, context), copyTo);

server/src/test/java/org/elasticsearch/index/mapper/TextFieldMapperTests.java

+75
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package org.elasticsearch.index.mapper;
2121

22+
import org.apache.lucene.document.FieldType;
2223
import org.apache.lucene.index.DocValuesType;
2324
import org.apache.lucene.index.IndexOptions;
2425
import org.apache.lucene.index.IndexableField;
@@ -595,6 +596,80 @@ public void testEmptyName() throws IOException {
595596
assertThat(e.getMessage(), containsString("name cannot be empty string"));
596597
}
597598

599+
public void testIndexPrefixIndexTypes() throws IOException {
600+
QueryShardContext queryShardContext = indexService.newQueryShardContext(
601+
randomInt(20), null, () -> {
602+
throw new UnsupportedOperationException();
603+
}, null);
604+
605+
{
606+
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
607+
.startObject("properties").startObject("field")
608+
.field("type", "text")
609+
.field("analyzer", "english")
610+
.startObject("index_prefix").endObject()
611+
.field("index_options", "offsets")
612+
.endObject().endObject().endObject().endObject());
613+
614+
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
615+
616+
FieldMapper prefix = mapper.mappers().getMapper("field._index_prefix");
617+
FieldType ft = prefix.fieldType;
618+
assertEquals(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS, ft.indexOptions());
619+
}
620+
621+
{
622+
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
623+
.startObject("properties").startObject("field")
624+
.field("type", "text")
625+
.field("analyzer", "english")
626+
.startObject("index_prefix").endObject()
627+
.field("index_options", "positions")
628+
.endObject().endObject().endObject().endObject());
629+
630+
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
631+
632+
FieldMapper prefix = mapper.mappers().getMapper("field._index_prefix");
633+
FieldType ft = prefix.fieldType;
634+
assertEquals(IndexOptions.DOCS, ft.indexOptions());
635+
assertFalse(ft.storeTermVectors());
636+
}
637+
638+
{
639+
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
640+
.startObject("properties").startObject("field")
641+
.field("type", "text")
642+
.field("analyzer", "english")
643+
.startObject("index_prefix").endObject()
644+
.field("term_vector", "with_positions_offsets")
645+
.endObject().endObject().endObject().endObject());
646+
647+
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
648+
649+
FieldMapper prefix = mapper.mappers().getMapper("field._index_prefix");
650+
FieldType ft = prefix.fieldType;
651+
assertEquals(IndexOptions.DOCS, ft.indexOptions());
652+
assertTrue(ft.storeTermVectorOffsets());
653+
}
654+
655+
{
656+
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
657+
.startObject("properties").startObject("field")
658+
.field("type", "text")
659+
.field("analyzer", "english")
660+
.startObject("index_prefix").endObject()
661+
.field("term_vector", "with_positions")
662+
.endObject().endObject().endObject().endObject());
663+
664+
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
665+
666+
FieldMapper prefix = mapper.mappers().getMapper("field._index_prefix");
667+
FieldType ft = prefix.fieldType;
668+
assertEquals(IndexOptions.DOCS, ft.indexOptions());
669+
assertFalse(ft.storeTermVectorOffsets());
670+
}
671+
}
672+
598673
public void testIndexPrefixMapping() throws IOException {
599674

600675
QueryShardContext queryShardContext = indexService.newQueryShardContext(

0 commit comments

Comments
 (0)