diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/20_fvh.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/20_fvh.yml
index d411f55106d7..a55873fed307 100644
--- a/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/20_fvh.yml
+++ b/rest-api-spec/src/main/resources/rest-api-spec/test/search.highlight/20_fvh.yml
@@ -1,23 +1,42 @@
setup:
- do:
indices.create:
- index: test
- body:
- mappings:
- "properties":
- "title":
- "type": "text"
- "term_vector": "with_positions_offsets"
- "description":
- "type": "text"
- "term_vector": "with_positions_offsets"
+ index: test
+ body:
+ mappings:
+ "properties":
+ "id":
+ "type": "integer"
+ "title":
+ "type": "text"
+ "term_vector": "with_positions_offsets"
+ "description":
+ "type": "text"
+ "term_vector": "with_positions_offsets"
+ "nested":
+ "type": "nested"
+ "properties":
+ "title":
+ "type": "text"
+ "term_vector": "with_positions_offsets"
+ - do:
+ index:
+ index: test
+ body:
+ id: 1
+ "title" : "The quick brown fox is brown"
+ "description" : "The quick pink panther is pink"
+
- do:
index:
index: test
- id: 1
body:
- "title" : "The quick brown fox is brown"
- "description" : "The quick pink panther is pink"
+ id: 2
+ "title" : "The quick blue fox is blue"
+ "nested":
+ - "title": "purple octopus"
+ - "title": "purple fish"
+
- do:
indices.refresh: {}
@@ -27,19 +46,69 @@ setup:
search:
rest_total_hits_as_int: true
body:
- highlight:
- type: fvh
- fields:
- description:
- type: fvh
- highlight_query:
- prefix:
- description: br
- title:
- type: fvh
- highlight_query:
- prefix:
- title: br
+ highlight:
+ type: fvh
+ fields:
+ description:
+ type: fvh
+ highlight_query:
+ prefix:
+ description: br
+ title:
+ type: fvh
+ highlight_query:
+ prefix:
+ title: br
- match: {hits.hits.0.highlight.title.0: "The quick brown fox is brown"}
- is_false: hits.hits.0.highlight.description
+
+---
+"Highlight multiple documents":
+ - skip:
+ version: " - 7.99.99"
+ reason: Bug fix not yet backported
+ - do:
+ search:
+ rest_total_hits_as_int: true
+ body:
+ query:
+ match:
+ title: fox
+ sort: ["id"]
+ highlight:
+ type: fvh
+ fields:
+ title:
+ type: fvh
+
+ - match: {hits.hits.0.highlight.title.0: "The quick brown fox is brown"}
+ - is_false: hits.hits.0.highlight.description
+ - match: {hits.hits.1.highlight.title.0: "The quick blue fox is blue"}
+ - is_false: hits.hits.1.highlight.description
+
+---
+"Highlight multiple nested documents":
+ - skip:
+ version: " - 7.99.99"
+ reason: Bug fix not yet backported
+ - do:
+ search:
+ rest_total_hits_as_int: true
+ body:
+ query:
+ nested:
+ path: nested
+ query:
+ match:
+ nested.title: purple
+ inner_hits:
+ name: nested_hits
+ highlight:
+ type: fvh
+ fields:
+ nested.title:
+ type: fvh
+
+ - match: {hits.hits.0.inner_hits.nested_hits.hits.hits.0.highlight.nested\\.title.0: "purple octopus"}
+ - match: {hits.hits.0.inner_hits.nested_hits.hits.hits.1.highlight.nested\\.title.0: "purple fish"}
diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/FastVectorHighlighter.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/FastVectorHighlighter.java
index 6c98251b9d46..7172fbac5e65 100644
--- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/FastVectorHighlighter.java
+++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/FastVectorHighlighter.java
@@ -41,6 +41,7 @@
import org.elasticsearch.search.fetch.FetchSubPhase;
import org.elasticsearch.search.fetch.subphase.highlight.SearchHighlightContext.Field;
import org.elasticsearch.search.fetch.subphase.highlight.SearchHighlightContext.FieldOptions;
+import org.elasticsearch.search.lookup.SourceLookup;
import java.io.IOException;
import java.text.BreakIterator;
@@ -48,6 +49,7 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.function.Function;
public class FastVectorHighlighter implements Highlighter {
private static final BoundaryScanner DEFAULT_SIMPLE_BOUNDARY_SCANNER = new SimpleBoundaryScanner();
@@ -89,42 +91,16 @@ public HighlightField highlight(FieldHighlightContext fieldContext) throws IOExc
FieldHighlightEntry entry = cache.fields.get(fieldType);
if (entry == null) {
FragListBuilder fragListBuilder;
- BaseFragmentsBuilder fragmentsBuilder;
-
- final BoundaryScanner boundaryScanner = getBoundaryScanner(field);
if (field.fieldOptions().numberOfFragments() == 0) {
fragListBuilder = new SingleFragListBuilder();
-
- if (!forceSource && fieldType.isStored()) {
- fragmentsBuilder = new SimpleFragmentsBuilder(fieldType, fixBrokenAnalysis, field.fieldOptions().preTags(),
- field.fieldOptions().postTags(), boundaryScanner);
- } else {
- fragmentsBuilder = new SourceSimpleFragmentsBuilder(fieldType, fixBrokenAnalysis, hitContext.sourceLookup(),
- field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
- }
} else {
fragListBuilder = field.fieldOptions().fragmentOffset() == -1 ?
new SimpleFragListBuilder() : new SimpleFragListBuilder(field.fieldOptions().fragmentOffset());
- if (field.fieldOptions().scoreOrdered()) {
- if (!forceSource && fieldType.isStored()) {
- fragmentsBuilder = new ScoreOrderFragmentsBuilder(field.fieldOptions().preTags(),
- field.fieldOptions().postTags(), boundaryScanner);
- } else {
- fragmentsBuilder = new SourceScoreOrderFragmentsBuilder(fieldType, fixBrokenAnalysis, hitContext.sourceLookup(),
- field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
- }
- } else {
- if (!forceSource && fieldType.isStored()) {
- fragmentsBuilder = new SimpleFragmentsBuilder(fieldType, fixBrokenAnalysis, field.fieldOptions().preTags(),
- field.fieldOptions().postTags(), boundaryScanner);
- } else {
- fragmentsBuilder =
- new SourceSimpleFragmentsBuilder(fieldType, fixBrokenAnalysis, hitContext.sourceLookup(),
- field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
- }
- }
}
- fragmentsBuilder.setDiscreteMultiValueHighlighting(termVectorMultiValue);
+
+ Function fragmentsBuilderSupplier = fragmentsBuilderSupplier(
+ field, fieldType, forceSource, fixBrokenAnalysis);
+
entry = new FieldHighlightEntry();
if (field.fieldOptions().requireFieldMatch()) {
/*
@@ -142,7 +118,7 @@ public HighlightField highlight(FieldHighlightContext fieldContext) throws IOExc
hitContext.topLevelReader(), true, field.fieldOptions().requireFieldMatch());
}
entry.fragListBuilder = fragListBuilder;
- entry.fragmentsBuilder = fragmentsBuilder;
+ entry.fragmentsBuilderSupplier = fragmentsBuilderSupplier;
if (cache.fvh == null) {
// parameters to FVH are not requires since:
// first two booleans are not relevant since they are set on the CustomFieldQuery
@@ -161,6 +137,7 @@ public HighlightField highlight(FieldHighlightContext fieldContext) throws IOExc
cache.fvh.setPhraseLimit(field.fieldOptions().phraseLimit());
String[] fragments;
+ FragmentsBuilder fragmentsBuilder = entry.fragmentsBuilderSupplier.apply(hitContext.sourceLookup());
// a HACK to make highlighter do highlighting, even though its using the single frag list builder
int numberOfFragments = field.fieldOptions().numberOfFragments() == 0 ?
@@ -172,12 +149,12 @@ public HighlightField highlight(FieldHighlightContext fieldContext) throws IOExc
if (field.fieldOptions().matchedFields() != null && !field.fieldOptions().matchedFields().isEmpty()) {
fragments = cache.fvh.getBestFragments(fieldQuery, hitContext.reader(), hitContext.docId(),
fieldType.name(), field.fieldOptions().matchedFields(), fragmentCharSize,
- numberOfFragments, entry.fragListBuilder, entry.fragmentsBuilder, field.fieldOptions().preTags(),
+ numberOfFragments, entry.fragListBuilder, fragmentsBuilder, field.fieldOptions().preTags(),
field.fieldOptions().postTags(), encoder);
} else {
fragments = cache.fvh.getBestFragments(fieldQuery, hitContext.reader(), hitContext.docId(),
fieldType.name(), fragmentCharSize, numberOfFragments, entry.fragListBuilder,
- entry.fragmentsBuilder, field.fieldOptions().preTags(), field.fieldOptions().postTags(), encoder);
+ fragmentsBuilder, field.fieldOptions().preTags(), field.fieldOptions().postTags(), encoder);
}
if (CollectionUtils.isEmpty(fragments) == false) {
@@ -190,7 +167,7 @@ public HighlightField highlight(FieldHighlightContext fieldContext) throws IOExc
// the normal fragmentsBuilder
FieldFragList fieldFragList = new SimpleFieldFragList(-1 /*ignored*/);
fieldFragList.add(0, noMatchSize, Collections.emptyList());
- fragments = entry.fragmentsBuilder.createFragments(hitContext.reader(), hitContext.docId(),
+ fragments = fragmentsBuilder.createFragments(hitContext.reader(), hitContext.docId(),
fieldType.name(), fieldFragList, 1, field.fieldOptions().preTags(),
field.fieldOptions().postTags(), encoder);
if (CollectionUtils.isEmpty(fragments) == false) {
@@ -201,6 +178,37 @@ public HighlightField highlight(FieldHighlightContext fieldContext) throws IOExc
return null;
}
+ private Function fragmentsBuilderSupplier(SearchHighlightContext.Field field,
+ MappedFieldType fieldType,
+ boolean forceSource,
+ boolean fixBrokenAnalysis) {
+ BoundaryScanner boundaryScanner = getBoundaryScanner(field);
+ FieldOptions options = field.fieldOptions();
+ Function supplier;
+ if (!forceSource && fieldType.isStored()) {
+ if (options.numberOfFragments() != 0 && options.scoreOrdered()) {
+ supplier = ignored -> new ScoreOrderFragmentsBuilder(options.preTags(), options.postTags(), boundaryScanner);
+ } else {
+ supplier = ignored -> new SimpleFragmentsBuilder(fieldType, fixBrokenAnalysis,
+ options.preTags(), options.postTags(), boundaryScanner);
+ }
+ } else {
+ if (options.numberOfFragments() != 0 && options.scoreOrdered()) {
+ supplier = lookup -> new SourceScoreOrderFragmentsBuilder(fieldType, fixBrokenAnalysis, lookup,
+ options.preTags(), options.postTags(), boundaryScanner);
+ } else {
+ supplier = lookup -> new SourceSimpleFragmentsBuilder(fieldType, fixBrokenAnalysis, lookup,
+ options.preTags(), options.postTags(), boundaryScanner);
+ }
+ }
+
+ return lookup -> {
+ BaseFragmentsBuilder builder = supplier.apply(lookup);
+ builder.setDiscreteMultiValueHighlighting(termVectorMultiValue);
+ return builder;
+ };
+ }
+
@Override
public boolean canHighlight(MappedFieldType ft) {
return ft.getTextSearchInfo().termVectors() == TextSearchInfo.TermVector.OFFSETS;
@@ -238,7 +246,7 @@ private static BoundaryScanner getBoundaryScanner(Field field) {
private static class FieldHighlightEntry {
public FragListBuilder fragListBuilder;
- public FragmentsBuilder fragmentsBuilder;
+ public Function fragmentsBuilderSupplier;
public FieldQuery noFieldMatchFieldQuery;
public FieldQuery fieldMatchFieldQuery;
}