Merge pull request #15324 from cbuescher/highlight-builder-searchContextHighlight
Enable HighlightBuilder to create SearchContextHighlight
This commit is contained in:
commit
79cdc40afe
|
@ -125,7 +125,7 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the fragment size in characters, defaults to {@link HighlighterParseElement#DEFAULT_FRAGMENT_CHAR_SIZE}
|
* Set the fragment size in characters, defaults to {@link HighlightBuilder#DEFAULT_FRAGMENT_CHAR_SIZE}
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public HB fragmentSize(Integer fragmentSize) {
|
public HB fragmentSize(Integer fragmentSize) {
|
||||||
|
@ -141,7 +141,7 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the number of fragments, defaults to {@link HighlighterParseElement#DEFAULT_NUMBER_OF_FRAGMENTS}
|
* Set the number of fragments, defaults to {@link HighlightBuilder#DEFAULT_NUMBER_OF_FRAGMENTS}
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public HB numOfFragments(Integer numOfFragments) {
|
public HB numOfFragments(Integer numOfFragments) {
|
||||||
|
@ -428,7 +428,7 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* internal hashCode calculation to overwrite for the implementing classes.
|
* fields only present in subclass should contribute to hashCode in the implementation
|
||||||
*/
|
*/
|
||||||
protected abstract int doHashCode();
|
protected abstract int doHashCode();
|
||||||
|
|
||||||
|
@ -462,7 +462,7 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* internal equals to overwrite for the implementing classes.
|
* fields only present in subclass should be checked for equality in the implementation
|
||||||
*/
|
*/
|
||||||
protected abstract boolean doEquals(HB other);
|
protected abstract boolean doEquals(HB other);
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
package org.elasticsearch.search.highlight;
|
package org.elasticsearch.search.highlight;
|
||||||
|
|
||||||
|
import org.apache.lucene.search.Query;
|
||||||
|
import org.apache.lucene.search.vectorhighlight.SimpleBoundaryScanner;
|
||||||
import org.elasticsearch.ExceptionsHelper;
|
import org.elasticsearch.ExceptionsHelper;
|
||||||
import org.elasticsearch.common.ParsingException;
|
import org.elasticsearch.common.ParsingException;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
@ -28,13 +30,20 @@ import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
|
import org.elasticsearch.search.highlight.SearchContextHighlight.FieldOptions;
|
||||||
|
import org.elasticsearch.search.highlight.SearchContextHighlight.FieldOptions.Builder;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A builder for search highlighting. Settings can control how large fields
|
* A builder for search highlighting. Settings can control how large fields
|
||||||
|
@ -48,6 +57,51 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
|
||||||
|
|
||||||
public static final String HIGHLIGHT_ELEMENT_NAME = "highlight";
|
public static final String HIGHLIGHT_ELEMENT_NAME = "highlight";
|
||||||
|
|
||||||
|
/** default for whether to highlight fields based on the source even if stored separately */
|
||||||
|
public static final boolean DEFAULT_FORCE_SOURCE = false;
|
||||||
|
/** default for whether a field should be highlighted only if a query matches that field */
|
||||||
|
public static final boolean DEFAULT_REQUIRE_FIELD_MATCH = true;
|
||||||
|
/** default for whether <tt>fvh</tt> should provide highlighting on filter clauses */
|
||||||
|
public static final boolean DEFAULT_HIGHLIGHT_FILTER = false;
|
||||||
|
/** default for highlight fragments being ordered by score */
|
||||||
|
public static final boolean DEFAULT_SCORE_ORDERED = false;
|
||||||
|
/** the default encoder setting */
|
||||||
|
public static final String DEFAULT_ENCODER = "default";
|
||||||
|
/** default for the maximum number of phrases the fvh will consider */
|
||||||
|
public static final int DEFAULT_PHRASE_LIMIT = 256;
|
||||||
|
/** default for fragment size when there are no matches */
|
||||||
|
public static final int DEFAULT_NO_MATCH_SIZE = 0;
|
||||||
|
/** the default number of fragments for highlighting */
|
||||||
|
public static final int DEFAULT_NUMBER_OF_FRAGMENTS = 5;
|
||||||
|
/** the default number of fragments size in characters */
|
||||||
|
public static final int DEFAULT_FRAGMENT_CHAR_SIZE = 100;
|
||||||
|
/** the default opening tag */
|
||||||
|
public static final String[] DEFAULT_PRE_TAGS = new String[]{"<em>"};
|
||||||
|
/** the default closing tag */
|
||||||
|
public static final String[] DEFAULT_POST_TAGS = new String[]{"</em>"};
|
||||||
|
|
||||||
|
/** the default opening tags when <tt>tag_schema = "styled"</tt> */
|
||||||
|
public static final String[] DEFAULT_STYLED_PRE_TAG = {
|
||||||
|
"<em class=\"hlt1\">", "<em class=\"hlt2\">", "<em class=\"hlt3\">",
|
||||||
|
"<em class=\"hlt4\">", "<em class=\"hlt5\">", "<em class=\"hlt6\">",
|
||||||
|
"<em class=\"hlt7\">", "<em class=\"hlt8\">", "<em class=\"hlt9\">",
|
||||||
|
"<em class=\"hlt10\">"
|
||||||
|
};
|
||||||
|
/** the default closing tags when <tt>tag_schema = "styled"</tt> */
|
||||||
|
public static final String[] DEFAULT_STYLED_POST_TAGS = {"</em>"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a {@link FieldOptions.Builder} with default settings
|
||||||
|
*/
|
||||||
|
public final static Builder defaultFieldOptions() {
|
||||||
|
return new SearchContextHighlight.FieldOptions.Builder()
|
||||||
|
.preTags(DEFAULT_PRE_TAGS).postTags(DEFAULT_POST_TAGS).scoreOrdered(DEFAULT_SCORE_ORDERED).highlightFilter(DEFAULT_HIGHLIGHT_FILTER)
|
||||||
|
.requireFieldMatch(DEFAULT_REQUIRE_FIELD_MATCH).forceSource(DEFAULT_FORCE_SOURCE).fragmentCharSize(DEFAULT_FRAGMENT_CHAR_SIZE).numberOfFragments(DEFAULT_NUMBER_OF_FRAGMENTS)
|
||||||
|
.encoder(DEFAULT_ENCODER).boundaryMaxScan(SimpleBoundaryScanner.DEFAULT_MAX_SCAN)
|
||||||
|
.boundaryChars(SimpleBoundaryScanner.DEFAULT_BOUNDARY_CHARS)
|
||||||
|
.noMatchSize(DEFAULT_NO_MATCH_SIZE).phraseLimit(DEFAULT_PHRASE_LIMIT);
|
||||||
|
}
|
||||||
|
|
||||||
private final List<Field> fields = new ArrayList<>();
|
private final List<Field> fields = new ArrayList<>();
|
||||||
|
|
||||||
private String encoder;
|
private String encoder;
|
||||||
|
@ -120,12 +174,12 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
|
||||||
public HighlightBuilder tagsSchema(String schemaName) {
|
public HighlightBuilder tagsSchema(String schemaName) {
|
||||||
switch (schemaName) {
|
switch (schemaName) {
|
||||||
case "default":
|
case "default":
|
||||||
preTags(HighlighterParseElement.DEFAULT_PRE_TAGS);
|
preTags(DEFAULT_PRE_TAGS);
|
||||||
postTags(HighlighterParseElement.DEFAULT_POST_TAGS);
|
postTags(DEFAULT_POST_TAGS);
|
||||||
break;
|
break;
|
||||||
case "styled":
|
case "styled":
|
||||||
preTags(HighlighterParseElement.STYLED_PRE_TAG);
|
preTags(DEFAULT_STYLED_PRE_TAG);
|
||||||
postTags(HighlighterParseElement.STYLED_POST_TAGS);
|
postTags(DEFAULT_STYLED_POST_TAGS);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown tag schema ["+ schemaName +"]");
|
throw new IllegalArgumentException("Unknown tag schema ["+ schemaName +"]");
|
||||||
|
@ -289,7 +343,87 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
|
||||||
return highlightBuilder;
|
return highlightBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SearchContextHighlight build(QueryShardContext context) throws IOException {
|
||||||
|
// create template global options that are later merged with any partial field options
|
||||||
|
final SearchContextHighlight.FieldOptions.Builder globalOptionsBuilder = new SearchContextHighlight.FieldOptions.Builder();
|
||||||
|
globalOptionsBuilder.encoder(this.encoder);
|
||||||
|
transferOptions(this, globalOptionsBuilder, context);
|
||||||
|
|
||||||
|
// overwrite unset global options by default values
|
||||||
|
globalOptionsBuilder.merge(defaultFieldOptions().build());
|
||||||
|
|
||||||
|
// create field options
|
||||||
|
Collection<org.elasticsearch.search.highlight.SearchContextHighlight.Field> fieldOptions = new ArrayList<>();
|
||||||
|
for (Field field : this.fields) {
|
||||||
|
final SearchContextHighlight.FieldOptions.Builder fieldOptionsBuilder = new SearchContextHighlight.FieldOptions.Builder();
|
||||||
|
fieldOptionsBuilder.fragmentOffset(field.fragmentOffset);
|
||||||
|
if (field.matchedFields != null) {
|
||||||
|
Set<String> matchedFields = new HashSet<String>(field.matchedFields.length);
|
||||||
|
Collections.addAll(matchedFields, field.matchedFields);
|
||||||
|
fieldOptionsBuilder.matchedFields(matchedFields);
|
||||||
|
}
|
||||||
|
transferOptions(field, fieldOptionsBuilder, context);
|
||||||
|
fieldOptions.add(new SearchContextHighlight.Field(field.name(), fieldOptionsBuilder.merge(globalOptionsBuilder.build()).build()));
|
||||||
|
}
|
||||||
|
return new SearchContextHighlight(fieldOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfers field options present in the input {@link AbstractHighlighterBuilder} to the receiving
|
||||||
|
* {@link FieldOptions.Builder}, effectively overwriting existing settings
|
||||||
|
* @param targetOptionsBuilder the receiving options builder
|
||||||
|
* @param highlighterBuilder highlight builder with the input options
|
||||||
|
* @param context needed to convert {@link QueryBuilder} to {@link Query}
|
||||||
|
* @throws IOException on errors parsing any optional nested highlight query
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
private static void transferOptions(AbstractHighlighterBuilder highlighterBuilder, SearchContextHighlight.FieldOptions.Builder targetOptionsBuilder, QueryShardContext context) throws IOException {
|
||||||
|
targetOptionsBuilder.preTags(highlighterBuilder.preTags);
|
||||||
|
targetOptionsBuilder.postTags(highlighterBuilder.postTags);
|
||||||
|
targetOptionsBuilder.scoreOrdered("score".equals(highlighterBuilder.order));
|
||||||
|
if (highlighterBuilder.highlightFilter != null) {
|
||||||
|
targetOptionsBuilder.highlightFilter(highlighterBuilder.highlightFilter);
|
||||||
|
}
|
||||||
|
if (highlighterBuilder.fragmentSize != null) {
|
||||||
|
targetOptionsBuilder.fragmentCharSize(highlighterBuilder.fragmentSize);
|
||||||
|
}
|
||||||
|
if (highlighterBuilder.numOfFragments != null) {
|
||||||
|
targetOptionsBuilder.numberOfFragments(highlighterBuilder.numOfFragments);
|
||||||
|
}
|
||||||
|
if (highlighterBuilder.requireFieldMatch != null) {
|
||||||
|
targetOptionsBuilder.requireFieldMatch(highlighterBuilder.requireFieldMatch);
|
||||||
|
}
|
||||||
|
if (highlighterBuilder.boundaryMaxScan != null) {
|
||||||
|
targetOptionsBuilder.boundaryMaxScan(highlighterBuilder.boundaryMaxScan);
|
||||||
|
}
|
||||||
|
targetOptionsBuilder.boundaryChars(convertCharArray(highlighterBuilder.boundaryChars));
|
||||||
|
targetOptionsBuilder.highlighterType(highlighterBuilder.highlighterType);
|
||||||
|
targetOptionsBuilder.fragmenter(highlighterBuilder.fragmenter);
|
||||||
|
if (highlighterBuilder.noMatchSize != null) {
|
||||||
|
targetOptionsBuilder.noMatchSize(highlighterBuilder.noMatchSize);
|
||||||
|
}
|
||||||
|
if (highlighterBuilder.forceSource != null) {
|
||||||
|
targetOptionsBuilder.forceSource(highlighterBuilder.forceSource);
|
||||||
|
}
|
||||||
|
if (highlighterBuilder.phraseLimit != null) {
|
||||||
|
targetOptionsBuilder.phraseLimit(highlighterBuilder.phraseLimit);
|
||||||
|
}
|
||||||
|
targetOptionsBuilder.options(highlighterBuilder.options);
|
||||||
|
if (highlighterBuilder.highlightQuery != null) {
|
||||||
|
targetOptionsBuilder.highlightQuery(highlighterBuilder.highlightQuery.toQuery(context));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Character[] convertCharArray(char[] array) {
|
||||||
|
if (array == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Character[] charArray = new Character[array.length];
|
||||||
|
for (int i = 0; i < array.length; i++) {
|
||||||
|
charArray[i] = array[i];
|
||||||
|
}
|
||||||
|
return charArray;
|
||||||
|
}
|
||||||
|
|
||||||
public void innerXContent(XContentBuilder builder) throws IOException {
|
public void innerXContent(XContentBuilder builder) throws IOException {
|
||||||
// first write common options
|
// first write common options
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
package org.elasticsearch.search.highlight;
|
package org.elasticsearch.search.highlight;
|
||||||
|
|
||||||
import org.apache.lucene.search.vectorhighlight.SimpleBoundaryScanner;
|
|
||||||
import org.elasticsearch.common.collect.Tuple;
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.query.QueryShardContext;
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
|
@ -52,39 +51,6 @@ import java.util.Set;
|
||||||
*/
|
*/
|
||||||
public class HighlighterParseElement implements SearchParseElement {
|
public class HighlighterParseElement implements SearchParseElement {
|
||||||
|
|
||||||
/** default for whether to highlight fields based on the source even if stored separately */
|
|
||||||
public static final boolean DEFAULT_FORCE_SOURCE = false;
|
|
||||||
/** default for whether a field should be highlighted only if a query matches that field */
|
|
||||||
public static final boolean DEFAULT_REQUIRE_FIELD_MATCH = true;
|
|
||||||
/** default for whether <tt>fvh</tt> should provide highlighting on filter clauses */
|
|
||||||
public static final boolean DEFAULT_HIGHLIGHT_FILTER = false;
|
|
||||||
/** default for highlight fragments being ordered by score */
|
|
||||||
public static final boolean DEFAULT_SCORE_ORDERED = false;
|
|
||||||
/** the default encoder setting */
|
|
||||||
public static final String DEFAULT_ENCODER = "default";
|
|
||||||
/** default for the maximum number of phrases the fvh will consider */
|
|
||||||
public static final int DEFAULT_PHRASE_LIMIT = 256;
|
|
||||||
/** default for fragment size when there are no matches */
|
|
||||||
public static final int DEFAULT_NO_MATCH_SIZE = 0;
|
|
||||||
/** the default number of fragments for highlighting */
|
|
||||||
public static final int DEFAULT_NUMBER_OF_FRAGMENTS = 5;
|
|
||||||
/** the default number of fragments size in characters */
|
|
||||||
public static final int DEFAULT_FRAGMENT_CHAR_SIZE = 100;
|
|
||||||
/** the default opening tag */
|
|
||||||
public static final String[] DEFAULT_PRE_TAGS = new String[]{"<em>"};
|
|
||||||
/** the default closing tag */
|
|
||||||
public static final String[] DEFAULT_POST_TAGS = new String[]{"</em>"};
|
|
||||||
|
|
||||||
/** the default opening tags when <tt>tag_schema = "styled"</tt> */
|
|
||||||
public static final String[] STYLED_PRE_TAG = {
|
|
||||||
"<em class=\"hlt1\">", "<em class=\"hlt2\">", "<em class=\"hlt3\">",
|
|
||||||
"<em class=\"hlt4\">", "<em class=\"hlt5\">", "<em class=\"hlt6\">",
|
|
||||||
"<em class=\"hlt7\">", "<em class=\"hlt8\">", "<em class=\"hlt9\">",
|
|
||||||
"<em class=\"hlt10\">"
|
|
||||||
};
|
|
||||||
/** the default closing tags when <tt>tag_schema = "styled"</tt> */
|
|
||||||
public static final String[] STYLED_POST_TAGS = {"</em>"};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parse(XContentParser parser, SearchContext context) throws Exception {
|
public void parse(XContentParser parser, SearchContext context) throws Exception {
|
||||||
try {
|
try {
|
||||||
|
@ -99,12 +65,7 @@ public class HighlighterParseElement implements SearchParseElement {
|
||||||
String topLevelFieldName = null;
|
String topLevelFieldName = null;
|
||||||
final List<Tuple<String, SearchContextHighlight.FieldOptions.Builder>> fieldsOptions = new ArrayList<>();
|
final List<Tuple<String, SearchContextHighlight.FieldOptions.Builder>> fieldsOptions = new ArrayList<>();
|
||||||
|
|
||||||
final SearchContextHighlight.FieldOptions.Builder globalOptionsBuilder = new SearchContextHighlight.FieldOptions.Builder()
|
final SearchContextHighlight.FieldOptions.Builder globalOptionsBuilder = HighlightBuilder.defaultFieldOptions();
|
||||||
.preTags(DEFAULT_PRE_TAGS).postTags(DEFAULT_POST_TAGS).scoreOrdered(DEFAULT_SCORE_ORDERED).highlightFilter(DEFAULT_HIGHLIGHT_FILTER)
|
|
||||||
.requireFieldMatch(DEFAULT_REQUIRE_FIELD_MATCH).forceSource(DEFAULT_FORCE_SOURCE).fragmentCharSize(DEFAULT_FRAGMENT_CHAR_SIZE).numberOfFragments(DEFAULT_NUMBER_OF_FRAGMENTS)
|
|
||||||
.encoder(DEFAULT_ENCODER).boundaryMaxScan(SimpleBoundaryScanner.DEFAULT_MAX_SCAN)
|
|
||||||
.boundaryChars(SimpleBoundaryScanner.DEFAULT_BOUNDARY_CHARS)
|
|
||||||
.noMatchSize(DEFAULT_NO_MATCH_SIZE).phraseLimit(DEFAULT_PHRASE_LIMIT);
|
|
||||||
|
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
|
@ -147,8 +108,8 @@ public class HighlighterParseElement implements SearchParseElement {
|
||||||
} else if ("tags_schema".equals(topLevelFieldName) || "tagsSchema".equals(topLevelFieldName)) {
|
} else if ("tags_schema".equals(topLevelFieldName) || "tagsSchema".equals(topLevelFieldName)) {
|
||||||
String schema = parser.text();
|
String schema = parser.text();
|
||||||
if ("styled".equals(schema)) {
|
if ("styled".equals(schema)) {
|
||||||
globalOptionsBuilder.preTags(STYLED_PRE_TAG);
|
globalOptionsBuilder.preTags(HighlightBuilder.DEFAULT_STYLED_PRE_TAG);
|
||||||
globalOptionsBuilder.postTags(STYLED_POST_TAGS);
|
globalOptionsBuilder.postTags(HighlightBuilder.DEFAULT_STYLED_POST_TAGS);
|
||||||
}
|
}
|
||||||
} else if ("highlight_filter".equals(topLevelFieldName) || "highlightFilter".equals(topLevelFieldName)) {
|
} else if ("highlight_filter".equals(topLevelFieldName) || "highlightFilter".equals(topLevelFieldName)) {
|
||||||
globalOptionsBuilder.highlightFilter(parser.booleanValue());
|
globalOptionsBuilder.highlightFilter(parser.booleanValue());
|
||||||
|
|
|
@ -53,6 +53,10 @@ public class SearchContextHighlight {
|
||||||
this.globalForceSource = globalForceSource;
|
this.globalForceSource = globalForceSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean globalForceSource() {
|
||||||
|
return this.globalForceSource;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean forceSource(Field field) {
|
public boolean forceSource(Field field) {
|
||||||
if (globalForceSource) {
|
if (globalForceSource) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -21,6 +21,8 @@ package org.elasticsearch.search.highlight;
|
||||||
|
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
import org.elasticsearch.common.ParseFieldMatcher;
|
||||||
import org.elasticsearch.common.ParsingException;
|
import org.elasticsearch.common.ParsingException;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
||||||
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
|
@ -32,6 +34,13 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
|
import org.elasticsearch.index.Index;
|
||||||
|
import org.elasticsearch.index.IndexSettings;
|
||||||
|
import org.elasticsearch.index.mapper.ContentPath;
|
||||||
|
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||||
|
import org.elasticsearch.index.mapper.Mapper;
|
||||||
|
import org.elasticsearch.index.mapper.MapperBuilders;
|
||||||
|
import org.elasticsearch.index.mapper.core.StringFieldMapper;
|
||||||
import org.elasticsearch.index.query.IdsQueryBuilder;
|
import org.elasticsearch.index.query.IdsQueryBuilder;
|
||||||
import org.elasticsearch.index.query.IdsQueryParser;
|
import org.elasticsearch.index.query.IdsQueryParser;
|
||||||
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
import org.elasticsearch.index.query.MatchAllQueryBuilder;
|
||||||
|
@ -39,11 +48,15 @@ import org.elasticsearch.index.query.MatchAllQueryParser;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
import org.elasticsearch.index.query.QueryParseContext;
|
import org.elasticsearch.index.query.QueryParseContext;
|
||||||
import org.elasticsearch.index.query.QueryParser;
|
import org.elasticsearch.index.query.QueryParser;
|
||||||
|
import org.elasticsearch.index.query.QueryShardContext;
|
||||||
import org.elasticsearch.index.query.TermQueryBuilder;
|
import org.elasticsearch.index.query.TermQueryBuilder;
|
||||||
import org.elasticsearch.index.query.TermQueryParser;
|
import org.elasticsearch.index.query.TermQueryParser;
|
||||||
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
|
||||||
|
import org.elasticsearch.search.highlight.HighlightBuilder;
|
||||||
import org.elasticsearch.search.highlight.HighlightBuilder.Field;
|
import org.elasticsearch.search.highlight.HighlightBuilder.Field;
|
||||||
|
import org.elasticsearch.search.highlight.SearchContextHighlight.FieldOptions;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.elasticsearch.test.IndexSettingsModule;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
|
@ -51,6 +64,7 @@ import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -128,7 +142,7 @@ public class HighlightBuilderTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic test that creates new highlighter from the test highlighter and checks both for equality
|
* creates random highlighter, renders it to xContent and back to new instance that should be equal to original
|
||||||
*/
|
*/
|
||||||
public void testFromXContent() throws IOException {
|
public void testFromXContent() throws IOException {
|
||||||
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry);
|
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry);
|
||||||
|
@ -261,6 +275,70 @@ public class HighlightBuilderTests extends ESTestCase {
|
||||||
} catch (ParsingException e) {
|
} catch (ParsingException e) {
|
||||||
assertEquals("cannot parse object with name [bad_fieldname]", e.getMessage());
|
assertEquals("cannot parse object with name [bad_fieldname]", e.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* test that build() outputs a {@link SearchContextHighlight} that is similar to the one
|
||||||
|
* we would get when parsing the xContent the test highlight builder is rendering out
|
||||||
|
*/
|
||||||
|
public void testBuildSearchContextHighlight() throws IOException {
|
||||||
|
Settings indexSettings = Settings.settingsBuilder()
|
||||||
|
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build();
|
||||||
|
Index index = new Index(randomAsciiOfLengthBetween(1, 10));
|
||||||
|
IndexSettings idxSettings = IndexSettingsModule.newIndexSettings(index, indexSettings);
|
||||||
|
// shard context will only need indicesQueriesRegistry for building Query objects nested in highlighter
|
||||||
|
QueryShardContext mockShardContext = new QueryShardContext(idxSettings, null, null, null, null, null, null, indicesQueriesRegistry) {
|
||||||
|
@Override
|
||||||
|
public MappedFieldType fieldMapper(String name) {
|
||||||
|
StringFieldMapper.Builder builder = MapperBuilders.stringField(name);
|
||||||
|
return builder.build(new Mapper.BuilderContext(idxSettings.getSettings(), new ContentPath(1))).fieldType();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mockShardContext.setMapUnmappedFieldAsString(true);
|
||||||
|
|
||||||
|
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
|
||||||
|
HighlightBuilder highlightBuilder = randomHighlighterBuilder();
|
||||||
|
SearchContextHighlight highlight = highlightBuilder.build(mockShardContext);
|
||||||
|
XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()));
|
||||||
|
if (randomBoolean()) {
|
||||||
|
builder.prettyPrint();
|
||||||
|
}
|
||||||
|
builder.startObject();
|
||||||
|
highlightBuilder.innerXContent(builder);
|
||||||
|
builder.endObject();
|
||||||
|
XContentParser parser = XContentHelper.createParser(builder.bytes());
|
||||||
|
|
||||||
|
SearchContextHighlight parsedHighlight = new HighlighterParseElement().parse(parser, mockShardContext);
|
||||||
|
assertNotSame(highlight, parsedHighlight);
|
||||||
|
assertEquals(highlight.globalForceSource(), parsedHighlight.globalForceSource());
|
||||||
|
assertEquals(highlight.fields().size(), parsedHighlight.fields().size());
|
||||||
|
|
||||||
|
Iterator<org.elasticsearch.search.highlight.SearchContextHighlight.Field> iterator = parsedHighlight.fields().iterator();
|
||||||
|
for (org.elasticsearch.search.highlight.SearchContextHighlight.Field field : highlight.fields()) {
|
||||||
|
org.elasticsearch.search.highlight.SearchContextHighlight.Field otherField = iterator.next();
|
||||||
|
assertEquals(field.field(), otherField.field());
|
||||||
|
FieldOptions options = field.fieldOptions();
|
||||||
|
FieldOptions otherOptions = otherField.fieldOptions();
|
||||||
|
assertArrayEquals(options.boundaryChars(), options.boundaryChars());
|
||||||
|
assertEquals(options.boundaryMaxScan(), otherOptions.boundaryMaxScan());
|
||||||
|
assertEquals(options.encoder(), otherOptions.encoder());
|
||||||
|
assertEquals(options.fragmentCharSize(), otherOptions.fragmentCharSize());
|
||||||
|
assertEquals(options.fragmenter(), otherOptions.fragmenter());
|
||||||
|
assertEquals(options.fragmentOffset(), otherOptions.fragmentOffset());
|
||||||
|
assertEquals(options.highlighterType(), otherOptions.highlighterType());
|
||||||
|
assertEquals(options.highlightFilter(), otherOptions.highlightFilter());
|
||||||
|
assertEquals(options.highlightQuery(), otherOptions.highlightQuery());
|
||||||
|
assertEquals(options.matchedFields(), otherOptions.matchedFields());
|
||||||
|
assertEquals(options.noMatchSize(), otherOptions.noMatchSize());
|
||||||
|
assertEquals(options.numberOfFragments(), otherOptions.numberOfFragments());
|
||||||
|
assertEquals(options.options(), otherOptions.options());
|
||||||
|
assertEquals(options.phraseLimit(), otherOptions.phraseLimit());
|
||||||
|
assertArrayEquals(options.preTags(), otherOptions.preTags());
|
||||||
|
assertArrayEquals(options.postTags(), otherOptions.postTags());
|
||||||
|
assertEquals(options.requireFieldMatch(), otherOptions.requireFieldMatch());
|
||||||
|
assertEquals(options.scoreOrdered(), otherOptions.scoreOrdered());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,9 +355,9 @@ public class HighlightBuilderTests extends ESTestCase {
|
||||||
|
|
||||||
context.reset(parser);
|
context.reset(parser);
|
||||||
HighlightBuilder highlightBuilder = HighlightBuilder.fromXContent(context);
|
HighlightBuilder highlightBuilder = HighlightBuilder.fromXContent(context);
|
||||||
assertArrayEquals("setting tags_schema 'styled' should alter pre_tags", HighlighterParseElement.STYLED_PRE_TAG,
|
assertArrayEquals("setting tags_schema 'styled' should alter pre_tags", HighlightBuilder.DEFAULT_STYLED_PRE_TAG,
|
||||||
highlightBuilder.preTags());
|
highlightBuilder.preTags());
|
||||||
assertArrayEquals("setting tags_schema 'styled' should alter post_tags", HighlighterParseElement.STYLED_POST_TAGS,
|
assertArrayEquals("setting tags_schema 'styled' should alter post_tags", HighlightBuilder.DEFAULT_STYLED_POST_TAGS,
|
||||||
highlightBuilder.postTags());
|
highlightBuilder.postTags());
|
||||||
|
|
||||||
highlightElement = "{\n" +
|
highlightElement = "{\n" +
|
||||||
|
@ -289,9 +367,9 @@ public class HighlightBuilderTests extends ESTestCase {
|
||||||
|
|
||||||
context.reset(parser);
|
context.reset(parser);
|
||||||
highlightBuilder = HighlightBuilder.fromXContent(context);
|
highlightBuilder = HighlightBuilder.fromXContent(context);
|
||||||
assertArrayEquals("setting tags_schema 'default' should alter pre_tags", HighlighterParseElement.DEFAULT_PRE_TAGS,
|
assertArrayEquals("setting tags_schema 'default' should alter pre_tags", HighlightBuilder.DEFAULT_PRE_TAGS,
|
||||||
highlightBuilder.preTags());
|
highlightBuilder.preTags());
|
||||||
assertArrayEquals("setting tags_schema 'default' should alter post_tags", HighlighterParseElement.DEFAULT_POST_TAGS,
|
assertArrayEquals("setting tags_schema 'default' should alter post_tags", HighlightBuilder.DEFAULT_POST_TAGS,
|
||||||
highlightBuilder.postTags());
|
highlightBuilder.postTags());
|
||||||
|
|
||||||
highlightElement = "{\n" +
|
highlightElement = "{\n" +
|
||||||
|
|
Loading…
Reference in New Issue