Skip to content

Commit a8dae10

Browse files
authored
Remove the SearchContext from the highlighter context (#47733)
Today built-in highlighter and plugins have access to the SearchContext through the highlighter context. However most of the information exposed in the SearchContext are not needed and a QueryShardContext would be enough to perform highlighting. This change replaces the SearchContext by the informations that are absolutely required by highlighter: a QueryShardContext and the SearchContextHighlight. This change allows to reduce the exposure of the complex SearchContext and remove the needs to clone it in the percolator sub phase. Relates #47198 Relates #46523
1 parent 1092b39 commit a8dae10

File tree

12 files changed

+103
-76
lines changed

12 files changed

+103
-76
lines changed

modules/percolator/src/main/java/org/elasticsearch/percolator/PercolatorHighlightSubFetchPhase.java

+9-17
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,14 @@
3131
import org.elasticsearch.common.bytes.BytesReference;
3232
import org.elasticsearch.common.document.DocumentField;
3333
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
34-
import org.elasticsearch.index.query.ParsedQuery;
34+
import org.elasticsearch.index.query.QueryShardContext;
3535
import org.elasticsearch.search.SearchHit;
3636
import org.elasticsearch.search.fetch.FetchSubPhase;
3737
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
3838
import org.elasticsearch.search.fetch.subphase.highlight.HighlightPhase;
3939
import org.elasticsearch.search.fetch.subphase.highlight.Highlighter;
4040
import org.elasticsearch.search.fetch.subphase.highlight.SearchContextHighlight;
4141
import org.elasticsearch.search.internal.SearchContext;
42-
import org.elasticsearch.search.internal.SubSearchContext;
4342

4443
import java.io.IOException;
4544
import java.util.ArrayList;
@@ -99,15 +98,19 @@ public void hitsExecute(SearchContext context, SearchHit[] hits) throws IOExcept
9998
for (Object matchedSlot : field.getValues()) {
10099
int slot = (int) matchedSlot;
101100
BytesReference document = percolateQuery.getDocuments().get(slot);
102-
SubSearchContext subSearchContext =
103-
createSubSearchContext(context, percolatorLeafReaderContext, document, slot);
104-
subSearchContext.parsedQuery(new ParsedQuery(query));
101+
SearchContextHighlight highlight = new SearchContextHighlight(context.highlight().fields());
102+
// Enforce highlighting by source, because MemoryIndex doesn't support stored fields.
103+
highlight.globalForceSource(true);
104+
QueryShardContext shardContext = new QueryShardContext(context.getQueryShardContext());
105+
shardContext.freezeContext();
106+
shardContext.lookup().source().setSegmentAndDocument(percolatorLeafReaderContext, slot);
107+
shardContext.lookup().source().setSource(document);
105108
hitContext.reset(
106109
new SearchHit(slot, "unknown", Collections.emptyMap()),
107110
percolatorLeafReaderContext, slot, percolatorIndexSearcher
108111
);
109112
hitContext.cache().clear();
110-
highlightPhase.hitExecute(subSearchContext, hitContext);
113+
highlightPhase.hitExecute(context.shardTarget(), shardContext, query, highlight, hitContext);
111114
for (Map.Entry<String, HighlightField> entry : hitContext.hit().getHighlightFields().entrySet()) {
112115
if (percolateQuery.getDocuments().size() == 1) {
113116
String hlFieldName;
@@ -165,15 +168,4 @@ static List<PercolateQuery> locatePercolatorQuery(Query query) {
165168
}
166169
return Collections.emptyList();
167170
}
168-
169-
private SubSearchContext createSubSearchContext(SearchContext context, LeafReaderContext leafReaderContext,
170-
BytesReference source, int docId) {
171-
SubSearchContext subSearchContext = new SubSearchContext(context);
172-
subSearchContext.highlight(new SearchContextHighlight(context.highlight().fields()));
173-
// Enforce highlighting by source, because MemoryIndex doesn't support stored fields.
174-
subSearchContext.highlight().globalForceSource(true);
175-
subSearchContext.lookup().source().setSegmentAndDocument(leafReaderContext, docId);
176-
subSearchContext.lookup().source().setSource(source);
177-
return subSearchContext;
178-
}
179171
}

plugins/mapper-annotated-text/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/AnnotatedTextHighlighter.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
import org.elasticsearch.index.mapper.MappedFieldType;
2727
import org.elasticsearch.index.mapper.annotatedtext.AnnotatedTextFieldMapper.AnnotatedHighlighterAnalyzer;
2828
import org.elasticsearch.index.mapper.annotatedtext.AnnotatedTextFieldMapper.AnnotatedText;
29+
import org.elasticsearch.index.query.QueryShardContext;
2930
import org.elasticsearch.search.fetch.FetchSubPhase.HitContext;
3031
import org.elasticsearch.search.fetch.subphase.highlight.SearchContextHighlight.Field;
31-
import org.elasticsearch.search.internal.SearchContext;
3232

3333
import java.io.IOException;
3434
import java.util.ArrayList;
@@ -45,18 +45,21 @@ protected Analyzer getAnalyzer(DocumentMapper docMapper, HitContext hitContext)
4545

4646
// Convert the marked-up values held on-disk to plain-text versions for highlighting
4747
@Override
48-
protected List<Object> loadFieldValues(MappedFieldType fieldType, Field field, SearchContext context, HitContext hitContext)
49-
throws IOException {
50-
List<Object> fieldValues = super.loadFieldValues(fieldType, field, context, hitContext);
48+
protected List<Object> loadFieldValues(MappedFieldType fieldType,
49+
Field field,
50+
QueryShardContext context,
51+
HitContext hitContext,
52+
boolean forceSource) throws IOException {
53+
List<Object> fieldValues = super.loadFieldValues(fieldType, field, context, hitContext, forceSource);
5154
String[] fieldValuesAsString = fieldValues.toArray(new String[fieldValues.size()]);
52-
55+
5356
AnnotatedText[] annotations = new AnnotatedText[fieldValuesAsString.length];
5457
for (int i = 0; i < fieldValuesAsString.length; i++) {
5558
annotations[i] = AnnotatedText.parse(fieldValuesAsString[i]);
5659
}
5760
// Store the annotations in the hitContext
5861
hitContext.cache().put(AnnotatedText.class.getName(), annotations);
59-
62+
6063
ArrayList<Object> result = new ArrayList<>(annotations.length);
6164
for (int i = 0; i < annotations.length; i++) {
6265
result.add(annotations[i].textMinusMarkup);

server/src/main/java/org/elasticsearch/search/fetch/subphase/InnerHitsFetchSubPhase.java

+5
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ public void hitsExecute(SearchContext context, SearchHit[] hits) throws IOExcept
7373
}
7474
innerHits.docIdsToLoad(docIdsToLoad, 0, docIdsToLoad.length);
7575
innerHits.setUid(new Uid(MapperService.SINGLE_MAPPING_NAME, hit.getId()));
76+
innerHits.lookup().source().setSource(context.lookup().source().internalSourceRef());
77+
if (context.lookup().source().source() != null) {
78+
innerHits.lookup().source().setSource(context.lookup().source().source());
79+
}
80+
7681
fetchPhase.execute(innerHits);
7782
FetchSearchResult fetchResult = innerHits.fetchResult();
7883
SearchHit[] internalHits = fetchResult.fetchResult().hits().getHits();

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@
3737
import org.elasticsearch.common.settings.Settings;
3838
import org.elasticsearch.common.text.Text;
3939
import org.elasticsearch.index.mapper.MappedFieldType;
40+
import org.elasticsearch.index.query.QueryShardContext;
4041
import org.elasticsearch.search.fetch.FetchPhaseExecutionException;
4142
import org.elasticsearch.search.fetch.FetchSubPhase;
4243
import org.elasticsearch.search.fetch.subphase.highlight.SearchContextHighlight.Field;
4344
import org.elasticsearch.search.fetch.subphase.highlight.SearchContextHighlight.FieldOptions;
44-
import org.elasticsearch.search.internal.SearchContext;
4545

4646
import java.text.BreakIterator;
4747
import java.util.Collections;
@@ -69,7 +69,7 @@ public FastVectorHighlighter(Settings settings) {
6969
@Override
7070
public HighlightField highlight(HighlighterContext highlighterContext) {
7171
SearchContextHighlight.Field field = highlighterContext.field;
72-
SearchContext context = highlighterContext.context;
72+
QueryShardContext context = highlighterContext.context;
7373
FetchSubPhase.HitContext hitContext = highlighterContext.hitContext;
7474
MappedFieldType fieldType = highlighterContext.fieldType;
7575

@@ -93,7 +93,7 @@ public HighlightField highlight(HighlighterContext highlighterContext) {
9393
BaseFragmentsBuilder fragmentsBuilder;
9494

9595
final BoundaryScanner boundaryScanner = getBoundaryScanner(field);
96-
boolean forceSource = context.highlight().forceSource(field);
96+
boolean forceSource = highlighterContext.highlight.forceSource(field);
9797
if (field.fieldOptions().numberOfFragments() == 0) {
9898
fragListBuilder = new SingleFragListBuilder();
9999

@@ -203,7 +203,7 @@ public HighlightField highlight(HighlighterContext highlighterContext) {
203203
return null;
204204

205205
} catch (Exception e) {
206-
throw new FetchPhaseExecutionException(context.shardTarget(),
206+
throw new FetchPhaseExecutionException(highlighterContext.shardTarget,
207207
"Failed to highlight field [" + highlighterContext.fieldName + "]", e);
208208
}
209209
}

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

+20-10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.elasticsearch.index.mapper.MappedFieldType;
2626
import org.elasticsearch.index.mapper.SourceFieldMapper;
2727
import org.elasticsearch.index.mapper.TextFieldMapper;
28+
import org.elasticsearch.index.query.QueryShardContext;
29+
import org.elasticsearch.search.SearchShardTarget;
2830
import org.elasticsearch.search.fetch.FetchSubPhase;
2931
import org.elasticsearch.search.internal.SearchContext;
3032

@@ -45,26 +47,34 @@ public void hitExecute(SearchContext context, HitContext hitContext) {
4547
if (context.highlight() == null) {
4648
return;
4749
}
50+
hitExecute(context.shardTarget(), context.getQueryShardContext(), context.parsedQuery().query(), context.highlight(), hitContext);
51+
}
52+
53+
public void hitExecute(SearchShardTarget shardTarget,
54+
QueryShardContext context,
55+
Query query,
56+
SearchContextHighlight highlight,
57+
HitContext hitContext) {
4858
Map<String, HighlightField> highlightFields = new HashMap<>();
49-
for (SearchContextHighlight.Field field : context.highlight().fields()) {
59+
for (SearchContextHighlight.Field field : highlight.fields()) {
5060
Collection<String> fieldNamesToHighlight;
5161
if (Regex.isSimpleMatchPattern(field.field())) {
52-
fieldNamesToHighlight = context.mapperService().simpleMatchToFullName(field.field());
62+
fieldNamesToHighlight = context.getMapperService().simpleMatchToFullName(field.field());
5363
} else {
5464
fieldNamesToHighlight = Collections.singletonList(field.field());
5565
}
5666

57-
if (context.highlight().forceSource(field)) {
58-
SourceFieldMapper sourceFieldMapper = context.mapperService().documentMapper().sourceMapper();
59-
if (!sourceFieldMapper.enabled()) {
67+
if (highlight.forceSource(field)) {
68+
SourceFieldMapper sourceFieldMapper = context.getMapperService().documentMapper().sourceMapper();
69+
if (sourceFieldMapper.enabled() == false) {
6070
throw new IllegalArgumentException("source is forced for fields " + fieldNamesToHighlight
61-
+ " but _source is disabled");
71+
+ " but _source is disabled");
6272
}
6373
}
6474

6575
boolean fieldNameContainsWildcards = field.field().contains("*");
6676
for (String fieldName : fieldNamesToHighlight) {
67-
MappedFieldType fieldType = context.mapperService().fullName(fieldName);
77+
MappedFieldType fieldType = context.getMapperService().fullName(fieldName);
6878
if (fieldType == null) {
6979
continue;
7080
}
@@ -90,15 +100,15 @@ public void hitExecute(SearchContext context, HitContext hitContext) {
90100
Highlighter highlighter = highlighters.get(highlighterType);
91101
if (highlighter == null) {
92102
throw new IllegalArgumentException("unknown highlighter type [" + highlighterType
93-
+ "] for the field [" + fieldName + "]");
103+
+ "] for the field [" + fieldName + "]");
94104
}
95105

96106
Query highlightQuery = field.fieldOptions().highlightQuery();
97107
if (highlightQuery == null) {
98-
highlightQuery = context.parsedQuery().query();
108+
highlightQuery = query;
99109
}
100110
HighlighterContext highlighterContext = new HighlighterContext(fieldType.name(),
101-
field, fieldType, context, hitContext, highlightQuery);
111+
field, fieldType, shardTarget, context, highlight, hitContext, highlightQuery);
102112

103113
if ((highlighter.canHighlight(fieldType) == false) && fieldNameContainsWildcards) {
104114
// if several fieldnames matched the wildcard then we want to skip those that we cannot highlight

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

+7-8
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
import org.apache.lucene.search.highlight.SimpleHTMLEncoder;
2424
import org.elasticsearch.index.fieldvisitor.CustomFieldsVisitor;
2525
import org.elasticsearch.index.mapper.MappedFieldType;
26+
import org.elasticsearch.index.query.QueryShardContext;
2627
import org.elasticsearch.search.fetch.FetchSubPhase;
27-
import org.elasticsearch.search.internal.SearchContext;
2828
import org.elasticsearch.search.lookup.SourceLookup;
2929

3030
import java.io.IOException;
@@ -46,14 +46,13 @@ private HighlightUtils() {
4646
/**
4747
* Load field values for highlighting.
4848
*/
49-
public static List<Object> loadFieldValues(SearchContextHighlight.Field field,
50-
MappedFieldType fieldType,
51-
SearchContext searchContext,
52-
FetchSubPhase.HitContext hitContext) throws IOException {
49+
public static List<Object> loadFieldValues(MappedFieldType fieldType,
50+
QueryShardContext context,
51+
FetchSubPhase.HitContext hitContext,
52+
boolean forceSource) throws IOException {
5353
//percolator needs to always load from source, thus it sets the global force source to true
54-
boolean forceSource = searchContext.highlight().forceSource(field);
5554
List<Object> textsToHighlight;
56-
if (!forceSource && fieldType.stored()) {
55+
if (forceSource == false && fieldType.stored()) {
5756
CustomFieldsVisitor fieldVisitor = new CustomFieldsVisitor(singleton(fieldType.name()), false);
5857
hitContext.reader().document(hitContext.docId(), fieldVisitor);
5958
textsToHighlight = fieldVisitor.fields().get(fieldType.name());
@@ -62,7 +61,7 @@ public static List<Object> loadFieldValues(SearchContextHighlight.Field field,
6261
textsToHighlight = Collections.emptyList();
6362
}
6463
} else {
65-
SourceLookup sourceLookup = searchContext.lookup().source();
64+
SourceLookup sourceLookup = context.lookup().source();
6665
sourceLookup.setSegmentAndDocument(hitContext.readerContext(), hitContext.docId());
6766
textsToHighlight = sourceLookup.extractRawValues(fieldType.name());
6867
}

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

+10-3
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,35 @@
2020

2121
import org.apache.lucene.search.Query;
2222
import org.elasticsearch.index.mapper.MappedFieldType;
23+
import org.elasticsearch.index.query.QueryShardContext;
24+
import org.elasticsearch.search.SearchShardTarget;
2325
import org.elasticsearch.search.fetch.FetchSubPhase;
24-
import org.elasticsearch.search.internal.SearchContext;
2526

2627
public class HighlighterContext {
2728

2829
public final String fieldName;
2930
public final SearchContextHighlight.Field field;
3031
public final MappedFieldType fieldType;
31-
public final SearchContext context;
32+
public final SearchShardTarget shardTarget;
33+
public final QueryShardContext context;
34+
public final SearchContextHighlight highlight;
3235
public final FetchSubPhase.HitContext hitContext;
3336
public final Query query;
3437

3538
public HighlighterContext(String fieldName,
3639
SearchContextHighlight.Field field,
3740
MappedFieldType fieldType,
38-
SearchContext context,
41+
SearchShardTarget shardTarget,
42+
QueryShardContext context,
43+
SearchContextHighlight highlight,
3944
FetchSubPhase.HitContext hitContext,
4045
Query query) {
4146
this.fieldName = fieldName;
4247
this.field = field;
4348
this.fieldType = fieldType;
49+
this.shardTarget = shardTarget;
4450
this.context = context;
51+
this.highlight = highlight;
4552
this.hitContext = hitContext;
4653
this.query = query;
4754
}

0 commit comments

Comments
 (0)