Skip to content

Commit cc1eac1

Browse files
committed
Allow to specify highlighter parameters on a per field level basis, closes #356.
1 parent 8efe5dc commit cc1eac1

File tree

4 files changed

+115
-126
lines changed

4 files changed

+115
-126
lines changed

modules/elasticsearch/src/main/java/org/apache/lucene/search/vectorhighlight/SingleFragListBuilder.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
/*
2+
* Licensed to Elastic Search and Shay Banon under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. Elastic Search licenses this
6+
* file to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
120
package org.apache.lucene.search.vectorhighlight;
221

322
import java.util.ArrayList;
@@ -9,6 +28,7 @@
928
* http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/contrib/highlighter/src/java/org/apache/lucene/search/vectorhighlight/SingleFragListBuilder.java
1029
* This class in not available in 3.0.2 release yet.
1130
*/
31+
// LUCENE MONITOR
1232
public class SingleFragListBuilder implements FragListBuilder {
1333

1434
@Override public FieldFragList createFieldFragList(FieldPhraseList fieldPhraseList, int fragCharSize) {

modules/elasticsearch/src/main/java/org/elasticsearch/search/highlight/HighlightPhase.java

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.apache.lucene.search.vectorhighlight.*;
2525
import org.elasticsearch.ElasticSearchException;
2626
import org.elasticsearch.common.collect.ImmutableMap;
27-
import org.elasticsearch.common.collect.Tuple;
2827
import org.elasticsearch.index.mapper.DocumentMapper;
2928
import org.elasticsearch.index.mapper.FieldMapper;
3029
import org.elasticsearch.search.SearchHit;
@@ -35,10 +34,9 @@
3534
import org.elasticsearch.search.internal.SearchContext;
3635

3736
import java.io.IOException;
38-
import java.util.Arrays;
3937
import java.util.Map;
4038

41-
import static org.elasticsearch.common.collect.Maps.newHashMap;
39+
import static org.elasticsearch.common.collect.Maps.*;
4240

4341
/**
4442
* @author kimchy (shay.banon)
@@ -57,78 +55,58 @@ public class HighlightPhase implements SearchPhase {
5755
return;
5856
}
5957

60-
Map<Integer,FastVectorHighlighter> highlighterMap = newHashMap();
61-
Map<Integer,FieldQuery> fieldQueryMap = newHashMap();
62-
6358
for (SearchHit hit : context.fetchResult().hits().hits()) {
6459
InternalSearchHit internalHit = (InternalSearchHit) hit;
6560

6661
DocumentMapper documentMapper = context.mapperService().type(internalHit.type());
6762
int docId = internalHit.docId();
6863

6964
Map<String, HighlightField> highlightFields = newHashMap();
70-
for (SearchContextHighlight.ParsedHighlightField parsedHighlightField : context.highlight().fields()) {
71-
String fieldName = parsedHighlightField.field();
72-
FieldMapper mapper = documentMapper.mappers().smartNameFieldMapper(parsedHighlightField.field());
65+
for (SearchContextHighlight.Field field : context.highlight().fields()) {
66+
String fieldName = field.field();
67+
FieldMapper mapper = documentMapper.mappers().smartNameFieldMapper(field.field());
7368
if (mapper != null) {
7469
fieldName = mapper.names().indexName();
7570
}
7671

77-
Tuple<Integer,FastVectorHighlighter> highlighterTuple = getHighlighter(highlighterMap, parsedHighlightField.settings());
78-
FastVectorHighlighter highlighter = highlighterTuple.v2();
79-
FieldQuery fieldQuery = getFieldQuery(highlighterTuple.v1(), fieldQueryMap, highlighter, context.query(), context.searcher().getIndexReader(), parsedHighlightField.settings());
80-
72+
FastVectorHighlighter highlighter = buildHighlighter(field);
73+
FieldQuery fieldQuery = buildFieldQuery(highlighter, context.query(), context.searcher().getIndexReader(), field);
74+
8175
String[] fragments;
8276
try {
83-
fragments = highlighter.getBestFragments(fieldQuery, context.searcher().getIndexReader(), docId, fieldName, parsedHighlightField.settings().fragmentCharSize(), parsedHighlightField.settings().numberOfFragments());
77+
fragments = highlighter.getBestFragments(fieldQuery, context.searcher().getIndexReader(), docId, fieldName, field.fragmentCharSize(), field.numberOfFragments());
8478
} catch (IOException e) {
85-
throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + parsedHighlightField.field() + "]", e);
79+
throw new FetchPhaseExecutionException(context, "Failed to highlight field [" + field.field() + "]", e);
8680
}
87-
HighlightField highlightField = new HighlightField(parsedHighlightField.field(), fragments);
81+
HighlightField highlightField = new HighlightField(field.field(), fragments);
8882
highlightFields.put(highlightField.name(), highlightField);
8983
}
9084

9185
internalHit.highlightFields(highlightFields);
9286
}
9387
}
9488

95-
private FieldQuery getFieldQuery(int key, Map<Integer,FieldQuery> fieldQueryMap, FastVectorHighlighter highlighter, Query query, IndexReader indexReader, SearchContextHighlight.ParsedHighlightSettings settings) {
96-
FieldQuery fq = fieldQueryMap.get(key);
97-
if (fq == null) {
98-
CustomFieldQuery.reader.set(indexReader);
99-
CustomFieldQuery.highlightFilters.set(settings.highlightFilter());
100-
fq = new CustomFieldQuery(query, highlighter);
101-
fieldQueryMap.put(key,fq);
102-
}
103-
return fq;
89+
private FieldQuery buildFieldQuery(FastVectorHighlighter highlighter, Query query, IndexReader indexReader, SearchContextHighlight.Field field) {
90+
CustomFieldQuery.reader.set(indexReader);
91+
CustomFieldQuery.highlightFilters.set(field.highlightFilter());
92+
return new CustomFieldQuery(query, highlighter);
10493
}
10594

106-
private Tuple<Integer, FastVectorHighlighter> getHighlighter(Map<Integer,FastVectorHighlighter> highlighterMap, SearchContextHighlight.ParsedHighlightSettings settings) {
107-
95+
private FastVectorHighlighter buildHighlighter(SearchContextHighlight.Field field) {
10896
FragListBuilder fragListBuilder;
10997
FragmentsBuilder fragmentsBuilder;
110-
if (!settings.fragmentsAllowed()) {
98+
if (field.numberOfFragments() == 0) {
11199
fragListBuilder = new SingleFragListBuilder();
112-
fragmentsBuilder = new SimpleFragmentsBuilder(settings.preTags(), settings.postTags());
100+
fragmentsBuilder = new SimpleFragmentsBuilder(field.preTags(), field.postTags());
113101
} else {
114102
fragListBuilder = new SimpleFragListBuilder();
115-
if (settings.scoreOrdered()) {
116-
fragmentsBuilder = new ScoreOrderFragmentsBuilder(settings.preTags(), settings.postTags());
103+
if (field.scoreOrdered()) {
104+
fragmentsBuilder = new ScoreOrderFragmentsBuilder(field.preTags(), field.postTags());
117105
} else {
118-
fragmentsBuilder = new SimpleFragmentsBuilder(settings.preTags(), settings.postTags());
106+
fragmentsBuilder = new SimpleFragmentsBuilder(field.preTags(), field.postTags());
119107
}
120108
}
121109

122-
// highlighter key is determined by tags and FragList and Fragment builder classes.
123-
String[] mask = Arrays.copyOf(settings.preTags(), settings.preTags().length + settings.postTags().length);
124-
System.arraycopy(settings.postTags(), 0, mask, settings.preTags().length, settings.postTags().length);
125-
int key = (Arrays.toString(mask)+fragListBuilder.getClass().getSimpleName()+fragmentsBuilder.getClass().getSimpleName()).hashCode();
126-
127-
FastVectorHighlighter highlighter = highlighterMap.get(key);
128-
if (highlighter == null) {
129-
highlighter = new FastVectorHighlighter(true, false, fragListBuilder, fragmentsBuilder);
130-
highlighterMap.put(key,highlighter);
131-
}
132-
return Tuple.tuple(key, highlighter);
110+
return new FastVectorHighlighter(true, false, fragListBuilder, fragmentsBuilder);
133111
}
134112
}

modules/elasticsearch/src/main/java/org/elasticsearch/search/highlight/HighlighterParseElement.java

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,14 @@ public class HighlighterParseElement implements SearchParseElement {
6464
@Override public void parse(XContentParser parser, SearchContext context) throws Exception {
6565
XContentParser.Token token;
6666
String topLevelFieldName = null;
67-
List<SearchContextHighlight.ParsedHighlightField> fields = newArrayList();
68-
67+
List<SearchContextHighlight.Field> fields = newArrayList();
68+
6969
String[] globalPreTags = DEFAULT_PRE_TAGS;
7070
String[] globalPostTags = DEFAULT_POST_TAGS;
7171
boolean globalScoreOrdered = false;
7272
boolean globalHighlightFilter = true;
7373
int globalFragmentSize = 100;
7474
int globalNumOfFragments = 5;
75-
boolean globalFragmentsAllowed = true;
7675

7776
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
7877
if (token == XContentParser.Token.FIELD_NAME) {
@@ -105,9 +104,7 @@ public class HighlighterParseElement implements SearchParseElement {
105104
} else if ("fragment_size".equals(topLevelFieldName) || "fragmentSize".equals(topLevelFieldName)) {
106105
globalFragmentSize = parser.intValue();
107106
} else if ("number_of_fragments".equals(topLevelFieldName) || "numberOfFragments".equals(topLevelFieldName)) {
108-
globalNumOfFragments = parser.intValue();
109-
} else if ("fragment_type".equals(topLevelFieldName) || "fragmentType".equals(topLevelFieldName)) {
110-
globalFragmentsAllowed = !("content".equals(parser.text()));
107+
globalNumOfFragments = parser.intValue();
111108
}
112109
} else if (token == XContentParser.Token.START_OBJECT) {
113110
if ("fields".equals(topLevelFieldName)) {
@@ -116,16 +113,8 @@ public class HighlighterParseElement implements SearchParseElement {
116113
if (token == XContentParser.Token.FIELD_NAME) {
117114
highlightFieldName = parser.currentName();
118115
} else if (token == XContentParser.Token.START_OBJECT) {
116+
SearchContextHighlight.Field field = new SearchContextHighlight.Field(highlightFieldName);
119117
String fieldName = null;
120-
121-
int fragmentSize = globalFragmentSize;
122-
int numOfFragments = globalNumOfFragments;
123-
boolean highlightFilter = globalHighlightFilter;
124-
boolean scoreOrdered = globalScoreOrdered;
125-
boolean fragmentsAllowed = globalFragmentsAllowed;
126-
String[] preTags = globalPreTags;
127-
String[] postTags = globalPostTags;
128-
129118
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
130119
if (token == XContentParser.Token.FIELD_NAME) {
131120
fieldName = parser.currentName();
@@ -135,33 +124,27 @@ public class HighlighterParseElement implements SearchParseElement {
135124
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
136125
preTagsList.add(parser.text());
137126
}
138-
preTags = preTagsList.toArray(new String[preTagsList.size()]);
127+
field.preTags(preTagsList.toArray(new String[preTagsList.size()]));
139128
} else if ("post_tags".equals(fieldName) || "postTags".equals(fieldName)) {
140129
List<String> postTagsList = Lists.newArrayList();
141130
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
142131
postTagsList.add(parser.text());
143132
}
144-
postTags = postTagsList.toArray(new String[postTagsList.size()]);
133+
field.postTags(postTagsList.toArray(new String[postTagsList.size()]));
145134
}
146135
} else if (token.isValue()) {
147136
if ("fragment_size".equals(fieldName) || "fragmentSize".equals(fieldName)) {
148-
fragmentSize = parser.intValue();
137+
field.fragmentCharSize(parser.intValue());
149138
} else if ("number_of_fragments".equals(fieldName) || "numberOfFragments".equals(fieldName)) {
150-
numOfFragments = parser.intValue();
139+
field.numberOfFragments(parser.intValue());
151140
} else if ("highlight_filter".equals(fieldName) || "highlightFilter".equals(fieldName)) {
152-
highlightFilter = parser.booleanValue();
141+
field.highlightFilter(parser.booleanValue());
153142
} else if ("score".equals(fieldName)) {
154-
scoreOrdered = "score".equals(parser.text());;
155-
} else if ("fragment_type".equals(fieldName) || "fragmentType".equals(fieldName)) {
156-
fragmentsAllowed = !("content".equals(parser.text()));
143+
field.scoreOrdered("score".equals(parser.text()));
157144
}
158145
}
159146
}
160-
fields.add(new SearchContextHighlight.ParsedHighlightField(
161-
highlightFieldName,
162-
new SearchContextHighlight.ParsedHighlightSettings(
163-
fragmentSize, numOfFragments, preTags, postTags,
164-
scoreOrdered, highlightFilter, fragmentsAllowed)));
147+
fields.add(field);
165148
}
166149
}
167150
}
@@ -170,11 +153,29 @@ public class HighlighterParseElement implements SearchParseElement {
170153
if (globalPreTags != null && globalPostTags == null) {
171154
throw new SearchParseException(context, "Highlighter global preTags are set, but global postTags are not set");
172155
}
173-
context.highlight(new SearchContextHighlight(
174-
fields,
175-
new SearchContextHighlight.ParsedHighlightSettings(
176-
globalFragmentSize, globalNumOfFragments, globalPreTags, globalPostTags,
177-
globalScoreOrdered, globalHighlightFilter, globalFragmentsAllowed))
178-
);
156+
157+
// now, go over and fill all fields with default values from the global state
158+
for (SearchContextHighlight.Field field : fields) {
159+
if (field.preTags() == null) {
160+
field.preTags(globalPreTags);
161+
}
162+
if (field.postTags() == null) {
163+
field.postTags(globalPostTags);
164+
}
165+
if (field.highlightFilter() == null) {
166+
field.highlightFilter(globalHighlightFilter);
167+
}
168+
if (field.scoreOrdered() == null) {
169+
field.scoreOrdered(globalScoreOrdered);
170+
}
171+
if (field.fragmentCharSize() == -1) {
172+
field.fragmentCharSize(globalFragmentSize);
173+
}
174+
if (field.numberOfFragments() == -1) {
175+
field.numberOfFragments(globalNumOfFragments);
176+
}
177+
}
178+
179+
context.highlight(new SearchContextHighlight(fields));
179180
}
180181
}

0 commit comments

Comments
 (0)