mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-25 09:28:27 +00:00
Allow specifying an exclusive set of fields on ObjectParser (#52893)
ObjectParser allows you to declare a set of required fields, such that at least one of the set must appear in an xcontent object for it to be valid. This commit adds the similar concept of a set of exclusive fields, such that at most one of the set must be present. It also enables required fields on ConstructingObjectParser, and re-implements PercolateQueryBuilder.fromXContent() to use object parsing as an example of how this works.
This commit is contained in:
parent
510db25dd0
commit
3759063d34
@ -38,6 +38,7 @@ public abstract class AbstractObjectParser<Value, Context>
|
||||
implements BiFunction<XContentParser, Context, Value>, ContextParser<Context, Value> {
|
||||
|
||||
final List<String[]> requiredFieldSets = new ArrayList<>();
|
||||
final List<String[]> exclusiveFieldSets = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Declare some field. Usually it is easier to use {@link #declareString(BiConsumer, ParseField)} or
|
||||
@ -294,6 +295,25 @@ public abstract class AbstractObjectParser<Value, Context>
|
||||
this.requiredFieldSets.add(requiredSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a set of fields of which at most one must appear for parsing to succeed
|
||||
*
|
||||
* E.g. <code>declareExclusiveFieldSet("foo", "bar");</code> means that only one of 'foo'
|
||||
* or 'bar' must be present, and if both appear then an exception will be thrown. Note
|
||||
* that this does not make 'foo' or 'bar' required - see {@link #declareRequiredFieldSet(String...)}
|
||||
* for required fields.
|
||||
*
|
||||
* Multiple exclusive sets may be declared
|
||||
*
|
||||
* @param exclusiveSet a set of field names, at most one of which must appear
|
||||
*/
|
||||
public void declareExclusiveFieldSet(String... exclusiveSet) {
|
||||
if (exclusiveSet.length == 0) {
|
||||
return;
|
||||
}
|
||||
this.exclusiveFieldSets.add(exclusiveSet);
|
||||
}
|
||||
|
||||
private interface IOSupplier<T> {
|
||||
T get() throws IOException;
|
||||
}
|
||||
|
@ -299,6 +299,16 @@ public final class ConstructingObjectParser<Value, Context> extends AbstractObje
|
||||
return objectParser.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void declareRequiredFieldSet(String... requiredSet) {
|
||||
objectParser.declareRequiredFieldSet(requiredSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void declareExclusiveFieldSet(String... exclusiveSet) {
|
||||
objectParser.declareExclusiveFieldSet(exclusiveSet);
|
||||
}
|
||||
|
||||
private Consumer<Target> wrapOrderedModeCallBack(Consumer<Value> callback) {
|
||||
return (target) -> {
|
||||
if (target.targetObject != null) {
|
||||
|
@ -274,6 +274,10 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
|
||||
String currentFieldName = null;
|
||||
XContentLocation currentPosition = null;
|
||||
List<String[]> requiredFields = new ArrayList<>(this.requiredFieldSets);
|
||||
List<List<String>> exclusiveFields = new ArrayList<>();
|
||||
for (int i = 0; i < this.exclusiveFieldSets.size(); i++) {
|
||||
exclusiveFields.add(new ArrayList<>());
|
||||
}
|
||||
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||
if (token == XContentParser.Token.FIELD_NAME) {
|
||||
@ -302,18 +306,41 @@ public final class ObjectParser<Value, Context> extends AbstractObjectParser<Val
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this field is in an exclusive set, if it is then mark
|
||||
// it as seen.
|
||||
for (int i = 0; i < this.exclusiveFieldSets.size(); i++) {
|
||||
for (String field : this.exclusiveFieldSets.get(i)) {
|
||||
if (field.equals(currentFieldName)) {
|
||||
exclusiveFields.get(i).add(currentFieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseSub(parser, fieldParser, currentFieldName, value, context);
|
||||
}
|
||||
fieldParser = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a) multiple entries appearing in exclusive field sets and b) empty
|
||||
// required field entries
|
||||
StringBuilder message = new StringBuilder();
|
||||
for (List<String> fieldset : exclusiveFields) {
|
||||
if (fieldset.size() > 1) {
|
||||
message.append("The following fields are not allowed together: ").append(fieldset.toString()).append(" ");
|
||||
}
|
||||
}
|
||||
if (message.length() > 0) {
|
||||
throw new IllegalArgumentException(message.toString());
|
||||
}
|
||||
|
||||
if (requiredFields.isEmpty() == false) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
for (String[] fields : requiredFields) {
|
||||
message.append("Required one of fields ").append(Arrays.toString(fields)).append(", but none were specified. ");
|
||||
}
|
||||
throw new IllegalArgumentException(message.toString());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -530,4 +530,45 @@ public class ConstructingObjectParserTests extends ESTestCase {
|
||||
namedSuppliedInOrder = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void testRequiredAndExclusiveFields() throws IOException {
|
||||
|
||||
class TestStruct {
|
||||
final String a;
|
||||
final long b;
|
||||
TestStruct(String a) {
|
||||
this.a = a;
|
||||
this.b = 0;
|
||||
}
|
||||
TestStruct(long b) {
|
||||
this.a = null;
|
||||
this.b = b;
|
||||
}
|
||||
}
|
||||
|
||||
XContentParser ok = createParser(JsonXContent.jsonXContent, "{ \"a\" : \"a\" }");
|
||||
XContentParser toomany = createParser(JsonXContent.jsonXContent, "{ \"a\" : \"a\", \"b\" : 1 }");
|
||||
XContentParser notenough = createParser(JsonXContent.jsonXContent, "{ }");
|
||||
|
||||
ConstructingObjectParser<TestStruct, Void> parser = new ConstructingObjectParser<>("teststruct", args -> {
|
||||
if (args[0] != null) {
|
||||
return new TestStruct((String) args[0]);
|
||||
}
|
||||
return new TestStruct((Long) args[1]);
|
||||
});
|
||||
parser.declareString(optionalConstructorArg(), new ParseField("a"));
|
||||
parser.declareLong(optionalConstructorArg(), new ParseField("b"));
|
||||
parser.declareExclusiveFieldSet("a", "b");
|
||||
parser.declareRequiredFieldSet("a", "b");
|
||||
|
||||
TestStruct actual = parser.parse(ok, null);
|
||||
assertThat(actual.a, equalTo("a"));
|
||||
assertThat(actual.b, equalTo(0L));
|
||||
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> parser.parse(toomany, null));
|
||||
assertThat(e.getMessage(), containsString("allowed together: [a, b]"));
|
||||
|
||||
e = expectThrows(IllegalArgumentException.class, () -> parser.parse(notenough, null));
|
||||
assertThat(e.getMessage(), containsString("Required one of fields [a, b], but none were specified."));
|
||||
}
|
||||
}
|
||||
|
@ -851,30 +851,31 @@ public class ObjectParserTests extends ESTestCase {
|
||||
assertThat(obj.b, equalTo(456L));
|
||||
}
|
||||
|
||||
public void testMultipleRequiredFieldSet() throws IOException {
|
||||
class TestStruct {
|
||||
private Long a;
|
||||
private Long b;
|
||||
private Long c;
|
||||
private Long d;
|
||||
private static class TestStruct {
|
||||
private Long a;
|
||||
private Long b;
|
||||
private Long c;
|
||||
private Long d;
|
||||
|
||||
private void setA(long value) {
|
||||
this.a = value;
|
||||
}
|
||||
|
||||
private void setB(long value) {
|
||||
this.b = value;
|
||||
}
|
||||
|
||||
private void setC(long value) {
|
||||
this.c = value;
|
||||
}
|
||||
|
||||
private void setD(long value) {
|
||||
this.d = value;
|
||||
}
|
||||
private void setA(long value) {
|
||||
this.a = value;
|
||||
}
|
||||
|
||||
private void setB(long value) {
|
||||
this.b = value;
|
||||
}
|
||||
|
||||
private void setC(long value) {
|
||||
this.c = value;
|
||||
}
|
||||
|
||||
private void setD(long value) {
|
||||
this.d = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void testMultipleRequiredFieldSet() throws IOException {
|
||||
|
||||
XContentParser parser = createParser(JsonXContent.jsonXContent, "{\"unrelated\": \"123\"}");
|
||||
ObjectParser<TestStruct, Void> objectParser = new ObjectParser<>("foo", true, TestStruct::new);
|
||||
objectParser.declareLong(TestStruct::setA, new ParseField("a"));
|
||||
@ -889,6 +890,32 @@ public class ObjectParserTests extends ESTestCase {
|
||||
"Required one of fields [c, d], but none were specified. "));
|
||||
}
|
||||
|
||||
public void testExclusiveFieldSet() throws IOException {
|
||||
|
||||
XContentParser goodA = createParser(JsonXContent.jsonXContent, "{\"a\" : 1, \"c\" : 4}");
|
||||
XContentParser bad = createParser(JsonXContent.jsonXContent, "{\"a\" : 1, \"b\" : 2}");
|
||||
XContentParser badmulti = createParser(JsonXContent.jsonXContent, "{\"a\" : 1, \"b\" : 2, \"c\" : 3, \"d\" : 4 }");
|
||||
|
||||
ObjectParser<TestStruct, Void> parser = new ObjectParser<>("foo", TestStruct::new);
|
||||
parser.declareLong(TestStruct::setA, new ParseField("a"));
|
||||
parser.declareLong(TestStruct::setB, new ParseField("b"));
|
||||
parser.declareLong(TestStruct::setC, new ParseField("c"));
|
||||
parser.declareLong(TestStruct::setD, new ParseField("d"));
|
||||
parser.declareExclusiveFieldSet("a", "b");
|
||||
parser.declareExclusiveFieldSet("c", "d");
|
||||
|
||||
TestStruct actualA = parser.parse(goodA, null);
|
||||
assertThat(actualA.a, equalTo(1L));
|
||||
assertThat(actualA.c, equalTo(4L));
|
||||
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> parser.parse(bad, null));
|
||||
assertThat(e.getMessage(), containsString("The following fields are not allowed together: [a, b]"));
|
||||
|
||||
e = expectThrows(IllegalArgumentException.class, () -> parser.parse(badmulti, null));
|
||||
assertThat(e.getMessage(),
|
||||
containsString("allowed together: [a, b] The following fields are not allowed together: [c, d]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NamedXContentRegistry xContentRegistry() {
|
||||
return new NamedXContentRegistry(Arrays.asList(
|
||||
|
@ -49,7 +49,6 @@ import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.action.get.GetRequest;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.ParsingException;
|
||||
import org.elasticsearch.common.bytes.BytesReference;
|
||||
import org.elasticsearch.common.io.stream.InputStreamStreamInput;
|
||||
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
||||
@ -57,6 +56,7 @@ import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.logging.DeprecationLogger;
|
||||
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
|
||||
import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
|
||||
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||
import org.elasticsearch.common.xcontent.XContent;
|
||||
@ -93,6 +93,8 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
|
||||
import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;
|
||||
import static org.elasticsearch.search.SearchService.ALLOW_EXPENSIVE_QUERIES;
|
||||
|
||||
public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBuilder> {
|
||||
@ -376,119 +378,57 @@ public class PercolateQueryBuilder extends AbstractQueryBuilder<PercolateQueryBu
|
||||
builder.endObject();
|
||||
}
|
||||
|
||||
public static PercolateQueryBuilder fromXContent(XContentParser parser) throws IOException {
|
||||
float boost = AbstractQueryBuilder.DEFAULT_BOOST;
|
||||
|
||||
String field = null;
|
||||
String name = null;
|
||||
String documentType = null;
|
||||
|
||||
String indexedDocumentIndex = null;
|
||||
String indexedDocumentType = null;
|
||||
String indexedDocumentId = null;
|
||||
String indexedDocumentRouting = null;
|
||||
String indexedDocumentPreference = null;
|
||||
Long indexedDocumentVersion = null;
|
||||
|
||||
List<BytesReference> documents = new ArrayList<>();
|
||||
|
||||
String queryName = null;
|
||||
String currentFieldName = null;
|
||||
|
||||
boolean documentsSpecified = false;
|
||||
boolean documentSpecified = false;
|
||||
|
||||
XContentParser.Token token;
|
||||
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 (DOCUMENTS_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
if (documentSpecified) {
|
||||
throw new IllegalArgumentException("[" + PercolateQueryBuilder.NAME +
|
||||
"] Either specified [document] or [documents], not both");
|
||||
}
|
||||
documentsSpecified = true;
|
||||
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
|
||||
if (token == XContentParser.Token.START_OBJECT) {
|
||||
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||
builder.copyCurrentStructure(parser);
|
||||
builder.flush();
|
||||
documents.add(BytesReference.bytes(builder));
|
||||
}
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(), "[" + PercolateQueryBuilder.NAME +
|
||||
"] query does not support [" + token + "]");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(), "[" + PercolateQueryBuilder.NAME +
|
||||
"] query does not field name [" + currentFieldName + "]");
|
||||
}
|
||||
} else if (token == XContentParser.Token.START_OBJECT) {
|
||||
if (DOCUMENT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
if (documentsSpecified) {
|
||||
throw new IllegalArgumentException("[" + PercolateQueryBuilder.NAME +
|
||||
"] Either specified [document] or [documents], not both");
|
||||
}
|
||||
documentSpecified = true;
|
||||
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||
builder.copyCurrentStructure(parser);
|
||||
builder.flush();
|
||||
documents.add(BytesReference.bytes(builder));
|
||||
}
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(), "[" + PercolateQueryBuilder.NAME +
|
||||
"] query does not support field name [" + currentFieldName + "]");
|
||||
}
|
||||
} else if (token.isValue() || token == XContentParser.Token.VALUE_NULL) {
|
||||
if (QUERY_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
field = parser.text();
|
||||
} else if (NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
name = parser.textOrNull();
|
||||
} else if (DOCUMENT_TYPE_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
documentType = parser.textOrNull();
|
||||
} else if (INDEXED_DOCUMENT_FIELD_INDEX.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
indexedDocumentIndex = parser.text();
|
||||
} else if (INDEXED_DOCUMENT_FIELD_TYPE.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
indexedDocumentType = parser.text();
|
||||
} else if (INDEXED_DOCUMENT_FIELD_ID.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
indexedDocumentId = parser.text();
|
||||
} else if (INDEXED_DOCUMENT_FIELD_ROUTING.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
indexedDocumentRouting = parser.text();
|
||||
} else if (INDEXED_DOCUMENT_FIELD_PREFERENCE.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
indexedDocumentPreference = parser.text();
|
||||
} else if (INDEXED_DOCUMENT_FIELD_VERSION.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
indexedDocumentVersion = parser.longValue();
|
||||
} else if (AbstractQueryBuilder.BOOST_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
boost = parser.floatValue();
|
||||
} else if (AbstractQueryBuilder.NAME_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
||||
queryName = parser.text();
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(), "[" + PercolateQueryBuilder.NAME +
|
||||
"] query does not support [" + currentFieldName + "]");
|
||||
}
|
||||
} else {
|
||||
throw new ParsingException(parser.getTokenLocation(), "[" + PercolateQueryBuilder.NAME +
|
||||
"] query does not support [" + token + "]");
|
||||
}
|
||||
}
|
||||
|
||||
PercolateQueryBuilder queryBuilder;
|
||||
if (documents.isEmpty() == false) {
|
||||
queryBuilder = new PercolateQueryBuilder(field, documentType, documents, XContentType.JSON);
|
||||
} else if (indexedDocumentId != null) {
|
||||
queryBuilder = new PercolateQueryBuilder(field, documentType, indexedDocumentIndex, indexedDocumentType,
|
||||
indexedDocumentId, indexedDocumentRouting, indexedDocumentPreference, indexedDocumentVersion);
|
||||
private static final ConstructingObjectParser<PercolateQueryBuilder, Void> PARSER = new ConstructingObjectParser<>(NAME, args -> {
|
||||
String field = (String) args[0];
|
||||
BytesReference document = (BytesReference) args[1];
|
||||
@SuppressWarnings("unchecked")
|
||||
List<BytesReference> documents = (List<BytesReference>) args[2];
|
||||
String indexedDocId = (String) args[3];
|
||||
String indexedDocIndex = (String) args[4];
|
||||
String indexDocRouting = (String) args[5];
|
||||
String indexDocPreference = (String) args[6];
|
||||
Long indexedDocVersion = (Long) args[7];
|
||||
String indexedDocType = (String) args[8];
|
||||
String docType = (String) args[9];
|
||||
if (indexedDocId != null) {
|
||||
return new PercolateQueryBuilder(field, docType, indexedDocIndex, indexedDocType, indexedDocId, indexDocRouting,
|
||||
indexDocPreference, indexedDocVersion);
|
||||
} else if (document != null) {
|
||||
return new PercolateQueryBuilder(field, docType, Collections.singletonList(document), XContentType.JSON);
|
||||
} else {
|
||||
throw new IllegalArgumentException("[" + PercolateQueryBuilder.NAME + "] query, nothing to percolate");
|
||||
return new PercolateQueryBuilder(field, docType, documents, XContentType.JSON);
|
||||
}
|
||||
if (name != null) {
|
||||
queryBuilder.setName(name);
|
||||
});
|
||||
static {
|
||||
PARSER.declareString(constructorArg(), QUERY_FIELD);
|
||||
PARSER.declareObject(optionalConstructorArg(), (p, c) -> parseDocument(p), DOCUMENT_FIELD);
|
||||
PARSER.declareObjectArray(optionalConstructorArg(), (p, c) -> parseDocument(p), DOCUMENTS_FIELD);
|
||||
PARSER.declareString(optionalConstructorArg(), INDEXED_DOCUMENT_FIELD_ID);
|
||||
PARSER.declareString(optionalConstructorArg(), INDEXED_DOCUMENT_FIELD_INDEX);
|
||||
PARSER.declareString(optionalConstructorArg(), INDEXED_DOCUMENT_FIELD_ROUTING);
|
||||
PARSER.declareString(optionalConstructorArg(), INDEXED_DOCUMENT_FIELD_PREFERENCE);
|
||||
PARSER.declareLong(optionalConstructorArg(), INDEXED_DOCUMENT_FIELD_VERSION);
|
||||
PARSER.declareStringOrNull(optionalConstructorArg(), INDEXED_DOCUMENT_FIELD_TYPE);
|
||||
PARSER.declareStringOrNull(optionalConstructorArg(), DOCUMENT_TYPE_FIELD);
|
||||
PARSER.declareString(PercolateQueryBuilder::setName, NAME_FIELD);
|
||||
PARSER.declareString(PercolateQueryBuilder::queryName, AbstractQueryBuilder.NAME_FIELD);
|
||||
PARSER.declareFloat(PercolateQueryBuilder::boost, BOOST_FIELD);
|
||||
PARSER.declareRequiredFieldSet(DOCUMENT_FIELD.getPreferredName(),
|
||||
DOCUMENTS_FIELD.getPreferredName(), INDEXED_DOCUMENT_FIELD_ID.getPreferredName());
|
||||
PARSER.declareExclusiveFieldSet(DOCUMENT_FIELD.getPreferredName(),
|
||||
DOCUMENTS_FIELD.getPreferredName(), INDEXED_DOCUMENT_FIELD_ID.getPreferredName());
|
||||
}
|
||||
|
||||
private static BytesReference parseDocument(XContentParser parser) throws IOException {
|
||||
try (XContentBuilder builder = XContentFactory.jsonBuilder()) {
|
||||
builder.copyCurrentStructure(parser);
|
||||
builder.flush();
|
||||
return BytesReference.bytes(builder);
|
||||
}
|
||||
queryBuilder.queryName(queryName);
|
||||
queryBuilder.boost(boost);
|
||||
return queryBuilder;
|
||||
}
|
||||
|
||||
public static PercolateQueryBuilder fromXContent(XContentParser parser) throws IOException {
|
||||
return PARSER.parse(parser, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,6 +57,7 @@ import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
@ -267,9 +268,10 @@ public class PercolateQueryBuilderTests extends AbstractQueryTestCase<PercolateQ
|
||||
assertWarnings(PercolateQueryBuilder.TYPE_DEPRECATION_MESSAGE);
|
||||
}
|
||||
|
||||
public void testBothDocumentAndDocumentsSpecified() throws IOException {
|
||||
expectThrows(IllegalArgumentException.class,
|
||||
public void testBothDocumentAndDocumentsSpecified() {
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> parseQuery("{\"percolate\" : { \"document\": {}, \"documents\": [{}, {}], \"field\":\"" + queryField + "\"}}"));
|
||||
assertThat(e.getMessage(), containsString("The following fields are not allowed together: [document, documents]"));
|
||||
}
|
||||
|
||||
private static BytesReference randomSource(Set<String> usedFields) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user