SOLR-5127: Multiple highlight fields and wildcards are now supported e.g. hl.fl=title,text_*

(Sven-S. Porst, Daniel Debray, Simon Endele, Christine Poerschke)
This commit is contained in:
Christine Poerschke 2017-04-19 11:45:59 +01:00
parent e62a3ff46c
commit f9ca49a8d5
5 changed files with 80 additions and 20 deletions

View File

@ -215,6 +215,9 @@ Bug Fixes
* SOLR-10472: Fixed uninversion (aka: FieldCache) bugs with the numeric PointField classes, and CurrencyField (hossman) * SOLR-10472: Fixed uninversion (aka: FieldCache) bugs with the numeric PointField classes, and CurrencyField (hossman)
* SOLR-5127: Multiple highlight fields and wildcards are now supported e.g. hl.fl=title,text_*
(Sven-S. Porst, Daniel Debray, Simon Endele, Christine Poerschke)
Other Changes Other Changes
---------------------- ----------------------

View File

@ -24,9 +24,9 @@ import org.apache.solr.search.DocList;
import org.apache.solr.util.SolrPluginUtils; import org.apache.solr.util.SolrPluginUtils;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.LinkedHashSet;
import java.util.Set;
public abstract class SolrHighlighter public abstract class SolrHighlighter
{ {
@ -60,27 +60,19 @@ public abstract class SolrHighlighter
if (emptyArray(defaultFields)) { if (emptyArray(defaultFields)) {
String defaultSearchField = request.getSchema().getDefaultSearchFieldName(); String defaultSearchField = request.getSchema().getDefaultSearchFieldName();
fields = null == defaultSearchField ? new String[]{} : new String[]{defaultSearchField}; fields = null == defaultSearchField ? new String[]{} : new String[]{defaultSearchField};
} } else {
else {
fields = defaultFields; fields = defaultFields;
} }
}
else if (fields.length == 1) {
if (fields[0].contains("*")) {
// create a Java regular expression from the wildcard string
String fieldRegex = fields[0].replaceAll("\\*", ".*");
Collection<String> storedHighlightFieldNames = request.getSearcher().getDocFetcher().getStoredHighlightFieldNames();
List<String> storedFieldsToHighlight = new ArrayList<>();
for (String storedFieldName: storedHighlightFieldNames) {
if (storedFieldName.matches(fieldRegex)) {
storedFieldsToHighlight.add(storedFieldName);
}
}
fields = storedFieldsToHighlight.toArray(new String[storedFieldsToHighlight.size()]);
} else { } else {
// if there's a single request/handler value, it may be a space/comma separated list Set<String> expandedFields = new LinkedHashSet<String>();
fields = SolrPluginUtils.split(fields[0]); Collection<String> storedHighlightFieldNames = request.getSearcher().getDocFetcher().getStoredHighlightFieldNames();
for (String field : fields) {
expandWildcardsInHighlightFields(
expandedFields,
storedHighlightFieldNames,
SolrPluginUtils.split(field));
} }
fields = expandedFields.toArray(new String[]{});
} }
// Trim them now in case they haven't been yet. Not needed for all code-paths above but do it here. // Trim them now in case they haven't been yet. Not needed for all code-paths above but do it here.
@ -94,6 +86,25 @@ public abstract class SolrHighlighter
return (arr == null || arr.length == 0 || arr[0] == null || arr[0].trim().length() == 0); return (arr == null || arr.length == 0 || arr[0] == null || arr[0].trim().length() == 0);
} }
static private void expandWildcardsInHighlightFields (
Set<String> expandedFields,
Collection<String> storedHighlightFieldNames,
String... fields) {
for (String field : fields) {
if (field.contains("*")) {
// create a Java regular expression from the wildcard string
String fieldRegex = field.replaceAll("\\*", ".*");
for (String storedFieldName : storedHighlightFieldNames) {
if (storedFieldName.matches(fieldRegex)) {
expandedFields.add(storedFieldName);
}
}
} else {
expandedFields.add(field);
}
}
}
/** /**
* Generates a list of Highlighted query fragments for each item in a list * Generates a list of Highlighted query fragments for each item in a list
* of documents, or returns null if highlighting is disabled. * of documents, or returns null if highlighting is disabled.

View File

@ -20,7 +20,9 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.TokenStream;
@ -35,6 +37,7 @@ import org.apache.solr.common.params.HighlightParams;
import org.apache.solr.handler.component.HighlightComponent; import org.apache.solr.handler.component.HighlightComponent;
import org.apache.solr.handler.component.ResponseBuilder; import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.handler.component.SearchComponent; import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.response.SolrQueryResponse;
import org.apache.solr.search.DocSet; import org.apache.solr.search.DocSet;
@ -868,6 +871,8 @@ public class HighlighterTest extends SolrTestCaseJ4 {
"text", "test", // static not stored "text", "test", // static not stored
"foo_s", "test", // dynamic stored "foo_s", "test", // dynamic stored
"foo_sI", "test", // dynamic not stored "foo_sI", "test", // dynamic not stored
"bar_s", "test", // dynamic stored
"bar_sI", "test", // dynamic not stored
"weight", "1.0")); // stored but not text "weight", "1.0")); // stored but not text
assertU(commit()); assertU(commit());
assertU(optimize()); assertU(optimize());
@ -898,6 +903,21 @@ public class HighlighterTest extends SolrTestCaseJ4 {
assertEquals("Expected to highlight on field \"foo_s\"", "foo_s", assertEquals("Expected to highlight on field \"foo_s\"", "foo_s",
highlightFieldNames.get(0)); highlightFieldNames.get(0));
request.close(); request.close();
// SOLR-5127
args.put("hl.fl", (random().nextBoolean() ? "foo_*,bar_*" : "bar_*,foo_*"));
lrf = h.getRequestFactory("standard", 0, 10, args);
// hl.fl ordering need not be preserved in output
final Set<String> highlightedSetExpected = new HashSet<String>();
highlightedSetExpected.add("foo_s");
highlightedSetExpected.add("bar_s");
try (LocalSolrQueryRequest localRequest = lrf.makeRequest("test")) {
highlighter = HighlightComponent.getHighlighter(h.getCore());
final Set<String> highlightedSetActual = new HashSet<String>(
Arrays.asList(highlighter.getHighlightFields(null,
localRequest, new String[] {})));
assertEquals(highlightedSetExpected, highlightedSetActual);
}
} }
@Test @Test

View File

@ -99,6 +99,19 @@ public class TestPostingsSolrHighlighter extends SolrTestCaseJ4 {
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier <em>document</em>'"); "//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier <em>document</em>'");
} }
// SOLR-5127
public void testMultipleFieldsViaWildcard() {
assertQ("highlighting text and text3*",
req("q", (random().nextBoolean() ? "text:document text3:document" : "text3:document text:document"),
"sort", "id asc", "hl", "true",
"hl.fl", (random().nextBoolean() ? "text,text3*" : "text3*,text")),
"count(//lst[@name='highlighting']/*)=2",
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='<em>document</em> one'",
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text3']/str='crappy <em>document</em>'",
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second <em>document</em>'",
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier <em>document</em>'");
}
public void testMisconfiguredField() { public void testMisconfiguredField() {
ignoreException("was indexed without offsets"); ignoreException("was indexed without offsets");
try { try {

View File

@ -179,6 +179,19 @@ public class TestUnifiedSolrHighlighter extends SolrTestCaseJ4 {
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier <em>document</em>'"); "//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier <em>document</em>'");
} }
// SOLR-5127
public void testMultipleFieldsViaWildcard() {
assertQ("highlighting text and text3*",
req("q", (random().nextBoolean() ? "text:document text3:document" : "text3:document text:document"),
"sort", "id asc", "hl", "true",
"hl.fl", (random().nextBoolean() ? "text,text3*" : "text3*,text")),
"count(//lst[@name='highlighting']/*)=2",
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text']/str='<em>document</em> one'",
"//lst[@name='highlighting']/lst[@name='101']/arr[@name='text3']/str='crappy <em>document</em>'",
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text']/str='second <em>document</em>'",
"//lst[@name='highlighting']/lst[@name='102']/arr[@name='text3']/str='crappier <em>document</em>'");
}
public void testTags() { public void testTags() {
assertQ("different pre/post tags", assertQ("different pre/post tags",
req("q", "text:document", "sort", "id asc", "hl", "true", "hl.tag.pre", "[", "hl.tag.post", "]"), req("q", "text:document", "sort", "id asc", "hl", "true", "hl.tag.pre", "[", "hl.tag.post", "]"),