Add highlighter type switch

This commit is contained in:
Igor Motov 2012-10-17 19:31:03 -04:00 committed by Shay Banon
parent 4573abc737
commit c551f93cae
6 changed files with 172 additions and 4 deletions

View File

@ -646,6 +646,14 @@ public class SearchRequestBuilder extends ActionRequestBuilder<SearchRequest, Se
return this;
}
/**
* The highlighter type to use.
*/
public SearchRequestBuilder setHighlighterType(String type) {
highlightBuilder().highlighterType(type);
return this;
}
/**
* Sets the source of the request as a json string. Note, settings anything other
* than the search type will cause this source to be overridden, consider using

View File

@ -48,6 +48,9 @@ public class HighlightBuilder implements ToXContent {
private Boolean requireFieldMatch;
private String highlighterType;
/**
* Adds a field to be highlighted with default fragment size of 100 characters, and
* default number of fragments of 5 using the default encoder
@ -176,6 +179,15 @@ public class HighlightBuilder implements ToXContent {
return this;
}
/**
* Set type of highlighter to use. Supported types
* are <tt>highlighter</tt> and <tt>fast-vector-highlighter</tt>.
*/
public HighlightBuilder highlighterType(String highlighterType) {
this.highlighterType = highlighterType;
return this;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("highlight");
@ -197,6 +209,9 @@ public class HighlightBuilder implements ToXContent {
if (requireFieldMatch != null) {
builder.field("require_field_match", requireFieldMatch);
}
if (highlighterType != null) {
builder.field("type", highlighterType);
}
if (fields != null) {
builder.startObject("fields");
for (Field field : fields) {
@ -213,6 +228,9 @@ public class HighlightBuilder implements ToXContent {
if (field.requireFieldMatch != null) {
builder.field("require_field_match", field.requireFieldMatch);
}
if (field.highlighterType != null) {
builder.field("type", field.highlighterType);
}
builder.endObject();
}
@ -229,6 +247,7 @@ public class HighlightBuilder implements ToXContent {
int fragmentOffset = -1;
int numOfFragments = -1;
Boolean requireFieldMatch;
String highlighterType;
public Field(String name) {
this.name = name;
@ -257,5 +276,10 @@ public class HighlightBuilder implements ToXContent {
this.requireFieldMatch = requireFieldMatch;
return this;
}
public Field highlighterType(String highlighterType) {
this.highlighterType = highlighterType;
return this;
}
}
}

View File

@ -129,10 +129,22 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
continue;
}
}
// if we can do highlighting using Term Vectors, use FastVectorHighlighter, otherwise, use the
// slower plain highlighter
if (mapper.termVector() != Field.TermVector.WITH_POSITIONS_OFFSETS) {
boolean useFastVectorHighlighter;
if (field.highlighterType() == null) {
// if we can do highlighting using Term Vectors, use FastVectorHighlighter, otherwise, use the
// slower plain highlighter
useFastVectorHighlighter = mapper.termVector() == Field.TermVector.WITH_POSITIONS_OFFSETS;
} else if (field.highlighterType().equals("fast-vector-highlighter")) {
if (mapper.termVector() != Field.TermVector.WITH_POSITIONS_OFFSETS) {
throw new FetchPhaseExecutionException(context, "The field [" + field.field() + "] should be indexed with term vector with position offsets to be used with fast vector highlighter");
}
useFastVectorHighlighter = true;
} else if (field.highlighterType().equals("highlighter")) {
useFastVectorHighlighter = false;
} else {
throw new FetchPhaseExecutionException(context, "Unknown highlighter type [" + field.highlighterType() + "] for the field [" + field.field() + "]");
}
if (!useFastVectorHighlighter) {
MapperHighlightEntry entry = cache.mappers.get(mapper);
if (entry == null) {
// Don't use the context.query() since it might be rewritten, and we need to pass the non rewritten queries to

View File

@ -76,6 +76,7 @@ public class HighlighterParseElement implements SearchParseElement {
String globalEncoder = "default";
int globalBoundaryMaxScan = SimpleBoundaryScanner2.DEFAULT_MAX_SCAN;
char[] globalBoundaryChars = SimpleBoundaryScanner2.DEFAULT_BOUNDARY_CHARS;
String globalHighlighterType = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
@ -117,6 +118,8 @@ public class HighlighterParseElement implements SearchParseElement {
globalBoundaryMaxScan = parser.intValue();
} else if ("boundary_chars".equals(topLevelFieldName) || "boundaryChars".equals(topLevelFieldName)) {
globalBoundaryChars = parser.text().toCharArray();
} else if ("type".equals(topLevelFieldName)) {
globalHighlighterType = parser.text();
}
} else if (token == XContentParser.Token.START_OBJECT) {
if ("fields".equals(topLevelFieldName)) {
@ -161,6 +164,8 @@ public class HighlighterParseElement implements SearchParseElement {
field.boundaryMaxScan(parser.intValue());
} else if ("boundary_chars".equals(topLevelFieldName) || "boundaryChars".equals(topLevelFieldName)) {
field.boundaryChars(parser.text().toCharArray());
} else if ("type".equals(fieldName)) {
field.highlighterType(parser.text());
}
}
}
@ -206,6 +211,9 @@ public class HighlighterParseElement implements SearchParseElement {
if (field.boundaryChars() == null) {
field.boundaryChars(globalBoundaryChars);
}
if (field.highlighterType() == null) {
field.highlighterType(globalHighlighterType);
}
}
context.highlight(new SearchContextHighlight(fields));

View File

@ -58,6 +58,8 @@ public class SearchContextHighlight {
private Boolean requireFieldMatch;
private String highlighterType;
private int boundaryMaxScan = -1;
private char[] boundaryChars = null;
@ -141,6 +143,14 @@ public class SearchContextHighlight {
this.requireFieldMatch = requireFieldMatch;
}
public String highlighterType() {
return highlighterType;
}
public void highlighterType(String type) {
this.highlighterType = type;
}
public int boundaryMaxScan() {
return boundaryMaxScan;
}

View File

@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.indices.IndexMissingException;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@ -781,4 +782,109 @@ public class HighlighterSearchTests extends AbstractNodesTests {
hit = search.hits().getAt(0);
assertThat(hit.highlightFields().get("title.key").fragments()[0].string(), equalTo("<em>this</em> <em>is</em> <em>a</em> <em>test</em>"));
}
@Test
public void testFastVectorHighlighterShouldFailIfNoTermVectors() throws Exception {
try {
client.admin().indices().prepareDelete("test").execute().actionGet();
} catch (Exception e) {
// ignore
}
client.admin().indices().prepareCreate("test").setSettings(ImmutableSettings.settingsBuilder().put("number_of_shards", 2))
.addMapping("type1", jsonBuilder().startObject().startObject("type1").startObject("properties")
.startObject("title").field("type", "string").field("store", "yes").field("term_vector", "no").endObject()
.endObject().endObject().endObject())
.execute().actionGet();
for (int i = 0; i < 5; i++) {
client.prepareIndex("test", "type1", Integer.toString(i))
.setSource("title", "This is a test for the enabling fast vector highlighter").setRefresh(true).execute().actionGet();
}
SearchResponse search = client.prepareSearch()
.setQuery(matchPhraseQuery("title", "this is a test"))
.addHighlightedField("title", 50, 1, 10)
.execute().actionGet();
assertThat(Arrays.toString(search.shardFailures()), search.failedShards(), equalTo(0));
search = client.prepareSearch()
.setQuery(matchPhraseQuery("title", "this is a test"))
.addHighlightedField("title", 50, 1, 10)
.setHighlighterType("fast-vector-highlighter")
.execute().actionGet();
assertThat(search.failedShards(), equalTo(2));
}
@Test
public void testDisableFastVectorHighlighter() throws Exception {
try {
client.admin().indices().prepareDelete("test").execute().actionGet();
} catch (Exception e) {
// ignore
}
client.admin().indices().prepareCreate("test").setSettings(ImmutableSettings.settingsBuilder().put("number_of_shards", 2))
.addMapping("type1", jsonBuilder().startObject().startObject("type1").startObject("properties")
.startObject("title").field("type", "string").field("store", "yes").field("term_vector", "with_positions_offsets").endObject()
.endObject().endObject().endObject())
.execute().actionGet();
for (int i = 0; i < 5; i++) {
client.prepareIndex("test", "type1", Integer.toString(i))
.setSource("title", "This is a test for the workaround for the fast vector highlighting SOLR-3724").setRefresh(true).execute().actionGet();
}
SearchResponse search = client.prepareSearch()
.setQuery(matchPhraseQuery("title", "test for the workaround"))
.addHighlightedField("title", 50, 1, 10)
.execute().actionGet();
assertThat(Arrays.toString(search.shardFailures()), search.failedShards(), equalTo(0));
assertThat(search.hits().totalHits(), equalTo(5l));
assertThat(search.hits().hits().length, equalTo(5));
for (SearchHit hit : search.hits()) {
// Because of SOLR-3724 nothing is highlighted when FVH is used
assertThat(hit.highlightFields().isEmpty(), equalTo(true));
}
// Using plain highlighter instead of FVH
search = client.prepareSearch()
.setQuery(matchPhraseQuery("title", "test for the workaround"))
.addHighlightedField("title", 50, 1, 10)
.setHighlighterType("highlighter")
.execute().actionGet();
assertThat(Arrays.toString(search.shardFailures()), search.failedShards(), equalTo(0));
assertThat(search.hits().totalHits(), equalTo(5l));
assertThat(search.hits().hits().length, equalTo(5));
for (SearchHit hit : search.hits()) {
// With plain highlighter terms are highlighted correctly
assertThat(hit.highlightFields().get("title").fragments()[0].string(), equalTo("This is a <em>test</em> for the <em>workaround</em> for the fast vector highlighting SOLR-3724"));
}
// Using plain highlighter instead of FVH on the field level
search = client.prepareSearch()
.setQuery(matchPhraseQuery("title", "test for the workaround"))
.addHighlightedField(new HighlightBuilder.Field("title").highlighterType("highlighter"))
.setHighlighterType("highlighter")
.execute().actionGet();
assertThat(Arrays.toString(search.shardFailures()), search.failedShards(), equalTo(0));
assertThat(search.hits().totalHits(), equalTo(5l));
assertThat(search.hits().hits().length, equalTo(5));
for (SearchHit hit : search.hits()) {
// With plain highlighter terms are highlighted correctly
assertThat(hit.highlightFields().get("title").fragments()[0].string(), equalTo("This is a <em>test</em> for the <em>workaround</em> for the fast vector highlighting SOLR-3724"));
}
}
}