Test that rank_eval request parsing is not lenient (#28516)

Parsing of a ranking evaluation request and its subcomponents should throw parsing
errors on unknown fields. This change adds tests for this and changes the parser 
behaviour in cases where it is needed.
This commit is contained in:
Christoph Büscher 2018-02-08 17:38:45 +01:00 committed by GitHub
parent a55eda626f
commit 01791277cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 103 additions and 4 deletions

View File

@ -164,7 +164,7 @@ public class DiscountedCumulativeGain implements EvaluationMetric {
private static final ParseField K_FIELD = new ParseField("k");
private static final ParseField NORMALIZE_FIELD = new ParseField("normalize");
private static final ParseField UNKNOWN_DOC_RATING_FIELD = new ParseField("unknown_doc_rating");
private static final ConstructingObjectParser<DiscountedCumulativeGain, Void> PARSER = new ConstructingObjectParser<>("dcg_at", true,
private static final ConstructingObjectParser<DiscountedCumulativeGain, Void> PARSER = new ConstructingObjectParser<>("dcg_at", false,
args -> {
Boolean normalized = (Boolean) args[0];
Integer optK = (Integer) args[2];

View File

@ -20,7 +20,7 @@
package org.elasticsearch.index.rankeval;
import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.index.rankeval.RatedDocument.DocumentKey;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
@ -36,7 +36,7 @@ import java.util.stream.Collectors;
* Implementations of {@link EvaluationMetric} need to provide a way to compute the quality metric for
* a result list returned by some search (@link {@link SearchHits}) and a list of rated documents.
*/
public interface EvaluationMetric extends ToXContent, NamedWriteable {
public interface EvaluationMetric extends ToXContentObject, NamedWriteable {
/**
* Returns a single metric representing the ranking quality of a set of returned

View File

@ -213,7 +213,7 @@ public class RatedRequest implements Writeable, ToXContentObject {
private static final ParseField FIELDS_FIELD = new ParseField("summary_fields");
private static final ParseField TEMPLATE_ID_FIELD = new ParseField("template_id");
private static final ConstructingObjectParser<RatedRequest, Void> PARSER = new ConstructingObjectParser<>("requests",
private static final ConstructingObjectParser<RatedRequest, Void> PARSER = new ConstructingObjectParser<>("request",
a -> new RatedRequest((String) a[0], (List<RatedDocument>) a[1], (SearchSourceBuilder) a[2], (Map<String, Object>) a[3],
(String) a[4]));

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.rankeval;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.ToXContent;
@ -39,6 +40,8 @@ import java.util.List;
import static org.elasticsearch.index.rankeval.EvaluationMetric.filterUnknownDocuments;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
import static org.hamcrest.Matchers.startsWith;
public class DiscountedCumulativeGainTests extends ESTestCase {
@ -243,6 +246,20 @@ public class DiscountedCumulativeGainTests extends ESTestCase {
}
}
public void testXContentParsingIsNotLenient() throws IOException {
DiscountedCumulativeGain testItem = createTestItem();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference originalBytes = toShuffledXContent(testItem, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());
BytesReference withRandomFields = insertRandomFields(xContentType, originalBytes, null, random());
try (XContentParser parser = createParser(xContentType.xContent(), withRandomFields)) {
parser.nextToken();
parser.nextToken();
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
() -> DiscountedCumulativeGain.fromXContent(parser));
assertThat(exception.getMessage(), startsWith("[dcg_at] unknown field"));
}
}
public void testSerialization() throws IOException {
DiscountedCumulativeGain original = createTestItem();
DiscountedCumulativeGain deserialized = ESTestCase.copyWriteable(original, new NamedWriteableRegistry(Collections.emptyList()),

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.rankeval;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.ToXContent;
@ -40,6 +41,8 @@ import java.util.List;
import java.util.Vector;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
import static org.hamcrest.Matchers.startsWith;
public class MeanReciprocalRankTests extends ESTestCase {
@ -169,6 +172,20 @@ public class MeanReciprocalRankTests extends ESTestCase {
}
}
public void testXContentParsingIsNotLenient() throws IOException {
MeanReciprocalRank testItem = createTestItem();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference originalBytes = toShuffledXContent(testItem, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());
BytesReference withRandomFields = insertRandomFields(xContentType, originalBytes, null, random());
try (XContentParser parser = createParser(xContentType.xContent(), withRandomFields)) {
parser.nextToken();
parser.nextToken();
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
() -> MeanReciprocalRank.fromXContent(parser));
assertThat(exception.getMessage(), startsWith("[reciprocal_rank] unknown field"));
}
}
/**
* Create SearchHits for testing, starting from dociId 'from' up to docId 'to'.
* The search hits index also need to be provided

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.rankeval;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.ToXContent;
@ -40,6 +41,8 @@ import java.util.List;
import java.util.Vector;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
import static org.hamcrest.Matchers.startsWith;
public class PrecisionAtKTests extends ESTestCase {
@ -182,6 +185,19 @@ public class PrecisionAtKTests extends ESTestCase {
}
}
public void testXContentParsingIsNotLenient() throws IOException {
PrecisionAtK testItem = createTestItem();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference originalBytes = toShuffledXContent(testItem, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());
BytesReference withRandomFields = insertRandomFields(xContentType, originalBytes, null, random());
try (XContentParser parser = createParser(xContentType.xContent(), withRandomFields)) {
parser.nextToken();
parser.nextToken();
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> PrecisionAtK.fromXContent(parser));
assertThat(exception.getMessage(), startsWith("[precision] unknown field"));
}
}
public void testSerialization() throws IOException {
PrecisionAtK original = createTestItem();
PrecisionAtK deserialized = ESTestCase.copyWriteable(original, new NamedWriteableRegistry(Collections.emptyList()),

View File

@ -19,12 +19,14 @@
package org.elasticsearch.index.rankeval;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
@ -47,6 +49,8 @@ import java.util.Map.Entry;
import java.util.function.Supplier;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
import static org.hamcrest.Matchers.startsWith;
public class RankEvalSpecTests extends ESTestCase {
@ -123,6 +127,17 @@ public class RankEvalSpecTests extends ESTestCase {
}
}
public void testXContentParsingIsNotLenient() throws IOException {
RankEvalSpec testItem = createTestItem();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference originalBytes = toShuffledXContent(testItem, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());
BytesReference withRandomFields = insertRandomFields(xContentType, originalBytes, null, random());
try (XContentParser parser = createParser(xContentType.xContent(), withRandomFields)) {
Exception exception = expectThrows(Exception.class, () -> RankEvalSpec.parse(parser));
assertThat(exception.getMessage(), startsWith("[rank_eval] failed to parse field"));
}
}
public void testSerialization() throws IOException {
RankEvalSpec original = createTestItem();
RankEvalSpec deserialized = copy(original);

View File

@ -19,6 +19,7 @@
package org.elasticsearch.index.rankeval;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
@ -31,6 +32,8 @@ import java.io.IOException;
import java.util.Collections;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
import static org.hamcrest.Matchers.startsWith;
public class RatedDocumentTests extends ESTestCase {
@ -50,6 +53,17 @@ public class RatedDocumentTests extends ESTestCase {
}
}
public void testXContentParsingIsNotLenient() throws IOException {
RatedDocument testItem = createRatedDocument();
XContentType xContentType = randomFrom(XContentType.values());
BytesReference originalBytes = toShuffledXContent(testItem, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());
BytesReference withRandomFields = insertRandomFields(xContentType, originalBytes, null, random());
try (XContentParser parser = createParser(xContentType.xContent(), withRandomFields)) {
Exception exception = expectThrows(IllegalArgumentException.class, () -> RatedDocument.fromXContent(parser));
assertThat(exception.getMessage(), startsWith("[rated_document] unknown field"));
}
}
public void testSerialization() throws IOException {
RatedDocument original = createRatedDocument();
RatedDocument deserialized = ESTestCase.copyWriteable(original, new NamedWriteableRegistry(Collections.emptyList()),

View File

@ -19,6 +19,8 @@
package org.elasticsearch.index.rankeval;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
@ -48,6 +50,8 @@ import java.util.stream.Stream;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
import static org.hamcrest.Matchers.startsWith;
public class RatedRequestsTests extends ESTestCase {
@ -123,6 +127,22 @@ public class RatedRequestsTests extends ESTestCase {
}
}
public void testXContentParsingIsNotLenient() throws IOException {
RatedRequest testItem = createTestItem(randomBoolean());
XContentType xContentType = randomFrom(XContentType.values());
BytesReference originalBytes = toShuffledXContent(testItem, xContentType, ToXContent.EMPTY_PARAMS, randomBoolean());
BytesReference withRandomFields = insertRandomFields(xContentType, originalBytes, null, random());
try (XContentParser parser = createParser(xContentType.xContent(), withRandomFields)) {
Exception exception = expectThrows(Exception.class, () -> RatedRequest.fromXContent(parser));
if (exception instanceof IllegalArgumentException) {
assertThat(exception.getMessage(), startsWith("[request] unknown field"));
}
if (exception instanceof ParsingException) {
assertThat(exception.getMessage(), startsWith("[request] failed to parse field"));
}
}
}
public void testSerialization() throws IOException {
RatedRequest original = createTestItem(randomBoolean());
RatedRequest deserialized = copy(original);