Allow highlighting on wildcard fields.. ie, comment_*

closes #2396
This commit is contained in:
Shay Banon 2012-12-26 14:55:44 -08:00
parent 4b69846ba2
commit 2f4b759df7
3 changed files with 236 additions and 186 deletions

View File

@ -129,7 +129,7 @@ public class DocumentFieldMappers implements Iterable<FieldMapper> {
if (Regex.simpleMatch(pattern, fieldMapper.names().fullName())) { if (Regex.simpleMatch(pattern, fieldMapper.names().fullName())) {
fields.add(fieldMapper.names().indexName()); fields.add(fieldMapper.names().indexName());
} else if (Regex.simpleMatch(pattern, fieldMapper.names().indexName())) { } else if (Regex.simpleMatch(pattern, fieldMapper.names().indexName())) {
fields.add(fieldMapper.names().name()); fields.add(fieldMapper.names().indexName());
} else if (Regex.simpleMatch(pattern, fieldMapper.names().name())) { } else if (Regex.simpleMatch(pattern, fieldMapper.names().name())) {
fields.add(fieldMapper.names().indexName()); fields.add(fieldMapper.names().indexName());
} }
@ -137,6 +137,20 @@ public class DocumentFieldMappers implements Iterable<FieldMapper> {
return fields; return fields;
} }
public Set<String> simpleMatchToFullName(String pattern) {
Set<String> fields = Sets.newHashSet();
for (FieldMapper fieldMapper : fieldMappers) {
if (Regex.simpleMatch(pattern, fieldMapper.names().fullName())) {
fields.add(fieldMapper.names().fullName());
} else if (Regex.simpleMatch(pattern, fieldMapper.names().indexName())) {
fields.add(fieldMapper.names().fullName());
} else if (Regex.simpleMatch(pattern, fieldMapper.names().name())) {
fields.add(fieldMapper.names().fullName());
}
}
return fields;
}
/** /**
* Tries to find first based on {@link #fullName(String)}, then by {@link #indexName(String)}, and last * Tries to find first based on {@link #fullName(String)}, then by {@link #indexName(String)}, and last
* by {@link #name(String)}. * by {@link #name(String)}.

View File

@ -34,6 +34,7 @@ import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.FastStringReader; import org.elasticsearch.common.io.FastStringReader;
import org.elasticsearch.common.lucene.search.vectorhighlight.SimpleBoundaryScanner2; import org.elasticsearch.common.lucene.search.vectorhighlight.SimpleBoundaryScanner2;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.StringText; import org.elasticsearch.common.text.StringText;
import org.elasticsearch.index.fieldvisitor.CustomFieldsVisitor; import org.elasticsearch.index.fieldvisitor.CustomFieldsVisitor;
@ -109,9 +110,19 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
} else { } else {
encoder = Encoders.DEFAULT; encoder = Encoders.DEFAULT;
} }
FieldMapper mapper = documentMapper.mappers().smartNameFieldMapper(field.field());
Set<String> fieldNamesToHighlight;
if (Regex.isSimpleMatchPattern(field.field())) {
fieldNamesToHighlight = documentMapper.mappers().simpleMatchToFullName(field.field());
} else {
fieldNamesToHighlight = ImmutableSet.of(field.field());
}
for (String fieldName : fieldNamesToHighlight) {
FieldMapper mapper = documentMapper.mappers().smartNameFieldMapper(fieldName);
if (mapper == null) { if (mapper == null) {
MapperService.SmartNameFieldMappers fullMapper = context.mapperService().smartName(field.field()); MapperService.SmartNameFieldMappers fullMapper = context.mapperService().smartName(fieldName);
if (fullMapper == null || !fullMapper.hasDocMapper()) { if (fullMapper == null || !fullMapper.hasDocMapper()) {
//Save skipping missing fields //Save skipping missing fields
continue; continue;
@ -131,13 +142,13 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
useFastVectorHighlighter = mapper.fieldType().storeTermVectors() && mapper.fieldType().storeTermVectorOffsets() && mapper.fieldType().storeTermVectorPositions(); useFastVectorHighlighter = mapper.fieldType().storeTermVectors() && mapper.fieldType().storeTermVectorOffsets() && mapper.fieldType().storeTermVectorPositions();
} else if (field.highlighterType().equals("fast-vector-highlighter") || field.highlighterType().equals("fvh")) { } else if (field.highlighterType().equals("fast-vector-highlighter") || field.highlighterType().equals("fvh")) {
if (!(mapper.fieldType().storeTermVectors() && mapper.fieldType().storeTermVectorOffsets() && mapper.fieldType().storeTermVectorPositions())) { if (!(mapper.fieldType().storeTermVectors() && mapper.fieldType().storeTermVectorOffsets() && mapper.fieldType().storeTermVectorPositions())) {
throw new ElasticSearchIllegalArgumentException("the field [" + field.field() + "] should be indexed with term vector with position offsets to be used with fast vector highlighter"); throw new ElasticSearchIllegalArgumentException("the field [" + fieldName + "] should be indexed with term vector with position offsets to be used with fast vector highlighter");
} }
useFastVectorHighlighter = true; useFastVectorHighlighter = true;
} else if (field.highlighterType().equals("highlighter") || field.highlighterType().equals("plain")) { } else if (field.highlighterType().equals("highlighter") || field.highlighterType().equals("plain")) {
useFastVectorHighlighter = false; useFastVectorHighlighter = false;
} else { } else {
throw new ElasticSearchIllegalArgumentException("unknown highlighter type [" + field.highlighterType() + "] for the field [" + field.field() + "]"); throw new ElasticSearchIllegalArgumentException("unknown highlighter type [" + field.highlighterType() + "] for the field [" + fieldName + "]");
} }
if (!useFastVectorHighlighter) { if (!useFastVectorHighlighter) {
MapperHighlightEntry entry = cache.mappers.get(mapper); MapperHighlightEntry entry = cache.mappers.get(mapper);
@ -158,7 +169,7 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
} else if ("span".equals(field.fragmenter())) { } else if ("span".equals(field.fragmenter())) {
fragmenter = new SimpleSpanFragmenter(queryScorer, field.fragmentCharSize()); fragmenter = new SimpleSpanFragmenter(queryScorer, field.fragmentCharSize());
} else { } else {
throw new ElasticSearchIllegalArgumentException("unknown fragmenter option [" + field.fragmenter() + "] for the field [" + field.field() + "]"); throw new ElasticSearchIllegalArgumentException("unknown fragmenter option [" + field.fragmenter() + "] for the field [" + fieldName + "]");
} }
Formatter formatter = new SimpleHTMLFormatter(field.preTags()[0], field.postTags()[0]); Formatter formatter = new SimpleHTMLFormatter(field.preTags()[0], field.postTags()[0]);
@ -179,7 +190,7 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
hitContext.reader().document(hitContext.docId(), fieldVisitor); hitContext.reader().document(hitContext.docId(), fieldVisitor);
textsToHighlight = fieldVisitor.fields().get(mapper.names().indexName()); textsToHighlight = fieldVisitor.fields().get(mapper.names().indexName());
} catch (Exception e) { } catch (Exception e) {
throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + field.field() + "]", e); throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + fieldName + "]", e);
} }
} else { } else {
SearchLookup lookup = context.lookup(); SearchLookup lookup = context.lookup();
@ -204,7 +215,7 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
} }
} }
} catch (Exception e) { } catch (Exception e) {
throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + field.field() + "]", e); throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + fieldName + "]", e);
} }
if (field.scoreOrdered()) { if (field.scoreOrdered()) {
Collections.sort(fragsList, new Comparator<TextFragment>() { Collections.sort(fragsList, new Comparator<TextFragment>() {
@ -230,7 +241,7 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
} }
if (fragments != null && fragments.length > 0) { if (fragments != null && fragments.length > 0) {
HighlightField highlightField = new HighlightField(field.field(), StringText.convertFromStringArray(fragments)); HighlightField highlightField = new HighlightField(fieldName, StringText.convertFromStringArray(fragments));
highlightFields.put(highlightField.name(), highlightField); highlightFields.put(highlightField.name(), highlightField);
} }
} else { } else {
@ -311,11 +322,12 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
entry.fragListBuilder, entry.fragmentsBuilder, field.preTags(), field.postTags(), encoder); entry.fragListBuilder, entry.fragmentsBuilder, field.preTags(), field.postTags(), encoder);
if (fragments != null && fragments.length > 0) { if (fragments != null && fragments.length > 0) {
HighlightField highlightField = new HighlightField(field.field(), StringText.convertFromStringArray(fragments)); HighlightField highlightField = new HighlightField(fieldName, StringText.convertFromStringArray(fragments));
highlightFields.put(highlightField.name(), highlightField); highlightFields.put(highlightField.name(), highlightField);
} }
} catch (Exception e) { } catch (Exception e) {
throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + field.field() + "]", e); throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + fieldName + "]", e);
}
} }
} }
} }

View File

@ -257,6 +257,30 @@ public class HighlighterSearchTests extends AbstractNodesTests {
assertThat(search.hits().hits()[0].highlightFields().get("titleTV").fragments()[1].string(), equalTo("<em>highlight</em> other text")); assertThat(search.hits().hits()[0].highlightFields().get("titleTV").fragments()[1].string(), equalTo("<em>highlight</em> other text"));
} }
@Test
public void testHighlightingOnWildcardFields() throws Exception {
client.admin().indices().prepareDelete().execute().actionGet();
client.admin().indices().prepareCreate("test").execute().actionGet();
client.admin().cluster().prepareHealth("test").setWaitForGreenStatus().execute().actionGet();
client.prepareIndex("test", "type1")
.setSource("field1", "this is a test", "field2", "this is another test")
.setRefresh(true).execute().actionGet();
logger.info("--> highlighting and searching on field*");
SearchSourceBuilder source = searchSource()
.query(termQuery("field1", "test"))
.from(0).size(60).explain(true)
.highlight(highlight().field("field*").order("score").preTags("<xxx>").postTags("</xxx>"));
SearchResponse searchResponse = client.search(searchRequest("test").source(source).searchType(QUERY_THEN_FETCH).scroll(timeValueMinutes(10))).actionGet();
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
assertThat(searchResponse.hits().totalHits(), equalTo(1l));
assertThat(searchResponse.hits().getAt(0).highlightFields().get("field1").fragments()[0].string(), equalTo("this is a <xxx>test</xxx>"));
assertThat(searchResponse.hits().getAt(0).highlightFields().get("field2").fragments()[0].string(), equalTo("this is another <xxx>test</xxx>"));
}
@Test @Test
public void testPlainHighlighter() throws Exception { public void testPlainHighlighter() throws Exception {
try { try {