Merge pull request #15324 from cbuescher/highlight-builder-searchContextHighlight

Enable HighlightBuilder to create SearchContextHighlight
This commit is contained in:
Christoph Büscher 2015-12-10 11:15:43 +01:00
commit 79cdc40afe
5 changed files with 233 additions and 56 deletions

View File

@ -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);

View File

@ -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

View File

@ -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());

View File

@ -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;

View File

@ -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" +