summaryrefslogtreecommitdiffstats
path: root/config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java
diff options
context:
space:
mode:
Diffstat (limited to 'config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java')
-rw-r--r--config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java443
1 files changed, 443 insertions, 0 deletions
diff --git a/config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java b/config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java
new file mode 100644
index 00000000000..67d8ce4ff78
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/schema/SchemaTestCase.java
@@ -0,0 +1,443 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.schema;
+
+import com.yahoo.schema.document.Stemming;
+import com.yahoo.schema.parser.ParseException;
+import com.yahoo.schema.processing.ImportedFieldsResolver;
+import com.yahoo.schema.processing.OnnxModelTypeResolver;
+import com.yahoo.vespa.documentmodel.DocumentSummary;
+import com.yahoo.vespa.model.test.utils.DeployLoggerStub;
+import org.junit.Test;
+
+import static com.yahoo.config.model.test.TestUtil.joinLines;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Schema tests that don't depend on files.
+ *
+ * @author bratseth
+ */
+public class SchemaTestCase {
+
+ @Test
+ public void testValidationOfInheritedSchema() throws ParseException {
+ try {
+ String schema = joinLines(
+ "schema test inherits nonesuch {" +
+ " document test inherits nonesuch {" +
+ " }" +
+ "}");
+ DeployLoggerStub logger = new DeployLoggerStub();
+ ApplicationBuilder.createFromStrings(logger, schema);
+ assertEquals("schema 'test' inherits 'nonesuch', but this schema does not exist",
+ logger.entries.get(0).message);
+ fail("Expected exception");
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("schema 'test' inherits 'nonesuch', but this schema does not exist", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testValidationOfSchemaAndDocumentInheritanceConsistency() throws ParseException {
+ try {
+ String parent = joinLines(
+ "schema parent {" +
+ " document parent {" +
+ " field pf1 type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ "}");
+ String child = joinLines(
+ "schema child inherits parent {" +
+ " document child {" +
+ " field cf1 type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ "}");
+ ApplicationBuilder.createFromStrings(new DeployLoggerStub(), parent, child);
+ fail("Expected exception");
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("schema 'child' inherits 'parent', " +
+ "but its document type does not inherit the parent's document type"
+ , e.getMessage());
+ }
+ }
+
+ @Test
+ public void testSchemaInheritance() throws ParseException {
+ String parentLines = joinLines(
+ "schema parent {" +
+ " document parent {" +
+ " field pf1 type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ " fieldset parent_set {" +
+ " fields: pf1" +
+ " }" +
+ " stemming: none" +
+ " index parent_index {" +
+ " stemming: best" +
+ " }" +
+ " field parent_field type string {" +
+ " indexing: input pf1 | lowercase | index | attribute | summary" +
+ " }" +
+ " rank-profile parent_profile {" +
+ " }" +
+ " constant parent_constant {" +
+ " file: constants/my_constant_tensor_file.json" +
+ " type: tensor<float>(x{},y{})" +
+ " }" +
+ " onnx-model parent_model {" +
+ " file: models/my_model.onnx" +
+ " }" +
+ " document-summary parent_summary {" +
+ " summary pf1 type string {}" +
+ " }" +
+ " import field parentschema_ref.name as parent_imported {}" +
+ " raw-as-base64-in-summary" +
+ "}");
+ String child1Lines = joinLines(
+ "schema child1 inherits parent {" +
+ " document child1 inherits parent {" +
+ " field c1f1 type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ " fieldset child1_set {" +
+ " fields: c1f1, pf1" +
+ " }" +
+ " stemming: shortest" +
+ " index child1_index {" +
+ " stemming: shortest" +
+ " }" +
+ " field child1_field type string {" +
+ " indexing: input pf1 | lowercase | index | attribute | summary" +
+ " }" +
+ " rank-profile child1_profile inherits parent_profile {" +
+ " constants {" +
+ " child1_constant tensor<float>(x{},y{}): file:constants/my_constant_tensor_file.json" +
+ " }" +
+ " }" +
+ " onnx-model child1_model {" +
+ " file: models/my_model.onnx" +
+ " }" +
+ " document-summary child1_summary inherits parent_summary {" +
+ " summary c1f1 type string {}" +
+ " }" +
+ " import field parentschema_ref.name as child1_imported {}" +
+ "}");
+ String child2Lines = joinLines(
+ "schema child2 inherits parent {" +
+ " document child2 inherits parent {" +
+ " field c2f1 type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ " fieldset child2_set {" +
+ " fields: c2f1, pf1" +
+ " }" +
+ " stemming: shortest" +
+ " index child2_index {" +
+ " stemming: shortest" +
+ " }" +
+ " field child2_field type string {" +
+ " indexing: input pf1 | lowercase | index | attribute | summary" +
+ " }" +
+ " rank-profile child2_profile inherits parent_profile {" +
+ " }" +
+ " constant child2_constant {" +
+ " file: constants/my_constant_tensor_file.json" +
+ " type: tensor<float>(x{},y{})" +
+ " }" +
+ " onnx-model child2_model {" +
+ " file: models/my_model.onnx" +
+ " }" +
+ " document-summary child2_summary inherits parent_summary {" +
+ " summary c2f1 type string {}" +
+ " }" +
+ " import field parentschema_ref.name as child2_imported {}" +
+ "}");
+
+ ApplicationBuilder builder = new ApplicationBuilder(new DeployLoggerStub());
+ builder.processorsToSkip().add(OnnxModelTypeResolver.class); // Avoid discovering the Onnx model referenced does not exist
+ builder.processorsToSkip().add(ImportedFieldsResolver.class); // Avoid discovering the document reference leads nowhere
+ builder.addSchema(parentLines);
+ builder.addSchema(child1Lines);
+ builder.addSchema(child2Lines);
+ builder.build(true);
+ var application = builder.application();
+
+ var child1 = application.schemas().get("child1");
+ assertEquals("pf1", child1.fieldSets().userFieldSets().get("parent_set").getFieldNames().stream().findFirst().get());
+ assertEquals("[c1f1, pf1]", child1.fieldSets().userFieldSets().get("child1_set").getFieldNames().toString());
+ assertEquals(Stemming.SHORTEST, child1.getStemming());
+ assertEquals(Stemming.BEST, child1.getIndex("parent_index").getStemming());
+ assertEquals(Stemming.SHORTEST, child1.getIndex("child1_index").getStemming());
+ assertNotNull(child1.getField("parent_field"));
+ assertNotNull(child1.getField("child1_field"));
+ assertNotNull(child1.getExtraField("parent_field"));
+ assertNotNull(child1.getExtraField("child1_field"));
+ assertNotNull(builder.getRankProfileRegistry().get(child1, "parent_profile"));
+ assertNotNull(builder.getRankProfileRegistry().get(child1, "child1_profile"));
+ var child1profile = builder.getRankProfileRegistry().get(child1, "child1_profile");
+ assertEquals("parent_profile", builder.getRankProfileRegistry().get(child1, "child1_profile").inheritedNames().get(0));
+ assertNotNull(child1.constants().get(FeatureNames.asConstantFeature("parent_constant")));
+ assertNotNull(child1profile.constants().get(FeatureNames.asConstantFeature("child1_constant")));
+ assertTrue(child1.constants().containsKey(FeatureNames.asConstantFeature("parent_constant")));
+ assertTrue(child1profile.constants().containsKey(FeatureNames.asConstantFeature("child1_constant")));
+ assertTrue(child1profile.constants().containsKey(FeatureNames.asConstantFeature("parent_constant")));
+ assertNotNull(child1.onnxModels().get("parent_model"));
+ assertNotNull(child1.onnxModels().get("child1_model"));
+ assertTrue(child1.onnxModels().containsKey("parent_model"));
+ assertTrue(child1.onnxModels().containsKey("child1_model"));
+ assertNotNull(child1.getSummary("parent_summary"));
+ assertNotNull(child1.getSummary("child1_summary"));
+ assertEquals("parent_summary", child1.getSummary("child1_summary").inherited().get().getName());
+ assertTrue(child1.getSummaries().containsKey("parent_summary"));
+ assertTrue(child1.getSummaries().containsKey("child1_summary"));
+ assertNotNull(child1.getSummaryField("pf1"));
+ assertNotNull(child1.getSummaryField("c1f1"));
+ assertNotNull(child1.getExplicitSummaryField("pf1"));
+ assertNotNull(child1.getExplicitSummaryField("c1f1"));
+ assertNotNull(child1.getUniqueNamedSummaryFields().get("pf1"));
+ assertNotNull(child1.getUniqueNamedSummaryFields().get("c1f1"));
+ assertNotNull(child1.temporaryImportedFields().get().fields().get("parent_imported"));
+ assertNotNull(child1.temporaryImportedFields().get().fields().get("child1_imported"));
+
+ var child2 = application.schemas().get("child2");
+ assertEquals("pf1", child2.fieldSets().userFieldSets().get("parent_set").getFieldNames().stream().findFirst().get());
+ assertEquals("[c2f1, pf1]", child2.fieldSets().userFieldSets().get("child2_set").getFieldNames().toString());
+ assertEquals(Stemming.SHORTEST, child2.getStemming());
+ assertEquals(Stemming.BEST, child2.getIndex("parent_index").getStemming());
+ assertEquals(Stemming.SHORTEST, child2.getIndex("child2_index").getStemming());
+ assertNotNull(child2.getField("parent_field"));
+ assertNotNull(child2.getField("child2_field"));
+ assertNotNull(child2.getExtraField("parent_field"));
+ assertNotNull(child2.getExtraField("child2_field"));
+ assertNotNull(builder.getRankProfileRegistry().get(child2, "parent_profile"));
+ assertNotNull(builder.getRankProfileRegistry().get(child2, "child2_profile"));
+ assertEquals("parent_profile", builder.getRankProfileRegistry().get(child2, "child2_profile").inheritedNames().get(0));
+ assertNotNull(child2.constants().get(FeatureNames.asConstantFeature("parent_constant")));
+ assertNotNull(child2.constants().get(FeatureNames.asConstantFeature("child2_constant")));
+ assertTrue(child2.constants().containsKey(FeatureNames.asConstantFeature("parent_constant")));
+ assertTrue(child2.constants().containsKey(FeatureNames.asConstantFeature("child2_constant")));
+ assertNotNull(child2.onnxModels().get("parent_model"));
+ assertNotNull(child2.onnxModels().get("child2_model"));
+ assertTrue(child2.onnxModels().containsKey("parent_model"));
+ assertTrue(child2.onnxModels().containsKey("child2_model"));
+ assertNotNull(child2.getSummary("parent_summary"));
+ assertNotNull(child2.getSummary("child2_summary"));
+ assertEquals("parent_summary", child2.getSummary("child2_summary").inherited().get().getName());
+ assertTrue(child2.getSummaries().containsKey("parent_summary"));
+ assertTrue(child2.getSummaries().containsKey("child2_summary"));
+ assertNotNull(child2.getSummaryField("pf1"));
+ assertNotNull(child2.getSummaryField("c2f1"));
+ assertNotNull(child2.getExplicitSummaryField("pf1"));
+ assertNotNull(child2.getExplicitSummaryField("c2f1"));
+ assertNotNull(child2.getUniqueNamedSummaryFields().get("pf1"));
+ assertNotNull(child2.getUniqueNamedSummaryFields().get("c2f1"));
+ assertNotNull(child2.temporaryImportedFields().get().fields().get("parent_imported"));
+ assertNotNull(child2.temporaryImportedFields().get().fields().get("child2_imported"));
+ DocumentSummary child2DefaultSummary = child2.getSummary("default");
+ assertEquals(6, child2DefaultSummary.getSummaryFields().size());
+ assertTrue(child2DefaultSummary.getSummaryFields().containsKey("child2_field"));
+ assertTrue(child2DefaultSummary.getSummaryFields().containsKey("parent_field"));
+ assertTrue(child2DefaultSummary.getSummaryFields().containsKey("pf1"));
+ assertTrue(child2DefaultSummary.getSummaryFields().containsKey("c2f1"));
+ DocumentSummary child2AttributeprefetchSummary = child2.getSummary("attributeprefetch");
+ assertEquals(4, child2AttributeprefetchSummary.getSummaryFields().size());
+ assertTrue(child2AttributeprefetchSummary.getSummaryFields().containsKey("child2_field"));
+ assertTrue(child2AttributeprefetchSummary.getSummaryFields().containsKey("parent_field"));
+ }
+
+ @Test
+ public void testSchemaInheritanceEmptyChildren() throws ParseException {
+ String parentLines = joinLines(
+ "schema parent {" +
+ " document parent {" +
+ " field pf1 type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ " fieldset parent_set {" +
+ " fields: pf1" +
+ " }" +
+ " stemming: none" +
+ " index parent_index {" +
+ " stemming: best" +
+ " }" +
+ " field parent_field type string {" +
+ " indexing: input pf1 | lowercase | index | attribute | summary" +
+ " }" +
+ " rank-profile parent_profile {" +
+ " }" +
+ " constant parent_constant {" +
+ " file: constants/my_constant_tensor_file.json" +
+ " type: tensor<float>(x{},y{})" +
+ " }" +
+ " onnx-model parent_model {" +
+ " file: models/my_model.onnx" +
+ " }" +
+ " document-summary parent_summary {" +
+ " summary pf1 type string {}" +
+ " }" +
+ " import field parentschema_ref.name as parent_imported {}" +
+ " raw-as-base64-in-summary" +
+ "}");
+ String childLines = joinLines(
+ "schema child inherits parent {" +
+ " document child inherits parent {" +
+ " field cf1 type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ "}");
+ String grandchildLines = joinLines(
+ "schema grandchild inherits child {" +
+ " document grandchild inherits child {" +
+ " field gf1 type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ "}");
+
+ ApplicationBuilder builder = new ApplicationBuilder(new DeployLoggerStub());
+ builder.processorsToSkip().add(OnnxModelTypeResolver.class); // Avoid discovering the Onnx model referenced does not exist
+ builder.processorsToSkip().add(ImportedFieldsResolver.class); // Avoid discovering the document reference leads nowhere
+ builder.addSchema(parentLines);
+ builder.addSchema(childLines);
+ builder.addSchema(grandchildLines);
+ builder.build(true);
+ var application = builder.application();
+
+ assertInheritedFromParent(application.schemas().get("child"), builder.getRankProfileRegistry());
+ assertInheritedFromParent(application.schemas().get("grandchild"), builder.getRankProfileRegistry());
+ }
+
+ @Test
+ public void testInheritingMultipleRankProfilesWithOverlappingConstructsIsDisallowed1() throws ParseException {
+ try {
+ String profile = joinLines(
+ "schema test {" +
+ " document test {" +
+ " field title type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ " rank-profile r1 {" +
+ " first-phase {" +
+ " expression: fieldMatch(title)" +
+ " }" +
+ " }" +
+ " rank-profile r2 {" +
+ " first-phase {" +
+ " expression: fieldMatch(title)" +
+ " }" +
+ " }" +
+ " rank-profile r3 inherits r1, r2 {" +
+ " }" +
+ "}");
+ ApplicationBuilder.createFromStrings(new DeployLoggerStub(), profile);
+ fail("Expected exception");
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("Only one of the profiles inherited by rank profile 'r3' can contain first-phase expression, but it is present in multiple",
+ e.getMessage());
+ }
+ }
+
+ @Test
+ public void testInheritingMultipleRankProfilesWithOverlappingConstructsIsAllowedWhenDefinedInChild() throws ParseException {
+ String profile = joinLines(
+ "schema test {" +
+ " document test {" +
+ " field title type string {" +
+ " indexing: summary" +
+ " }" +
+ " field myFilter type string {" +
+ " indexing: attribute\n" +
+ " rank: filter" +
+ " }" +
+ " }" +
+ " rank-profile r1 {" +
+ " first-phase {" +
+ " expression: fieldMatch(title)" +
+ " }" +
+ " }" +
+ " rank-profile r2 {" +
+ " first-phase {" +
+ " expression: fieldMatch(title)" +
+ " }" +
+ " }" +
+ " rank-profile r3 inherits r1, r2 {" +
+ " first-phase {" + // Redefined here so this does not cause failure
+ " expression: nativeRank" +
+ " }" +
+ " }" +
+ "}");
+ var builder = ApplicationBuilder.createFromStrings(new DeployLoggerStub(), profile);
+ var r3 = builder.getRankProfileRegistry().resolve(builder.application().schemas().get("test").getDocument(), "r3");
+ assertEquals(1, r3.allFilterFields().size());
+ }
+
+ @Test
+ public void testInheritingMultipleRankProfilesWithOverlappingConstructsIsDisallowed2() throws ParseException {
+ try {
+ String profile = joinLines(
+ "schema test {" +
+ " document test {" +
+ " field title type string {" +
+ " indexing: summary" +
+ " }" +
+ " }" +
+ " rank-profile r1 {" +
+ " function f1() {" +
+ " expression: fieldMatch(title)" +
+ " }" +
+ " }" +
+ " rank-profile r2 {" +
+ " function f1() {" +
+ " expression: fieldMatch(title)" +
+ " }" +
+ " }" +
+ " rank-profile r3 inherits r1, r2 {" +
+ " }" +
+ "}");
+ ApplicationBuilder.createFromStrings(new DeployLoggerStub(), profile);
+ fail("Expected exception");
+ }
+ catch (IllegalArgumentException e) {
+ assertEquals("rank profile 'r3' inherits rank profile 'r2' which contains function 'f1', but this function is already defined in another profile this inherits",
+ e.getMessage());
+ }
+ }
+
+ private void assertInheritedFromParent(Schema schema, RankProfileRegistry rankProfileRegistry) {
+ assertEquals("pf1", schema.fieldSets().userFieldSets().get("parent_set").getFieldNames().stream().findFirst().get());
+ assertEquals(Stemming.NONE, schema.getStemming());
+ assertEquals(Stemming.BEST, schema.getIndex("parent_index").getStemming());
+ assertNotNull(schema.getField("parent_field"));
+ assertNotNull(schema.getExtraField("parent_field"));
+ assertNotNull(rankProfileRegistry.get(schema, "parent_profile"));
+ assertNotNull(schema.constants().get(FeatureNames.asConstantFeature("parent_constant")));
+ assertTrue(schema.constants().containsKey(FeatureNames.asConstantFeature("parent_constant")));
+ assertNotNull(schema.onnxModels().get("parent_model"));
+ assertTrue(schema.onnxModels().containsKey("parent_model"));
+ assertNotNull(schema.getSummary("parent_summary"));
+ assertTrue(schema.getSummaries().containsKey("parent_summary"));
+ assertNotNull(schema.getSummaryField("pf1"));
+ assertNotNull(schema.getExplicitSummaryField("pf1"));
+ assertNotNull(schema.getUniqueNamedSummaryFields().get("pf1"));
+ assertNotNull(schema.temporaryImportedFields().get().fields().get("parent_imported"));
+ assertTrue(schema.isRawAsBase64());
+ }
+
+}