Made SearchContextHighlight.Field class immutable to prevent from erroneously updating it, as it doesn't necessarily map to a single field
A Field instance can map to multiple actual fields when using wildcard expressions. Each actual field should use the proper highlighter depending on the available data structure (e.g. term_vectors), while we currently select the highlighter for the first field and we keep using the same for all the fields that match the wildcard expression. Modified also how the PercolateContext sets the forceSource option, in a global manner now rather than per field. Closes #5175
This commit is contained in:
parent
92f132aede
commit
16e350b266
|
@ -189,6 +189,10 @@ public class PercolateContext extends SearchContext {
|
|||
|
||||
@Override
|
||||
public void highlight(SearchContextHighlight highlight) {
|
||||
if (highlight != null) {
|
||||
// Enforce highlighting by source, because MemoryIndex doesn't support stored fields.
|
||||
highlight.globalForceSource(true);
|
||||
}
|
||||
this.highlight = highlight;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,10 +64,7 @@ import org.elasticsearch.index.query.ParsedQuery;
|
|||
import org.elasticsearch.index.service.IndexService;
|
||||
import org.elasticsearch.index.shard.service.IndexShard;
|
||||
import org.elasticsearch.indices.IndicesService;
|
||||
import org.elasticsearch.percolator.QueryCollector.Count;
|
||||
import org.elasticsearch.percolator.QueryCollector.Match;
|
||||
import org.elasticsearch.percolator.QueryCollector.MatchAndScore;
|
||||
import org.elasticsearch.percolator.QueryCollector.MatchAndSort;
|
||||
import org.elasticsearch.percolator.QueryCollector.*;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
import org.elasticsearch.search.SearchParseElement;
|
||||
import org.elasticsearch.search.SearchShardTarget;
|
||||
|
@ -79,7 +76,6 @@ import org.elasticsearch.search.facet.InternalFacet;
|
|||
import org.elasticsearch.search.facet.InternalFacets;
|
||||
import org.elasticsearch.search.highlight.HighlightField;
|
||||
import org.elasticsearch.search.highlight.HighlightPhase;
|
||||
import org.elasticsearch.search.highlight.SearchContextHighlight;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
import org.elasticsearch.search.sort.SortParseElement;
|
||||
|
||||
|
@ -307,11 +303,6 @@ public class PercolatorService extends AbstractComponent {
|
|||
// We need to get the actual source from the request body for highlighting, so parse the request body again
|
||||
// and only get the doc source.
|
||||
if (context.highlight() != null) {
|
||||
// Enforce highlighting by source, because MemoryIndex doesn't support stored fields.
|
||||
for (SearchContextHighlight.Field field : context.highlight().fields()) {
|
||||
field.forceSource(true);
|
||||
}
|
||||
|
||||
parser.close();
|
||||
currentFieldName = null;
|
||||
parser = XContentFactory.xContent(source).createParser(source);
|
||||
|
@ -370,10 +361,6 @@ public class PercolatorService extends AbstractComponent {
|
|||
doc = docMapper.parse(source(parser).type(type).flyweight(true));
|
||||
|
||||
if (context.highlight() != null) {
|
||||
// Enforce highlighting by source, because MemoryIndex doesn't support stored fields.
|
||||
for (SearchContextHighlight.Field field : context.highlight().fields()) {
|
||||
field.forceSource(true);
|
||||
}
|
||||
doc.setSource(fetchedDoc);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
|
|
@ -68,7 +68,7 @@ public class FastVectorHighlighter implements Highlighter {
|
|||
throw new ElasticsearchIllegalArgumentException("the field [" + highlighterContext.fieldName + "] should be indexed with term vector with position offsets to be used with fast vector highlighter");
|
||||
}
|
||||
|
||||
Encoder encoder = field.encoder().equals("html") ? HighlightUtils.Encoders.HTML : HighlightUtils.Encoders.DEFAULT;
|
||||
Encoder encoder = field.fieldOptions().encoder().equals("html") ? HighlightUtils.Encoders.HTML : HighlightUtils.Encoders.DEFAULT;
|
||||
|
||||
if (!hitContext.cache().containsKey(CACHE_KEY)) {
|
||||
hitContext.cache().put(CACHE_KEY, new HighlighterEntry());
|
||||
|
@ -77,16 +77,16 @@ public class FastVectorHighlighter implements Highlighter {
|
|||
|
||||
try {
|
||||
FieldQuery fieldQuery;
|
||||
if (field.requireFieldMatch()) {
|
||||
if (field.fieldOptions().requireFieldMatch()) {
|
||||
if (cache.fieldMatchFieldQuery == null) {
|
||||
// we use top level reader to rewrite the query against all readers, with use caching it across hits (and across readers...)
|
||||
cache.fieldMatchFieldQuery = new CustomFieldQuery(highlighterContext.query.originalQuery(), hitContext.topLevelReader(), true, field.requireFieldMatch());
|
||||
cache.fieldMatchFieldQuery = new CustomFieldQuery(highlighterContext.query.originalQuery(), hitContext.topLevelReader(), true, field.fieldOptions().requireFieldMatch());
|
||||
}
|
||||
fieldQuery = cache.fieldMatchFieldQuery;
|
||||
} else {
|
||||
if (cache.noFieldMatchFieldQuery == null) {
|
||||
// we use top level reader to rewrite the query against all readers, with use caching it across hits (and across readers...)
|
||||
cache.noFieldMatchFieldQuery = new CustomFieldQuery(highlighterContext.query.originalQuery(), hitContext.topLevelReader(), true, field.requireFieldMatch());
|
||||
cache.noFieldMatchFieldQuery = new CustomFieldQuery(highlighterContext.query.originalQuery(), hitContext.topLevelReader(), true, field.fieldOptions().requireFieldMatch());
|
||||
}
|
||||
fieldQuery = cache.noFieldMatchFieldQuery;
|
||||
}
|
||||
|
@ -97,31 +97,31 @@ public class FastVectorHighlighter implements Highlighter {
|
|||
BaseFragmentsBuilder fragmentsBuilder;
|
||||
|
||||
BoundaryScanner boundaryScanner = DEFAULT_BOUNDARY_SCANNER;
|
||||
if (field.boundaryMaxScan() != SimpleBoundaryScanner.DEFAULT_MAX_SCAN || field.boundaryChars() != SimpleBoundaryScanner.DEFAULT_BOUNDARY_CHARS) {
|
||||
boundaryScanner = new SimpleBoundaryScanner(field.boundaryMaxScan(), field.boundaryChars());
|
||||
if (field.fieldOptions().boundaryMaxScan() != SimpleBoundaryScanner.DEFAULT_MAX_SCAN || field.fieldOptions().boundaryChars() != SimpleBoundaryScanner.DEFAULT_BOUNDARY_CHARS) {
|
||||
boundaryScanner = new SimpleBoundaryScanner(field.fieldOptions().boundaryMaxScan(), field.fieldOptions().boundaryChars());
|
||||
}
|
||||
|
||||
if (field.numberOfFragments() == 0) {
|
||||
boolean forceSource = context.highlight().forceSource(field);
|
||||
if (field.fieldOptions().numberOfFragments() == 0) {
|
||||
fragListBuilder = new SingleFragListBuilder();
|
||||
|
||||
if (!field.forceSource() && mapper.fieldType().stored()) {
|
||||
fragmentsBuilder = new SimpleFragmentsBuilder(mapper, field.preTags(), field.postTags(), boundaryScanner);
|
||||
if (!forceSource && mapper.fieldType().stored()) {
|
||||
fragmentsBuilder = new SimpleFragmentsBuilder(mapper, field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
|
||||
} else {
|
||||
fragmentsBuilder = new SourceSimpleFragmentsBuilder(mapper, context, field.preTags(), field.postTags(), boundaryScanner);
|
||||
fragmentsBuilder = new SourceSimpleFragmentsBuilder(mapper, context, field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
|
||||
}
|
||||
} else {
|
||||
fragListBuilder = field.fragmentOffset() == -1 ? new SimpleFragListBuilder() : new SimpleFragListBuilder(field.fragmentOffset());
|
||||
if (field.scoreOrdered()) {
|
||||
if (!field.forceSource() && mapper.fieldType().stored()) {
|
||||
fragmentsBuilder = new ScoreOrderFragmentsBuilder(field.preTags(), field.postTags(), boundaryScanner);
|
||||
fragListBuilder = field.fieldOptions().fragmentOffset() == -1 ? new SimpleFragListBuilder() : new SimpleFragListBuilder(field.fieldOptions().fragmentOffset());
|
||||
if (field.fieldOptions().scoreOrdered()) {
|
||||
if (!forceSource && mapper.fieldType().stored()) {
|
||||
fragmentsBuilder = new ScoreOrderFragmentsBuilder(field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
|
||||
} else {
|
||||
fragmentsBuilder = new SourceScoreOrderFragmentsBuilder(mapper, context, field.preTags(), field.postTags(), boundaryScanner);
|
||||
fragmentsBuilder = new SourceScoreOrderFragmentsBuilder(mapper, context, field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
|
||||
}
|
||||
} else {
|
||||
if (!field.forceSource() && mapper.fieldType().stored()) {
|
||||
fragmentsBuilder = new SimpleFragmentsBuilder(mapper, field.preTags(), field.postTags(), boundaryScanner);
|
||||
if (!forceSource && mapper.fieldType().stored()) {
|
||||
fragmentsBuilder = new SimpleFragmentsBuilder(mapper, field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
|
||||
} else {
|
||||
fragmentsBuilder = new SourceSimpleFragmentsBuilder(mapper, context, field.preTags(), field.postTags(), boundaryScanner);
|
||||
fragmentsBuilder = new SourceSimpleFragmentsBuilder(mapper, context, field.fieldOptions().preTags(), field.fieldOptions().postTags(), boundaryScanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -135,37 +135,37 @@ public class FastVectorHighlighter implements Highlighter {
|
|||
// fragment builders are used explicitly
|
||||
cache.fvh = new org.apache.lucene.search.vectorhighlight.FastVectorHighlighter();
|
||||
}
|
||||
CustomFieldQuery.highlightFilters.set(field.highlightFilter());
|
||||
CustomFieldQuery.highlightFilters.set(field.fieldOptions().highlightFilter());
|
||||
cache.mappers.put(mapper, entry);
|
||||
}
|
||||
cache.fvh.setPhraseLimit(field.phraseLimit());
|
||||
cache.fvh.setPhraseLimit(field.fieldOptions().phraseLimit());
|
||||
|
||||
String[] fragments;
|
||||
|
||||
// a HACK to make highlighter do highlighting, even though its using the single frag list builder
|
||||
int numberOfFragments = field.numberOfFragments() == 0 ? Integer.MAX_VALUE : field.numberOfFragments();
|
||||
int fragmentCharSize = field.numberOfFragments() == 0 ? Integer.MAX_VALUE : field.fragmentCharSize();
|
||||
int numberOfFragments = field.fieldOptions().numberOfFragments() == 0 ? Integer.MAX_VALUE : field.fieldOptions().numberOfFragments();
|
||||
int fragmentCharSize = field.fieldOptions().numberOfFragments() == 0 ? Integer.MAX_VALUE : field.fieldOptions().fragmentCharSize();
|
||||
// we highlight against the low level reader and docId, because if we load source, we want to reuse it if possible
|
||||
// Only send matched fields if they were requested to save time.
|
||||
if (field.matchedFields() != null && !field.matchedFields().isEmpty()) {
|
||||
fragments = cache.fvh.getBestFragments(fieldQuery, hitContext.reader(), hitContext.docId(), mapper.names().indexName(), field.matchedFields(), fragmentCharSize,
|
||||
numberOfFragments, entry.fragListBuilder, entry.fragmentsBuilder, field.preTags(), field.postTags(), encoder);
|
||||
if (field.fieldOptions().matchedFields() != null && !field.fieldOptions().matchedFields().isEmpty()) {
|
||||
fragments = cache.fvh.getBestFragments(fieldQuery, hitContext.reader(), hitContext.docId(), mapper.names().indexName(), field.fieldOptions().matchedFields(), fragmentCharSize,
|
||||
numberOfFragments, entry.fragListBuilder, entry.fragmentsBuilder, field.fieldOptions().preTags(), field.fieldOptions().postTags(), encoder);
|
||||
} else {
|
||||
fragments = cache.fvh.getBestFragments(fieldQuery, hitContext.reader(), hitContext.docId(), mapper.names().indexName(), fragmentCharSize,
|
||||
numberOfFragments, entry.fragListBuilder, entry.fragmentsBuilder, field.preTags(), field.postTags(), encoder);
|
||||
numberOfFragments, entry.fragListBuilder, entry.fragmentsBuilder, field.fieldOptions().preTags(), field.fieldOptions().postTags(), encoder);
|
||||
}
|
||||
|
||||
if (fragments != null && fragments.length > 0) {
|
||||
return new HighlightField(highlighterContext.fieldName, StringText.convertFromStringArray(fragments));
|
||||
}
|
||||
|
||||
int noMatchSize = highlighterContext.field.noMatchSize();
|
||||
int noMatchSize = highlighterContext.field.fieldOptions().noMatchSize();
|
||||
if (noMatchSize > 0) {
|
||||
// Essentially we just request that a fragment is built from 0 to noMatchSize using the normal fragmentsBuilder
|
||||
FieldFragList fieldFragList = new SimpleFieldFragList(-1 /*ignored*/);
|
||||
fieldFragList.add(0, noMatchSize, Collections.<WeightedPhraseInfo>emptyList());
|
||||
fragments = entry.fragmentsBuilder.createFragments(hitContext.reader(), hitContext.docId(), mapper.names().indexName(),
|
||||
fieldFragList, 1, field.preTags(), field.postTags(), encoder);
|
||||
fieldFragList, 1, field.fieldOptions().preTags(), field.fieldOptions().postTags(), encoder);
|
||||
if (fragments != null && fragments.length > 0) {
|
||||
return new HighlightField(highlighterContext.fieldName, StringText.convertFromStringArray(fragments));
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
|
|||
fieldNamesToHighlight = ImmutableSet.of(field.field());
|
||||
}
|
||||
|
||||
if (field.forceSource()) {
|
||||
if (context.highlight().forceSource(field)) {
|
||||
SourceFieldMapper sourceFieldMapper = context.mapperService().documentMapper(hitContext.hit().type()).sourceMapper();
|
||||
if (!sourceFieldMapper.enabled()) {
|
||||
throw new ElasticsearchIllegalArgumentException("source is forced for fields " + fieldNamesToHighlight + " but type [" + hitContext.hit().type() + "] has disabled _source");
|
||||
|
@ -99,27 +99,28 @@ public class HighlightPhase extends AbstractComponent implements FetchSubPhase {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (field.highlighterType() == null) {
|
||||
String highlighterType = field.fieldOptions().highlighterType();
|
||||
if (highlighterType == null) {
|
||||
boolean useFastVectorHighlighter = fieldMapper.fieldType().storeTermVectors() && fieldMapper.fieldType().storeTermVectorOffsets() && fieldMapper.fieldType().storeTermVectorPositions();
|
||||
if (useFastVectorHighlighter) {
|
||||
field.highlighterType("fvh");
|
||||
highlighterType = "fvh";
|
||||
} else if (fieldMapper.fieldType().indexOptions() == FieldInfo.IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS) {
|
||||
field.highlighterType("postings");
|
||||
highlighterType = "postings";
|
||||
} else {
|
||||
field.highlighterType("plain");
|
||||
highlighterType = "plain";
|
||||
}
|
||||
}
|
||||
|
||||
Highlighter highlighter = highlighters.get(field.highlighterType());
|
||||
Highlighter highlighter = highlighters.get(highlighterType);
|
||||
if (highlighter == null) {
|
||||
throw new ElasticsearchIllegalArgumentException("unknown highlighter type [" + field.highlighterType() + "] for the field [" + fieldName + "]");
|
||||
throw new ElasticsearchIllegalArgumentException("unknown highlighter type [" + highlighterType + "] for the field [" + fieldName + "]");
|
||||
}
|
||||
|
||||
HighlighterContext.HighlightQuery highlightQuery;
|
||||
if (field.highlightQuery() == null) {
|
||||
if (field.fieldOptions().highlightQuery() == null) {
|
||||
highlightQuery = new HighlighterContext.HighlightQuery(context.parsedQuery().query(), context.query(), context.queryRewritten());
|
||||
} else {
|
||||
highlightQuery = new HighlighterContext.HighlightQuery(field.highlightQuery(), field.highlightQuery(), false);
|
||||
highlightQuery = new HighlighterContext.HighlightQuery(field.fieldOptions().highlightQuery(), field.fieldOptions().highlightQuery(), false);
|
||||
}
|
||||
HighlighterContext highlighterContext = new HighlighterContext(fieldName, field, fieldMapper, context, hitContext, highlightQuery);
|
||||
HighlightField highlightField = highlighter.highlight(highlighterContext);
|
||||
|
|
|
@ -41,7 +41,9 @@ public final class HighlightUtils {
|
|||
|
||||
}
|
||||
|
||||
static List<Object> loadFieldValues(FieldMapper<?> mapper, SearchContext searchContext, FetchSubPhase.HitContext hitContext, boolean forceSource) throws IOException {
|
||||
static List<Object> loadFieldValues(SearchContextHighlight.Field field, FieldMapper<?> mapper, SearchContext searchContext, FetchSubPhase.HitContext hitContext) throws IOException {
|
||||
//percolator needs to always load from source, thus it sets the global force source to true
|
||||
boolean forceSource = searchContext.highlight().forceSource(field);
|
||||
List<Object> textsToHighlight;
|
||||
if (!forceSource && mapper.fieldType().stored()) {
|
||||
CustomFieldsVisitor fieldVisitor = new CustomFieldsVisitor(ImmutableSet.of(mapper.names().indexName()), false);
|
||||
|
|
|
@ -21,15 +21,14 @@ package org.elasticsearch.search.highlight;
|
|||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.vectorhighlight.SimpleBoundaryScanner;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.search.SearchParseElement;
|
||||
import org.elasticsearch.search.SearchParseException;
|
||||
import org.elasticsearch.search.internal.SearchContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.google.common.collect.Lists.newArrayList;
|
||||
|
@ -68,25 +67,14 @@ public class HighlighterParseElement implements SearchParseElement {
|
|||
public void parse(XContentParser parser, SearchContext context) throws Exception {
|
||||
XContentParser.Token token;
|
||||
String topLevelFieldName = null;
|
||||
List<SearchContextHighlight.Field> fields = newArrayList();
|
||||
List<Tuple<String, SearchContextHighlight.FieldOptions.Builder>> fieldsOptions = newArrayList();
|
||||
|
||||
String[] globalPreTags = DEFAULT_PRE_TAGS;
|
||||
String[] globalPostTags = DEFAULT_POST_TAGS;
|
||||
boolean globalScoreOrdered = false;
|
||||
boolean globalHighlightFilter = false;
|
||||
boolean globalRequireFieldMatch = false;
|
||||
boolean globalForceSource = false;
|
||||
int globalFragmentSize = 100;
|
||||
int globalNumOfFragments = 5;
|
||||
String globalEncoder = "default";
|
||||
int globalBoundaryMaxScan = SimpleBoundaryScanner.DEFAULT_MAX_SCAN;
|
||||
Character[] globalBoundaryChars = SimpleBoundaryScanner.DEFAULT_BOUNDARY_CHARS;
|
||||
String globalHighlighterType = null;
|
||||
String globalFragmenter = null;
|
||||
Map<String, Object> globalOptions = null;
|
||||
Query globalHighlightQuery = null;
|
||||
int globalNoMatchSize = 0;
|
||||
int globalPhraseLimit = 256;
|
||||
SearchContextHighlight.FieldOptions.Builder globalOptionsBuilder = new SearchContextHighlight.FieldOptions.Builder()
|
||||
.preTags(DEFAULT_PRE_TAGS).postTags(DEFAULT_POST_TAGS).scoreOrdered(false).highlightFilter(false)
|
||||
.requireFieldMatch(false).forceSource(false).fragmentCharSize(100).numberOfFragments(5)
|
||||
.encoder("default").boundaryMaxScan(SimpleBoundaryScanner.DEFAULT_MAX_SCAN)
|
||||
.boundaryChars(SimpleBoundaryScanner.DEFAULT_BOUNDARY_CHARS)
|
||||
.noMatchSize(0).phraseLimit(256);
|
||||
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
|
@ -97,54 +85,55 @@ public class HighlighterParseElement implements SearchParseElement {
|
|||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
preTagsList.add(parser.text());
|
||||
}
|
||||
globalPreTags = preTagsList.toArray(new String[preTagsList.size()]);
|
||||
globalOptionsBuilder.preTags(preTagsList.toArray(new String[preTagsList.size()]));
|
||||
} else if ("post_tags".equals(topLevelFieldName) || "postTags".equals(topLevelFieldName)) {
|
||||
List<String> postTagsList = Lists.newArrayList();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
postTagsList.add(parser.text());
|
||||
}
|
||||
globalPostTags = postTagsList.toArray(new String[postTagsList.size()]);
|
||||
globalOptionsBuilder.postTags(postTagsList.toArray(new String[postTagsList.size()]));
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("order".equals(topLevelFieldName)) {
|
||||
globalScoreOrdered = "score".equals(parser.text());
|
||||
globalOptionsBuilder.scoreOrdered("score".equals(parser.text()));
|
||||
} else if ("tags_schema".equals(topLevelFieldName) || "tagsSchema".equals(topLevelFieldName)) {
|
||||
String schema = parser.text();
|
||||
if ("styled".equals(schema)) {
|
||||
globalPreTags = STYLED_PRE_TAG;
|
||||
globalPostTags = STYLED_POST_TAGS;
|
||||
globalOptionsBuilder.preTags(STYLED_PRE_TAG);
|
||||
globalOptionsBuilder.postTags(STYLED_POST_TAGS);
|
||||
}
|
||||
} else if ("highlight_filter".equals(topLevelFieldName) || "highlightFilter".equals(topLevelFieldName)) {
|
||||
globalHighlightFilter = parser.booleanValue();
|
||||
globalOptionsBuilder.highlightFilter(parser.booleanValue());
|
||||
} else if ("fragment_size".equals(topLevelFieldName) || "fragmentSize".equals(topLevelFieldName)) {
|
||||
globalFragmentSize = parser.intValue();
|
||||
globalOptionsBuilder.fragmentCharSize(parser.intValue());
|
||||
} else if ("number_of_fragments".equals(topLevelFieldName) || "numberOfFragments".equals(topLevelFieldName)) {
|
||||
globalNumOfFragments = parser.intValue();
|
||||
globalOptionsBuilder.numberOfFragments(parser.intValue());
|
||||
} else if ("encoder".equals(topLevelFieldName)) {
|
||||
globalEncoder = parser.text();
|
||||
globalOptionsBuilder.encoder(parser.text());
|
||||
} else if ("require_field_match".equals(topLevelFieldName) || "requireFieldMatch".equals(topLevelFieldName)) {
|
||||
globalRequireFieldMatch = parser.booleanValue();
|
||||
globalOptionsBuilder.requireFieldMatch(parser.booleanValue());
|
||||
} else if ("boundary_max_scan".equals(topLevelFieldName) || "boundaryMaxScan".equals(topLevelFieldName)) {
|
||||
globalBoundaryMaxScan = parser.intValue();
|
||||
globalOptionsBuilder.boundaryMaxScan(parser.intValue());
|
||||
} else if ("boundary_chars".equals(topLevelFieldName) || "boundaryChars".equals(topLevelFieldName)) {
|
||||
char[] charsArr = parser.text().toCharArray();
|
||||
globalBoundaryChars = new Character[charsArr.length];
|
||||
Character[] globalBoundaryChars = new Character[charsArr.length];
|
||||
for (int i = 0; i < charsArr.length; i++) {
|
||||
globalBoundaryChars[i] = charsArr[i];
|
||||
}
|
||||
globalOptionsBuilder.boundaryChars(globalBoundaryChars);
|
||||
} else if ("type".equals(topLevelFieldName)) {
|
||||
globalHighlighterType = parser.text();
|
||||
globalOptionsBuilder.highlighterType(parser.text());
|
||||
} else if ("fragmenter".equals(topLevelFieldName)) {
|
||||
globalFragmenter = parser.text();
|
||||
globalOptionsBuilder.fragmenter(parser.text());
|
||||
} else if ("no_match_size".equals(topLevelFieldName) || "noMatchSize".equals(topLevelFieldName)) {
|
||||
globalNoMatchSize = parser.intValue();
|
||||
globalOptionsBuilder.noMatchSize(parser.intValue());
|
||||
} else if ("force_source".equals(topLevelFieldName) || "forceSource".equals(topLevelFieldName)) {
|
||||
globalForceSource = parser.booleanValue();
|
||||
globalOptionsBuilder.forceSource(parser.booleanValue());
|
||||
} else if ("phrase_limit".equals(topLevelFieldName) || "phraseLimit".equals(topLevelFieldName)) {
|
||||
globalPhraseLimit = parser.intValue();
|
||||
globalOptionsBuilder.phraseLimit(parser.intValue());
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT && "options".equals(topLevelFieldName)) {
|
||||
globalOptions = parser.map();
|
||||
globalOptionsBuilder.options(parser.map());
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if ("fields".equals(topLevelFieldName)) {
|
||||
String highlightFieldName = null;
|
||||
|
@ -152,7 +141,7 @@ public class HighlighterParseElement implements SearchParseElement {
|
|||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
highlightFieldName = parser.currentName();
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
SearchContextHighlight.Field field = new SearchContextHighlight.Field(highlightFieldName);
|
||||
SearchContextHighlight.FieldOptions.Builder fieldOptionsBuilder = new SearchContextHighlight.FieldOptions.Builder();
|
||||
String fieldName = null;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
|
@ -163,126 +152,79 @@ public class HighlighterParseElement implements SearchParseElement {
|
|||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
preTagsList.add(parser.text());
|
||||
}
|
||||
field.preTags(preTagsList.toArray(new String[preTagsList.size()]));
|
||||
fieldOptionsBuilder.preTags(preTagsList.toArray(new String[preTagsList.size()]));
|
||||
} else if ("post_tags".equals(fieldName) || "postTags".equals(fieldName)) {
|
||||
List<String> postTagsList = Lists.newArrayList();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
postTagsList.add(parser.text());
|
||||
}
|
||||
field.postTags(postTagsList.toArray(new String[postTagsList.size()]));
|
||||
fieldOptionsBuilder.postTags(postTagsList.toArray(new String[postTagsList.size()]));
|
||||
} else if ("matched_fields".equals(fieldName) || "matchedFields".equals(fieldName)) {
|
||||
Set<String> matchedFields = Sets.newHashSet();
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
matchedFields.add(parser.text());
|
||||
}
|
||||
field.matchedFields(matchedFields);
|
||||
fieldOptionsBuilder.matchedFields(matchedFields);
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if ("fragment_size".equals(fieldName) || "fragmentSize".equals(fieldName)) {
|
||||
field.fragmentCharSize(parser.intValue());
|
||||
fieldOptionsBuilder.fragmentCharSize(parser.intValue());
|
||||
} else if ("number_of_fragments".equals(fieldName) || "numberOfFragments".equals(fieldName)) {
|
||||
field.numberOfFragments(parser.intValue());
|
||||
fieldOptionsBuilder.numberOfFragments(parser.intValue());
|
||||
} else if ("fragment_offset".equals(fieldName) || "fragmentOffset".equals(fieldName)) {
|
||||
field.fragmentOffset(parser.intValue());
|
||||
fieldOptionsBuilder.fragmentOffset(parser.intValue());
|
||||
} else if ("highlight_filter".equals(fieldName) || "highlightFilter".equals(fieldName)) {
|
||||
field.highlightFilter(parser.booleanValue());
|
||||
fieldOptionsBuilder.highlightFilter(parser.booleanValue());
|
||||
} else if ("order".equals(fieldName)) {
|
||||
field.scoreOrdered("score".equals(parser.text()));
|
||||
fieldOptionsBuilder.scoreOrdered("score".equals(parser.text()));
|
||||
} else if ("require_field_match".equals(fieldName) || "requireFieldMatch".equals(fieldName)) {
|
||||
field.requireFieldMatch(parser.booleanValue());
|
||||
fieldOptionsBuilder.requireFieldMatch(parser.booleanValue());
|
||||
} else if ("boundary_max_scan".equals(topLevelFieldName) || "boundaryMaxScan".equals(topLevelFieldName)) {
|
||||
field.boundaryMaxScan(parser.intValue());
|
||||
fieldOptionsBuilder.boundaryMaxScan(parser.intValue());
|
||||
} else if ("boundary_chars".equals(topLevelFieldName) || "boundaryChars".equals(topLevelFieldName)) {
|
||||
char[] charsArr = parser.text().toCharArray();
|
||||
Character[] boundaryChars = new Character[charsArr.length];
|
||||
for (int i = 0; i < charsArr.length; i++) {
|
||||
boundaryChars[i] = charsArr[i];
|
||||
}
|
||||
field.boundaryChars(boundaryChars);
|
||||
fieldOptionsBuilder.boundaryChars(boundaryChars);
|
||||
} else if ("type".equals(fieldName)) {
|
||||
field.highlighterType(parser.text());
|
||||
fieldOptionsBuilder.highlighterType(parser.text());
|
||||
} else if ("fragmenter".equals(fieldName)) {
|
||||
field.fragmenter(parser.text());
|
||||
fieldOptionsBuilder.fragmenter(parser.text());
|
||||
} else if ("no_match_size".equals(fieldName) || "noMatchSize".equals(fieldName)) {
|
||||
field.noMatchSize(parser.intValue());
|
||||
fieldOptionsBuilder.noMatchSize(parser.intValue());
|
||||
} else if ("force_source".equals(fieldName) || "forceSource".equals(fieldName)) {
|
||||
field.forceSource(parser.booleanValue());
|
||||
fieldOptionsBuilder.forceSource(parser.booleanValue());
|
||||
} else if ("phrase_limit".equals(fieldName) || "phraseLimit".equals(fieldName)) {
|
||||
field.phraseLimit(parser.intValue());
|
||||
fieldOptionsBuilder.phraseLimit(parser.intValue());
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if ("highlight_query".equals(fieldName) || "highlightQuery".equals(fieldName)) {
|
||||
field.highlightQuery(context.queryParserService().parse(parser).query());
|
||||
} else if (fieldName.equals("options")) {
|
||||
field.options(parser.map());
|
||||
fieldOptionsBuilder.highlightQuery(context.queryParserService().parse(parser).query());
|
||||
} else if ("options".equals(fieldName)) {
|
||||
fieldOptionsBuilder.options(parser.map());
|
||||
}
|
||||
}
|
||||
}
|
||||
fields.add(field);
|
||||
fieldsOptions.add(Tuple.tuple(highlightFieldName, fieldOptionsBuilder));
|
||||
}
|
||||
}
|
||||
} else if ("highlight_query".equals(topLevelFieldName) || "highlightQuery".equals(topLevelFieldName)) {
|
||||
globalHighlightQuery = context.queryParserService().parse(parser).query();
|
||||
globalOptionsBuilder.highlightQuery(context.queryParserService().parse(parser).query());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (globalPreTags != null && globalPostTags == null) {
|
||||
|
||||
SearchContextHighlight.FieldOptions globalOptions = globalOptionsBuilder.build();
|
||||
if (globalOptions.preTags() != null && globalOptions.postTags() == null) {
|
||||
throw new SearchParseException(context, "Highlighter global preTags are set, but global postTags are not set");
|
||||
}
|
||||
|
||||
// now, go over and fill all fields with default values from the global state
|
||||
for (SearchContextHighlight.Field field : fields) {
|
||||
if (field.preTags() == null) {
|
||||
field.preTags(globalPreTags);
|
||||
}
|
||||
if (field.postTags() == null) {
|
||||
field.postTags(globalPostTags);
|
||||
}
|
||||
if (field.highlightFilter() == null) {
|
||||
field.highlightFilter(globalHighlightFilter);
|
||||
}
|
||||
if (field.scoreOrdered() == null) {
|
||||
field.scoreOrdered(globalScoreOrdered);
|
||||
}
|
||||
if (field.fragmentCharSize() == -1) {
|
||||
field.fragmentCharSize(globalFragmentSize);
|
||||
}
|
||||
if (field.numberOfFragments() == -1) {
|
||||
field.numberOfFragments(globalNumOfFragments);
|
||||
}
|
||||
if (field.encoder() == null) {
|
||||
field.encoder(globalEncoder);
|
||||
}
|
||||
if (field.requireFieldMatch() == null) {
|
||||
field.requireFieldMatch(globalRequireFieldMatch);
|
||||
}
|
||||
if (field.boundaryMaxScan() == -1) {
|
||||
field.boundaryMaxScan(globalBoundaryMaxScan);
|
||||
}
|
||||
if (field.boundaryChars() == null) {
|
||||
field.boundaryChars(globalBoundaryChars);
|
||||
}
|
||||
if (field.highlighterType() == null) {
|
||||
field.highlighterType(globalHighlighterType);
|
||||
}
|
||||
if (field.fragmenter() == null) {
|
||||
field.fragmenter(globalFragmenter);
|
||||
}
|
||||
if (field.options() == null || field.options().size() == 0) {
|
||||
field.options(globalOptions);
|
||||
}
|
||||
if (field.highlightQuery() == null && globalHighlightQuery != null) {
|
||||
field.highlightQuery(globalHighlightQuery);
|
||||
}
|
||||
if (field.noMatchSize() == -1) {
|
||||
field.noMatchSize(globalNoMatchSize);
|
||||
}
|
||||
if (field.forceSource() == null) {
|
||||
field.forceSource(globalForceSource);
|
||||
}
|
||||
if (field.phraseLimit() == -1) {
|
||||
field.phraseLimit(globalPhraseLimit);
|
||||
}
|
||||
List<SearchContextHighlight.Field> fields = Lists.newArrayList();
|
||||
// now, go over and fill all fieldsOptions with default values from the global state
|
||||
for (Tuple<String, SearchContextHighlight.FieldOptions.Builder> tuple : fieldsOptions) {
|
||||
fields.add(new SearchContextHighlight.Field(tuple.v1(), tuple.v2().merge(globalOptions).build()));
|
||||
}
|
||||
|
||||
context.highlight(new SearchContextHighlight(fields));
|
||||
|
|
|
@ -58,32 +58,33 @@ public class PlainHighlighter implements Highlighter {
|
|||
FetchSubPhase.HitContext hitContext = highlighterContext.hitContext;
|
||||
FieldMapper<?> mapper = highlighterContext.mapper;
|
||||
|
||||
Encoder encoder = field.encoder().equals("html") ? HighlightUtils.Encoders.HTML : HighlightUtils.Encoders.DEFAULT;
|
||||
Encoder encoder = field.fieldOptions().encoder().equals("html") ? HighlightUtils.Encoders.HTML : HighlightUtils.Encoders.DEFAULT;
|
||||
|
||||
if (!hitContext.cache().containsKey(CACHE_KEY)) {
|
||||
Map<FieldMapper<?>, org.apache.lucene.search.highlight.Highlighter> mappers = Maps.newHashMap();
|
||||
hitContext.cache().put(CACHE_KEY, mappers);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<FieldMapper<?>, org.apache.lucene.search.highlight.Highlighter> cache = (Map<FieldMapper<?>, org.apache.lucene.search.highlight.Highlighter>) hitContext.cache().get(CACHE_KEY);
|
||||
|
||||
org.apache.lucene.search.highlight.Highlighter entry = cache.get(mapper);
|
||||
if (entry == null) {
|
||||
Query query = highlighterContext.query.originalQuery();
|
||||
QueryScorer queryScorer = new CustomQueryScorer(query, field.requireFieldMatch() ? mapper.names().indexName() : null);
|
||||
QueryScorer queryScorer = new CustomQueryScorer(query, field.fieldOptions().requireFieldMatch() ? mapper.names().indexName() : null);
|
||||
queryScorer.setExpandMultiTermQuery(true);
|
||||
Fragmenter fragmenter;
|
||||
if (field.numberOfFragments() == 0) {
|
||||
if (field.fieldOptions().numberOfFragments() == 0) {
|
||||
fragmenter = new NullFragmenter();
|
||||
} else if (field.fragmenter() == null) {
|
||||
fragmenter = new SimpleSpanFragmenter(queryScorer, field.fragmentCharSize());
|
||||
} else if ("simple".equals(field.fragmenter())) {
|
||||
fragmenter = new SimpleFragmenter(field.fragmentCharSize());
|
||||
} else if ("span".equals(field.fragmenter())) {
|
||||
fragmenter = new SimpleSpanFragmenter(queryScorer, field.fragmentCharSize());
|
||||
} else if (field.fieldOptions().fragmenter() == null) {
|
||||
fragmenter = new SimpleSpanFragmenter(queryScorer, field.fieldOptions().fragmentCharSize());
|
||||
} else if ("simple".equals(field.fieldOptions().fragmenter())) {
|
||||
fragmenter = new SimpleFragmenter(field.fieldOptions().fragmentCharSize());
|
||||
} else if ("span".equals(field.fieldOptions().fragmenter())) {
|
||||
fragmenter = new SimpleSpanFragmenter(queryScorer, field.fieldOptions().fragmentCharSize());
|
||||
} else {
|
||||
throw new ElasticsearchIllegalArgumentException("unknown fragmenter option [" + field.fragmenter() + "] for the field [" + highlighterContext.fieldName + "]");
|
||||
throw new ElasticsearchIllegalArgumentException("unknown fragmenter option [" + field.fieldOptions().fragmenter() + "] for the field [" + highlighterContext.fieldName + "]");
|
||||
}
|
||||
Formatter formatter = new SimpleHTMLFormatter(field.preTags()[0], field.postTags()[0]);
|
||||
Formatter formatter = new SimpleHTMLFormatter(field.fieldOptions().preTags()[0], field.fieldOptions().postTags()[0]);
|
||||
|
||||
entry = new org.apache.lucene.search.highlight.Highlighter(formatter, encoder, queryScorer);
|
||||
entry.setTextFragmenter(fragmenter);
|
||||
|
@ -94,12 +95,12 @@ public class PlainHighlighter implements Highlighter {
|
|||
}
|
||||
|
||||
// a HACK to make highlighter do highlighting, even though its using the single frag list builder
|
||||
int numberOfFragments = field.numberOfFragments() == 0 ? 1 : field.numberOfFragments();
|
||||
int numberOfFragments = field.fieldOptions().numberOfFragments() == 0 ? 1 : field.fieldOptions().numberOfFragments();
|
||||
ArrayList<TextFragment> fragsList = new ArrayList<TextFragment>();
|
||||
List<Object> textsToHighlight;
|
||||
|
||||
try {
|
||||
textsToHighlight = HighlightUtils.loadFieldValues(mapper, context, hitContext, field.forceSource());
|
||||
textsToHighlight = HighlightUtils.loadFieldValues(field, mapper, context, hitContext);
|
||||
|
||||
for (Object textToHighlight : textsToHighlight) {
|
||||
String text = textToHighlight.toString();
|
||||
|
@ -119,7 +120,7 @@ public class PlainHighlighter implements Highlighter {
|
|||
} catch (Exception e) {
|
||||
throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + highlighterContext.fieldName + "]", e);
|
||||
}
|
||||
if (field.scoreOrdered()) {
|
||||
if (field.fieldOptions().scoreOrdered()) {
|
||||
CollectionUtil.introSort(fragsList, new Comparator<TextFragment>() {
|
||||
public int compare(TextFragment o1, TextFragment o2) {
|
||||
return Math.round(o2.getScore() - o1.getScore());
|
||||
|
@ -128,7 +129,7 @@ public class PlainHighlighter implements Highlighter {
|
|||
}
|
||||
String[] fragments;
|
||||
// number_of_fragments is set to 0 but we have a multivalued field
|
||||
if (field.numberOfFragments() == 0 && textsToHighlight.size() > 1 && fragsList.size() > 0) {
|
||||
if (field.fieldOptions().numberOfFragments() == 0 && textsToHighlight.size() > 1 && fragsList.size() > 0) {
|
||||
fragments = new String[fragsList.size()];
|
||||
for (int i = 0; i < fragsList.size(); i++) {
|
||||
fragments[i] = fragsList.get(i).toString();
|
||||
|
@ -142,11 +143,11 @@ public class PlainHighlighter implements Highlighter {
|
|||
}
|
||||
}
|
||||
|
||||
if (fragments != null && fragments.length > 0) {
|
||||
if (fragments.length > 0) {
|
||||
return new HighlightField(highlighterContext.fieldName, StringText.convertFromStringArray(fragments));
|
||||
}
|
||||
|
||||
int noMatchSize = highlighterContext.field.noMatchSize();
|
||||
int noMatchSize = highlighterContext.field.fieldOptions().noMatchSize();
|
||||
if (noMatchSize > 0 && textsToHighlight.size() > 0) {
|
||||
// Pull an excerpt from the beginning of the string but make sure to split the string on a term boundary.
|
||||
String fieldContents = textsToHighlight.get(0).toString();
|
||||
|
|
|
@ -83,27 +83,27 @@ public class PostingsHighlighter implements Highlighter {
|
|||
MapperHighlighterEntry mapperHighlighterEntry = highlighterEntry.mappers.get(fieldMapper);
|
||||
|
||||
if (mapperHighlighterEntry == null) {
|
||||
Encoder encoder = field.encoder().equals("html") ? HighlightUtils.Encoders.HTML : HighlightUtils.Encoders.DEFAULT;
|
||||
CustomPassageFormatter passageFormatter = new CustomPassageFormatter(field.preTags()[0], field.postTags()[0], encoder);
|
||||
BytesRef[] filteredQueryTerms = filterTerms(highlighterEntry.queryTerms, fieldMapper.names().indexName(), field.requireFieldMatch());
|
||||
Encoder encoder = field.fieldOptions().encoder().equals("html") ? HighlightUtils.Encoders.HTML : HighlightUtils.Encoders.DEFAULT;
|
||||
CustomPassageFormatter passageFormatter = new CustomPassageFormatter(field.fieldOptions().preTags()[0], field.fieldOptions().postTags()[0], encoder);
|
||||
BytesRef[] filteredQueryTerms = filterTerms(highlighterEntry.queryTerms, fieldMapper.names().indexName(), field.fieldOptions().requireFieldMatch());
|
||||
mapperHighlighterEntry = new MapperHighlighterEntry(passageFormatter, filteredQueryTerms);
|
||||
}
|
||||
|
||||
//we merge back multiple values into a single value using the paragraph separator, unless we have to highlight every single value separately (number_of_fragments=0).
|
||||
boolean mergeValues = field.numberOfFragments() != 0;
|
||||
boolean mergeValues = field.fieldOptions().numberOfFragments() != 0;
|
||||
List<Snippet> snippets = new ArrayList<Snippet>();
|
||||
int numberOfFragments;
|
||||
|
||||
try {
|
||||
//we manually load the field values (from source if needed)
|
||||
List<Object> textsToHighlight = HighlightUtils.loadFieldValues(fieldMapper, context, hitContext, field.forceSource());
|
||||
CustomPostingsHighlighter highlighter = new CustomPostingsHighlighter(mapperHighlighterEntry.passageFormatter, textsToHighlight, mergeValues, Integer.MAX_VALUE-1, field.noMatchSize());
|
||||
List<Object> textsToHighlight = HighlightUtils.loadFieldValues(field, fieldMapper, context, hitContext);
|
||||
CustomPostingsHighlighter highlighter = new CustomPostingsHighlighter(mapperHighlighterEntry.passageFormatter, textsToHighlight, mergeValues, Integer.MAX_VALUE-1, field.fieldOptions().noMatchSize());
|
||||
|
||||
if (field.numberOfFragments() == 0) {
|
||||
if (field.fieldOptions().numberOfFragments() == 0) {
|
||||
highlighter.setBreakIterator(new WholeBreakIterator());
|
||||
numberOfFragments = 1; //1 per value since we highlight per value
|
||||
} else {
|
||||
numberOfFragments = field.numberOfFragments();
|
||||
numberOfFragments = field.fieldOptions().numberOfFragments();
|
||||
}
|
||||
|
||||
//we highlight every value separately calling the highlight method multiple times, only if we need to have back a snippet per value (whole value)
|
||||
|
@ -123,9 +123,9 @@ public class PostingsHighlighter implements Highlighter {
|
|||
throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + highlighterContext.fieldName + "]", e);
|
||||
}
|
||||
|
||||
snippets = filterSnippets(snippets, field.numberOfFragments());
|
||||
snippets = filterSnippets(snippets, field.fieldOptions().numberOfFragments());
|
||||
|
||||
if (field.scoreOrdered()) {
|
||||
if (field.fieldOptions().scoreOrdered()) {
|
||||
//let's sort the snippets by score if needed
|
||||
CollectionUtil.introSort(snippets, new Comparator<Snippet>() {
|
||||
public int compare(Snippet o1, Snippet o2) {
|
||||
|
|
|
@ -19,31 +19,68 @@
|
|||
|
||||
package org.elasticsearch.search.highlight;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class SearchContextHighlight {
|
||||
|
||||
private final List<Field> fields;
|
||||
private final Map<String, Field> fields;
|
||||
|
||||
public SearchContextHighlight(List<Field> fields) {
|
||||
this.fields = fields;
|
||||
private boolean globalForceSource = false;
|
||||
|
||||
public SearchContextHighlight(Collection<Field> fields) {
|
||||
assert fields != null;
|
||||
this.fields = Maps.newHashMap();
|
||||
for (Field field : fields) {
|
||||
this.fields.put(field.field, field);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Field> fields() {
|
||||
return fields;
|
||||
public Collection<Field> fields() {
|
||||
return fields.values();
|
||||
}
|
||||
|
||||
public void globalForceSource(boolean globalForceSource) {
|
||||
this.globalForceSource = globalForceSource;
|
||||
}
|
||||
|
||||
public boolean forceSource(Field field) {
|
||||
if (globalForceSource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Field _field = fields.get(field.field);
|
||||
return _field == null ? false : _field.fieldOptions.forceSource;
|
||||
}
|
||||
|
||||
public static class Field {
|
||||
// Fields that default to null or -1 are often set to their real default in HighlighterParseElement#parse
|
||||
private final String field;
|
||||
private final FieldOptions fieldOptions;
|
||||
|
||||
Field(String field, FieldOptions fieldOptions) {
|
||||
assert field != null;
|
||||
assert fieldOptions != null;
|
||||
this.field = field;
|
||||
this.fieldOptions = fieldOptions;
|
||||
}
|
||||
|
||||
public String field() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public FieldOptions fieldOptions() {
|
||||
return fieldOptions;
|
||||
}
|
||||
}
|
||||
|
||||
public static class FieldOptions {
|
||||
|
||||
// Field options that default to null or -1 are often set to their real default in HighlighterParseElement#parse
|
||||
private int fragmentCharSize = -1;
|
||||
|
||||
private int numberOfFragments = -1;
|
||||
|
@ -82,164 +119,236 @@ public class SearchContextHighlight {
|
|||
|
||||
private int phraseLimit = -1;
|
||||
|
||||
public Field(String field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public String field() {
|
||||
return field;
|
||||
}
|
||||
|
||||
public int fragmentCharSize() {
|
||||
return fragmentCharSize;
|
||||
}
|
||||
|
||||
public void fragmentCharSize(int fragmentCharSize) {
|
||||
this.fragmentCharSize = fragmentCharSize;
|
||||
}
|
||||
|
||||
public int numberOfFragments() {
|
||||
return numberOfFragments;
|
||||
}
|
||||
|
||||
public void numberOfFragments(int numberOfFragments) {
|
||||
this.numberOfFragments = numberOfFragments;
|
||||
}
|
||||
|
||||
public int fragmentOffset() {
|
||||
return fragmentOffset;
|
||||
}
|
||||
|
||||
public void fragmentOffset(int fragmentOffset) {
|
||||
this.fragmentOffset = fragmentOffset;
|
||||
}
|
||||
|
||||
public String encoder() {
|
||||
return encoder;
|
||||
}
|
||||
|
||||
public void encoder(String encoder) {
|
||||
this.encoder = encoder;
|
||||
}
|
||||
|
||||
public String[] preTags() {
|
||||
return preTags;
|
||||
}
|
||||
|
||||
public void preTags(String[] preTags) {
|
||||
this.preTags = preTags;
|
||||
}
|
||||
|
||||
public String[] postTags() {
|
||||
return postTags;
|
||||
}
|
||||
|
||||
public void postTags(String[] postTags) {
|
||||
this.postTags = postTags;
|
||||
}
|
||||
|
||||
public Boolean scoreOrdered() {
|
||||
return scoreOrdered;
|
||||
}
|
||||
|
||||
public void scoreOrdered(boolean scoreOrdered) {
|
||||
this.scoreOrdered = scoreOrdered;
|
||||
}
|
||||
|
||||
public Boolean highlightFilter() {
|
||||
return highlightFilter;
|
||||
}
|
||||
|
||||
public void highlightFilter(boolean highlightFilter) {
|
||||
this.highlightFilter = highlightFilter;
|
||||
}
|
||||
|
||||
public Boolean requireFieldMatch() {
|
||||
return requireFieldMatch;
|
||||
}
|
||||
|
||||
public void requireFieldMatch(boolean requireFieldMatch) {
|
||||
this.requireFieldMatch = requireFieldMatch;
|
||||
}
|
||||
|
||||
public String highlighterType() {
|
||||
return highlighterType;
|
||||
}
|
||||
|
||||
public void highlighterType(String type) {
|
||||
this.highlighterType = type;
|
||||
}
|
||||
|
||||
public Boolean forceSource() {
|
||||
return forceSource;
|
||||
}
|
||||
|
||||
public void forceSource(boolean forceSource) {
|
||||
this.forceSource = forceSource;
|
||||
}
|
||||
|
||||
public String fragmenter() {
|
||||
return fragmenter;
|
||||
}
|
||||
|
||||
public void fragmenter(String fragmenter) {
|
||||
this.fragmenter = fragmenter;
|
||||
}
|
||||
|
||||
public int boundaryMaxScan() {
|
||||
return boundaryMaxScan;
|
||||
}
|
||||
|
||||
public void boundaryMaxScan(int boundaryMaxScan) {
|
||||
this.boundaryMaxScan = boundaryMaxScan;
|
||||
}
|
||||
|
||||
public Character[] boundaryChars() {
|
||||
return boundaryChars;
|
||||
}
|
||||
|
||||
public void boundaryChars(Character[] boundaryChars) {
|
||||
this.boundaryChars = boundaryChars;
|
||||
}
|
||||
|
||||
public Query highlightQuery() {
|
||||
return highlightQuery;
|
||||
}
|
||||
|
||||
public void highlightQuery(Query highlightQuery) {
|
||||
this.highlightQuery = highlightQuery;
|
||||
}
|
||||
|
||||
public int noMatchSize() {
|
||||
return noMatchSize;
|
||||
}
|
||||
|
||||
public void noMatchSize(int noMatchSize) {
|
||||
this.noMatchSize = noMatchSize;
|
||||
}
|
||||
|
||||
public int phraseLimit() {
|
||||
return phraseLimit;
|
||||
}
|
||||
|
||||
public void phraseLimit(int phraseLimit) {
|
||||
this.phraseLimit = phraseLimit;
|
||||
}
|
||||
|
||||
public Set<String> matchedFields() {
|
||||
return matchedFields;
|
||||
}
|
||||
|
||||
public void matchedFields(Set<String> matchedFields) {
|
||||
this.matchedFields = matchedFields;
|
||||
}
|
||||
|
||||
public Map<String, Object> options() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public void options(Map<String, Object> options) {
|
||||
this.options = options;
|
||||
static class Builder {
|
||||
|
||||
private final FieldOptions fieldOptions = new FieldOptions();
|
||||
|
||||
Builder fragmentCharSize(int fragmentCharSize) {
|
||||
fieldOptions.fragmentCharSize = fragmentCharSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder numberOfFragments(int numberOfFragments) {
|
||||
fieldOptions.numberOfFragments = numberOfFragments;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder fragmentOffset(int fragmentOffset) {
|
||||
fieldOptions.fragmentOffset = fragmentOffset;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder encoder(String encoder) {
|
||||
fieldOptions.encoder = encoder;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder preTags(String[] preTags) {
|
||||
fieldOptions.preTags = preTags;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder postTags(String[] postTags) {
|
||||
fieldOptions.postTags = postTags;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder scoreOrdered(boolean scoreOrdered) {
|
||||
fieldOptions.scoreOrdered = scoreOrdered;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder highlightFilter(boolean highlightFilter) {
|
||||
fieldOptions.highlightFilter = highlightFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder requireFieldMatch(boolean requireFieldMatch) {
|
||||
fieldOptions.requireFieldMatch = requireFieldMatch;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder highlighterType(String type) {
|
||||
fieldOptions.highlighterType = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder forceSource(boolean forceSource) {
|
||||
fieldOptions.forceSource = forceSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder fragmenter(String fragmenter) {
|
||||
fieldOptions.fragmenter = fragmenter;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder boundaryMaxScan(int boundaryMaxScan) {
|
||||
fieldOptions.boundaryMaxScan = boundaryMaxScan;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder boundaryChars(Character[] boundaryChars) {
|
||||
fieldOptions.boundaryChars = boundaryChars;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder highlightQuery(Query highlightQuery) {
|
||||
fieldOptions.highlightQuery = highlightQuery;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder noMatchSize(int noMatchSize) {
|
||||
fieldOptions.noMatchSize = noMatchSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder phraseLimit(int phraseLimit) {
|
||||
fieldOptions.phraseLimit = phraseLimit;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder matchedFields(Set<String> matchedFields) {
|
||||
fieldOptions.matchedFields = matchedFields;
|
||||
return this;
|
||||
}
|
||||
|
||||
Builder options(Map<String, Object> options) {
|
||||
fieldOptions.options = options;
|
||||
return this;
|
||||
}
|
||||
|
||||
FieldOptions build() {
|
||||
return fieldOptions;
|
||||
}
|
||||
|
||||
Builder merge(FieldOptions globalOptions) {
|
||||
if (fieldOptions.preTags == null && globalOptions.preTags != null) {
|
||||
fieldOptions.preTags = Arrays.copyOf(globalOptions.preTags, globalOptions.preTags.length);
|
||||
}
|
||||
if (fieldOptions.postTags == null && globalOptions.postTags != null) {
|
||||
fieldOptions.postTags = Arrays.copyOf(globalOptions.postTags, globalOptions.postTags.length);
|
||||
}
|
||||
if (fieldOptions.highlightFilter == null) {
|
||||
fieldOptions.highlightFilter = globalOptions.highlightFilter;
|
||||
}
|
||||
if (fieldOptions.scoreOrdered == null) {
|
||||
fieldOptions.scoreOrdered = globalOptions.scoreOrdered;
|
||||
}
|
||||
if (fieldOptions.fragmentCharSize == -1) {
|
||||
fieldOptions.fragmentCharSize = globalOptions.fragmentCharSize;
|
||||
}
|
||||
if (fieldOptions.numberOfFragments == -1) {
|
||||
fieldOptions.numberOfFragments = globalOptions.numberOfFragments;
|
||||
}
|
||||
if (fieldOptions.encoder == null) {
|
||||
fieldOptions.encoder = globalOptions.encoder;
|
||||
}
|
||||
if (fieldOptions.requireFieldMatch == null) {
|
||||
fieldOptions.requireFieldMatch = globalOptions.requireFieldMatch;
|
||||
}
|
||||
if (fieldOptions.boundaryMaxScan == -1) {
|
||||
fieldOptions.boundaryMaxScan = globalOptions.boundaryMaxScan;
|
||||
}
|
||||
if (fieldOptions.boundaryChars == null && globalOptions.boundaryChars != null) {
|
||||
fieldOptions.boundaryChars = Arrays.copyOf(globalOptions.boundaryChars, globalOptions.boundaryChars.length);
|
||||
}
|
||||
if (fieldOptions.highlighterType == null) {
|
||||
fieldOptions.highlighterType = globalOptions.highlighterType;
|
||||
}
|
||||
if (fieldOptions.fragmenter == null) {
|
||||
fieldOptions.fragmenter = globalOptions.fragmenter;
|
||||
}
|
||||
if ((fieldOptions.options == null || fieldOptions.options.size() == 0) && globalOptions.options != null) {
|
||||
fieldOptions.options = Maps.newHashMap(globalOptions.options);
|
||||
}
|
||||
if (fieldOptions.highlightQuery == null && globalOptions.highlightQuery != null) {
|
||||
fieldOptions.highlightQuery = globalOptions.highlightQuery;
|
||||
}
|
||||
if (fieldOptions.noMatchSize == -1) {
|
||||
fieldOptions.noMatchSize = globalOptions.noMatchSize;
|
||||
}
|
||||
if (fieldOptions.forceSource == null) {
|
||||
fieldOptions.forceSource = globalOptions.forceSource;
|
||||
}
|
||||
if (fieldOptions.phraseLimit == -1) {
|
||||
fieldOptions.phraseLimit = globalOptions.phraseLimit;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ public class CustomHighlighter implements Highlighter {
|
|||
List<Text> responses = Lists.newArrayList();
|
||||
responses.add(new StringText("standard response"));
|
||||
|
||||
if (field.options() != null) {
|
||||
for (Map.Entry<String, Object> entry : field.options().entrySet()) {
|
||||
if (field.fieldOptions().options() != null) {
|
||||
for (Map.Entry<String, Object> entry : field.fieldOptions().options().entrySet()) {
|
||||
responses.add(new StringText("field:" + entry.getKey() + ":" + entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -490,25 +490,31 @@ public class HighlighterSearchTests extends ElasticsearchIntegrationTest {
|
|||
assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("this is another <field2>test</field2>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Test //https://github.com/elasticsearch/elasticsearch/issues/5175
|
||||
public void testHighlightingOnWildcardFields() throws Exception {
|
||||
createIndex("test");
|
||||
assertAcked(prepareCreate("test")
|
||||
.addMapping("type1",
|
||||
"field-postings", "type=string,index_options=offsets",
|
||||
"field-fvh", "type=string,term_vector=with_positions_offsets",
|
||||
"field-plain", "type=string"));
|
||||
ensureGreen();
|
||||
|
||||
client().prepareIndex("test", "type1")
|
||||
.setSource("field1", "this is a test", "field2", "this is another test").get();
|
||||
.setSource("field-postings", "This is the first test sentence. Here is the second one.",
|
||||
"field-fvh", "This is the test with term_vectors",
|
||||
"field-plain", "This is the test for the plain highlighter").get();
|
||||
refresh();
|
||||
|
||||
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>"));
|
||||
.query(termQuery("field-plain", "test"))
|
||||
.highlight(highlight().field("field*").preTags("<xxx>").postTags("</xxx>"));
|
||||
|
||||
SearchResponse searchResponse = client().search(searchRequest("test").source(source).searchType(QUERY_THEN_FETCH)).actionGet();
|
||||
SearchResponse searchResponse = client().search(searchRequest("test").source(source)).actionGet();
|
||||
|
||||
assertHighlight(searchResponse, 0, "field1", 0, 1, equalTo("this is a <xxx>test</xxx>"));
|
||||
assertHighlight(searchResponse, 0, "field2", 0, 1, equalTo("this is another <xxx>test</xxx>"));
|
||||
assertHighlight(searchResponse, 0, "field-postings", 0, 1, equalTo("This is the first <xxx>test</xxx> sentence."));
|
||||
assertHighlight(searchResponse, 0, "field-fvh", 0, 1, equalTo("This is the <xxx>test</xxx> with term_vectors"));
|
||||
assertHighlight(searchResponse, 0, "field-plain", 0, 1, equalTo("This is the <xxx>test</xxx> for the plain highlighter"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue