Skip to content

Commit 6be55b4

Browse files
committed
Hack in highlighter so it actually produces a response
1 parent 8adea56 commit 6be55b4

File tree

5 files changed

+61
-34
lines changed

5 files changed

+61
-34
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,13 @@ public boolean canHighlight(MappedFieldType fieldType) {
6060
@Override
6161
public HighlightField highlight(FieldHighlightContext fieldContext) throws IOException {
6262
@SuppressWarnings("unchecked")
63-
Map<String, CustomUnifiedHighlighter> cache = (Map<String, CustomUnifiedHighlighter>) fieldContext.cache.computeIfAbsent(
63+
// Map<String, CustomUnifiedHighlighter> cache = (Map<String, CustomUnifiedHighlighter>) fieldContext.cache.computeIfAbsent(
64+
// UnifiedHighlighter.class.getName(),
65+
// k -> new HashMap<>()
66+
// );
67+
Map<String, CustomUnifiedHighlighter> cache = (Map<String, CustomUnifiedHighlighter>) fieldContext.cache.getOrDefault(
6468
UnifiedHighlighter.class.getName(),
65-
k -> new HashMap<>()
69+
new HashMap<>()
6670
);
6771
if (cache.containsKey(fieldContext.fieldName) == false) {
6872
cache.put(fieldContext.fieldName, buildHighlighter(fieldContext));
@@ -114,7 +118,7 @@ public HighlightField highlight(FieldHighlightContext fieldContext) throws IOExc
114118

115119
CustomUnifiedHighlighter buildHighlighter(FieldHighlightContext fieldContext) {
116120
IndexSettings indexSettings = fieldContext.context.getSearchExecutionContext().getIndexSettings();
117-
Encoder encoder = fieldContext.field.fieldOptions().encoder().equals("html")
121+
Encoder encoder = "html".equals(fieldContext.field.fieldOptions().encoder())
118122
? HighlightUtils.Encoders.HTML
119123
: HighlightUtils.Encoders.DEFAULT;
120124

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,17 +209,17 @@ Builder encoder(String encoder) {
209209
return this;
210210
}
211211

212-
Builder preTags(String[] preTags) {
212+
public Builder preTags(String[] preTags) {
213213
fieldOptions.preTags = preTags;
214214
return this;
215215
}
216216

217-
Builder postTags(String[] postTags) {
217+
public Builder postTags(String[] postTags) {
218218
fieldOptions.postTags = postTags;
219219
return this;
220220
}
221221

222-
Builder scoreOrdered(boolean scoreOrdered) {
222+
public Builder scoreOrdered(boolean scoreOrdered) {
223223
fieldOptions.scoreOrdered = scoreOrdered;
224224
return this;
225225
}
@@ -229,7 +229,7 @@ Builder highlightFilter(boolean highlightFilter) {
229229
return this;
230230
}
231231

232-
Builder requireFieldMatch(boolean requireFieldMatch) {
232+
public Builder requireFieldMatch(boolean requireFieldMatch) {
233233
fieldOptions.requireFieldMatch = requireFieldMatch;
234234
return this;
235235
}

x-pack/plugin/esql/compute/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
requires org.elasticsearch.geo;
2222
requires org.elasticsearch.xcore;
2323
requires hppc;
24+
requires org.apache.lucene.highlighter;
2425

2526
exports org.elasticsearch.compute;
2627
exports org.elasticsearch.compute.aggregation;

x-pack/plugin/esql/compute/src/main/java/org/elasticsearch/compute/lucene/HighlighterExpressionEvaluator.java

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,18 @@
2828
import org.elasticsearch.search.fetch.FetchSubPhase;
2929
import org.elasticsearch.search.fetch.subphase.highlight.DefaultHighlighter;
3030
import org.elasticsearch.search.fetch.subphase.highlight.FieldHighlightContext;
31+
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
3132
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
3233
import org.elasticsearch.search.fetch.subphase.highlight.Highlighter;
3334
import org.elasticsearch.search.fetch.subphase.highlight.SearchHighlightContext;
3435
import org.elasticsearch.search.internal.SearchContext;
3536
import org.elasticsearch.search.lookup.Source;
37+
import org.elasticsearch.xcontent.Text;
3638

3739
import java.io.IOException;
3840
import java.io.UncheckedIOException;
3941
import java.util.Collections;
42+
import java.util.HashMap;
4043
import java.util.Map;
4144
import java.util.function.Supplier;
4245

@@ -45,20 +48,23 @@ public class HighlighterExpressionEvaluator extends LuceneQueryEvaluator<BytesRe
4548
EvalOperator.ExpressionEvaluator {
4649

4750
private final String fieldName;
51+
private final Integer numFragments;
52+
private final Integer fragmentLength;
4853
private final SearchContext searchContext;
49-
private final SourceLoader sourceLoader;
5054

5155
HighlighterExpressionEvaluator(
5256
BlockFactory blockFactory,
5357
ShardConfig[] shardConfigs,
5458
String fieldName,
55-
SearchContext searchContext,
56-
SourceLoader sourceLoader
59+
Integer numFragments,
60+
Integer fragmentLength,
61+
SearchContext searchContext
5762
) {
5863
super(blockFactory, shardConfigs);
5964
this.fieldName = fieldName;
65+
this.numFragments = numFragments;
66+
this.fragmentLength = fragmentLength;
6067
this.searchContext = searchContext;
61-
this.sourceLoader = sourceLoader;
6268
}
6369

6470
@Override
@@ -73,47 +79,47 @@ protected Vector createNoMatchVector(BlockFactory blockFactory, int size) {
7379

7480
@Override
7581
protected BytesRefVector.Builder createVectorBuilder(BlockFactory blockFactory, int size) {
76-
return blockFactory.newBytesRefVectorBuilder(size);
82+
return blockFactory.newBytesRefVectorBuilder(size * numFragments);
7783
}
7884

7985
@Override
8086
protected void appendMatch(BytesRefVector.Builder builder, Scorable scorer, int docId, LeafReaderContext leafReaderContext, Query query)
8187
throws IOException {
8288

83-
// I was trying to find the way to build the highligher from the context, but probably we should just build the
84-
// CustomUnifiedHighligher directly so we don't need specific fetch phase classes for this
89+
// TODO: Can we build a custom highlighter directly here, so we don't have to rely on fetch phase classes?
8590
SearchHighlightContext.FieldOptions.Builder optionsBuilder = new SearchHighlightContext.FieldOptions.Builder();
86-
optionsBuilder.numberOfFragments(10);
87-
optionsBuilder.fragmentCharSize(100);
91+
optionsBuilder.numberOfFragments(numFragments != null ? numFragments : HighlightBuilder.DEFAULT_NUMBER_OF_FRAGMENTS);
92+
optionsBuilder.fragmentCharSize(fragmentLength != null ? fragmentLength : HighlightBuilder.DEFAULT_FRAGMENT_CHAR_SIZE);
93+
optionsBuilder.preTags(new String[] { "" });
94+
optionsBuilder.postTags(new String[] { "" });
95+
optionsBuilder.requireFieldMatch(false);
96+
optionsBuilder.scoreOrdered(true);
8897
SearchHighlightContext.Field field = new SearchHighlightContext.Field(fieldName, optionsBuilder.build());
98+
// Create a source loader for highlighter use
99+
SourceLoader sourceLoader = searchContext.newSourceLoader(null);
89100
FetchContext fetchContext = new FetchContext(searchContext, sourceLoader);
90101
MappedFieldType fieldType = searchContext.getSearchExecutionContext().getFieldType(fieldName);
91102
SearchHit searchHit = new SearchHit(docId);
92103
Source source = Source.lazy(lazyStoredSourceLoader(leafReaderContext, docId));
93104

94-
95-
FetchSubPhase.HitContext hitContext = new FetchSubPhase.HitContext(
96-
searchHit,
97-
leafReaderContext,
98-
docId,
99-
Map.of(),
100-
source,
101-
null
102-
);
105+
FetchSubPhase.HitContext hitContext = new FetchSubPhase.HitContext(searchHit, leafReaderContext, docId, Map.of(), source, null);
103106
FieldHighlightContext highlightContext = new FieldHighlightContext(
104107
fieldName,
105108
field,
106109
fieldType,
107110
fetchContext,
108111
hitContext,
109112
query,
110-
Map.of()
113+
new HashMap<>()
111114
);
112115
Highlighter highlighter = new DefaultHighlighter();
113116
HighlightField highlight = highlighter.highlight(highlightContext);
114117

115-
// Iterate over fragments etc
116-
builder.appendBytesRef(new BytesRef(highlight.fragments()[0].bytes().bytes()));
118+
// TODO: Even when I have 2 fragments coming back, it's only ever returning the first bytes ref vector. Is this the appropriate data
119+
// structure?
120+
for (Text highlightText : highlight.fragments()) {
121+
builder.appendBytesRef(new BytesRef(highlightText.bytes().bytes()));
122+
}
117123
}
118124

119125
private static Supplier<Source> lazyStoredSourceLoader(LeafReaderContext ctx, int doc) {
@@ -131,20 +137,31 @@ private static Supplier<Source> lazyStoredSourceLoader(LeafReaderContext ctx, in
131137

132138
@Override
133139
protected void appendNoMatch(BytesRefVector.Builder builder) {
134-
135-
140+
// builder.appendBytesRef(new BytesRef());
136141
}
137142

138143
@Override
139144
public Block eval(Page page) {
140145
return executeQuery(page);
141146
}
142147

143-
public record Factory(ShardConfig[] shardConfigs) implements EvalOperator.ExpressionEvaluator.Factory {
148+
public record Factory(
149+
ShardConfig[] shardConfigs,
150+
String fieldName,
151+
Integer numFragments,
152+
Integer fragmentSize,
153+
SearchContext searchContext
154+
) implements EvalOperator.ExpressionEvaluator.Factory {
144155
@Override
145156
public EvalOperator.ExpressionEvaluator get(DriverContext context) {
146-
// We need to get field name, search context, and source loader. We should be able to remove the source loader by getting the field value
147-
return new HighlighterExpressionEvaluator(context.blockFactory(), shardConfigs, fieldName, searchContext, context.sourceLoader());
157+
return new HighlighterExpressionEvaluator(
158+
context.blockFactory(),
159+
shardConfigs,
160+
fieldName,
161+
numFragments,
162+
fragmentSize,
163+
searchContext
164+
);
148165
}
149166
}
150167
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ExtractSnippets.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,12 @@ public ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) {
237237

238238
shardConfigs[i++] = new LuceneQueryEvaluator.ShardConfig(shardContext.toQuery(queryBuilder), shardContext.searcher());
239239
}
240-
return new HighlighterExpressionEvaluator.Factory(shardConfigs);
240+
// Get field name and search context from the first shard context
241+
String fieldNameStr = field.sourceText();
242+
int numFragments = numSnippets == null ? DEFAULT_NUM_SNIPPETS : Integer.parseInt(numSnippets.sourceText());
243+
int fragmentSize = snippetLength == null ? DEFAULT_SNIPPET_LENGTH : Integer.parseInt(snippetLength.sourceText());
244+
SearchContext firstSearchContext = shardContexts.isEmpty() ? null : shardContexts.get(0).searchContext();
245+
return new HighlighterExpressionEvaluator.Factory(shardConfigs, fieldNameStr, numFragments, fragmentSize, firstSearchContext);
241246
}
242247

243248
@Override

0 commit comments

Comments
 (0)