Skip to content

Commit 4e2e02d

Browse files
committed
Enable Mapped Field Types to Override Default Highlighter
This commit introduces the `MappedFieldType#getDefaultHighlighter`, allowing a specific highlighter to be enforced for a field. The semantic field mapper utilizes this new functionality to set the `semantic` highlighter as the default. All other fields will continue to use the `unified` highlighter by default.
1 parent 348a0bc commit 4e2e02d

File tree

7 files changed

+50
-17
lines changed

7 files changed

+50
-17
lines changed

docs/reference/mapping/types/semantic-text.asciidoc

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,13 @@ You can extract the most relevant fragments from a semantic text field by using
133133
PUT test-index
134134
{
135135
"query": {
136-
"semantic": {
137-
"field": "my_semantic_field"
136+
"match": {
137+
"my_semantic_field": "Which country is Paris in?"
138138
}
139139
},
140140
"highlight": {
141141
"fields": {
142142
"my_semantic_field": {
143-
"type": "semantic",
144143
"number_of_fragments": 2, <1>
145144
"order": "score" <2>
146145
}
@@ -152,6 +151,33 @@ PUT test-index
152151
<1> Specifies the maximum number of fragments to return.
153152
<2> Sorts highlighted fragments by score when set to `score`. By default, fragments will be output in the order they appear in the field (order: none).
154153

154+
Highlighting is supported on fields other than semantic_text.
155+
However, if you want to restrict highlighting to the semantic highlighter and return no fragments when the field is not of type semantic_text,
156+
you can explicitly enforce the `semantic` highlighter in the query:
157+
158+
[source,console]
159+
------------------------------------------------------------
160+
PUT test-index
161+
{
162+
"query": {
163+
"match": {
164+
"my_field": "Which country is Paris in?"
165+
}
166+
},
167+
"highlight": {
168+
"fields": {
169+
"my_field": {
170+
"type": "semantic", <1>
171+
"number_of_fragments": 2,
172+
"order": "score"
173+
}
174+
}
175+
}
176+
}
177+
------------------------------------------------------------
178+
// TEST[skip:Requires inference endpoint]
179+
<1> Ensures that highlighting is applied exclusively to semantic_text fields.
180+
155181
[discrete]
156182
[[custom-indexing]]
157183
==== Customizing `semantic_text` indexing

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.elasticsearch.index.query.SearchExecutionContext;
4242
import org.elasticsearch.search.DocValueFormat;
4343
import org.elasticsearch.search.fetch.subphase.FetchFieldsPhase;
44+
import org.elasticsearch.search.fetch.subphase.highlight.DefaultHighlighter;
4445
import org.elasticsearch.search.lookup.SearchLookup;
4546

4647
import java.io.IOException;
@@ -217,6 +218,13 @@ public TimeSeriesParams.MetricType getMetricType() {
217218
return null;
218219
}
219220

221+
/**
222+
* Returns the default highlighter type to use when highlighting the field.
223+
*/
224+
public String getDefaultHighlighter() {
225+
return DefaultHighlighter.NAME;
226+
}
227+
220228
/** Generates a query that will only match documents that contain the given value.
221229
* The default implementation returns a {@link TermQuery} over the value bytes
222230
* @throws IllegalArgumentException if {@code value} cannot be converted to the expected data type or if the field is not searchable

server/src/main/java/org/elasticsearch/search/SearchModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,7 @@ private static Map<String, Highlighter> setupHighlighters(Settings settings, Lis
913913
NamedRegistry<Highlighter> highlighters = new NamedRegistry<>("highlighter");
914914
highlighters.register("fvh", new FastVectorHighlighter(settings));
915915
highlighters.register("plain", new PlainHighlighter());
916-
highlighters.register("unified", new DefaultHighlighter());
916+
highlighters.register(DefaultHighlighter.NAME, new DefaultHighlighter());
917917
highlighters.extractAndRegister(plugins, SearchPlugin::getHighlighters);
918918

919919
return unmodifiableMap(highlighters.getRegistry());

server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/DefaultHighlighter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050

5151
public class DefaultHighlighter implements Highlighter {
5252

53+
public static final String NAME = "unified";
54+
5355
@Override
5456
public boolean canHighlight(MappedFieldType fieldType) {
5557
return true;

server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/HighlightPhase.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void process(HitContext hitContext) throws IOException {
6666
Map<String, Function<HitContext, FieldHighlightContext>> contextBuilders = fieldContext.builders;
6767
for (String field : contextBuilders.keySet()) {
6868
FieldHighlightContext fieldContext = contextBuilders.get(field).apply(hitContext);
69-
Highlighter highlighter = getHighlighter(fieldContext.field);
69+
Highlighter highlighter = getHighlighter(fieldContext.field, fieldContext.fieldType);
7070
HighlightField highlightField = highlighter.highlight(fieldContext);
7171
if (highlightField != null) {
7272
// Note that we make sure to use the original field name in the response. This is because the
@@ -80,10 +80,10 @@ public void process(HitContext hitContext) throws IOException {
8080
};
8181
}
8282

83-
private Highlighter getHighlighter(SearchHighlightContext.Field field) {
83+
private Highlighter getHighlighter(SearchHighlightContext.Field field, MappedFieldType fieldType) {
8484
String highlighterType = field.fieldOptions().highlighterType();
8585
if (highlighterType == null) {
86-
highlighterType = "unified";
86+
highlighterType = fieldType.getDefaultHighlighter();
8787
}
8888
Highlighter highlighter = highlighters.get(highlighterType);
8989
if (highlighter == null) {
@@ -103,15 +103,14 @@ private FieldContext contextBuilders(
103103
Map<String, Function<HitContext, FieldHighlightContext>> builders = new LinkedHashMap<>();
104104
StoredFieldsSpec storedFieldsSpec = StoredFieldsSpec.NO_REQUIREMENTS;
105105
for (SearchHighlightContext.Field field : highlightContext.fields()) {
106-
Highlighter highlighter = getHighlighter(field);
107-
108106
Collection<String> fieldNamesToHighlight = context.getSearchExecutionContext().getMatchingFieldNames(field.field());
109107

110108
boolean fieldNameContainsWildcards = field.field().contains("*");
111109
Set<String> storedFields = new HashSet<>();
112110
boolean sourceRequired = false;
113111
for (String fieldName : fieldNamesToHighlight) {
114112
MappedFieldType fieldType = context.getSearchExecutionContext().getFieldType(fieldName);
113+
Highlighter highlighter = getHighlighter(field, fieldType);
115114

116115
// We should prevent highlighting if a field is anything but a text, match_only_text,
117116
// or keyword field.

x-pack/plugin/inference/src/main/java/org/elasticsearch/xpack/inference/mapper/SemanticTextFieldMapper.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
import org.elasticsearch.xpack.core.ml.inference.results.MlTextEmbeddingResults;
7474
import org.elasticsearch.xpack.core.ml.inference.results.TextExpansionResults;
7575
import org.elasticsearch.xpack.core.ml.search.SparseVectorQueryBuilder;
76+
import org.elasticsearch.xpack.inference.highlight.SemanticTextHighlighter;
7677

7778
import java.io.IOException;
7879
import java.io.UncheckedIOException;
@@ -580,6 +581,11 @@ public String familyTypeName() {
580581
return TextFieldMapper.CONTENT_TYPE;
581582
}
582583

584+
@Override
585+
public String getDefaultHighlighter() {
586+
return SemanticTextHighlighter.NAME;
587+
}
588+
583589
public String getInferenceId() {
584590
return inferenceId;
585591
}

x-pack/plugin/inference/src/yamlRestTest/resources/rest-api-spec/test/inference/90_semantic_text_highlighter.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ setup:
8282
highlight:
8383
fields:
8484
body:
85-
type: "semantic"
8685
number_of_fragments: 1
8786

8887
- match: { hits.total.value: 1 }
@@ -101,7 +100,6 @@ setup:
101100
highlight:
102101
fields:
103102
body:
104-
type: "semantic"
105103
number_of_fragments: 2
106104

107105
- match: { hits.total.value: 1 }
@@ -121,7 +119,6 @@ setup:
121119
highlight:
122120
fields:
123121
body:
124-
type: "semantic"
125122
order: "score"
126123
number_of_fragments: 1
127124

@@ -141,7 +138,6 @@ setup:
141138
highlight:
142139
fields:
143140
body:
144-
type: "semantic"
145141
order: "score"
146142
number_of_fragments: 2
147143

@@ -174,7 +170,6 @@ setup:
174170
highlight:
175171
fields:
176172
body:
177-
type: "semantic"
178173
number_of_fragments: 1
179174

180175
- match: { hits.total.value: 1 }
@@ -193,7 +188,6 @@ setup:
193188
highlight:
194189
fields:
195190
body:
196-
type: "semantic"
197191
number_of_fragments: 2
198192

199193
- match: { hits.total.value: 1 }
@@ -213,7 +207,6 @@ setup:
213207
highlight:
214208
fields:
215209
body:
216-
type: "semantic"
217210
order: "score"
218211
number_of_fragments: 1
219212

@@ -233,7 +226,6 @@ setup:
233226
highlight:
234227
fields:
235228
body:
236-
type: "semantic"
237229
order: "score"
238230
number_of_fragments: 2
239231

0 commit comments

Comments
 (0)