Merge pull request #15157 from cbuescher/highlight-builder-fromXContent

Add fromXContent() method to HighlightBuilder
This commit is contained in:
Christoph Büscher 2015-12-08 19:39:00 +01:00
commit d866906612
4 changed files with 472 additions and 40 deletions

View File

@ -21,6 +21,7 @@ package org.elasticsearch.search.highlight;
import org.apache.lucene.search.highlight.SimpleFragmenter; import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleSpanFragmenter; import org.apache.lucene.search.highlight.SimpleSpanFragmenter;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -35,7 +36,29 @@ import java.util.Objects;
* This abstract class holds parameters shared by {@link HighlightBuilder} and {@link HighlightBuilder.Field} * This abstract class holds parameters shared by {@link HighlightBuilder} and {@link HighlightBuilder.Field}
* and provides the common setters, equality, hashCode calculation and common serialization * and provides the common setters, equality, hashCode calculation and common serialization
*/ */
public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterBuilder> { public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterBuilder<?>> {
public static final ParseField PRE_TAGS_FIELD = new ParseField("pre_tags");
public static final ParseField POST_TAGS_FIELD = new ParseField("post_tags");
public static final ParseField FIELDS_FIELD = new ParseField("fields");
public static final ParseField ORDER_FIELD = new ParseField("order");
public static final ParseField TAGS_SCHEMA_FIELD = new ParseField("tags_schema");
public static final ParseField HIGHLIGHT_FILTER_FIELD = new ParseField("highlight_filter");
public static final ParseField FRAGMENT_SIZE_FIELD = new ParseField("fragment_size");
public static final ParseField FRAGMENT_OFFSET_FIELD = new ParseField("fragment_offset");
public static final ParseField NUMBER_OF_FRAGMENTS_FIELD = new ParseField("number_of_fragments");
public static final ParseField ENCODER_FIELD = new ParseField("encoder");
public static final ParseField REQUIRE_FIELD_MATCH_FIELD = new ParseField("require_field_match");
public static final ParseField BOUNDARY_MAX_SCAN_FIELD = new ParseField("boundary_max_scan");
public static final ParseField BOUNDARY_CHARS_FIELD = new ParseField("boundary_chars");
public static final ParseField TYPE_FIELD = new ParseField("type");
public static final ParseField FRAGMENTER_FIELD = new ParseField("fragmenter");
public static final ParseField NO_MATCH_SIZE_FIELD = new ParseField("no_match_size");
public static final ParseField FORCE_SOURCE_FIELD = new ParseField("force_source");
public static final ParseField PHRASE_LIMIT_FIELD = new ParseField("phrase_limit");
public static final ParseField OPTIONS_FIELD = new ParseField("options");
public static final ParseField HIGHLIGHT_QUERY_FIELD = new ParseField("highlight_query");
public static final ParseField MATCHED_FIELDS_FIELD = new ParseField("matched_fields");
protected String[] preTags; protected String[] preTags;
@ -49,7 +72,7 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
protected String fragmenter; protected String fragmenter;
protected QueryBuilder highlightQuery; protected QueryBuilder<?> highlightQuery;
protected String order; protected String order;
@ -175,7 +198,7 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
* Sets a query to be used for highlighting instead of the search query. * Sets a query to be used for highlighting instead of the search query.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public HB highlightQuery(QueryBuilder highlightQuery) { public HB highlightQuery(QueryBuilder<?> highlightQuery) {
this.highlightQuery = highlightQuery; this.highlightQuery = highlightQuery;
return (HB) this; return (HB) this;
} }
@ -183,7 +206,7 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
/** /**
* @return the value set by {@link #highlightQuery(QueryBuilder)} * @return the value set by {@link #highlightQuery(QueryBuilder)}
*/ */
public QueryBuilder highlightQuery() { public QueryBuilder<?> highlightQuery() {
return this.highlightQuery; return this.highlightQuery;
} }
@ -347,52 +370,52 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
void commonOptionsToXContent(XContentBuilder builder) throws IOException { void commonOptionsToXContent(XContentBuilder builder) throws IOException {
if (preTags != null) { if (preTags != null) {
builder.array("pre_tags", preTags); builder.array(PRE_TAGS_FIELD.getPreferredName(), preTags);
} }
if (postTags != null) { if (postTags != null) {
builder.array("post_tags", postTags); builder.array(POST_TAGS_FIELD.getPreferredName(), postTags);
} }
if (fragmentSize != null) { if (fragmentSize != null) {
builder.field("fragment_size", fragmentSize); builder.field(FRAGMENT_SIZE_FIELD.getPreferredName(), fragmentSize);
} }
if (numOfFragments != null) { if (numOfFragments != null) {
builder.field("number_of_fragments", numOfFragments); builder.field(NUMBER_OF_FRAGMENTS_FIELD.getPreferredName(), numOfFragments);
} }
if (highlighterType != null) { if (highlighterType != null) {
builder.field("type", highlighterType); builder.field(TYPE_FIELD.getPreferredName(), highlighterType);
} }
if (fragmenter != null) { if (fragmenter != null) {
builder.field("fragmenter", fragmenter); builder.field(FRAGMENTER_FIELD.getPreferredName(), fragmenter);
} }
if (highlightQuery != null) { if (highlightQuery != null) {
builder.field("highlight_query", highlightQuery); builder.field(HIGHLIGHT_QUERY_FIELD.getPreferredName(), highlightQuery);
} }
if (order != null) { if (order != null) {
builder.field("order", order); builder.field(ORDER_FIELD.getPreferredName(), order);
} }
if (highlightFilter != null) { if (highlightFilter != null) {
builder.field("highlight_filter", highlightFilter); builder.field(HIGHLIGHT_FILTER_FIELD.getPreferredName(), highlightFilter);
} }
if (boundaryMaxScan != null) { if (boundaryMaxScan != null) {
builder.field("boundary_max_scan", boundaryMaxScan); builder.field(BOUNDARY_MAX_SCAN_FIELD.getPreferredName(), boundaryMaxScan);
} }
if (boundaryChars != null) { if (boundaryChars != null) {
builder.field("boundary_chars", boundaryChars); builder.field(BOUNDARY_CHARS_FIELD.getPreferredName(), new String(boundaryChars));
} }
if (options != null && options.size() > 0) { if (options != null && options.size() > 0) {
builder.field("options", options); builder.field(OPTIONS_FIELD.getPreferredName(), options);
} }
if (forceSource != null) { if (forceSource != null) {
builder.field("force_source", forceSource); builder.field(FORCE_SOURCE_FIELD.getPreferredName(), forceSource);
} }
if (requireFieldMatch != null) { if (requireFieldMatch != null) {
builder.field("require_field_match", requireFieldMatch); builder.field(REQUIRE_FIELD_MATCH_FIELD.getPreferredName(), requireFieldMatch);
} }
if (noMatchSize != null) { if (noMatchSize != null) {
builder.field("no_match_size", noMatchSize); builder.field(NO_MATCH_SIZE_FIELD.getPreferredName(), noMatchSize);
} }
if (phraseLimit != null) { if (phraseLimit != null) {
builder.field("phrase_limit", phraseLimit); builder.field(PHRASE_LIMIT_FIELD.getPreferredName(), phraseLimit);
} }
} }

View File

@ -20,12 +20,15 @@
package org.elasticsearch.search.highlight; package org.elasticsearch.search.highlight;
import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.ExceptionsHelper;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable; import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.ToXContent; 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.index.query.QueryParseContext;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -43,6 +46,8 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
public static final HighlightBuilder PROTOTYPE = new HighlightBuilder(); public static final HighlightBuilder PROTOTYPE = new HighlightBuilder();
public static final String HIGHLIGHT_ELEMENT_NAME = "highlight";
private final List<Field> fields = new ArrayList<>(); private final List<Field> fields = new ArrayList<>();
private String encoder; private String encoder;
@ -164,24 +169,140 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
@Override @Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject("highlight"); builder.startObject(HIGHLIGHT_ELEMENT_NAME);
innerXContent(builder); innerXContent(builder);
builder.endObject(); builder.endObject();
return builder; return builder;
} }
/**
* Creates a new {@link HighlightBuilder} from the highlighter held by the {@link QueryParseContext}
* in {@link org.elasticsearch.common.xcontent.XContent} format
*
* @param parseContext
* the input parse context. The state on the parser contained in
* this context will be changed as a side effect of this method
* call
* @return the new {@link HighlightBuilder}
*/
public static HighlightBuilder fromXContent(QueryParseContext parseContext) throws IOException {
XContentParser parser = parseContext.parser();
XContentParser.Token token;
String topLevelFieldName = null;
HighlightBuilder highlightBuilder = new HighlightBuilder();
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
topLevelFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_ARRAY) {
if (parseContext.parseFieldMatcher().match(topLevelFieldName, PRE_TAGS_FIELD)) {
List<String> preTagsList = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
preTagsList.add(parser.text());
}
highlightBuilder.preTags(preTagsList.toArray(new String[preTagsList.size()]));
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, POST_TAGS_FIELD)) {
List<String> postTagsList = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
postTagsList.add(parser.text());
}
highlightBuilder.postTags(postTagsList.toArray(new String[postTagsList.size()]));
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, FIELDS_FIELD)) {
highlightBuilder.useExplicitFieldOrder(true);
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.START_OBJECT) {
String highlightFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
if (highlightFieldName != null) {
throw new ParsingException(parser.getTokenLocation(), "If highlighter fields is an array it must contain objects containing a single field");
}
highlightFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
highlightBuilder.field(Field.fromXContent(highlightFieldName, parseContext));
}
}
} else {
throw new ParsingException(parser.getTokenLocation(), "If highlighter fields is an array it must contain objects containing a single field");
}
}
} else {
throw new ParsingException(parser.getTokenLocation(), "cannot parse array with name [{}]", topLevelFieldName);
}
} else if (token.isValue()) {
if (parseContext.parseFieldMatcher().match(topLevelFieldName, ORDER_FIELD)) {
highlightBuilder.order(parser.text());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, TAGS_SCHEMA_FIELD)) {
highlightBuilder.tagsSchema(parser.text());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, HIGHLIGHT_FILTER_FIELD)) {
highlightBuilder.highlightFilter(parser.booleanValue());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, FRAGMENT_SIZE_FIELD)) {
highlightBuilder.fragmentSize(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, NUMBER_OF_FRAGMENTS_FIELD)) {
highlightBuilder.numOfFragments(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, ENCODER_FIELD)) {
highlightBuilder.encoder(parser.text());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, REQUIRE_FIELD_MATCH_FIELD)) {
highlightBuilder.requireFieldMatch(parser.booleanValue());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, BOUNDARY_MAX_SCAN_FIELD)) {
highlightBuilder.boundaryMaxScan(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, BOUNDARY_CHARS_FIELD)) {
highlightBuilder.boundaryChars(parser.text().toCharArray());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, TYPE_FIELD)) {
highlightBuilder.highlighterType(parser.text());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, FRAGMENTER_FIELD)) {
highlightBuilder.fragmenter(parser.text());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, NO_MATCH_SIZE_FIELD)) {
highlightBuilder.noMatchSize(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, FORCE_SOURCE_FIELD)) {
highlightBuilder.forceSource(parser.booleanValue());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, PHRASE_LIMIT_FIELD)) {
highlightBuilder.phraseLimit(parser.intValue());
} else {
throw new ParsingException(parser.getTokenLocation(), "unexpected fieldname [{}]", topLevelFieldName);
}
} else if (token == XContentParser.Token.START_OBJECT && topLevelFieldName != null) {
if (parseContext.parseFieldMatcher().match(topLevelFieldName, OPTIONS_FIELD)) {
highlightBuilder.options(parser.map());
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, FIELDS_FIELD)) {
String highlightFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
highlightFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_OBJECT) {
highlightBuilder.field(Field.fromXContent(highlightFieldName, parseContext));
}
}
} else if (parseContext.parseFieldMatcher().match(topLevelFieldName, HIGHLIGHT_QUERY_FIELD)) {
highlightBuilder.highlightQuery(parseContext.parseInnerQueryBuilder());
} else {
throw new ParsingException(parser.getTokenLocation(), "cannot parse object with name [{}]", topLevelFieldName);
}
} else if (topLevelFieldName != null) {
throw new ParsingException(parser.getTokenLocation(), "unexpected token [{}] after [{}]", token, topLevelFieldName);
}
}
if (highlightBuilder.preTags() != null && highlightBuilder.postTags() == null) {
throw new ParsingException(parser.getTokenLocation(), "Highlighter global preTags are set, but global postTags are not set");
}
return highlightBuilder;
}
public void innerXContent(XContentBuilder builder) throws IOException { public void innerXContent(XContentBuilder builder) throws IOException {
// first write common options // first write common options
commonOptionsToXContent(builder); commonOptionsToXContent(builder);
// special options for top-level highlighter // special options for top-level highlighter
if (encoder != null) { if (encoder != null) {
builder.field("encoder", encoder); builder.field(ENCODER_FIELD.getPreferredName(), encoder);
} }
if (fields.size() > 0) { if (fields.size() > 0) {
if (useExplicitFieldOrder) { if (useExplicitFieldOrder) {
builder.startArray("fields"); builder.startArray(FIELDS_FIELD.getPreferredName());
} else { } else {
builder.startObject("fields"); builder.startObject(FIELDS_FIELD.getPreferredName());
} }
for (Field field : fields) { for (Field field : fields) {
if (useExplicitFieldOrder) { if (useExplicitFieldOrder) {
@ -205,7 +326,7 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
try { try {
XContentBuilder builder = XContentFactory.jsonBuilder(); XContentBuilder builder = XContentFactory.jsonBuilder();
builder.prettyPrint(); builder.prettyPrint();
toXContent(builder, ToXContent.EMPTY_PARAMS); toXContent(builder, EMPTY_PARAMS);
return builder.string(); return builder.string();
} catch (Exception e) { } catch (Exception e) {
return "{ \"error\" : \"" + ExceptionsHelper.detailedMessage(e) + "\"}"; return "{ \"error\" : \"" + ExceptionsHelper.detailedMessage(e) + "\"}";
@ -286,14 +407,90 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
commonOptionsToXContent(builder); commonOptionsToXContent(builder);
// write special field-highlighter options // write special field-highlighter options
if (fragmentOffset != -1) { if (fragmentOffset != -1) {
builder.field("fragment_offset", fragmentOffset); builder.field(FRAGMENT_OFFSET_FIELD.getPreferredName(), fragmentOffset);
} }
if (matchedFields != null) { if (matchedFields != null) {
builder.field("matched_fields", matchedFields); builder.field(MATCHED_FIELDS_FIELD.getPreferredName(), matchedFields);
} }
builder.endObject(); builder.endObject();
} }
private static HighlightBuilder.Field fromXContent(String fieldname, QueryParseContext parseContext) throws IOException {
XContentParser parser = parseContext.parser();
XContentParser.Token token;
final HighlightBuilder.Field field = new HighlightBuilder.Field(fieldname);
String currentFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName();
} else if (token == XContentParser.Token.START_ARRAY) {
if (parseContext.parseFieldMatcher().match(currentFieldName, PRE_TAGS_FIELD)) {
List<String> preTagsList = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
preTagsList.add(parser.text());
}
field.preTags(preTagsList.toArray(new String[preTagsList.size()]));
} else if (parseContext.parseFieldMatcher().match(currentFieldName, POST_TAGS_FIELD)) {
List<String> postTagsList = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
postTagsList.add(parser.text());
}
field.postTags(postTagsList.toArray(new String[postTagsList.size()]));
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MATCHED_FIELDS_FIELD)) {
List<String> matchedFields = new ArrayList<>();
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
matchedFields.add(parser.text());
}
field.matchedFields(matchedFields.toArray(new String[matchedFields.size()]));
} else {
throw new ParsingException(parser.getTokenLocation(), "cannot parse array with name [{}]", currentFieldName);
}
} else if (token.isValue()) {
if (parseContext.parseFieldMatcher().match(currentFieldName, FRAGMENT_SIZE_FIELD)) {
field.fragmentSize(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NUMBER_OF_FRAGMENTS_FIELD)) {
field.numOfFragments(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FRAGMENT_OFFSET_FIELD)) {
field.fragmentOffset(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, HIGHLIGHT_FILTER_FIELD)) {
field.highlightFilter(parser.booleanValue());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ORDER_FIELD)) {
field.order(parser.text());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, REQUIRE_FIELD_MATCH_FIELD)) {
field.requireFieldMatch(parser.booleanValue());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, BOUNDARY_MAX_SCAN_FIELD)) {
field.boundaryMaxScan(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, BOUNDARY_CHARS_FIELD)) {
field.boundaryChars(parser.text().toCharArray());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
field.highlighterType(parser.text());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FRAGMENTER_FIELD)) {
field.fragmenter(parser.text());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NO_MATCH_SIZE_FIELD)) {
field.noMatchSize(parser.intValue());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FORCE_SOURCE_FIELD)) {
field.forceSource(parser.booleanValue());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, PHRASE_LIMIT_FIELD)) {
field.phraseLimit(parser.intValue());
} else {
throw new ParsingException(parser.getTokenLocation(), "unexpected fieldname [{}]", currentFieldName);
}
} else if (token == XContentParser.Token.START_OBJECT && currentFieldName != null) {
if (parseContext.parseFieldMatcher().match(currentFieldName, HIGHLIGHT_QUERY_FIELD)) {
field.highlightQuery(parseContext.parseInnerQueryBuilder());
} else if (parseContext.parseFieldMatcher().match(currentFieldName, OPTIONS_FIELD)) {
field.options(parser.map());
} else {
throw new ParsingException(parser.getTokenLocation(), "cannot parse object with name [{}]", currentFieldName);
}
} else if (currentFieldName != null) {
throw new ParsingException(parser.getTokenLocation(), "unexpected token [{}] after [{}]", token, currentFieldName);
}
}
return field;
}
@Override @Override
protected int doHashCode() { protected int doHashCode() {
return Objects.hash(name, fragmentOffset, Arrays.hashCode(matchedFields)); return Objects.hash(name, fragmentOffset, Arrays.hashCode(matchedFields));

View File

@ -211,7 +211,7 @@ public class HighlighterParseElement implements SearchParseElement {
return new SearchContextHighlight(fields); return new SearchContextHighlight(fields);
} }
protected SearchContextHighlight.FieldOptions.Builder parseFields(XContentParser parser, QueryShardContext queryShardContext) throws IOException { private static SearchContextHighlight.FieldOptions.Builder parseFields(XContentParser parser, QueryShardContext queryShardContext) throws IOException {
XContentParser.Token token; XContentParser.Token token;
final SearchContextHighlight.FieldOptions.Builder fieldOptionsBuilder = new SearchContextHighlight.FieldOptions.Builder(); final SearchContextHighlight.FieldOptions.Builder fieldOptionsBuilder = new SearchContextHighlight.FieldOptions.Builder();

View File

@ -19,14 +19,29 @@
package org.elasticsearch.search.highlight; package org.elasticsearch.search.highlight;
import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException;
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;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.IdsQueryBuilder; import org.elasticsearch.index.query.IdsQueryBuilder;
import org.elasticsearch.index.query.IdsQueryParser;
import org.elasticsearch.index.query.MatchAllQueryBuilder; import org.elasticsearch.index.query.MatchAllQueryBuilder;
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.QueryParser;
import org.elasticsearch.index.query.TermQueryBuilder; import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.query.TermQueryParser;
import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.search.highlight.HighlightBuilder.Field; import org.elasticsearch.search.highlight.HighlightBuilder.Field;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import org.junit.AfterClass; import org.junit.AfterClass;
@ -35,8 +50,10 @@ import org.junit.BeforeClass;
import java.io.IOException; 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.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
@ -45,23 +62,26 @@ public class HighlightBuilderTests extends ESTestCase {
private static final int NUMBER_OF_TESTBUILDERS = 20; private static final int NUMBER_OF_TESTBUILDERS = 20;
private static NamedWriteableRegistry namedWriteableRegistry; private static NamedWriteableRegistry namedWriteableRegistry;
private static IndicesQueriesRegistry indicesQueriesRegistry;
/** /**
* setup for the whole base test class * setup for the whole base test class
*/ */
@BeforeClass @BeforeClass
public static void init() { public static void init() {
if (namedWriteableRegistry == null) {
namedWriteableRegistry = new NamedWriteableRegistry(); namedWriteableRegistry = new NamedWriteableRegistry();
namedWriteableRegistry.registerPrototype(QueryBuilder.class, new MatchAllQueryBuilder()); @SuppressWarnings("rawtypes")
namedWriteableRegistry.registerPrototype(QueryBuilder.class, new IdsQueryBuilder()); Set<QueryParser> injectedQueryParsers = new HashSet<>();
namedWriteableRegistry.registerPrototype(QueryBuilder.class, new TermQueryBuilder("field", "value")); injectedQueryParsers.add(new MatchAllQueryParser());
} injectedQueryParsers.add(new IdsQueryParser());
injectedQueryParsers.add(new TermQueryParser());
indicesQueriesRegistry = new IndicesQueriesRegistry(Settings.settingsBuilder().build(), injectedQueryParsers, namedWriteableRegistry);
} }
@AfterClass @AfterClass
public static void afterClass() throws Exception { public static void afterClass() throws Exception {
namedWriteableRegistry = null; namedWriteableRegistry = null;
indicesQueriesRegistry = null;
} }
/** /**
@ -107,6 +127,196 @@ public class HighlightBuilderTests extends ESTestCase {
} }
} }
/**
* Generic test that creates new highlighter from the test highlighter and checks both for equality
*/
public void testFromXContent() throws IOException {
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry);
context.parseFieldMatcher(new ParseFieldMatcher(Settings.EMPTY));
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
HighlightBuilder highlightBuilder = randomHighlighterBuilder();
XContentBuilder builder = XContentFactory.contentBuilder(randomFrom(XContentType.values()));
if (randomBoolean()) {
builder.prettyPrint();
}
builder.startObject();
highlightBuilder.innerXContent(builder);
builder.endObject();
XContentParser parser = XContentHelper.createParser(builder.bytes());
context.reset(parser);
HighlightBuilder secondHighlightBuilder = HighlightBuilder.fromXContent(context);
assertNotSame(highlightBuilder, secondHighlightBuilder);
assertEquals(highlightBuilder, secondHighlightBuilder);
assertEquals(highlightBuilder.hashCode(), secondHighlightBuilder.hashCode());
}
}
/**
* test that unknown array fields cause exception
*/
public void testUnknownArrayNameExpection() throws IOException {
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry);
context.parseFieldMatcher(new ParseFieldMatcher(Settings.EMPTY));
String highlightElement = "{\n" +
" \"bad_fieldname\" : [ \"field1\" 1 \"field2\" ]\n" +
"}\n";
XContentParser parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
try {
HighlightBuilder.fromXContent(context);
fail("expected a parsing exception");
} catch (ParsingException e) {
assertEquals("cannot parse array with name [bad_fieldname]", e.getMessage());
}
highlightElement = "{\n" +
" \"fields\" : {\n" +
" \"body\" : {\n" +
" \"bad_fieldname\" : [ \"field1\" , \"field2\" ]\n" +
" }\n" +
" }\n" +
"}\n";
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
try {
HighlightBuilder.fromXContent(context);
fail("expected a parsing exception");
} catch (ParsingException e) {
assertEquals("cannot parse array with name [bad_fieldname]", e.getMessage());
}
}
/**
* test that unknown field name cause exception
*/
public void testUnknownFieldnameExpection() throws IOException {
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry);
context.parseFieldMatcher(new ParseFieldMatcher(Settings.EMPTY));
String highlightElement = "{\n" +
" \"bad_fieldname\" : \"value\"\n" +
"}\n";
XContentParser parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
try {
HighlightBuilder.fromXContent(context);
fail("expected a parsing exception");
} catch (ParsingException e) {
assertEquals("unexpected fieldname [bad_fieldname]", e.getMessage());
}
highlightElement = "{\n" +
" \"fields\" : {\n" +
" \"body\" : {\n" +
" \"bad_fieldname\" : \"value\"\n" +
" }\n" +
" }\n" +
"}\n";
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
try {
HighlightBuilder.fromXContent(context);
fail("expected a parsing exception");
} catch (ParsingException e) {
assertEquals("unexpected fieldname [bad_fieldname]", e.getMessage());
}
}
/**
* test that unknown field name cause exception
*/
public void testUnknownObjectFieldnameExpection() throws IOException {
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry);
context.parseFieldMatcher(new ParseFieldMatcher(Settings.EMPTY));
String highlightElement = "{\n" +
" \"bad_fieldname\" : { \"field\" : \"value\" }\n \n" +
"}\n";
XContentParser parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
try {
HighlightBuilder.fromXContent(context);
fail("expected a parsing exception");
} catch (ParsingException e) {
assertEquals("cannot parse object with name [bad_fieldname]", e.getMessage());
}
highlightElement = "{\n" +
" \"fields\" : {\n" +
" \"body\" : {\n" +
" \"bad_fieldname\" : { \"field\" : \"value\" }\n" +
" }\n" +
" }\n" +
"}\n";
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
try {
HighlightBuilder.fromXContent(context);
fail("expected a parsing exception");
} catch (ParsingException e) {
assertEquals("cannot parse object with name [bad_fieldname]", e.getMessage());
}
}
/**
* `tags_schema` is not produced by toXContent in the builder but should be parseable, so this
* adds a simple json test for this.
*/
public void testParsingTagsSchema() throws IOException {
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry);
context.parseFieldMatcher(new ParseFieldMatcher(Settings.EMPTY));
String highlightElement = "{\n" +
" \"tags_schema\" : \"styled\"\n" +
"}\n";
XContentParser parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
HighlightBuilder highlightBuilder = HighlightBuilder.fromXContent(context);
assertArrayEquals("setting tags_schema 'styled' should alter pre_tags", HighlighterParseElement.STYLED_PRE_TAG,
highlightBuilder.preTags());
assertArrayEquals("setting tags_schema 'styled' should alter post_tags", HighlighterParseElement.STYLED_POST_TAGS,
highlightBuilder.postTags());
highlightElement = "{\n" +
" \"tags_schema\" : \"default\"\n" +
"}\n";
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
highlightBuilder = HighlightBuilder.fromXContent(context);
assertArrayEquals("setting tags_schema 'default' should alter pre_tags", HighlighterParseElement.DEFAULT_PRE_TAGS,
highlightBuilder.preTags());
assertArrayEquals("setting tags_schema 'default' should alter post_tags", HighlighterParseElement.DEFAULT_POST_TAGS,
highlightBuilder.postTags());
highlightElement = "{\n" +
" \"tags_schema\" : \"somthing_else\"\n" +
"}\n";
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
context.reset(parser);
try {
highlightBuilder = HighlightBuilder.fromXContent(context);
fail("setting unknown tag schema should throw exception");
} catch (IllegalArgumentException e) {
assertEquals("Unknown tag schema [somthing_else]", e.getMessage());
}
}
protected static XContentBuilder toXContent(HighlightBuilder highlight, XContentType contentType) throws IOException {
XContentBuilder builder = XContentFactory.contentBuilder(contentType);
if (randomBoolean()) {
builder.prettyPrint();
}
highlight.toXContent(builder, ToXContent.EMPTY_PARAMS);
return builder;
}
/** /**
* create random shape that is put under test * create random shape that is put under test
*/ */
@ -132,11 +342,11 @@ public class HighlightBuilderTests extends ESTestCase {
return testHighlighter; return testHighlighter;
} }
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void setRandomCommonOptions(AbstractHighlighterBuilder highlightBuilder) { private static void setRandomCommonOptions(AbstractHighlighterBuilder highlightBuilder) {
if (randomBoolean()) { if (randomBoolean()) {
// need to set this together, otherwise parsing will complain
highlightBuilder.preTags(randomStringArray(0, 3)); highlightBuilder.preTags(randomStringArray(0, 3));
}
if (randomBoolean()) {
highlightBuilder.postTags(randomStringArray(0, 3)); highlightBuilder.postTags(randomStringArray(0, 3));
} }
if (randomBoolean()) { if (randomBoolean()) {
@ -213,7 +423,7 @@ public class HighlightBuilderTests extends ESTestCase {
} }
} }
@SuppressWarnings("unchecked") @SuppressWarnings({ "unchecked", "rawtypes" })
private static void mutateCommonOptions(AbstractHighlighterBuilder highlightBuilder) { private static void mutateCommonOptions(AbstractHighlighterBuilder highlightBuilder) {
switch (randomIntBetween(1, 16)) { switch (randomIntBetween(1, 16)) {
case 1: case 1:
@ -242,6 +452,7 @@ public class HighlightBuilderTests extends ESTestCase {
break; break;
case 9: case 9:
highlightBuilder.highlightFilter(toggleOrSet(highlightBuilder.highlightFilter())); highlightBuilder.highlightFilter(toggleOrSet(highlightBuilder.highlightFilter()));
break;
case 10: case 10:
highlightBuilder.forceSource(toggleOrSet(highlightBuilder.forceSource())); highlightBuilder.forceSource(toggleOrSet(highlightBuilder.forceSource()));
break; break;
@ -316,6 +527,7 @@ public class HighlightBuilderTests extends ESTestCase {
fieldToChange.matchedFields(randomStringArray(5, 10)); fieldToChange.matchedFields(randomStringArray(5, 10));
} }
} }
break;
} }
} }
return mutation; return mutation;