diff --git a/docs/changelog/96068.yaml b/docs/changelog/96068.yaml new file mode 100644 index 0000000000000..9faf74414d36e --- /dev/null +++ b/docs/changelog/96068.yaml @@ -0,0 +1,5 @@ +pr: 96068 +summary: Use the Weight#matches mode for highlighting by default +area: Search +type: enhancement +issues: [] \ No newline at end of file diff --git a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java index 39284978dbeec..d7e4dbf4c0c01 100644 --- a/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java +++ b/modules/analysis-common/src/test/java/org/elasticsearch/analysis/common/HighlighterWithAnalyzersTests.java @@ -258,7 +258,7 @@ public void testPhrasePrefix() throws IOException { .highlighter(highlight().field("field0").order("score").preTags("").postTags("")); searchResponse = client().search(new SearchRequest("first_test_index").source(source)).actionGet(); - assertHighlight(searchResponse, 0, "field0", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); + assertHighlight(searchResponse, 0, "field0", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); logger.info("--> highlighting and searching on field1"); source = searchSource().query( @@ -293,8 +293,8 @@ public void testPhrasePrefix() throws IOException { 0, 1, anyOf( - equalTo("The quick browse button is a fancy thing, right bro?"), - equalTo("The quick brown fox jumps over the lazy dog") + equalTo("The quick browse button is a fancy thing, right bro?"), + equalTo("The quick brown fox jumps over the lazy dog") ) ); assertHighlight( @@ -304,8 +304,8 @@ public void testPhrasePrefix() throws IOException { 0, 1, anyOf( - equalTo("The quick browse button is a fancy thing, right bro?"), - equalTo("The quick brown fox jumps over the lazy dog") + equalTo("The quick browse button is a fancy thing, right bro?"), + equalTo("The quick brown fox jumps over the lazy dog") ) ); @@ -343,7 +343,7 @@ public void testPhrasePrefix() throws IOException { searchResponse = client().search(new SearchRequest("second_test_index").source(source)).actionGet(); - assertHighlight(searchResponse, 0, "field3", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); + assertHighlight(searchResponse, 0, "field3", 0, 1, equalTo("The quick brown fox jumps over the lazy dog")); logger.info("--> highlighting and searching on field4"); source = searchSource().postFilter(termQuery("type", "type2")) @@ -358,8 +358,8 @@ public void testPhrasePrefix() throws IOException { 0, 1, anyOf( - equalTo("The quick browse button is a fancy thing, right bro?"), - equalTo("The quick brown fox jumps over the lazy dog") + equalTo("The quick browse button is a fancy thing, right bro?"), + equalTo("The quick brown fox jumps over the lazy dog") ) ); assertHighlight( @@ -369,8 +369,8 @@ public void testPhrasePrefix() throws IOException { 0, 1, anyOf( - equalTo("The quick browse button is a fancy thing, right bro?"), - equalTo("The quick brown fox jumps over the lazy dog") + equalTo("The quick browse button is a fancy thing, right bro?"), + equalTo("The quick brown fox jumps over the lazy dog") ) ); diff --git a/plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextHighlighter.java b/plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextHighlighter.java index 4a9cc95839fd1..45c2a9208b8d6 100644 --- a/plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextHighlighter.java +++ b/plugins/mapper-annotated-text/src/main/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextHighlighter.java @@ -17,14 +17,14 @@ import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.lucene.search.uhighlight.CustomUnifiedHighlighter; import org.elasticsearch.search.fetch.FetchSubPhase.HitContext; +import org.elasticsearch.search.fetch.subphase.highlight.DefaultHighlighter; import org.elasticsearch.search.fetch.subphase.highlight.SearchHighlightContext; -import org.elasticsearch.search.fetch.subphase.highlight.UnifiedHighlighter; import java.io.IOException; import java.util.ArrayList; import java.util.List; -public class AnnotatedTextHighlighter extends UnifiedHighlighter { +public class AnnotatedTextHighlighter extends DefaultHighlighter { public static final String NAME = "annotated"; diff --git a/plugins/mapper-annotated-text/src/test/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextHighlighterTests.java b/plugins/mapper-annotated-text/src/test/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextHighlighterTests.java index f73bb7cbacbdc..5a121ffa53658 100644 --- a/plugins/mapper-annotated-text/src/test/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextHighlighterTests.java +++ b/plugins/mapper-annotated-text/src/test/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextHighlighterTests.java @@ -131,21 +131,23 @@ private void assertHighlightOneDoc( TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), 1, Sort.INDEXORDER); assertThat(topDocs.totalHits.value, equalTo(1L)); String rawValue = Strings.collectionToDelimitedString(plainTextForHighlighter, String.valueOf(MULTIVAL_SEP_CHAR)); + UnifiedHighlighter.Builder builder = UnifiedHighlighter.builder(searcher, hiliteAnalyzer); + builder.withBreakIterator(() -> breakIterator); + builder.withFieldMatcher(name -> "text".equals(name)); + builder.withFormatter(passageFormatter); CustomUnifiedHighlighter highlighter = new CustomUnifiedHighlighter( - searcher, - hiliteAnalyzer, + builder, UnifiedHighlighter.OffsetSource.ANALYSIS, - passageFormatter, locale, - breakIterator, "index", "text", query, noMatchSize, expectedPassages.length, - name -> "text".equals(name), maxAnalyzedOffset, - queryMaxAnalyzedOffset + queryMaxAnalyzedOffset, + true, + true ); highlighter.setFieldMatcher((name) -> "text".equals(name)); final Snippet[] snippets = highlighter.highlightField(getOnlyLeafReader(reader), topDocs.scoreDocs[0].doc, () -> rawValue); @@ -236,7 +238,7 @@ public void testAnnotatedTextSingleFieldWithBreakIterator() throws Exception { public void testAnnotatedTextSingleFieldWithPhraseQuery() throws Exception { final String[] markedUpInputs = { "[Donald Trump](Donald+Trump) visited Singapore", "Donald Jr was with Melania Trump" }; - String[] expectedPassages = { "[Donald](_hit_term=donald) [Trump](_hit_term=trump) visited Singapore" }; + String[] expectedPassages = { "[Donald Trump](_hit_term=donald+trump&Donald+Trump) visited Singapore" }; Query query = new PhraseQuery("text", "donald", "trump"); BreakIterator breakIterator = new CustomSeparatorBreakIterator(MULTIVAL_SEP_CHAR); assertHighlightOneDoc("text", markedUpInputs, query, Locale.ROOT, breakIterator, 0, expectedPassages); diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.highlight/10_unified.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.highlight/10_unified.yml index 3916386abc244..1a03896f6d087 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.highlight/10_unified.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.highlight/10_unified.yml @@ -23,6 +23,18 @@ setup: - do: indices.refresh: {} +--- +teardown: + - skip: + version: " - 8.9.99" + reason: "setting added in 8.10.0" + + - do: + indices.put_settings: + index: test + body: + index.highlight.weight_matches_mode.enabled: null + --- "Basic multi_match query": - do: @@ -49,3 +61,35 @@ setup: - match: {hits.hits.0.highlight.text.0: "The quick brown fox is brown."} - match: {hits.hits.0.highlight.text\.fvh.0: "The quick brown fox is brown."} - match: {hits.hits.0.highlight.text\.postings.0: "The quick brown fox is brown."} + +--- +"Disable weight matches": + - skip: + version: " - 8.9.99" + reason: "support for weight matches was added in 8.10" + + - do: + search: + body: { + "query": { "multi_match": { "query": "quick brown fox", "type": "phrase", "fields": [ "text*" ] } }, + "highlight": { "type": "unified", "fields": { "*": { } } } } + + - match: { hits.hits.0.highlight.text.0: "The quick brown fox is brown." } + - match: { hits.hits.0.highlight.text\.fvh.0: "The quick brown fox is brown." } + - match: { hits.hits.0.highlight.text\.postings.0: "The quick brown fox is brown." } + + - do: + indices.put_settings: + index: test + body: + index.highlight.weight_matches_mode.enabled: "false" + + - do: + search: + body: { + "query" : { "multi_match" : { "query" : "quick brown fox", "type": "phrase", "fields" : [ "text*"] } }, + "highlight" : { "type" : "unified", "fields" : { "*" : {} } } } + + - match: {hits.hits.0.highlight.text.0: "The quick brown fox is brown."} + - match: {hits.hits.0.highlight.text\.fvh.0: "The quick brown fox is brown."} + - match: {hits.hits.0.highlight.text\.postings.0: "The quick brown fox is brown."} diff --git a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.highlight/50_synthetic_source.yml b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.highlight/50_synthetic_source.yml index 8f2333605b31f..2b75b48d8a09c 100644 --- a/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.highlight/50_synthetic_source.yml +++ b/rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/search.highlight/50_synthetic_source.yml @@ -210,7 +210,7 @@ text single unified from reanalysis: fields: foo.analyze: type: unified - - match: { hits.hits.0.highlight.foo\.analyze.0: the quick brown fox jumped over the lazy dog } + - match: { hits.hits.0.highlight.foo\.analyze.0: the quick brown fox jumped over the lazy dog } --- text multi unified from reanalysis: @@ -225,7 +225,7 @@ text multi unified from reanalysis: fields: foo.analyze: type: unified - - match: { hits.hits.0.highlight.foo\.analyze.0: That makes calamity of so long life. } + - match: { hits.hits.0.highlight.foo\.analyze.0: That makes calamity of so long life. } --- text single unified from positions: @@ -240,7 +240,7 @@ text single unified from positions: fields: foo.positions: type: unified - - match: { hits.hits.0.highlight.foo\.positions.0: the quick brown fox jumped over the lazy dog } + - match: { hits.hits.0.highlight.foo\.positions.0: the quick brown fox jumped over the lazy dog } --- text multi unified from positions: @@ -255,7 +255,7 @@ text multi unified from positions: fields: foo.positions: type: unified - - match: { hits.hits.0.highlight.foo\.positions.0: That makes calamity of so long life. } + - match: { hits.hits.0.highlight.foo\.positions.0: That makes calamity of so long life. } --- text single unified from vectors: @@ -270,7 +270,7 @@ text single unified from vectors: fields: foo.vectors: type: unified - - match: { hits.hits.0.highlight.foo\.vectors.0: the quick brown fox jumped over the lazy dog } + - match: { hits.hits.0.highlight.foo\.vectors.0: the quick brown fox jumped over the lazy dog } --- text multi unified from vectors: @@ -285,7 +285,7 @@ text multi unified from vectors: fields: foo.vectors: type: unified - - match: { hits.hits.0.highlight.foo\.vectors.0: That makes calamity of so long life. } + - match: { hits.hits.0.highlight.foo\.vectors.0: That makes calamity of so long life. } --- text single fvh source order: diff --git a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java index be09379c5b4c5..c65cdac5bbf69 100644 --- a/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java +++ b/server/src/internalClusterTest/java/org/elasticsearch/search/fetch/subphase/highlight/HighlighterSearchIT.java @@ -2217,14 +2217,7 @@ public void testPostingsHighlighter() throws Exception { searchResponse = client().search(new SearchRequest("test").source(source)).actionGet(); - assertHighlight( - searchResponse, - 0, - "field2", - 0, - 1, - equalTo("The quick brown fox jumps over the lazy quick dog") - ); + assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("The quick brown fox jumps over the lazy quick dog")); // lets fall back to the standard highlighter then, what people would do to highlight query matches logger.info("--> searching on field2, highlighting on field2, falling back to the plain highlighter"); diff --git a/server/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java b/server/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java index 545fbf6cb7b59..872f7c94490d3 100644 --- a/server/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java +++ b/server/src/main/java/org/elasticsearch/common/lucene/search/MultiPhrasePrefixQuery.java @@ -17,10 +17,12 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.MultiPhraseQuery; +import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.StringHelper; +import org.apache.lucene.util.automaton.CompiledAutomaton; import java.io.IOException; import java.util.ArrayList; @@ -295,20 +297,12 @@ public void visit(QueryVisitor visitor) { shouldVisitor.consumeTerms(this, termArrays.get(i)); } } - /* We don't report automata here because this breaks the unified highlighter, - which extracts automata separately from phrases. MPPQ gets rewritten to a - SpanMTQQuery by the PhraseHelper in any case, so highlighting is taken - care of there instead. If we extract automata here then the trailing prefix - word will be highlighted wherever it appears in the document, instead of only - as part of a phrase. This can be re-instated once we switch to using Matches - to highlight. for (Term prefixTerm : termArrays.get(termArrays.size() - 1)) { visitor.consumeTermsMatching(this, field, () -> { CompiledAutomaton ca = new CompiledAutomaton(PrefixQuery.toAutomaton(prefixTerm.bytes())); return ca.runAutomaton; }); } - */ } } } diff --git a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java index 5dabfb5a6314f..01e4824579b90 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java @@ -114,6 +114,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings { IndexSettings.MAX_SHINGLE_DIFF_SETTING, IndexSettings.MAX_RESCORE_WINDOW_SETTING, IndexSettings.MAX_ANALYZED_OFFSET_SETTING, + IndexSettings.WEIGHT_MATCHES_MODE_ENABLED_SETTING, IndexSettings.MAX_TERMS_COUNT_SETTING, IndexSettings.INDEX_TRANSLOG_SYNC_INTERVAL_SETTING, IndexSettings.DEFAULT_FIELD_SETTING, diff --git a/server/src/main/java/org/elasticsearch/index/IndexSettings.java b/server/src/main/java/org/elasticsearch/index/IndexSettings.java index 6a439f92b0430..74d68276c1e3f 100644 --- a/server/src/main/java/org/elasticsearch/index/IndexSettings.java +++ b/server/src/main/java/org/elasticsearch/index/IndexSettings.java @@ -10,6 +10,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.util.Strings; import org.apache.lucene.index.MergePolicy; +import org.apache.lucene.search.uhighlight.UnifiedHighlighter; import org.elasticsearch.cluster.metadata.IndexMetadata; import org.elasticsearch.cluster.node.DiscoveryNode; import org.elasticsearch.cluster.routing.IndexRouting; @@ -187,6 +188,17 @@ public final class IndexSettings { Property.IndexScope ); + /** + * Index setting to enable/disable the {@link UnifiedHighlighter.HighlightFlag#WEIGHT_MATCHES} + * mode of the unified highlighter. + */ + public static final Setting WEIGHT_MATCHES_MODE_ENABLED_SETTING = Setting.boolSetting( + "index.highlight.weight_matches_mode.enabled", + true, + Property.Dynamic, + Property.IndexScope + ); + /** * Index setting describing the maximum number of terms that can be used in Terms Query. * The default maximum of 65536 terms is defensive, as extra processing and memory is involved @@ -730,6 +742,7 @@ private void setRetentionLeaseMillis(final TimeValue retentionLease) { private volatile int maxShingleDiff; private volatile TimeValue searchIdleAfter; private volatile int maxAnalyzedOffset; + private volatile boolean weightMatchesEnabled; private volatile int maxTermsCount; private volatile String defaultPipeline; private volatile String requiredPipeline; @@ -871,6 +884,7 @@ public IndexSettings(final IndexMetadata indexMetadata, final Settings nodeSetti maxRefreshListeners = scopedSettings.get(MAX_REFRESH_LISTENERS_PER_SHARD); maxSlicesPerScroll = scopedSettings.get(MAX_SLICES_PER_SCROLL); maxAnalyzedOffset = scopedSettings.get(MAX_ANALYZED_OFFSET_SETTING); + weightMatchesEnabled = scopedSettings.get(WEIGHT_MATCHES_MODE_ENABLED_SETTING); maxTermsCount = scopedSettings.get(MAX_TERMS_COUNT_SETTING); maxRegexLength = scopedSettings.get(MAX_REGEX_LENGTH_SETTING); this.mergePolicyConfig = new MergePolicyConfig(logger, this); @@ -946,6 +960,7 @@ public IndexSettings(final IndexMetadata indexMetadata, final Settings nodeSetti scopedSettings.addSettingsUpdateConsumer(INDEX_REFRESH_INTERVAL_SETTING, this::setRefreshInterval); scopedSettings.addSettingsUpdateConsumer(MAX_REFRESH_LISTENERS_PER_SHARD, this::setMaxRefreshListeners); scopedSettings.addSettingsUpdateConsumer(MAX_ANALYZED_OFFSET_SETTING, this::setHighlightMaxAnalyzedOffset); + scopedSettings.addSettingsUpdateConsumer(WEIGHT_MATCHES_MODE_ENABLED_SETTING, this::setWeightMatchesEnabled); scopedSettings.addSettingsUpdateConsumer(MAX_TERMS_COUNT_SETTING, this::setMaxTermsCount); scopedSettings.addSettingsUpdateConsumer(MAX_SLICES_PER_SCROLL, this::setMaxSlicesPerScroll); scopedSettings.addSettingsUpdateConsumer(DEFAULT_FIELD_SETTING, this::setDefaultFields); @@ -1325,6 +1340,14 @@ private void setHighlightMaxAnalyzedOffset(int maxAnalyzedOffset) { this.maxAnalyzedOffset = maxAnalyzedOffset; } + public boolean isWeightMatchesEnabled() { + return this.weightMatchesEnabled; + } + + private void setWeightMatchesEnabled(boolean value) { + this.weightMatchesEnabled = value; + } + /** * Returns the maximum number of terms that can be used in a Terms Query request */ diff --git a/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/BoundedBreakIteratorScanner.java b/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/BoundedBreakIteratorScanner.java index 8394628746392..626820ce89906 100644 --- a/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/BoundedBreakIteratorScanner.java +++ b/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/BoundedBreakIteratorScanner.java @@ -131,15 +131,16 @@ public int preceding(int offset) { } /** - * Can be invoked only after a call to preceding(offset+1). + * Can be invoked only after a call to preceding(). + * * See {@link FieldHighlighter} for usage. */ @Override public int following(int offset) { - if (offset != lastPrecedingOffset || innerEnd == -1) { - throw new IllegalArgumentException("offset != lastPrecedingOffset: " + "usage doesn't look like UnifiedHighlighter"); + if (innerEnd == -1) { + throw new IllegalArgumentException("preceding should be called first, usage doesn't look like UnifiedHighlighter"); } - return innerEnd; + return Math.max(offset, innerEnd); } /** diff --git a/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomFieldHighlighter.java b/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomFieldHighlighter.java index cd781829dd08c..3d8d6c41183ee 100644 --- a/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomFieldHighlighter.java +++ b/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomFieldHighlighter.java @@ -15,14 +15,10 @@ import org.apache.lucene.search.uhighlight.Passage; import org.apache.lucene.search.uhighlight.PassageFormatter; import org.apache.lucene.search.uhighlight.PassageScorer; -import org.apache.lucene.util.BytesRef; import java.io.IOException; import java.text.BreakIterator; -import java.util.Arrays; -import java.util.Comparator; import java.util.Locale; -import java.util.PriorityQueue; import static org.elasticsearch.lucene.search.uhighlight.CustomUnifiedHighlighter.MULTIVAL_SEP_CHAR; @@ -102,86 +98,11 @@ protected Passage[] getSummaryPassagesNoHighlight(int maxPassages) { return EMPTY_PASSAGE; } - // TODO: use FieldHighlighter::highlightOffsetsEnums and modify BoundedBreakIteratorScanner to work with it - // LUCENE-9093 modified how FieldHighlighter breaks texts into passages, - // which doesn't work well with BoundedBreakIteratorScanner - // This is the copy of highlightOffsetsEnums before LUCENE-9093. @Override protected Passage[] highlightOffsetsEnums(OffsetsEnum off) throws IOException { - if (queryMaxAnalyzedOffset != null) { off = new LimitedOffsetsEnum(off, queryMaxAnalyzedOffset); } - - final int contentLength = this.breakIterator.getText().getEndIndex(); - - if (off.nextPosition() == false) { - return new Passage[0]; - } - - PriorityQueue passageQueue = new PriorityQueue<>(Math.min(64, maxPassages + 1), (left, right) -> { - if (left.getScore() < right.getScore()) { - return -1; - } else if (left.getScore() > right.getScore()) { - return 1; - } else { - return left.getStartOffset() - right.getStartOffset(); - } - }); - Passage passage = new Passage(); // the current passage in-progress. Will either get reset or added to queue. - - do { - int start = off.startOffset(); - if (start == -1) { - throw new IllegalArgumentException("field '" + field + "' was indexed without offsets, cannot highlight"); - } - int end = off.endOffset(); - if (start < contentLength && end > contentLength) { - continue; - } - // See if this term should be part of a new passage. - if (start >= passage.getEndOffset()) { - passage = maybeAddPassage(passageQueue, passageScorer, passage, contentLength); - // if we exceed limit, we are done - if (start >= contentLength) { - break; - } - passage.setStartOffset(Math.max(this.breakIterator.preceding(start + 1), 0)); - passage.setEndOffset(Math.min(this.breakIterator.following(start), contentLength)); - } - // Add this term to the passage. - BytesRef term = off.getTerm();// a reference; safe to refer to - assert term != null; - passage.addMatch(start, end, term, off.freq()); - } while (off.nextPosition()); - maybeAddPassage(passageQueue, passageScorer, passage, contentLength); - - Passage[] passages = passageQueue.toArray(new Passage[passageQueue.size()]); - // sort in ascending order - Arrays.sort(passages, Comparator.comparingInt(Passage::getStartOffset)); - return passages; - } - - // TODO: use FieldHighlighter::maybeAddPassage - // After removing CustomFieldHighlighter::highlightOffsetsEnums, remove this method as well. - private Passage maybeAddPassage(PriorityQueue passageQueue, PassageScorer scorer, Passage passage, int contentLength) { - if (passage.getStartOffset() == -1) { - // empty passage, we can ignore it - return passage; - } - passage.setScore(scorer.score(passage, contentLength)); - // new sentence: first add 'passage' to queue - if (passageQueue.size() == maxPassages && passage.getScore() < passageQueue.peek().getScore()) { - passage.reset(); // can't compete, just reset it - } else { - passageQueue.offer(passage); - if (passageQueue.size() > maxPassages) { - passage = passageQueue.poll(); - passage.reset(); - } else { - passage = new Passage(); - } - } - return passage; + return super.highlightOffsetsEnums(off); } } diff --git a/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighter.java b/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighter.java index ca5d50ba10e89..3b857d4221523 100644 --- a/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighter.java +++ b/server/src/main/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighter.java @@ -8,7 +8,6 @@ package org.elasticsearch.lucene.search.uhighlight; -import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.LeafReader; import org.apache.lucene.index.Term; import org.apache.lucene.queries.spans.SpanMultiTermQueryWrapper; @@ -16,32 +15,30 @@ import org.apache.lucene.queries.spans.SpanOrQuery; import org.apache.lucene.queries.spans.SpanQuery; import org.apache.lucene.queries.spans.SpanTermQuery; -import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; +import org.apache.lucene.search.uhighlight.FieldHighlighter; import org.apache.lucene.search.uhighlight.FieldOffsetStrategy; -import org.apache.lucene.search.uhighlight.LabelledCharArrayMatcher; import org.apache.lucene.search.uhighlight.NoOpOffsetStrategy; import org.apache.lucene.search.uhighlight.PassageFormatter; -import org.apache.lucene.search.uhighlight.PhraseHelper; -import org.apache.lucene.search.uhighlight.SplittingBreakIterator; -import org.apache.lucene.search.uhighlight.UHComponents; +import org.apache.lucene.search.uhighlight.PassageScorer; import org.apache.lucene.search.uhighlight.UnifiedHighlighter; -import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.automaton.ByteRunAutomaton; import org.elasticsearch.common.CheckedSupplier; import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery; import org.elasticsearch.core.Nullable; import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.search.ESToParentBlockJoinQuery; +import org.elasticsearch.search.runtime.AbstractScriptFieldQuery; import java.io.IOException; import java.text.BreakIterator; import java.util.Collection; import java.util.Collections; -import java.util.EnumSet; import java.util.Locale; -import java.util.Set; -import java.util.function.Predicate; +import java.util.function.Supplier; import static org.elasticsearch.search.fetch.subphase.highlight.AbstractHighlighterBuilder.MAX_ANALYZED_OFFSET_FIELD; @@ -58,8 +55,6 @@ public class CustomUnifiedHighlighter extends UnifiedHighlighter { private static final Snippet[] EMPTY_SNIPPET = new Snippet[0]; private final OffsetSource offsetSource; - private final PassageFormatter passageFormatter; - private final BreakIterator breakIterator; private final String index; private final String field; private final Locale breakIteratorLocale; @@ -71,51 +66,45 @@ public class CustomUnifiedHighlighter extends UnifiedHighlighter { /** * Creates a new instance of {@link CustomUnifiedHighlighter} * - * @param analyzer the analyzer used for the field at index time, used for multi term queries internally. + * @param builder the {@link UnifiedHighlighter.Builder} for the underlying highlighter. * @param offsetSource the {@link OffsetSource} to used for offsets retrieval. - * @param passageFormatter our own {@link CustomPassageFormatter} - * which generates snippets in forms of {@link Snippet} objects. * @param breakIteratorLocale the {@link Locale} to use for dividing text into passages. * If null {@link Locale#ROOT} is used. - * @param breakIterator the {@link BreakIterator} to use for dividing text into passages. - * If null {@link BreakIterator#getSentenceInstance(Locale)} is used. * @param index the index we're highlighting, mostly used for error messages * @param field the name of the field we're highlighting * @param query the query we're highlighting * @param noMatchSize The size of the text that should be returned when no highlighting can be performed. * @param maxPassages the maximum number of passes to highlight - * @param fieldMatcher decides which terms should be highlighted * @param maxAnalyzedOffset if the field is more than this long we'll refuse to use the ANALYZED * offset source for it because it'd be super slow + * @param weightMatchesEnabled whether the {@link HighlightFlag#WEIGHT_MATCHES} should be enabled */ public CustomUnifiedHighlighter( - IndexSearcher searcher, - Analyzer analyzer, + Builder builder, OffsetSource offsetSource, - PassageFormatter passageFormatter, @Nullable Locale breakIteratorLocale, - @Nullable BreakIterator breakIterator, String index, String field, Query query, int noMatchSize, int maxPassages, - Predicate fieldMatcher, int maxAnalyzedOffset, - Integer queryMaxAnalyzedOffset - ) throws IOException { - super(searcher, analyzer); + Integer queryMaxAnalyzedOffset, + boolean requireFieldMatch, + boolean weightMatchesEnabled + ) { + super(builder); this.offsetSource = offsetSource; - this.breakIterator = breakIterator; this.breakIteratorLocale = breakIteratorLocale == null ? Locale.ROOT : breakIteratorLocale; - this.passageFormatter = passageFormatter; this.index = index; this.field = field; this.noMatchSize = noMatchSize; - this.setFieldMatcher(fieldMatcher); this.maxAnalyzedOffset = maxAnalyzedOffset; this.queryMaxAnalyzedOffset = queryMaxAnalyzedOffset; - fieldHighlighter = getFieldHighlighter(field, query, extractTerms(query), maxPassages); + if (weightMatchesEnabled == false || requireFieldMatch == false || weightMatchesUnsupported(query)) { + getFlags(field).remove(HighlightFlag.WEIGHT_MATCHES); + } + fieldHighlighter = (CustomFieldHighlighter) getFieldHighlighter(field, query, extractTerms(query), maxPassages); } /** @@ -132,7 +121,7 @@ public Snippet[] highlightField(LeafReader reader, int docId, CheckedSupplier maxAnalyzedOffset) - && (offsetSource == OffsetSource.ANALYSIS) + && (getOffsetSource(field) == OffsetSource.ANALYSIS) && (fieldValueLength > maxAnalyzedOffset))) { throw new IllegalArgumentException( "The length [" @@ -149,7 +138,7 @@ public Snippet[] highlightField(LeafReader reader, int docId, CheckedSupplier allTerms, int maxPassages) { - Predicate fieldMatcher = getFieldMatcher(field); - BytesRef[] terms = filterExtractedTerms(fieldMatcher, allTerms); - Set highlightFlags = getFlags(field); - PhraseHelper phraseHelper = getPhraseHelper(field, query, highlightFlags); - LabelledCharArrayMatcher[] automata = getAutomata(field, query, highlightFlags); - UHComponents components = new UHComponents(field, fieldMatcher, query, terms, phraseHelper, automata, false, highlightFlags); - OffsetSource offsetSource = getOptimizedOffsetSource(components); - BreakIterator breakIterator = new SplittingBreakIterator(getBreakIterator(field), UnifiedHighlighter.MULTIVAL_SEP_CHAR); - FieldOffsetStrategy strategy = getOffsetStrategy(offsetSource, components); + protected FieldHighlighter newFieldHighlighter( + String field, + FieldOffsetStrategy fieldOffsetStrategy, + BreakIterator breakIterator, + PassageScorer passageScorer, + int maxPassages, + int maxNoHighlightPassages, + PassageFormatter passageFormatter + ) { return new CustomFieldHighlighter( field, - strategy, + fieldOffsetStrategy, breakIteratorLocale, breakIterator, getScorer(field), @@ -198,30 +176,8 @@ protected CustomFieldHighlighter getFieldHighlighter(String field, Query query, ); } - @Override - protected Set getFlags(String field) { - Set highlightFlags = EnumSet.noneOf(HighlightFlag.class); - if (shouldHandleMultiTermQuery(field)) { - highlightFlags.add(HighlightFlag.MULTI_TERM_QUERY); - } - if (shouldHighlightPhrasesStrictly(field)) { - highlightFlags.add(HighlightFlag.PHRASES); - } - if (shouldPreferPassageRelevancyOverSpeed(field)) { - highlightFlags.add(HighlightFlag.PASSAGE_RELEVANCY_OVER_SPEED); - } - return highlightFlags; - } - @Override protected Collection preSpanQueryRewrite(Query query) { - return rewriteCustomQuery(query); - } - - /** - * Translate custom queries in queries that are supported by the unified highlighter. - */ - private static Collection rewriteCustomQuery(Query query) { if (query instanceof MultiPhrasePrefixQuery mpq) { Term[][] terms = mpq.getTerms(); int[] positions = mpq.getPositions(); @@ -272,4 +228,66 @@ protected OffsetSource getOffsetSource(String field) { } return offsetSource; } + + /** + * Returns true if the provided {@link Query} is not compatible with the {@link HighlightFlag#WEIGHT_MATCHES} + * mode of this highlighter. + * + * @param query The query to highlight + */ + private boolean weightMatchesUnsupported(Query query) { + boolean[] hasUnknownLeaf = new boolean[1]; + query.visit(new QueryVisitor() { + @Override + public void visitLeaf(Query leafQuery) { + /** + * The parent-child query requires to load global ordinals and to access + * documents outside of the scope of the highlighted doc. + * We disable the {@link HighlightFlag#WEIGHT_MATCHES} mode in this case + * in order to preserve the compatibility. + */ + if (leafQuery.getClass().getSimpleName().equals("LateParsingQuery")) { + hasUnknownLeaf[0] = true; + } + super.visitLeaf(query); + } + + @Override + public void consumeTerms(Query leafQuery, Term... terms) { + if (leafQuery instanceof AbstractScriptFieldQuery) { + /** + * Queries on runtime fields don't support the matches API. + * TODO: We should add the support for keyword runtime fields. + * + */ + hasUnknownLeaf[0] = true; + } + super.consumeTerms(query, terms); + } + + @Override + public void consumeTermsMatching(Query leafQuery, String field, Supplier automaton) { + if (leafQuery instanceof AbstractScriptFieldQuery) { + /** + * Queries on runtime fields don't support the matches API. + * TODO: We should add the support for keyword runtime fields. + */ + hasUnknownLeaf[0] = true; + } + super.consumeTermsMatching(query, field, automaton); + } + + @Override + public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) { + /** + * Nested queries don't support the matches API. + */ + if (parent instanceof ESToParentBlockJoinQuery) { + hasUnknownLeaf[0] = true; + } + return super.getSubVisitor(occur, parent); + } + }); + return hasUnknownLeaf[0]; + } } diff --git a/server/src/main/java/org/elasticsearch/search/SearchModule.java b/server/src/main/java/org/elasticsearch/search/SearchModule.java index 5dd6c33b50753..350a727158d5d 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchModule.java +++ b/server/src/main/java/org/elasticsearch/search/SearchModule.java @@ -217,11 +217,11 @@ import org.elasticsearch.search.fetch.subphase.ScriptFieldsPhase; import org.elasticsearch.search.fetch.subphase.SeqNoPrimaryTermPhase; import org.elasticsearch.search.fetch.subphase.StoredFieldsPhase; +import org.elasticsearch.search.fetch.subphase.highlight.DefaultHighlighter; import org.elasticsearch.search.fetch.subphase.highlight.FastVectorHighlighter; import org.elasticsearch.search.fetch.subphase.highlight.HighlightPhase; import org.elasticsearch.search.fetch.subphase.highlight.Highlighter; import org.elasticsearch.search.fetch.subphase.highlight.PlainHighlighter; -import org.elasticsearch.search.fetch.subphase.highlight.UnifiedHighlighter; import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.search.rescore.QueryRescorerBuilder; import org.elasticsearch.search.rescore.RescorerBuilder; @@ -872,7 +872,7 @@ private static Map setupHighlighters(Settings settings, Lis NamedRegistry highlighters = new NamedRegistry<>("highlighter"); highlighters.register("fvh", new FastVectorHighlighter(settings)); highlighters.register("plain", new PlainHighlighter()); - highlighters.register("unified", new UnifiedHighlighter()); + highlighters.register("unified", new DefaultHighlighter()); highlighters.extractAndRegister(plugins, SearchPlugin::getHighlighters); return unmodifiableMap(highlighters.getRegistry()); diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/UnifiedHighlighter.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/DefaultHighlighter.java similarity index 88% rename from server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/UnifiedHighlighter.java rename to server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/DefaultHighlighter.java index e880f82f10953..d90aba24a94df 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/UnifiedHighlighter.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/DefaultHighlighter.java @@ -12,6 +12,8 @@ import org.apache.lucene.search.highlight.Encoder; import org.apache.lucene.search.uhighlight.CustomSeparatorBreakIterator; import org.apache.lucene.search.uhighlight.PassageFormatter; +import org.apache.lucene.search.uhighlight.UnifiedHighlighter; +import org.apache.lucene.search.uhighlight.UnifiedHighlighter.Builder; import org.apache.lucene.search.uhighlight.UnifiedHighlighter.OffsetSource; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.CollectionUtil; @@ -19,6 +21,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.text.Text; +import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.mapper.IdFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; import org.elasticsearch.index.mapper.TextSearchInfo; @@ -42,7 +45,7 @@ import static org.elasticsearch.lucene.search.uhighlight.CustomUnifiedHighlighter.MULTIVAL_SEP_CHAR; -public class UnifiedHighlighter implements Highlighter { +public class DefaultHighlighter implements Highlighter { @Override public boolean canHighlight(MappedFieldType fieldType) { return true; @@ -103,11 +106,14 @@ public HighlightField highlight(FieldHighlightContext fieldContext) throws IOExc return new HighlightField(fieldContext.fieldName, Text.convertFromStringArray(fragments)); } - CustomUnifiedHighlighter buildHighlighter(FieldHighlightContext fieldContext) throws IOException { + CustomUnifiedHighlighter buildHighlighter(FieldHighlightContext fieldContext) { + IndexSettings indexSettings = fieldContext.context.getSearchExecutionContext().getIndexSettings(); Encoder encoder = fieldContext.field.fieldOptions().encoder().equals("html") ? HighlightUtils.Encoders.HTML : HighlightUtils.Encoders.DEFAULT; - int maxAnalyzedOffset = fieldContext.context.getSearchExecutionContext().getIndexSettings().getHighlightMaxAnalyzedOffset(); + + int maxAnalyzedOffset = indexSettings.getHighlightMaxAnalyzedOffset(); + boolean weightMatchesEnabled = indexSettings.isWeightMatchesEnabled(); int numberOfFragments = fieldContext.field.fieldOptions().numberOfFragments(); Integer queryMaxAnalyzedOffset = fieldContext.field.fieldOptions().maxAnalyzedOffset(); Analyzer analyzer = wrapAnalyzer( @@ -118,7 +124,7 @@ CustomUnifiedHighlighter buildHighlighter(FieldHighlightContext fieldContext) th IndexSearcher searcher = fieldContext.context.searcher(); OffsetSource offsetSource = getOffsetSource(fieldContext.context, fieldContext.fieldType); BreakIterator breakIterator; - int higlighterNumberOfFragments; + int highlighterNumberOfFragments; if (numberOfFragments == 0 // non-tokenized fields should not use any break iterator (ignore boundaryScannerType) || fieldContext.fieldType.getTextSearchInfo().isTokenized() == false) { @@ -129,27 +135,29 @@ CustomUnifiedHighlighter buildHighlighter(FieldHighlightContext fieldContext) th * values of a field and we get back a snippet per value */ breakIterator = new CustomSeparatorBreakIterator(MULTIVAL_SEP_CHAR); - higlighterNumberOfFragments = numberOfFragments == 0 ? Integer.MAX_VALUE - 1 : numberOfFragments; + highlighterNumberOfFragments = numberOfFragments == 0 ? Integer.MAX_VALUE - 1 : numberOfFragments; } else { // using paragraph separator we make sure that each field value holds a discrete passage for highlighting breakIterator = getBreakIterator(fieldContext.field); - higlighterNumberOfFragments = numberOfFragments; + highlighterNumberOfFragments = numberOfFragments; } + Builder builder = UnifiedHighlighter.builder(searcher, analyzer); + builder.withBreakIterator(() -> breakIterator); + builder.withFieldMatcher(fieldMatcher(fieldContext)); + builder.withFormatter(passageFormatter); return new CustomUnifiedHighlighter( - searcher, - analyzer, + builder, offsetSource, - passageFormatter, fieldContext.field.fieldOptions().boundaryScannerLocale(), - breakIterator, fieldContext.context.getIndexName(), fieldContext.fieldName, fieldContext.query, fieldContext.field.fieldOptions().noMatchSize(), - higlighterNumberOfFragments, - fieldMatcher(fieldContext), + highlighterNumberOfFragments, maxAnalyzedOffset, - fieldContext.field.fieldOptions().maxAnalyzedOffset() + fieldContext.field.fieldOptions().maxAnalyzedOffset(), + fieldContext.field.fieldOptions().requireFieldMatch(), + weightMatchesEnabled ); } @@ -184,16 +192,17 @@ protected static BreakIterator getBreakIterator(SearchHighlightContext.Field fie : HighlightBuilder.BoundaryScannerType.SENTENCE; int maxLen = fieldOptions.fragmentCharSize(); switch (type) { - case SENTENCE: + case SENTENCE -> { if (maxLen > 0) { return BoundedBreakIteratorScanner.getSentence(locale, maxLen); } return BreakIterator.getSentenceInstance(locale); - case WORD: + } + case WORD -> { // ignore maxLen return BreakIterator.getWordInstance(locale); - default: - throw new IllegalArgumentException("Invalid boundary scanner type: " + type); + } + default -> throw new IllegalArgumentException("Invalid boundary scanner type: " + type); } } diff --git a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/PlainHighlighter.java b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/PlainHighlighter.java index 475333ed5a012..e7fa0e67cb453 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/PlainHighlighter.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/subphase/highlight/PlainHighlighter.java @@ -37,7 +37,7 @@ import java.util.Map; import static org.elasticsearch.search.fetch.subphase.highlight.AbstractHighlighterBuilder.MAX_ANALYZED_OFFSET_FIELD; -import static org.elasticsearch.search.fetch.subphase.highlight.UnifiedHighlighter.convertFieldValue; +import static org.elasticsearch.search.fetch.subphase.highlight.DefaultHighlighter.convertFieldValue; public class PlainHighlighter implements Highlighter { private static final String CACHE_KEY = "highlight-plain"; diff --git a/server/src/test/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighterTests.java b/server/src/test/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighterTests.java index 74d70f179697b..5df6840640264 100644 --- a/server/src/test/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighterTests.java +++ b/server/src/test/java/org/elasticsearch/lucene/search/uhighlight/CustomUnifiedHighlighterTests.java @@ -134,21 +134,23 @@ private void assertHighlightOneDoc( TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), 1, Sort.INDEXORDER); assertThat(topDocs.totalHits.value, equalTo(1L)); String rawValue = Strings.arrayToDelimitedString(inputs, String.valueOf(MULTIVAL_SEP_CHAR)); + UnifiedHighlighter.Builder builder = UnifiedHighlighter.builder(searcher, analyzer); + builder.withBreakIterator(() -> breakIterator); + builder.withFieldMatcher(name -> "text".equals(name)); + builder.withFormatter(new CustomPassageFormatter("", "", new DefaultEncoder())); CustomUnifiedHighlighter highlighter = new CustomUnifiedHighlighter( - searcher, - analyzer, + builder, offsetSource, - new CustomPassageFormatter("", "", new DefaultEncoder()), locale, - breakIterator, "index", "text", query, noMatchSize, expectedPassages.length, - name -> "text".equals(name), maxAnalyzedOffset, - queryMaxAnalyzedOffset + queryMaxAnalyzedOffset, + true, + true ); final Snippet[] snippets = highlighter.highlightField(getOnlyLeafReader(reader), topDocs.scoreDocs[0].doc, () -> rawValue); assertEquals(snippets.length, expectedPassages.length); @@ -218,7 +220,7 @@ public void testMultiPhrasePrefixQuerySingleTerm() throws Exception { public void testMultiPhrasePrefixQuery() throws Exception { final String[] inputs = { "The quick brown fox." }; - final String[] outputs = { "The quick brown fox." }; + final String[] outputs = { "The quick brown fox." }; MultiPhrasePrefixQuery query = new MultiPhrasePrefixQuery("text"); query.add(new Term("text", "quick")); query.add(new Term("text", "brown")); @@ -241,7 +243,7 @@ public void testSentenceBoundedBreakIterator() throws Exception { final String[] outputs = { "The quick brown", "fox in a long", - "with another quick", + "another quick", "brown fox.", "sentence with brown", "fox.", }; @@ -277,7 +279,7 @@ public void testSmallSentenceBoundedBreakIterator() throws Exception { ); } - public void testRepeat() throws Exception { + public void testRepeatTerm() throws Exception { final String[] inputs = { "Fun fun fun fun fun fun fun fun fun fun" }; final String[] outputs = { "Fun fun fun", @@ -295,8 +297,12 @@ public void testRepeat() throws Exception { 0, outputs ); + } - query = new PhraseQuery.Builder().add(new Term("text", "fun")).add(new Term("text", "fun")).build(); + public void testRepeatPhrase() throws Exception { + final String[] inputs = { "Fun fun fun fun fun fun fun fun fun fun" }; + final String[] outputs = { "Fun fun fun", "fun fun ", "fun fun fun", "fun fun" }; + Query query = new PhraseQuery.Builder().add(new Term("text", "fun")).add(new Term("text", "fun")).build(); assertHighlightOneDoc( "text", inputs, diff --git a/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java b/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java index 42f14bd1c15c0..46c186e656b6b 100644 --- a/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java +++ b/server/src/test/java/org/elasticsearch/search/SearchModuleTests.java @@ -46,10 +46,10 @@ import org.elasticsearch.search.fetch.FetchSubPhase; import org.elasticsearch.search.fetch.subphase.ExplainPhase; import org.elasticsearch.search.fetch.subphase.highlight.CustomHighlighter; +import org.elasticsearch.search.fetch.subphase.highlight.DefaultHighlighter; import org.elasticsearch.search.fetch.subphase.highlight.FastVectorHighlighter; import org.elasticsearch.search.fetch.subphase.highlight.Highlighter; import org.elasticsearch.search.fetch.subphase.highlight.PlainHighlighter; -import org.elasticsearch.search.fetch.subphase.highlight.UnifiedHighlighter; import org.elasticsearch.search.internal.ShardSearchRequest; import org.elasticsearch.search.rescore.QueryRescorerBuilder; import org.elasticsearch.search.rescore.RescoreContext; @@ -298,7 +298,7 @@ public Map getHighlighters() { Map highlighters = module.getHighlighters(); assertEquals(FastVectorHighlighter.class, highlighters.get("fvh").getClass()); assertEquals(PlainHighlighter.class, highlighters.get("plain").getClass()); - assertEquals(UnifiedHighlighter.class, highlighters.get("unified").getClass()); + assertEquals(DefaultHighlighter.class, highlighters.get("unified").getClass()); assertSame(highlighters.get("custom"), customHighlighter); } diff --git a/test/framework/src/main/java/org/elasticsearch/search/fetch/HighlighterTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/fetch/HighlighterTestCase.java index f86572f560865..6911fda2b44c9 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/fetch/HighlighterTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/fetch/HighlighterTestCase.java @@ -23,12 +23,12 @@ import org.elasticsearch.index.query.SearchExecutionContext; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.builder.SearchSourceBuilder; +import org.elasticsearch.search.fetch.subphase.highlight.DefaultHighlighter; import org.elasticsearch.search.fetch.subphase.highlight.FastVectorHighlighter; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; import org.elasticsearch.search.fetch.subphase.highlight.HighlightPhase; import org.elasticsearch.search.fetch.subphase.highlight.Highlighter; import org.elasticsearch.search.fetch.subphase.highlight.PlainHighlighter; -import org.elasticsearch.search.fetch.subphase.highlight.UnifiedHighlighter; import org.elasticsearch.search.lookup.Source; import java.io.IOException; @@ -47,7 +47,7 @@ public class HighlighterTestCase extends MapperServiceTestCase { protected Map getHighlighters() { return Map.of( "unified", - new UnifiedHighlighter(), + new DefaultHighlighter(), "fvh", new FastVectorHighlighter(getIndexSettings()), "plain", diff --git a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java index fadf0355fb7a8..2a2ac669bcc41 100644 --- a/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java +++ b/x-pack/plugin/ccr/src/main/java/org/elasticsearch/xpack/ccr/action/TransportResumeFollowAction.java @@ -471,6 +471,7 @@ static String[] extractLeaderShardHistoryUUIDs(Map ccrIndexMetad IndexSettings.MAX_REGEX_LENGTH_SETTING, IndexSettings.MAX_TERMS_COUNT_SETTING, IndexSettings.MAX_ANALYZED_OFFSET_SETTING, + IndexSettings.WEIGHT_MATCHES_MODE_ENABLED_SETTING, IndexSettings.MAX_DOCVALUE_FIELDS_SEARCH_SETTING, IndexSettings.MAX_TOKEN_COUNT_SETTING, IndexSettings.MAX_SLICES_PER_SCROLL,