Use ObjectParser in highlighting
This commit is contained in:
parent
6f0581c67c
commit
c7780e6e0a
|
@ -373,7 +373,6 @@
|
|||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]util[/\\]concurrent[/\\]PrioritizedEsThreadPoolExecutor.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]util[/\\]concurrent[/\\]ThreadBarrier.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]util[/\\]concurrent[/\\]ThreadContext.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]xcontent[/\\]ObjectParser.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]xcontent[/\\]XContentBuilder.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]xcontent[/\\]XContentFactory.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]xcontent[/\\]XContentHelper.java" checks="LineLength" />
|
||||
|
@ -828,9 +827,7 @@
|
|||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]fetch[/\\]innerhits[/\\]InnerHitsParseElement.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]fetch[/\\]script[/\\]ScriptFieldsParseElement.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]fetch[/\\]source[/\\]FetchSourceContext.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]highlight[/\\]AbstractHighlighterBuilder.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]highlight[/\\]FastVectorHighlighter.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]highlight[/\\]HighlightBuilder.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]highlight[/\\]HighlightPhase.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]highlight[/\\]HighlightUtils.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]highlight[/\\]HighlighterParseElement.java" checks="LineLength" />
|
||||
|
@ -1051,7 +1048,6 @@
|
|||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]util[/\\]LongObjectHashMapTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]util[/\\]concurrent[/\\]EsExecutorsTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]util[/\\]concurrent[/\\]PrioritizedExecutorsTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]xcontent[/\\]ObjectParserTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]xcontent[/\\]XContentFactoryTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]xcontent[/\\]builder[/\\]XContentBuilderTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]xcontent[/\\]cbor[/\\]JsonVsCborTests.java" checks="LineLength" />
|
||||
|
@ -1347,7 +1343,6 @@
|
|||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]geo[/\\]GeoBoundingBoxIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]geo[/\\]GeoFilterIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]geo[/\\]GeoShapeQueryTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]highlight[/\\]HighlightBuilderTests.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]highlight[/\\]HighlighterSearchIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]innerhits[/\\]InnerHitsIT.java" checks="LineLength" />
|
||||
<suppress files="core[/\\]src[/\\]test[/\\]java[/\\]org[/\\]elasticsearch[/\\]search[/\\]matchedqueries[/\\]MatchedQueriesIT.java" checks="LineLength" />
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.elasticsearch.common.ParseFieldMatcher;
|
|||
import org.elasticsearch.common.ParsingException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
|
@ -31,17 +32,37 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.XContentParser.Token.START_ARRAY;
|
||||
import static org.elasticsearch.common.xcontent.XContentParser.Token.START_OBJECT;
|
||||
import static org.elasticsearch.common.xcontent.XContentParser.Token.VALUE_BOOLEAN;
|
||||
import static org.elasticsearch.common.xcontent.XContentParser.Token.VALUE_EMBEDDED_OBJECT;
|
||||
import static org.elasticsearch.common.xcontent.XContentParser.Token.VALUE_NULL;
|
||||
import static org.elasticsearch.common.xcontent.XContentParser.Token.VALUE_NUMBER;
|
||||
import static org.elasticsearch.common.xcontent.XContentParser.Token.VALUE_STRING;
|
||||
|
||||
/**
|
||||
* A declarative Object parser to parse any kind of XContent structures into existing object with setters.
|
||||
* The Parser is designed to be declarative and stateless. A single parser is defined for one object level, nested
|
||||
* elements can be added via {@link #declareObject(BiConsumer, BiFunction, ParseField)} which is commonly done by
|
||||
* declaring yet another instance of {@link ObjectParser}. Instances of {@link ObjectParser} are thread-safe and can be
|
||||
* re-used across parsing operations. It's recommended to use the high level declare methods like {@link #declareString(BiConsumer, ParseField)}
|
||||
* instead of {@link #declareField} which can be used to implement exceptional parsing operations not covered by the high level methods.
|
||||
* A declarative Object parser to parse any kind of XContent structures into existing object with setters. The Parser is designed to be
|
||||
* declarative and stateless. A single parser is defined for one object level, nested elements can be added via
|
||||
* {@link #declareObject(BiConsumer, BiFunction, ParseField)} which is commonly done by declaring yet another instance of
|
||||
* {@link ObjectParser}. Instances of {@link ObjectParser} are thread-safe and can be re-used across parsing operations. It's recommended to
|
||||
* use the high level declare methods like {@link #declareString(BiConsumer, ParseField)} instead of {@link #declareField} which can be used
|
||||
* to implement exceptional parsing operations not covered by the high level methods.
|
||||
*/
|
||||
public final class ObjectParser<Value, Context> implements BiFunction<XContentParser, Context, Value> {
|
||||
/**
|
||||
* Adapts an array (or varags) setter into a list setter.
|
||||
*/
|
||||
public static <Value, ElementValue> BiConsumer<Value, List<ElementValue>> fromList(Class<ElementValue> c,
|
||||
BiConsumer<Value, ElementValue[]> consumer) {
|
||||
return (Value v, List<ElementValue> l) -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
ElementValue[] array = (ElementValue[]) Array.newInstance(c, l.size());
|
||||
consumer.accept(v, l.toArray(array));
|
||||
};
|
||||
}
|
||||
|
||||
private final String name;
|
||||
private final Supplier<Value> valueSupplier;
|
||||
|
@ -125,12 +146,14 @@ public final class ObjectParser<Value, Context> implements BiFunction<XContentPa
|
|||
return value;
|
||||
}
|
||||
|
||||
private void parseArray(XContentParser parser, FieldParser<Value> fieldParser, String currentFieldName, Value value, Context context) throws IOException {
|
||||
private void parseArray(XContentParser parser, FieldParser<Value> fieldParser, String currentFieldName, Value value, Context context)
|
||||
throws IOException {
|
||||
assert parser.currentToken() == XContentParser.Token.START_ARRAY : "Token was: " + parser.currentToken();
|
||||
parseValue(parser, fieldParser, currentFieldName, value, context);
|
||||
}
|
||||
|
||||
private void parseValue(XContentParser parser, FieldParser<Value> fieldParser, String currentFieldName, Value value, Context context) throws IOException {
|
||||
private void parseValue(XContentParser parser, FieldParser<Value> fieldParser, String currentFieldName, Value value, Context context)
|
||||
throws IOException {
|
||||
try {
|
||||
fieldParser.parser.parse(parser, value, context);
|
||||
} catch (Exception ex) {
|
||||
|
@ -138,7 +161,8 @@ public final class ObjectParser<Value, Context> implements BiFunction<XContentPa
|
|||
}
|
||||
}
|
||||
|
||||
private void parseSub(XContentParser parser, FieldParser<Value> fieldParser, String currentFieldName, Value value, Context context) throws IOException {
|
||||
private void parseSub(XContentParser parser, FieldParser<Value> fieldParser, String currentFieldName, Value value, Context context)
|
||||
throws IOException {
|
||||
final XContentParser.Token token = parser.currentToken();
|
||||
switch (token) {
|
||||
case START_OBJECT:
|
||||
|
@ -237,12 +261,14 @@ public final class ObjectParser<Value, Context> implements BiFunction<XContentPa
|
|||
declareField((p, v, c) -> consumer.accept(v, objectParser.apply(p, c)), field, ValueType.OBJECT);
|
||||
}
|
||||
|
||||
public <T> void declareObjectArray(BiConsumer<Value, List<T>> consumer, BiFunction<XContentParser, Context, T> objectParser, ParseField field) {
|
||||
public <T> void declareObjectArray(BiConsumer<Value, List<T>> consumer, BiFunction<XContentParser, Context, T> objectParser,
|
||||
ParseField field) {
|
||||
declareField((p, v, c) -> consumer.accept(v, parseArray(p, () -> objectParser.apply(p, c))), field, ValueType.OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
public <T> void declareObjectOrDefault(BiConsumer<Value, T> consumer, BiFunction<XContentParser, Context, T> objectParser, Supplier<T> defaultValue, ParseField field) {
|
||||
public <T> void declareObjectOrDefault(BiConsumer<Value, T> consumer, BiFunction<XContentParser, Context, T> objectParser,
|
||||
Supplier<T> defaultValue, ParseField field) {
|
||||
declareField((p, v, c) -> {
|
||||
if (p.currentToken() == XContentParser.Token.VALUE_BOOLEAN) {
|
||||
if (p.booleanValue()) {
|
||||
|
@ -251,7 +277,7 @@ public final class ObjectParser<Value, Context> implements BiFunction<XContentPa
|
|||
} else {
|
||||
consumer.accept(v, objectParser.apply(p, c));
|
||||
}
|
||||
}, field, ValueType.OBJECT_OR_BOOLEAN);
|
||||
} , field, ValueType.OBJECT_OR_BOOLEAN);
|
||||
}
|
||||
|
||||
|
||||
|
@ -280,13 +306,135 @@ public final class ObjectParser<Value, Context> implements BiFunction<XContentPa
|
|||
}
|
||||
|
||||
public void declareStringOrNull(BiConsumer<Value, String> consumer, ParseField field) {
|
||||
declareField((p, v, c) -> consumer.accept(v, p.currentToken() == XContentParser.Token.VALUE_NULL ? null : p.text()), field, ValueType.STRING_OR_NULL);
|
||||
declareField((p, v, c) -> consumer.accept(v, p.currentToken() == XContentParser.Token.VALUE_NULL ? null : p.text()), field,
|
||||
ValueType.STRING_OR_NULL);
|
||||
}
|
||||
|
||||
public void declareBoolean(BiConsumer<Value, Boolean> consumer, ParseField field) {
|
||||
declareField((p, v, c) -> consumer.accept(v, p.booleanValue()), field, ValueType.BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares named objects in the style of highlighting's field element. These are usually named inside and object like this:
|
||||
* <pre><code>
|
||||
* {
|
||||
* "highlight": {
|
||||
* "fields": { <------ this one
|
||||
* "title": {},
|
||||
* "body": {},
|
||||
* "category": {}
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </code></pre>
|
||||
* but, when order is important, some may be written this way:
|
||||
* <pre><code>
|
||||
* {
|
||||
* "highlight": {
|
||||
* "fields": [ <------ this one
|
||||
* {"title": {}},
|
||||
* {"body": {}},
|
||||
* {"category": {}}
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* </code></pre>
|
||||
* This is because json doesn't enforce ordering. Elasticsearch reads it in the order sent but tools that generate json are free to put
|
||||
* object members in an unordered Map, jumbling them. Thus, if you care about order you can send the object in the second way.
|
||||
*
|
||||
* See NamedObjectHolder in ObjectParserTests for examples of how to invoke this.
|
||||
*
|
||||
* @param consumer sets the values once they have been parsed
|
||||
* @param namedObjectParser parses each named object
|
||||
* @param orderedModeCallback called when the named object is parsed using the "ordered" mode (the array of objects)
|
||||
* @param field the field to parse
|
||||
*/
|
||||
public <T> void declareNamedObjects(BiConsumer<Value, List<T>> consumer, NamedObjectParser<T, Context> namedObjectParser,
|
||||
Consumer<Value> orderedModeCallback, ParseField field) {
|
||||
// This creates and parses the named object
|
||||
BiFunction<XContentParser, Context, T> objectParser = (XContentParser p, Context c) -> {
|
||||
if (p.currentToken() != XContentParser.Token.FIELD_NAME) {
|
||||
throw new ParsingException(p.getTokenLocation(), "[" + field + "] can be a single object with any number of "
|
||||
+ "fields or an array where each entry is an object with a single field");
|
||||
}
|
||||
// This messy exception nesting has the nice side effect of telling the use which field failed to parse
|
||||
try {
|
||||
String name = p.currentName();
|
||||
try {
|
||||
return namedObjectParser.parse(p, c, name);
|
||||
} catch (Exception e) {
|
||||
throw new ParsingException(p.getTokenLocation(), "[" + field + "] failed to parse field [" + name + "]", e);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new ParsingException(p.getTokenLocation(), "[" + field + "] error while parsing", e);
|
||||
}
|
||||
};
|
||||
declareField((XContentParser p, Value v, Context c) -> {
|
||||
List<T> fields = new ArrayList<>();
|
||||
XContentParser.Token token;
|
||||
if (p.currentToken() == XContentParser.Token.START_OBJECT) {
|
||||
// Fields are just named entries in a single object
|
||||
while ((token = p.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
fields.add(objectParser.apply(p, c));
|
||||
}
|
||||
} else if (p.currentToken() == XContentParser.Token.START_ARRAY) {
|
||||
// Fields are objects in an array. Each object contains a named field.
|
||||
orderedModeCallback.accept(v);
|
||||
while ((token = p.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
if (token != XContentParser.Token.START_OBJECT) {
|
||||
throw new ParsingException(p.getTokenLocation(), "[" + field + "] can be a single object with any number of "
|
||||
+ "fields or an array where each entry is an object with a single field");
|
||||
}
|
||||
p.nextToken(); // Move to the first field in the object
|
||||
fields.add(objectParser.apply(p, c));
|
||||
p.nextToken(); // Move past the object, should be back to into the array
|
||||
if (p.currentToken() != XContentParser.Token.END_OBJECT) {
|
||||
throw new ParsingException(p.getTokenLocation(), "[" + field + "] can be a single object with any number of "
|
||||
+ "fields or an array where each entry is an object with a single field");
|
||||
}
|
||||
}
|
||||
}
|
||||
consumer.accept(v, fields);
|
||||
}, field, ValueType.OBJECT_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares named objects in the style of aggregations. These are named inside and object like this:
|
||||
* <pre><code>
|
||||
* {
|
||||
* "aggregations": {
|
||||
* "name_1": { "aggregation_type": {} },
|
||||
* "name_2": { "aggregation_type": {} },
|
||||
* "name_3": { "aggregation_type": {} }
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </code></pre>
|
||||
* Unlike the other version of this method, "ordered" mode (arrays of objects) is not supported.
|
||||
*
|
||||
* See NamedObjectHolder in ObjectParserTests for examples of how to invoke this.
|
||||
*
|
||||
* @param consumer sets the values once they have been parsed
|
||||
* @param namedObjectParser parses each named object
|
||||
* @param field the field to parse
|
||||
*/
|
||||
public <T> void declareNamedObjects(BiConsumer<Value, List<T>> consumer, NamedObjectParser<T, Context> namedObjectParser,
|
||||
ParseField field) {
|
||||
Consumer<Value> orderedModeCallback = (Value v) -> {
|
||||
throw new IllegalArgumentException("[" + field + "] doesn't support arrays. Use a single object with multiple fields.");
|
||||
};
|
||||
declareNamedObjects(consumer, namedObjectParser, orderedModeCallback, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface for instantiating and parsing named objects. See ObjectParserTests#NamedObject for the canonical way to
|
||||
* implement this for objects that themselves have a parser.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface NamedObjectParser<T, Context> {
|
||||
T parse(XContentParser p, Context c, String name) throws IOException;
|
||||
}
|
||||
|
||||
public static class FieldParser<T> {
|
||||
private final Parser parser;
|
||||
private final EnumSet<XContentParser.Token> supportedTokens;
|
||||
|
@ -305,7 +453,8 @@ public final class ObjectParser<Value, Context> implements BiFunction<XContentPa
|
|||
throw new IllegalStateException("[" + parserName + "] parsefield doesn't accept: " + currentFieldName);
|
||||
}
|
||||
if (supportedTokens.contains(token) == false) {
|
||||
throw new IllegalArgumentException("[" + parserName + "] " + currentFieldName + " doesn't support values of type: " + token);
|
||||
throw new IllegalArgumentException(
|
||||
"[" + parserName + "] " + currentFieldName + " doesn't support values of type: " + token);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,10 +462,14 @@ public final class ObjectParser<Value, Context> implements BiFunction<XContentPa
|
|||
public String toString() {
|
||||
String[] deprecatedNames = parseField.getDeprecatedNames();
|
||||
String allReplacedWith = parseField.getAllReplacedWith();
|
||||
String deprecated = "";
|
||||
if (deprecatedNames != null && deprecatedNames.length > 0) {
|
||||
deprecated = ", deprecated_names=" + Arrays.toString(deprecatedNames);
|
||||
}
|
||||
return "FieldParser{" +
|
||||
"preferred_name=" + parseField.getPreferredName() +
|
||||
", supportedTokens=" + supportedTokens +
|
||||
(deprecatedNames == null || deprecatedNames.length == 0 ? "" : ", deprecated_names=" + Arrays.toString(deprecatedNames )) +
|
||||
deprecated +
|
||||
(allReplacedWith == null ? "" : ", replaced_with=" + allReplacedWith) +
|
||||
", type=" + type.name() +
|
||||
'}';
|
||||
|
@ -325,27 +478,28 @@ public final class ObjectParser<Value, Context> implements BiFunction<XContentPa
|
|||
}
|
||||
|
||||
public enum ValueType {
|
||||
STRING(EnumSet.of(XContentParser.Token.VALUE_STRING)),
|
||||
STRING_OR_NULL(EnumSet.of(XContentParser.Token.VALUE_STRING, XContentParser.Token.VALUE_NULL)),
|
||||
FLOAT(EnumSet.of(XContentParser.Token.VALUE_NUMBER, XContentParser.Token.VALUE_STRING)),
|
||||
DOUBLE(EnumSet.of(XContentParser.Token.VALUE_NUMBER, XContentParser.Token.VALUE_STRING)),
|
||||
LONG(EnumSet.of(XContentParser.Token.VALUE_NUMBER, XContentParser.Token.VALUE_STRING)),
|
||||
INT(EnumSet.of(XContentParser.Token.VALUE_NUMBER, XContentParser.Token.VALUE_STRING)),
|
||||
BOOLEAN(EnumSet.of(XContentParser.Token.VALUE_BOOLEAN)), STRING_ARRAY(EnumSet.of(XContentParser.Token.START_ARRAY, XContentParser.Token.VALUE_STRING)),
|
||||
FLOAT_ARRAY(EnumSet.of(XContentParser.Token.START_ARRAY, XContentParser.Token.VALUE_NUMBER, XContentParser.Token.VALUE_STRING)),
|
||||
DOUBLE_ARRAY(EnumSet.of(XContentParser.Token.START_ARRAY, XContentParser.Token.VALUE_NUMBER, XContentParser.Token.VALUE_STRING)),
|
||||
LONG_ARRAY(EnumSet.of(XContentParser.Token.START_ARRAY, XContentParser.Token.VALUE_NUMBER, XContentParser.Token.VALUE_STRING)),
|
||||
INT_ARRAY(EnumSet.of(XContentParser.Token.START_ARRAY, XContentParser.Token.VALUE_NUMBER, XContentParser.Token.VALUE_STRING)),
|
||||
BOOLEAN_ARRAY(EnumSet.of(XContentParser.Token.START_ARRAY, XContentParser.Token.VALUE_BOOLEAN)),
|
||||
OBJECT(EnumSet.of(XContentParser.Token.START_OBJECT)),
|
||||
OBJECT_ARRAY(EnumSet.of(XContentParser.Token.START_OBJECT, XContentParser.Token.START_ARRAY)),
|
||||
OBJECT_OR_BOOLEAN(EnumSet.of(XContentParser.Token.START_OBJECT, XContentParser.Token.VALUE_BOOLEAN)),
|
||||
VALUE(EnumSet.of(XContentParser.Token.VALUE_BOOLEAN, XContentParser.Token.VALUE_NULL ,XContentParser.Token.VALUE_EMBEDDED_OBJECT,XContentParser.Token.VALUE_NUMBER,XContentParser.Token.VALUE_STRING));
|
||||
STRING(VALUE_STRING),
|
||||
STRING_OR_NULL(VALUE_STRING, VALUE_NULL),
|
||||
FLOAT(VALUE_NUMBER, VALUE_STRING),
|
||||
DOUBLE(VALUE_NUMBER, VALUE_STRING),
|
||||
LONG(VALUE_NUMBER, VALUE_STRING),
|
||||
INT(VALUE_NUMBER, VALUE_STRING),
|
||||
BOOLEAN(VALUE_BOOLEAN),
|
||||
STRING_ARRAY(START_ARRAY, VALUE_STRING),
|
||||
FLOAT_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING),
|
||||
DOUBLE_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING),
|
||||
LONG_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING),
|
||||
INT_ARRAY(START_ARRAY, VALUE_NUMBER, VALUE_STRING),
|
||||
BOOLEAN_ARRAY(START_ARRAY, VALUE_BOOLEAN),
|
||||
OBJECT(START_OBJECT),
|
||||
OBJECT_ARRAY(START_OBJECT, START_ARRAY),
|
||||
OBJECT_OR_BOOLEAN(START_OBJECT, VALUE_BOOLEAN),
|
||||
VALUE(VALUE_BOOLEAN, VALUE_NULL, VALUE_EMBEDDED_OBJECT, VALUE_NUMBER, VALUE_STRING);
|
||||
|
||||
private final EnumSet<XContentParser.Token> tokens;
|
||||
|
||||
ValueType(EnumSet<XContentParser.Token> tokens) {
|
||||
this.tokens = tokens;
|
||||
ValueType(XContentParser.Token first, XContentParser.Token... rest) {
|
||||
this.tokens = EnumSet.of(first, rest);
|
||||
}
|
||||
|
||||
public EnumSet<XContentParser.Token> supportedTokens() {
|
||||
|
|
|
@ -98,13 +98,8 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
|
|||
throw new ParsingException(p.getTokenLocation(), "Could not parse inner _source definition", e);
|
||||
}
|
||||
}, SearchSourceBuilder._SOURCE_FIELD, ObjectParser.ValueType.OBJECT_OR_BOOLEAN);
|
||||
PARSER.declareObject(InnerHitBuilder::setHighlightBuilder, (p, c) -> {
|
||||
try {
|
||||
return HighlightBuilder.PROTOTYPE.fromXContent(c);
|
||||
} catch (IOException e) {
|
||||
throw new ParsingException(p.getTokenLocation(), "Could not parse inner highlight definition", e);
|
||||
}
|
||||
}, SearchSourceBuilder.HIGHLIGHT_FIELD);
|
||||
PARSER.declareObject(InnerHitBuilder::setHighlightBuilder, (p, c) -> HighlightBuilder.fromXContent(c),
|
||||
SearchSourceBuilder.HIGHLIGHT_FIELD);
|
||||
PARSER.declareObject(InnerHitBuilder::setQuery, (p, c) ->{
|
||||
try {
|
||||
return c.parseInnerQueryBuilder();
|
||||
|
|
|
@ -121,7 +121,7 @@ public class TopHitsParser implements Aggregator.Parser {
|
|||
}
|
||||
factory.scriptFields(scriptFields);
|
||||
} else if (context.parseFieldMatcher().match(currentFieldName, SearchSourceBuilder.HIGHLIGHT_FIELD)) {
|
||||
factory.highlighter(HighlightBuilder.PROTOTYPE.fromXContent(context));
|
||||
factory.highlighter(HighlightBuilder.fromXContent(context));
|
||||
} else if (context.parseFieldMatcher().match(currentFieldName, SearchSourceBuilder.SORT_FIELD)) {
|
||||
List<SortBuilder<?>> sorts = SortBuilder.fromXContent(context);
|
||||
factory.sorts(sorts);
|
||||
|
|
|
@ -863,7 +863,7 @@ public final class SearchSourceBuilder extends ToXContentToBytes implements Writ
|
|||
} else if (context.parseFieldMatcher().match(currentFieldName, AGGREGATIONS_FIELD)) {
|
||||
aggregations = aggParsers.parseAggregators(parser, context);
|
||||
} else if (context.parseFieldMatcher().match(currentFieldName, HIGHLIGHT_FIELD)) {
|
||||
highlightBuilder = HighlightBuilder.PROTOTYPE.fromXContent(context);
|
||||
highlightBuilder = HighlightBuilder.fromXContent(context);
|
||||
} else if (context.parseFieldMatcher().match(currentFieldName, INNER_HITS_FIELD)) {
|
||||
innerHitsBuilder = InnerHitsBuilder.fromXContent(parser, context);
|
||||
} else if (context.parseFieldMatcher().match(currentFieldName, SUGGEST_FIELD)) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.common.ParseField;
|
|||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
|
@ -33,11 +34,12 @@ import org.elasticsearch.index.query.QueryParseContext;
|
|||
import org.elasticsearch.search.highlight.HighlightBuilder.Order;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ObjectParser.fromList;
|
||||
|
||||
/**
|
||||
* This abstract class holds parameters shared by {@link HighlightBuilder} and {@link HighlightBuilder.Field}
|
||||
|
@ -49,7 +51,6 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
|
|||
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");
|
||||
|
@ -444,100 +445,50 @@ public abstract class AbstractHighlighterBuilder<HB extends AbstractHighlighterB
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link HighlightBuilder} from the highlighter held by the {@link QueryParseContext}
|
||||
* in {@link org.elasticsearch.common.xcontent.XContent} format
|
||||
*
|
||||
* @param parseContext containing the parser positioned at the structure to be parsed from.
|
||||
* the state on the parser contained in this context will be changed as a side effect of this
|
||||
* method call
|
||||
* @return the new {@link AbstractHighlighterBuilder}
|
||||
*/
|
||||
public HB fromXContent(QueryParseContext parseContext) throws IOException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
XContentParser.Token token = parser.currentToken();
|
||||
String currentFieldName = null;
|
||||
HB highlightBuilder = createInstance(parser);
|
||||
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());
|
||||
}
|
||||
highlightBuilder.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());
|
||||
}
|
||||
highlightBuilder.postTags(postTagsList.toArray(new String[postTagsList.size()]));
|
||||
} else if (false == highlightBuilder.doFromXContent(parseContext, currentFieldName, token)) {
|
||||
throw new ParsingException(parser.getTokenLocation(), "cannot parse array with name [{}]", currentFieldName);
|
||||
}
|
||||
} else if (token.isValue()) {
|
||||
if (parseContext.parseFieldMatcher().match(currentFieldName, ORDER_FIELD)) {
|
||||
highlightBuilder.order(Order.fromString(parser.text()));
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, HIGHLIGHT_FILTER_FIELD)) {
|
||||
highlightBuilder.highlightFilter(parser.booleanValue());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FRAGMENT_SIZE_FIELD)) {
|
||||
highlightBuilder.fragmentSize(parser.intValue());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NUMBER_OF_FRAGMENTS_FIELD)) {
|
||||
highlightBuilder.numOfFragments(parser.intValue());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, REQUIRE_FIELD_MATCH_FIELD)) {
|
||||
highlightBuilder.requireFieldMatch(parser.booleanValue());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, BOUNDARY_MAX_SCAN_FIELD)) {
|
||||
highlightBuilder.boundaryMaxScan(parser.intValue());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, BOUNDARY_CHARS_FIELD)) {
|
||||
highlightBuilder.boundaryChars(parser.text().toCharArray());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, TYPE_FIELD)) {
|
||||
highlightBuilder.highlighterType(parser.text());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FRAGMENTER_FIELD)) {
|
||||
highlightBuilder.fragmenter(parser.text());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, NO_MATCH_SIZE_FIELD)) {
|
||||
highlightBuilder.noMatchSize(parser.intValue());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, FORCE_SOURCE_FIELD)) {
|
||||
highlightBuilder.forceSource(parser.booleanValue());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, PHRASE_LIMIT_FIELD)) {
|
||||
highlightBuilder.phraseLimit(parser.intValue());
|
||||
} else if (false == highlightBuilder.doFromXContent(parseContext, currentFieldName, token)) {
|
||||
throw new ParsingException(parser.getTokenLocation(), "unexpected fieldname [{}]", currentFieldName);
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT && currentFieldName != null) {
|
||||
if (parseContext.parseFieldMatcher().match(currentFieldName, OPTIONS_FIELD)) {
|
||||
highlightBuilder.options(parser.map());
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, HIGHLIGHT_QUERY_FIELD)) {
|
||||
highlightBuilder.highlightQuery(parseContext.parseInnerQueryBuilder());
|
||||
} else if (false == highlightBuilder.doFromXContent(parseContext, currentFieldName, token)) {
|
||||
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);
|
||||
static <HB extends AbstractHighlighterBuilder<HB>> BiFunction<QueryParseContext, HB, HB> setupParser(
|
||||
ObjectParser<HB, QueryParseContext> parser) {
|
||||
parser.declareStringArray(fromList(String.class, HB::preTags), PRE_TAGS_FIELD);
|
||||
parser.declareStringArray(fromList(String.class, HB::postTags), POST_TAGS_FIELD);
|
||||
parser.declareString(HB::order, ORDER_FIELD);
|
||||
parser.declareBoolean(HB::highlightFilter, HIGHLIGHT_FILTER_FIELD);
|
||||
parser.declareInt(HB::fragmentSize, FRAGMENT_SIZE_FIELD);
|
||||
parser.declareInt(HB::numOfFragments, NUMBER_OF_FRAGMENTS_FIELD);
|
||||
parser.declareBoolean(HB::requireFieldMatch, REQUIRE_FIELD_MATCH_FIELD);
|
||||
parser.declareInt(HB::boundaryMaxScan, BOUNDARY_MAX_SCAN_FIELD);
|
||||
parser.declareString((HB hb, String bc) -> hb.boundaryChars(bc.toCharArray()) , BOUNDARY_CHARS_FIELD);
|
||||
parser.declareString(HB::highlighterType, TYPE_FIELD);
|
||||
parser.declareString(HB::fragmenter, FRAGMENTER_FIELD);
|
||||
parser.declareInt(HB::noMatchSize, NO_MATCH_SIZE_FIELD);
|
||||
parser.declareBoolean(HB::forceSource, FORCE_SOURCE_FIELD);
|
||||
parser.declareInt(HB::phraseLimit, PHRASE_LIMIT_FIELD);
|
||||
parser.declareObject(HB::options, (XContentParser p, QueryParseContext c) -> {
|
||||
try {
|
||||
return p.map();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error parsing options", e);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}, OPTIONS_FIELD);
|
||||
parser.declareObject(HB::highlightQuery, (XContentParser p, QueryParseContext c) -> {
|
||||
try {
|
||||
return c.parseInnerQueryBuilder();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Error parsing query", e);
|
||||
}
|
||||
}, HIGHLIGHT_QUERY_FIELD);
|
||||
return (QueryParseContext c, HB hb) -> {
|
||||
try {
|
||||
parser.parse(c.parser(), hb, c);
|
||||
if (hb.preTags() != null && hb.postTags() == null) {
|
||||
throw new ParsingException(c.parser().getTokenLocation(),
|
||||
"pre_tags are set but post_tags are not set");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return hb;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parser the input parser. Implementing classes might advance the parser depending on the
|
||||
* information they need to instantiate a new instance
|
||||
* @return a new instance
|
||||
*/
|
||||
protected abstract HB createInstance(XContentParser parser) throws IOException;
|
||||
|
||||
/**
|
||||
* Implementing subclasses can handle parsing special options depending on the
|
||||
* current token, field name and the parse context.
|
||||
* @return <tt>true</tt> if an option was found and successfully parsed, otherwise <tt>false</tt>
|
||||
*/
|
||||
protected abstract boolean doFromXContent(QueryParseContext parseContext, String currentFieldName, XContentParser.Token endMarkerToken) throws IOException;
|
||||
|
||||
@Override
|
||||
public final int hashCode() {
|
||||
return Objects.hash(getClass(), Arrays.hashCode(preTags), Arrays.hashCode(postTags), fragmentSize,
|
||||
|
|
|
@ -25,9 +25,16 @@ import org.elasticsearch.common.ParsingException;
|
|||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.ExceptionsHelper;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser.NamedObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.common.xcontent.XContentParser.Token;
|
||||
import org.elasticsearch.index.query.QueryBuilder;
|
||||
import org.elasticsearch.index.query.QueryParseContext;
|
||||
import org.elasticsearch.index.query.QueryShardContext;
|
||||
|
@ -43,6 +50,9 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ObjectParser.fromList;
|
||||
|
||||
/**
|
||||
* A builder for search highlighting. Settings can control how large fields
|
||||
|
@ -157,6 +167,10 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
|
|||
return this;
|
||||
}
|
||||
|
||||
void fields(List<Field> fields) {
|
||||
this.fields.addAll(fields);
|
||||
}
|
||||
|
||||
public List<Field> fields() {
|
||||
return this.fields;
|
||||
}
|
||||
|
@ -217,46 +231,25 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
|
|||
return this.useExplicitFieldOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse options only present in top level highlight builder (`tags_schema`, `encoder` and nested `fields`)
|
||||
*/
|
||||
@Override
|
||||
protected boolean doFromXContent(QueryParseContext parseContext, String currentFieldName, Token currentToken) throws IOException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
XContentParser.Token token;
|
||||
boolean foundCurrentFieldMatch = false;
|
||||
if (currentToken.isValue()) {
|
||||
if (parseContext.parseFieldMatcher().match(currentFieldName, TAGS_SCHEMA_FIELD)) {
|
||||
tagsSchema(parser.text());
|
||||
foundCurrentFieldMatch = true;
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, ENCODER_FIELD)) {
|
||||
encoder(parser.text());
|
||||
foundCurrentFieldMatch = true;
|
||||
}
|
||||
} else if (currentToken == Token.START_ARRAY && parseContext.parseFieldMatcher().match(currentFieldName, FIELDS_FIELD)) {
|
||||
useExplicitFieldOrder(true);
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
if (token == XContentParser.Token.START_OBJECT) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
field(HighlightBuilder.Field.PROTOTYPE.fromXContent(parseContext));
|
||||
}
|
||||
}
|
||||
foundCurrentFieldMatch = true;
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(),
|
||||
"If highlighter fields is an array it must contain objects containing a single field");
|
||||
}
|
||||
}
|
||||
} else if (currentToken == Token.START_OBJECT && parseContext.parseFieldMatcher().match(currentFieldName, FIELDS_FIELD)) {
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
field(HighlightBuilder.Field.PROTOTYPE.fromXContent(parseContext));
|
||||
}
|
||||
}
|
||||
foundCurrentFieldMatch = true;
|
||||
}
|
||||
return foundCurrentFieldMatch;
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
innerXContent(builder);
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static final BiFunction<QueryParseContext, HighlightBuilder, HighlightBuilder> PARSER;
|
||||
static {
|
||||
ObjectParser<HighlightBuilder, QueryParseContext> parser = new ObjectParser<>("highlight");
|
||||
parser.declareString(HighlightBuilder::tagsSchema, new ParseField("tags_schema"));
|
||||
parser.declareString(HighlightBuilder::encoder, ENCODER_FIELD);
|
||||
parser.declareNamedObjects(HighlightBuilder::fields, Field.PARSER, (HighlightBuilder hb) -> hb.useExplicitFieldOrder(true),
|
||||
FIELDS_FIELD);
|
||||
PARSER = setupParser(parser);
|
||||
}
|
||||
public static HighlightBuilder fromXContent(QueryParseContext c) {
|
||||
return PARSER.apply(c, new HighlightBuilder());
|
||||
}
|
||||
|
||||
public SearchContextHighlight build(QueryShardContext context) throws IOException {
|
||||
|
@ -388,11 +381,6 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HighlightBuilder createInstance(XContentParser parser) {
|
||||
return new HighlightBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doHashCode() {
|
||||
return Objects.hash(encoder, useExplicitFieldOrder, fields);
|
||||
|
@ -431,6 +419,14 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
|
|||
|
||||
public static class Field extends AbstractHighlighterBuilder<Field> implements Writeable<Field> {
|
||||
static final Field PROTOTYPE = new Field("_na_");
|
||||
static final NamedObjectParser<Field, QueryParseContext> PARSER;
|
||||
static {
|
||||
ObjectParser<Field, QueryParseContext> parser = new ObjectParser<>("highlight_field");
|
||||
parser.declareInt(Field::fragmentOffset, FRAGMENT_OFFSET_FIELD);
|
||||
parser.declareStringArray(fromList(String.class, Field::matchedFields), MATCHED_FIELDS_FIELD);
|
||||
BiFunction<QueryParseContext, Field, Field> decoratedParser = setupParser(parser);
|
||||
PARSER = (XContentParser p, QueryParseContext c, String name) -> decoratedParser.apply(c, new Field(name));
|
||||
}
|
||||
|
||||
private final String name;
|
||||
|
||||
|
@ -476,39 +472,6 @@ public class HighlightBuilder extends AbstractHighlighterBuilder<HighlightBuilde
|
|||
builder.endObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* parse options only present in field highlight builder (`fragment_offset`, `matched_fields`)
|
||||
*/
|
||||
@Override
|
||||
protected boolean doFromXContent(QueryParseContext parseContext, String currentFieldName, Token currentToken) throws IOException {
|
||||
XContentParser parser = parseContext.parser();
|
||||
boolean foundCurrentFieldMatch = false;
|
||||
if (parseContext.parseFieldMatcher().match(currentFieldName, FRAGMENT_OFFSET_FIELD) && currentToken.isValue()) {
|
||||
fragmentOffset(parser.intValue());
|
||||
foundCurrentFieldMatch = true;
|
||||
} else if (parseContext.parseFieldMatcher().match(currentFieldName, MATCHED_FIELDS_FIELD)
|
||||
&& currentToken == XContentParser.Token.START_ARRAY) {
|
||||
List<String> matchedFields = new ArrayList<>();
|
||||
while (parser.nextToken() != XContentParser.Token.END_ARRAY) {
|
||||
matchedFields.add(parser.text());
|
||||
}
|
||||
matchedFields(matchedFields.toArray(new String[matchedFields.size()]));
|
||||
foundCurrentFieldMatch = true;
|
||||
}
|
||||
return foundCurrentFieldMatch;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Field createInstance(XContentParser parser) throws IOException {
|
||||
if (parser.currentToken() == XContentParser.Token.FIELD_NAME) {
|
||||
String fieldname = parser.currentName();
|
||||
return new Field(fieldname);
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(), "unknown token type [{}], expected field name",
|
||||
parser.currentToken());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doHashCode() {
|
||||
return Objects.hash(name, fragmentOffset, Arrays.hashCode(matchedFields));
|
||||
|
|
|
@ -21,6 +21,8 @@ package org.elasticsearch.common.xcontent;
|
|||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParseFieldMatcher;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser.NamedObjectParser;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser.ValueType;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -28,10 +30,17 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
|
||||
public class ObjectParserTests extends ESTestCase {
|
||||
|
||||
public void testBasics() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser("{\"test\" : \"foo\", \"test_number\" : 2, \"testArray\": [1,2,3,4]}");
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser(
|
||||
"{\n"
|
||||
+ " \"test\" : \"foo\",\n"
|
||||
+ " \"test_number\" : 2,\n"
|
||||
+ " \"testArray\": [1,2,3,4]\n"
|
||||
+ "}");
|
||||
class TestStruct {
|
||||
public String test;
|
||||
int testNumber;
|
||||
|
@ -44,7 +53,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||
this.ints = ints;
|
||||
}
|
||||
}
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser("foo");
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser<>("foo");
|
||||
TestStruct s = new TestStruct();
|
||||
|
||||
objectParser.declareField((i, c, x) -> c.test = i.text(), new ParseField("test"), ObjectParser.ValueType.STRING);
|
||||
|
@ -55,29 +64,17 @@ public class ObjectParserTests extends ESTestCase {
|
|||
assertEquals(s.test, "foo");
|
||||
assertEquals(s.testNumber, 2);
|
||||
assertEquals(s.ints, Arrays.asList(1, 2, 3, 4));
|
||||
assertEquals(objectParser.toString(), "ObjectParser{name='foo', fields=[FieldParser{preferred_name=test, supportedTokens=[VALUE_STRING], type=STRING}, FieldParser{preferred_name=test_number, supportedTokens=[VALUE_STRING, VALUE_NUMBER], type=INT}, FieldParser{preferred_name=test_array, supportedTokens=[START_ARRAY, VALUE_STRING, VALUE_NUMBER], type=INT_ARRAY}, FieldParser{preferred_name=test_array, supportedTokens=[START_ARRAY, VALUE_STRING, VALUE_NUMBER], type=INT_ARRAY}, FieldParser{preferred_name=test_number, supportedTokens=[VALUE_STRING, VALUE_NUMBER], type=INT}]}");
|
||||
}
|
||||
|
||||
public void testEmptyObject() throws Exception {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser("{}");
|
||||
class TestStruct {
|
||||
public String val = null;
|
||||
public void setVal(String val) {
|
||||
this.val = val;
|
||||
}
|
||||
}
|
||||
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser("eggplant");
|
||||
TestStruct s = new TestStruct();
|
||||
|
||||
objectParser.declareString(TestStruct::setVal, new ParseField("anything"));
|
||||
objectParser.parse(parser, s);
|
||||
assertNull("s.val should be null", s.val);
|
||||
assertEquals(objectParser.toString(), "ObjectParser{name='foo', fields=["
|
||||
+ "FieldParser{preferred_name=test, supportedTokens=[VALUE_STRING], type=STRING}, "
|
||||
+ "FieldParser{preferred_name=test_number, supportedTokens=[VALUE_STRING, VALUE_NUMBER], type=INT}, "
|
||||
+ "FieldParser{preferred_name=test_array, supportedTokens=[START_ARRAY, VALUE_STRING, VALUE_NUMBER], type=INT_ARRAY}, "
|
||||
+ "FieldParser{preferred_name=test_array, supportedTokens=[START_ARRAY, VALUE_STRING, VALUE_NUMBER], type=INT_ARRAY}, "
|
||||
+ "FieldParser{preferred_name=test_number, supportedTokens=[VALUE_STRING, VALUE_NUMBER], type=INT}]}");
|
||||
}
|
||||
|
||||
public void testObjectOrDefault() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser("{\"object\" : { \"test\": 2}}");
|
||||
ObjectParser<StaticTestStruct, Void> objectParser = new ObjectParser("foo", StaticTestStruct::new);
|
||||
ObjectParser<StaticTestStruct, Void> objectParser = new ObjectParser<>("foo", StaticTestStruct::new);
|
||||
objectParser.declareInt(StaticTestStruct::setTest, new ParseField("test"));
|
||||
objectParser.declareObjectOrDefault(StaticTestStruct::setObject, objectParser, StaticTestStruct::new, new ParseField("object"));
|
||||
StaticTestStruct s = objectParser.parse(parser);
|
||||
|
@ -96,13 +93,10 @@ public class ObjectParserTests extends ESTestCase {
|
|||
public void testExceptions() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser("{\"test\" : \"foo\"}");
|
||||
class TestStruct {
|
||||
public int test;
|
||||
|
||||
public void setTest(int test) {
|
||||
this.test = test;
|
||||
}
|
||||
}
|
||||
ObjectParser<TestStruct, TestStruct> objectParser = new ObjectParser("the_parser");
|
||||
ObjectParser<TestStruct, TestStruct> objectParser = new ObjectParser<>("the_parser");
|
||||
TestStruct s = new TestStruct();
|
||||
objectParser.declareInt(TestStruct::setTest, new ParseField("test"));
|
||||
|
||||
|
@ -128,7 +122,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||
class TestStruct {
|
||||
public String test;
|
||||
}
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser("foo");
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser<>("foo");
|
||||
TestStruct s = new TestStruct();
|
||||
|
||||
objectParser.declareField((i, v, c) -> v.test = i.text(), new ParseField("test", "old_test"), ObjectParser.ValueType.STRING);
|
||||
|
@ -153,7 +147,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||
class TestStruct {
|
||||
public String test;
|
||||
}
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser("foo");
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser<>("foo");
|
||||
TestStruct s = new TestStruct();
|
||||
|
||||
objectParser.declareField((i, c, x) -> c.test = i.text(), new ParseField("numeric_value"), ObjectParser.ValueType.FLOAT);
|
||||
|
@ -172,11 +166,11 @@ public class ObjectParserTests extends ESTestCase {
|
|||
public int test;
|
||||
TestStruct object;
|
||||
}
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser("foo");
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser<>("foo");
|
||||
TestStruct s = new TestStruct();
|
||||
s.object = new TestStruct();
|
||||
objectParser.declareField((i, c, x) -> c.test = i.intValue(), new ParseField("test"), ObjectParser.ValueType.INT);
|
||||
objectParser.declareField((i, c, x) -> objectParser.parse(parser, c.object), new ParseField("object"), ObjectParser.ValueType.OBJECT);
|
||||
objectParser.declareField((i, c, x) -> c.test = i.intValue(), new ParseField("test"), ValueType.INT);
|
||||
objectParser.declareField((i, c, x) -> objectParser.parse(parser, c.object), new ParseField("object"), ValueType.OBJECT);
|
||||
objectParser.parse(parser, s);
|
||||
assertEquals(s.test, 1);
|
||||
assertEquals(s.object.test, 2);
|
||||
|
@ -184,7 +178,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||
|
||||
public void testParseNestedShortcut() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser("{ \"test\" : 1, \"object\" : { \"test\": 2}}");
|
||||
ObjectParser<StaticTestStruct, Void> objectParser = new ObjectParser("foo", StaticTestStruct::new);
|
||||
ObjectParser<StaticTestStruct, Void> objectParser = new ObjectParser<>("foo", StaticTestStruct::new);
|
||||
objectParser.declareInt(StaticTestStruct::setTest, new ParseField("test"));
|
||||
objectParser.declareObject(StaticTestStruct::setObject, objectParser, new ParseField("object"));
|
||||
StaticTestStruct s = objectParser.parse(parser);
|
||||
|
@ -192,9 +186,26 @@ public class ObjectParserTests extends ESTestCase {
|
|||
assertEquals(s.object.test, 2);
|
||||
}
|
||||
|
||||
public void testEmptyObject() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser("{\"object\" : {}}");
|
||||
ObjectParser<StaticTestStruct, Void> objectParser = new ObjectParser<>("foo", StaticTestStruct::new);
|
||||
objectParser.declareObject(StaticTestStruct::setObject, objectParser, new ParseField("object"));
|
||||
StaticTestStruct s = objectParser.parse(parser);
|
||||
assertNotNull(s.object);
|
||||
}
|
||||
|
||||
public void testEmptyObjectInArray() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser("{\"object_array\" : [{}]}");
|
||||
ObjectParser<StaticTestStruct, Void> objectParser = new ObjectParser<>("foo", StaticTestStruct::new);
|
||||
objectParser.declareObjectArray(StaticTestStruct::setObjectArray, objectParser, new ParseField("object_array"));
|
||||
StaticTestStruct s = objectParser.parse(parser);
|
||||
assertNotNull(s.objectArray);
|
||||
}
|
||||
|
||||
static class StaticTestStruct {
|
||||
public int test;
|
||||
int test;
|
||||
StaticTestStruct object;
|
||||
List<StaticTestStruct> objectArray;
|
||||
|
||||
public void setTest(int test) {
|
||||
this.test = test;
|
||||
|
@ -203,6 +214,10 @@ public class ObjectParserTests extends ESTestCase {
|
|||
public void setObject(StaticTestStruct object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public void setObjectArray(List<StaticTestStruct> objectArray) {
|
||||
this.objectArray = objectArray;
|
||||
}
|
||||
}
|
||||
|
||||
enum TestEnum {
|
||||
|
@ -218,7 +233,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser("{ \"test\" : \"FOO\" }");
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser("foo");
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser<>("foo");
|
||||
objectParser.declareString((struct, value) -> struct.set(TestEnum.valueOf(value)), new ParseField("test"));
|
||||
TestStruct s = objectParser.parse(parser, new TestStruct());
|
||||
assertEquals(s.test, TestEnum.FOO);
|
||||
|
@ -314,7 +329,7 @@ public class ObjectParserTests extends ESTestCase {
|
|||
this.string_or_null = string_or_null;
|
||||
}
|
||||
}
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser("foo");
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser<>("foo");
|
||||
objectParser.declareInt(TestStruct::setInt_field, new ParseField("int_field"));
|
||||
objectParser.declareIntArray(TestStruct::setInt_array_field, new ParseField("int_array_field"));
|
||||
objectParser.declareLong(TestStruct::setLong_field, new ParseField("long_field"));
|
||||
|
@ -352,4 +367,117 @@ public class ObjectParserTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testParseNamedObject() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser(
|
||||
"{\"named\": {\n"
|
||||
+ " \"a\": {}"
|
||||
+ "}}");
|
||||
NamedObjectHolder h = NamedObjectHolder.PARSER.apply(parser, null);
|
||||
assertThat(h.named, hasSize(1));
|
||||
assertEquals("a", h.named.get(0).name);
|
||||
assertFalse(h.namedSuppliedInOrder);
|
||||
}
|
||||
|
||||
public void testParseNamedObjectInOrder() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser(
|
||||
"{\"named\": [\n"
|
||||
+ " {\"a\": {}}"
|
||||
+ "]}");
|
||||
NamedObjectHolder h = NamedObjectHolder.PARSER.apply(parser, null);
|
||||
assertThat(h.named, hasSize(1));
|
||||
assertEquals("a", h.named.get(0).name);
|
||||
assertTrue(h.namedSuppliedInOrder);
|
||||
}
|
||||
|
||||
public void testParseNamedObjectTwoFieldsInArray() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser(
|
||||
"{\"named\": [\n"
|
||||
+ " {\"a\": {}, \"b\": {}}"
|
||||
+ "]}");
|
||||
ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null));
|
||||
assertEquals("[named_object_holder] failed to parse field [named]", e.getMessage());
|
||||
assertEquals(
|
||||
"[named] can be a single object with any number of fields or an array where each entry is an object with a single field",
|
||||
e.getCause().getMessage());
|
||||
}
|
||||
|
||||
public void testParseNamedObjectNoFieldsInArray() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser(
|
||||
"{\"named\": [\n"
|
||||
+ " {}"
|
||||
+ "]}");
|
||||
ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null));
|
||||
assertEquals("[named_object_holder] failed to parse field [named]", e.getMessage());
|
||||
assertEquals(
|
||||
"[named] can be a single object with any number of fields or an array where each entry is an object with a single field",
|
||||
e.getCause().getMessage());
|
||||
}
|
||||
|
||||
public void testParseNamedObjectJunkInArray() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser(
|
||||
"{\"named\": [\n"
|
||||
+ " \"junk\""
|
||||
+ "]}");
|
||||
ParsingException e = expectThrows(ParsingException.class, () -> NamedObjectHolder.PARSER.apply(parser, null));
|
||||
assertEquals("[named_object_holder] failed to parse field [named]", e.getMessage());
|
||||
assertEquals(
|
||||
"[named] can be a single object with any number of fields or an array where each entry is an object with a single field",
|
||||
e.getCause().getMessage());
|
||||
}
|
||||
|
||||
public void testParseNamedObjectInOrderNotSupported() throws IOException {
|
||||
XContentParser parser = XContentType.JSON.xContent().createParser(
|
||||
"{\"named\": [\n"
|
||||
+ " {\"a\": {}}"
|
||||
+ "]}");
|
||||
|
||||
// Create our own parser for this test so we can disable support for the "ordered" mode specified by the array above
|
||||
ObjectParser<NamedObjectHolder, Void> objectParser = new ObjectParser<>("named_object_holder", NamedObjectHolder::new);
|
||||
objectParser.declareNamedObjects(NamedObjectHolder::setNamed, NamedObject.PARSER, new ParseField("named"));
|
||||
|
||||
// Now firing the xml through it fails
|
||||
ParsingException e = expectThrows(ParsingException.class, () -> objectParser.apply(parser, null));
|
||||
assertEquals("[named_object_holder] failed to parse field [named]", e.getMessage());
|
||||
assertEquals("[named] doesn't support arrays. Use a single object with multiple fields.", e.getCause().getMessage());
|
||||
}
|
||||
|
||||
static class NamedObjectHolder {
|
||||
public static final ObjectParser<NamedObjectHolder, Void> PARSER = new ObjectParser<>("named_object_holder",
|
||||
NamedObjectHolder::new);
|
||||
static {
|
||||
PARSER.declareNamedObjects(NamedObjectHolder::setNamed, NamedObject.PARSER, NamedObjectHolder::keepNamedInOrder,
|
||||
new ParseField("named"));
|
||||
}
|
||||
|
||||
private List<NamedObject> named;
|
||||
private boolean namedSuppliedInOrder = false;
|
||||
|
||||
public void setNamed(List<NamedObject> named) {
|
||||
this.named = named;
|
||||
}
|
||||
|
||||
public void keepNamedInOrder() {
|
||||
namedSuppliedInOrder = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NamedObject {
|
||||
public static final NamedObjectParser<NamedObject, Void> PARSER;
|
||||
static {
|
||||
ObjectParser<NamedObject, Void> parser = new ObjectParser<>("named");
|
||||
parser.declareInt(NamedObject::setFoo, new ParseField("foo"));
|
||||
PARSER = (XContentParser p, Void v, String name) -> parser.parse(p, new NamedObject(name));
|
||||
}
|
||||
|
||||
final String name;
|
||||
int foo;
|
||||
|
||||
public NamedObject(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setFoo(int foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,14 +122,17 @@ public class HighlightBuilderTests extends ESTestCase {
|
|||
assertTrue("highlighter is not equal to self", secondBuilder.equals(secondBuilder));
|
||||
assertTrue("highlighter is not equal to its copy", firstBuilder.equals(secondBuilder));
|
||||
assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder));
|
||||
assertThat("highlighter copy's hashcode is different from original hashcode", secondBuilder.hashCode(), equalTo(firstBuilder.hashCode()));
|
||||
assertThat("highlighter copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
|
||||
equalTo(firstBuilder.hashCode()));
|
||||
|
||||
HighlightBuilder thirdBuilder = serializedCopy(secondBuilder);
|
||||
assertTrue("highlighter is not equal to self", thirdBuilder.equals(thirdBuilder));
|
||||
assertTrue("highlighter is not equal to its copy", secondBuilder.equals(thirdBuilder));
|
||||
assertThat("highlighter copy's hashcode is different from original hashcode", secondBuilder.hashCode(), equalTo(thirdBuilder.hashCode()));
|
||||
assertThat("highlighter copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
|
||||
equalTo(thirdBuilder.hashCode()));
|
||||
assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder));
|
||||
assertThat("highlighter copy's hashcode is different from original hashcode", firstBuilder.hashCode(), equalTo(thirdBuilder.hashCode()));
|
||||
assertThat("highlighter copy's hashcode is different from original hashcode", firstBuilder.hashCode(),
|
||||
equalTo(thirdBuilder.hashCode()));
|
||||
assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder));
|
||||
assertTrue("equals is not symmetric", thirdBuilder.equals(firstBuilder));
|
||||
}
|
||||
|
@ -152,7 +155,12 @@ public class HighlightBuilderTests extends ESTestCase {
|
|||
XContentParser parser = XContentHelper.createParser(builder.bytes());
|
||||
context.reset(parser);
|
||||
parser.nextToken();
|
||||
HighlightBuilder secondHighlightBuilder = HighlightBuilder.PROTOTYPE.fromXContent(context);
|
||||
HighlightBuilder secondHighlightBuilder;
|
||||
try {
|
||||
secondHighlightBuilder = HighlightBuilder.fromXContent(context);
|
||||
} catch (RuntimeException e) {
|
||||
throw new RuntimeException("Error parsing " + highlightBuilder, e);
|
||||
}
|
||||
assertNotSame(highlightBuilder, secondHighlightBuilder);
|
||||
assertEquals(highlightBuilder, secondHighlightBuilder);
|
||||
assertEquals(highlightBuilder.hashCode(), secondHighlightBuilder.hashCode());
|
||||
|
@ -163,73 +171,56 @@ public class HighlightBuilderTests extends ESTestCase {
|
|||
* 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";
|
||||
{
|
||||
IllegalArgumentException e = expectParseThrows(IllegalArgumentException.class, "{\n" +
|
||||
" \"bad_fieldname\" : [ \"field1\" 1 \"field2\" ]\n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] unknown field [bad_fieldname], parser not found", e.getMessage());
|
||||
}
|
||||
|
||||
{
|
||||
ParsingException e = expectParseThrows(ParsingException.class, "{\n" +
|
||||
" \"fields\" : {\n" +
|
||||
" \"body\" : {\n" +
|
||||
" \"bad_fieldname\" : [ \"field1\" , \"field2\" ]\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] failed to parse field [fields]", e.getMessage());
|
||||
assertEquals("[fields] failed to parse field [body]", e.getCause().getMessage());
|
||||
assertEquals("[highlight_field] unknown field [bad_fieldname], parser not found", e.getCause().getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Throwable> T expectParseThrows(Class<T> exceptionClass, String highlightElement) throws IOException {
|
||||
XContentParser parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
|
||||
|
||||
QueryParseContext context = new QueryParseContext(indicesQueriesRegistry);
|
||||
context.reset(parser);
|
||||
try {
|
||||
HighlightBuilder.PROTOTYPE.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.PROTOTYPE.fromXContent(context);
|
||||
fail("expected a parsing exception");
|
||||
} catch (ParsingException e) {
|
||||
assertEquals("cannot parse array with name [bad_fieldname]", e.getMessage());
|
||||
}
|
||||
return expectThrows(exceptionClass, () -> HighlightBuilder.fromXContent(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.PROTOTYPE.fromXContent(context);
|
||||
fail("expected a parsing exception");
|
||||
} catch (ParsingException e) {
|
||||
assertEquals("unexpected fieldname [bad_fieldname]", e.getMessage());
|
||||
{
|
||||
IllegalArgumentException e = expectParseThrows(IllegalArgumentException.class, "{\n" +
|
||||
" \"bad_fieldname\" : \"value\"\n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] unknown field [bad_fieldname], parser not found", 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.PROTOTYPE.fromXContent(context);
|
||||
fail("expected a parsing exception");
|
||||
} catch (ParsingException e) {
|
||||
assertEquals("unexpected fieldname [bad_fieldname]", e.getMessage());
|
||||
{
|
||||
ParsingException e = expectParseThrows(ParsingException.class, "{\n" +
|
||||
" \"fields\" : {\n" +
|
||||
" \"body\" : {\n" +
|
||||
" \"bad_fieldname\" : \"value\"\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] failed to parse field [fields]", e.getMessage());
|
||||
assertEquals("[fields] failed to parse field [body]", e.getCause().getMessage());
|
||||
assertEquals("[highlight_field] unknown field [bad_fieldname], parser not found", e.getCause().getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,38 +228,57 @@ public class HighlightBuilderTests extends ESTestCase {
|
|||
* 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.PROTOTYPE.fromXContent(context);
|
||||
fail("expected a parsing exception");
|
||||
} catch (ParsingException e) {
|
||||
assertEquals("cannot parse object with name [bad_fieldname]", e.getMessage());
|
||||
{
|
||||
IllegalArgumentException e = expectParseThrows(IllegalArgumentException.class, "{\n" +
|
||||
" \"bad_fieldname\" : { \"field\" : \"value\" }\n \n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] unknown field [bad_fieldname], parser not found", 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.PROTOTYPE.fromXContent(context);
|
||||
fail("expected a parsing exception");
|
||||
} catch (ParsingException e) {
|
||||
assertEquals("cannot parse object with name [bad_fieldname]", e.getMessage());
|
||||
{
|
||||
ParsingException e = expectParseThrows(ParsingException.class, "{\n" +
|
||||
" \"fields\" : {\n" +
|
||||
" \"body\" : {\n" +
|
||||
" \"bad_fieldname\" : { \"field\" : \"value\" }\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] failed to parse field [fields]", e.getMessage());
|
||||
assertEquals("[fields] failed to parse field [body]", e.getCause().getMessage());
|
||||
assertEquals("[highlight_field] unknown field [bad_fieldname], parser not found", e.getCause().getCause().getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testStringInFieldsArray() throws IOException {
|
||||
ParsingException e = expectParseThrows(ParsingException.class, "{\"fields\" : [ \"junk\" ]}");
|
||||
assertEquals("[highlight] failed to parse field [fields]", e.getMessage());
|
||||
assertEquals(
|
||||
"[fields] can be a single object with any number of fields or an array where each entry is an object with a single field",
|
||||
e.getCause().getMessage());
|
||||
}
|
||||
|
||||
public void testNoFieldsInObjectInFieldsArray() throws IOException {
|
||||
ParsingException e = expectParseThrows(ParsingException.class, "{\n" +
|
||||
" \"fields\" : [ {\n" +
|
||||
" }] \n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] failed to parse field [fields]", e.getMessage());
|
||||
assertEquals(
|
||||
"[fields] can be a single object with any number of fields or an array where each entry is an object with a single field",
|
||||
e.getCause().getMessage());
|
||||
}
|
||||
|
||||
public void testTwoFieldsInObjectInFieldsArray() throws IOException {
|
||||
ParsingException e = expectParseThrows(ParsingException.class, "{\n" +
|
||||
" \"fields\" : [ {\n" +
|
||||
" \"body\" : {},\n" +
|
||||
" \"nope\" : {}\n" +
|
||||
" }] \n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] failed to parse field [fields]", e.getMessage());
|
||||
assertEquals(
|
||||
"[fields] can be a single object with any number of fields or an array where each entry is an object with a single field",
|
||||
e.getCause().getMessage()); }
|
||||
|
||||
/**
|
||||
* test that build() outputs a {@link SearchContextHighlight} that is has similar parameters
|
||||
|
@ -280,7 +290,8 @@ public class HighlightBuilderTests extends ESTestCase {
|
|||
Index index = new Index(randomAsciiOfLengthBetween(1, 10), "_na_");
|
||||
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, indicesQueriesRegistry, null) {
|
||||
QueryShardContext mockShardContext = new QueryShardContext(idxSettings, null, null, null, null, null, indicesQueriesRegistry,
|
||||
null) {
|
||||
@Override
|
||||
public MappedFieldType fieldMapper(String name) {
|
||||
TextFieldMapper.Builder builder = new TextFieldMapper.Builder(name);
|
||||
|
@ -396,7 +407,7 @@ public class HighlightBuilderTests extends ESTestCase {
|
|||
XContentParser parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
|
||||
|
||||
context.reset(parser);
|
||||
HighlightBuilder highlightBuilder = HighlightBuilder.PROTOTYPE.fromXContent(context);
|
||||
HighlightBuilder highlightBuilder = HighlightBuilder.fromXContent(context);
|
||||
assertArrayEquals("setting tags_schema 'styled' should alter pre_tags", HighlightBuilder.DEFAULT_STYLED_PRE_TAG,
|
||||
highlightBuilder.preTags());
|
||||
assertArrayEquals("setting tags_schema 'styled' should alter post_tags", HighlightBuilder.DEFAULT_STYLED_POST_TAGS,
|
||||
|
@ -408,24 +419,17 @@ public class HighlightBuilderTests extends ESTestCase {
|
|||
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
|
||||
|
||||
context.reset(parser);
|
||||
highlightBuilder = HighlightBuilder.PROTOTYPE.fromXContent(context);
|
||||
highlightBuilder = HighlightBuilder.fromXContent(context);
|
||||
assertArrayEquals("setting tags_schema 'default' should alter pre_tags", HighlightBuilder.DEFAULT_PRE_TAGS,
|
||||
highlightBuilder.preTags());
|
||||
assertArrayEquals("setting tags_schema 'default' should alter post_tags", HighlightBuilder.DEFAULT_POST_TAGS,
|
||||
highlightBuilder.postTags());
|
||||
|
||||
highlightElement = "{\n" +
|
||||
ParsingException e = expectParseThrows(ParsingException.class, "{\n" +
|
||||
" \"tags_schema\" : \"somthing_else\"\n" +
|
||||
"}\n";
|
||||
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
|
||||
|
||||
context.reset(parser);
|
||||
try {
|
||||
HighlightBuilder.PROTOTYPE.fromXContent(context);
|
||||
fail("setting unknown tag schema should throw exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Unknown tag schema [somthing_else]", e.getMessage());
|
||||
}
|
||||
"}\n");
|
||||
assertEquals("[highlight] failed to parse field [tags_schema]", e.getMessage());
|
||||
assertEquals("Unknown tag schema [somthing_else]", e.getCause().getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -438,24 +442,42 @@ public class HighlightBuilderTests extends ESTestCase {
|
|||
XContentParser parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
|
||||
|
||||
context.reset(parser);
|
||||
HighlightBuilder highlightBuilder = HighlightBuilder.PROTOTYPE.fromXContent(context);
|
||||
HighlightBuilder highlightBuilder = HighlightBuilder.fromXContent(context);
|
||||
assertEquals("expected plain HighlightBuilder", new HighlightBuilder(), highlightBuilder);
|
||||
|
||||
highlightElement = "{ \"fields\" : { } }";
|
||||
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
|
||||
|
||||
context.reset(parser);
|
||||
highlightBuilder = HighlightBuilder.PROTOTYPE.fromXContent(context);
|
||||
highlightBuilder = HighlightBuilder.fromXContent(context);
|
||||
assertEquals("defining no field should return plain HighlightBuilder", new HighlightBuilder(), highlightBuilder);
|
||||
|
||||
highlightElement = "{ \"fields\" : { \"foo\" : { } } }";
|
||||
parser = XContentFactory.xContent(highlightElement).createParser(highlightElement);
|
||||
|
||||
context.reset(parser);
|
||||
highlightBuilder = HighlightBuilder.PROTOTYPE.fromXContent(context);
|
||||
highlightBuilder = HighlightBuilder.fromXContent(context);
|
||||
assertEquals("expected HighlightBuilder with field", new HighlightBuilder().field(new Field("foo")), highlightBuilder);
|
||||
}
|
||||
|
||||
public void testPreTagsWithoutPostTags() throws IOException {
|
||||
ParsingException e = expectParseThrows(ParsingException.class, "{\n" +
|
||||
" \"pre_tags\" : [\"<a>\"]\n" +
|
||||
"}\n");
|
||||
assertEquals("pre_tags are set but post_tags are not set", e.getMessage());
|
||||
|
||||
e = expectParseThrows(ParsingException.class, "{\n" +
|
||||
" \"fields\" : {\n" +
|
||||
" \"body\" : {\n" +
|
||||
" \"pre_tags\" : [\"<a>\"]\n" +
|
||||
" }\n" +
|
||||
" }\n" +
|
||||
"}\n");
|
||||
assertEquals("[highlight] failed to parse field [fields]", e.getMessage());
|
||||
assertEquals("[fields] failed to parse field [body]", e.getCause().getMessage());
|
||||
assertEquals("pre_tags are set but post_tags are not set", e.getCause().getCause().getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* test ordinals of {@link Order}, since serialization depends on it
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue