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,