diff --git a/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/DiscountedCumulativeGain.java b/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/DiscountedCumulativeGain.java index 8ac09993b7c..edb69fcb935 100644 --- a/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/DiscountedCumulativeGain.java +++ b/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/DiscountedCumulativeGain.java @@ -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 PARSER = new ConstructingObjectParser<>("dcg_at", true, + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("dcg_at", false, args -> { Boolean normalized = (Boolean) args[0]; Integer optK = (Integer) args[2]; diff --git a/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/EvaluationMetric.java b/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/EvaluationMetric.java index 982d8fa8d30..c67511e051f 100644 --- a/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/EvaluationMetric.java +++ b/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/EvaluationMetric.java @@ -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 diff --git a/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/RatedRequest.java b/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/RatedRequest.java index 3426a6b9be4..770c91e82a6 100644 --- a/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/RatedRequest.java +++ b/modules/rank-eval/src/main/java/org/elasticsearch/index/rankeval/RatedRequest.java @@ -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 PARSER = new ConstructingObjectParser<>("requests", + private static final ConstructingObjectParser PARSER = new ConstructingObjectParser<>("request", a -> new RatedRequest((String) a[0], (List) a[1], (SearchSourceBuilder) a[2], (Map) a[3], (String) a[4])); diff --git a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/DiscountedCumulativeGainTests.java b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/DiscountedCumulativeGainTests.java index 78bbc01be94..ea14e51512b 100644 --- a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/DiscountedCumulativeGainTests.java +++ b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/DiscountedCumulativeGainTests.java @@ -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()), diff --git a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/MeanReciprocalRankTests.java b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/MeanReciprocalRankTests.java index 42f7e32671f..8ab4f146ff7 100644 --- a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/MeanReciprocalRankTests.java +++ b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/MeanReciprocalRankTests.java @@ -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 diff --git a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/PrecisionAtKTests.java b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/PrecisionAtKTests.java index 1b6a0881b77..a6d18c3457f 100644 --- a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/PrecisionAtKTests.java +++ b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/PrecisionAtKTests.java @@ -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()), diff --git a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RankEvalSpecTests.java b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RankEvalSpecTests.java index f3385514da7..2fed2c8311b 100644 --- a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RankEvalSpecTests.java +++ b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RankEvalSpecTests.java @@ -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); diff --git a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedDocumentTests.java b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedDocumentTests.java index 5ec9692d83a..cd38233bfa9 100644 --- a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedDocumentTests.java +++ b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedDocumentTests.java @@ -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()), diff --git a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java index e46d253dd4d..0f23178c683 100644 --- a/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java +++ b/modules/rank-eval/src/test/java/org/elasticsearch/index/rankeval/RatedRequestsTests.java @@ -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);