From 85e0f1a3bb40938346d6dd2996a0e1cdbeddc456 Mon Sep 17 00:00:00 2001 From: Geir Storli Date: Tue, 12 Jul 2022 14:52:33 +0000 Subject: Validate that complex fields do not have struct fields with 'indexing: index'. This is not supported for indexed search clusters, and should be replaced with 'indexing: attribute'. Validation is not executed for imported fields as those are validated in the parent schema. --- .../schema/document/ImmutableImportedSDField.java | 5 + .../yahoo/schema/document/ImmutableSDField.java | 8 ++ .../java/com/yahoo/schema/document/SDField.java | 9 ++ .../ComplexAttributeFieldsValidator.java | 89 ------------- ...exFieldsWithStructFieldAttributesValidator.java | 89 +++++++++++++ ...mplexFieldsWithStructFieldIndexesValidator.java | 72 ++++++++++ .../model/application/validation/Validation.java | 3 +- .../ComplexAttributeFieldsValidatorTestCase.java | 124 ----------------- .../validation/ComplexFieldsValidatorTestCase.java | 148 +++++++++++++++++++++ 9 files changed, 333 insertions(+), 214 deletions(-) delete mode 100644 config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java create mode 100644 config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldAttributesValidator.java create mode 100644 config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldIndexesValidator.java delete mode 100644 config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java create mode 100644 config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexFieldsValidatorTestCase.java (limited to 'config-model') diff --git a/config-model/src/main/java/com/yahoo/schema/document/ImmutableImportedSDField.java b/config-model/src/main/java/com/yahoo/schema/document/ImmutableImportedSDField.java index 335942de99d..221280dc1e4 100644 --- a/config-model/src/main/java/com/yahoo/schema/document/ImmutableImportedSDField.java +++ b/config-model/src/main/java/com/yahoo/schema/document/ImmutableImportedSDField.java @@ -82,6 +82,11 @@ public class ImmutableImportedSDField implements ImmutableSDField { return importedField.targetField().wasConfiguredToDoAttributing(); } + @Override + public boolean wasConfiguredToDoIndexing() { + return importedField.targetField().wasConfiguredToDoIndexing(); + } + @Override public DataType getDataType() { return importedField.targetField().getDataType(); diff --git a/config-model/src/main/java/com/yahoo/schema/document/ImmutableSDField.java b/config-model/src/main/java/com/yahoo/schema/document/ImmutableSDField.java index 44e442811ba..37cbb3a8171 100644 --- a/config-model/src/main/java/com/yahoo/schema/document/ImmutableSDField.java +++ b/config-model/src/main/java/com/yahoo/schema/document/ImmutableSDField.java @@ -45,6 +45,14 @@ public interface ImmutableSDField { */ boolean wasConfiguredToDoAttributing(); + /** + * Whether this field at some time was configured to do indexing. + * + * This function can typically return a different value than doesIndexing(), + * which uses the final state of the underlying indexing script instead. + */ + boolean wasConfiguredToDoIndexing(); + DataType getDataType(); Index getIndex(String name); diff --git a/config-model/src/main/java/com/yahoo/schema/document/SDField.java b/config-model/src/main/java/com/yahoo/schema/document/SDField.java index 943d6c6fc14..2d79d89a2a0 100644 --- a/config-model/src/main/java/com/yahoo/schema/document/SDField.java +++ b/config-model/src/main/java/com/yahoo/schema/document/SDField.java @@ -117,6 +117,7 @@ public class SDField extends Field implements TypedKey, ImmutableSDField { private boolean isExtraField = false; private boolean wasConfiguredToDoAttributing = false; + private boolean wasConfiguredToDoIndexing = false; /** * Creates a new field. This method is only used to create reserved fields. @@ -381,6 +382,11 @@ public class SDField extends Field implements TypedKey, ImmutableSDField { return wasConfiguredToDoAttributing; } + @Override + public boolean wasConfiguredToDoIndexing() { + return wasConfiguredToDoIndexing; + } + /** Parse an indexing expression which will use the simple linguistics implementation suitable for testing */ public void parseIndexingScript(String script) { parseIndexingScript(script, new SimpleLinguistics(), Embedder.throwsOnUse.asMap()); @@ -408,6 +414,9 @@ public class SDField extends Field implements TypedKey, ImmutableSDField { if (!wasConfiguredToDoAttributing()) { wasConfiguredToDoAttributing = doesAttributing(); } + if (!wasConfiguredToDoIndexing()) { + wasConfiguredToDoIndexing = doesIndexing(); + } if (!usesStructOrMap()) { new ExpressionVisitor() { diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java deleted file mode 100644 index 01bf846d4cb..00000000000 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.model.application.validation; - -import com.yahoo.config.model.deploy.DeployState; -import com.yahoo.schema.Schema; -import com.yahoo.schema.derived.SchemaInfo; -import com.yahoo.schema.document.ComplexAttributeFieldUtils; -import com.yahoo.schema.document.GeoPos; -import com.yahoo.schema.document.ImmutableSDField; -import com.yahoo.vespa.model.VespaModel; -import com.yahoo.vespa.model.search.SearchCluster; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Validates that complex fields (of type struct or map) that have struct field attributes are supported. - * - * Only applies for indexed search clusters. - * - * @author geirst - */ -public class ComplexAttributeFieldsValidator extends Validator { - - @Override - public void validate(VespaModel model, DeployState deployState) { - List searchClusters = model.getSearchClusters(); - for (SearchCluster cluster : searchClusters) { - if (cluster.isStreaming()) continue; - - for (SchemaInfo spec : cluster.schemas().values()) { - validateComplexFields(cluster.getClusterName(), spec.fullSchema()); - } - } - } - - private static void validateComplexFields(String clusterName, Schema schema) { - String unsupportedFields = schema.allFields() - .filter(field -> isUnsupportedComplexField(field)) - .map(ComplexAttributeFieldsValidator::toString) - .collect(Collectors.joining(", ")); - - if (!unsupportedFields.isEmpty()) { - throw new IllegalArgumentException( - String.format("For cluster '%s', search '%s': The following complex fields do not support using struct field attributes: %s. " + - "Only supported for the following complex field types: array or map of struct with primitive types, map of primitive types. " + - "The supported primitive types are: byte, int, long, float, double and string", - clusterName, schema.getName(), unsupportedFields)); - } - } - - private static boolean isUnsupportedComplexField(ImmutableSDField field) { - return (field.usesStructOrMap() && - !isSupportedComplexField(field) && - hasStructFieldAttributes(field.getStructFields())); - } - - private static boolean isSupportedComplexField(ImmutableSDField field) { - return (ComplexAttributeFieldUtils.isSupportedComplexField(field) || - GeoPos.isAnyPos(field)); - } - - private static String toString(ImmutableSDField field) { - return field.getName() + " (" + String.join(", ", getStructFieldAttributes(field.getStructFields(), false)) + ")"; - } - - private static boolean hasStructFieldAttributes(Collection structFields) { - return !getStructFieldAttributes(structFields, true).isEmpty(); - } - - private static List getStructFieldAttributes(Collection structFields, - boolean returnAllTypes) { - var result = new ArrayList(); - for (var structField : structFields) { - for (var attr : structField.getAttributes().values()) { - if (returnAllTypes || !ComplexAttributeFieldUtils.isPrimitiveType(attr)) { - result.add(attr.getName()); - } - } - if (structField.usesStructOrMap() && structField.wasConfiguredToDoAttributing()) { - result.add(structField.getName()); - } - result.addAll(getStructFieldAttributes(structField.getStructFields(), returnAllTypes)); - } - return result; - } -} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldAttributesValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldAttributesValidator.java new file mode 100644 index 00000000000..8515c34a377 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldAttributesValidator.java @@ -0,0 +1,89 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.application.validation; + +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.schema.Schema; +import com.yahoo.schema.derived.SchemaInfo; +import com.yahoo.schema.document.ComplexAttributeFieldUtils; +import com.yahoo.schema.document.GeoPos; +import com.yahoo.schema.document.ImmutableSDField; +import com.yahoo.vespa.model.VespaModel; +import com.yahoo.vespa.model.search.SearchCluster; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Validates that complex fields (of type struct or map) that have struct field attributes are supported. + * + * Only applies for indexed search clusters. + * + * @author geirst + */ +public class ComplexFieldsWithStructFieldAttributesValidator extends Validator { + + @Override + public void validate(VespaModel model, DeployState deployState) { + List searchClusters = model.getSearchClusters(); + for (SearchCluster cluster : searchClusters) { + if (cluster.isStreaming()) continue; + + for (SchemaInfo spec : cluster.schemas().values()) { + validateComplexFields(cluster.getClusterName(), spec.fullSchema()); + } + } + } + + private static void validateComplexFields(String clusterName, Schema schema) { + String unsupportedFields = schema.allFields() + .filter(field -> isUnsupportedComplexField(field)) + .map(ComplexFieldsWithStructFieldAttributesValidator::toString) + .collect(Collectors.joining(", ")); + + if (!unsupportedFields.isEmpty()) { + throw new IllegalArgumentException( + String.format("For cluster '%s', search '%s': The following complex fields do not support using struct field attributes: %s. " + + "Only supported for the following complex field types: array or map of struct with primitive types, map of primitive types. " + + "The supported primitive types are: byte, int, long, float, double and string", + clusterName, schema.getName(), unsupportedFields)); + } + } + + private static boolean isUnsupportedComplexField(ImmutableSDField field) { + return (field.usesStructOrMap() && + !isSupportedComplexField(field) && + hasStructFieldAttributes(field.getStructFields())); + } + + private static boolean isSupportedComplexField(ImmutableSDField field) { + return (ComplexAttributeFieldUtils.isSupportedComplexField(field) || + GeoPos.isAnyPos(field)); + } + + private static String toString(ImmutableSDField field) { + return field.getName() + " (" + String.join(", ", getStructFieldAttributes(field.getStructFields(), false)) + ")"; + } + + private static boolean hasStructFieldAttributes(Collection structFields) { + return !getStructFieldAttributes(structFields, true).isEmpty(); + } + + private static List getStructFieldAttributes(Collection structFields, + boolean returnAllTypes) { + var result = new ArrayList(); + for (var structField : structFields) { + for (var attr : structField.getAttributes().values()) { + if (returnAllTypes || !ComplexAttributeFieldUtils.isPrimitiveType(attr)) { + result.add(attr.getName()); + } + } + if (structField.usesStructOrMap() && structField.wasConfiguredToDoAttributing()) { + result.add(structField.getName()); + } + result.addAll(getStructFieldAttributes(structField.getStructFields(), returnAllTypes)); + } + return result; + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldIndexesValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldIndexesValidator.java new file mode 100644 index 00000000000..a18ce7e245d --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexFieldsWithStructFieldIndexesValidator.java @@ -0,0 +1,72 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.application.validation; + +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.schema.Schema; +import com.yahoo.schema.document.ImmutableSDField; +import com.yahoo.vespa.model.VespaModel; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Validates that complex fields (of type struct or map) do not have any struct fields with 'indexing: index'. + * This is not supported and will confuse the user if not validated. + * + * Only applies for indexed search clusters. + * + * @author geirst + */ +public class ComplexFieldsWithStructFieldIndexesValidator extends Validator { + + @Override + public void validate(VespaModel model, DeployState deployState) { + for (var cluster : model.getSearchClusters()) { + if (cluster.isStreaming()) { + continue; + } + for (var spec : cluster.schemas().values()) { + validateComplexFields(cluster.getClusterName(), spec.fullSchema()); + } + } + } + + private static void validateComplexFields(String clusterName, Schema schema) { + String unsupportedFields = schema.allFields() + .filter(field -> hasStructFieldsWithIndex(field)) + .map(ComplexFieldsWithStructFieldIndexesValidator::toString) + .collect(Collectors.joining(", ")); + + if (!unsupportedFields.isEmpty()) { + throw new IllegalArgumentException( + String.format("For cluster '%s', schema '%s': The following complex fields have struct fields with 'indexing: index' which is not supported: %s. " + + "Change to 'indexing: attribute' instead", + clusterName, schema.getName(), unsupportedFields)); + } + } + + private static boolean hasStructFieldsWithIndex(ImmutableSDField field) { + return (!field.isImportedField() && field.usesStructOrMap() && hasStructFieldsWithIndex(field.getStructFields())); + } + + private static String toString(ImmutableSDField field) { + return field.getName() + " (" + String.join(", ", getStructFieldsWithIndex(field.getStructFields())) + ")"; + } + + private static boolean hasStructFieldsWithIndex(Collection structFields) { + return !getStructFieldsWithIndex(structFields).isEmpty(); + } + + private static List getStructFieldsWithIndex(Collection structFields) { + var result = new ArrayList(); + for (var structField : structFields) { + if (structField.wasConfiguredToDoIndexing()) { + result.add(structField.getName()); + } + result.addAll(getStructFieldsWithIndex(structField.getStructFields())); + } + return result; + } +} diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java index ce61d3edc3b..dab1eeccc96 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/Validation.java @@ -71,7 +71,8 @@ public class Validation { new SchemasDirValidator().validate(model, deployState); new BundleValidator().validate(model, deployState); new SearchDataTypeValidator().validate(model, deployState); - new ComplexAttributeFieldsValidator().validate(model, deployState); + new ComplexFieldsWithStructFieldAttributesValidator().validate(model, deployState); + new ComplexFieldsWithStructFieldIndexesValidator().validate(model, deployState); new StreamingValidator().validate(model, deployState); new RankSetupValidator(validationParameters.ignoreValidationErrors()).validate(model, deployState); new NoPrefixForIndexes().validate(model, deployState); diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java deleted file mode 100644 index 85050aa0cf9..00000000000 --- a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.model.application.validation; - -import com.yahoo.config.application.api.ApplicationPackage; -import com.yahoo.config.model.NullConfigModelRegistry; -import com.yahoo.config.model.api.ValidationParameters; -import com.yahoo.config.model.api.ValidationParameters.CheckRouting; -import com.yahoo.config.model.deploy.DeployState; -import com.yahoo.config.model.test.MockApplicationPackage; -import com.yahoo.vespa.model.VespaModel; -import com.yahoo.vespa.model.content.utils.ContentClusterBuilder; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.xml.sax.SAXException; - -import java.io.IOException; -import java.util.List; - -import static com.yahoo.config.model.test.TestUtil.joinLines; - -/** - * @author geirst - */ -public class ComplexAttributeFieldsValidatorTestCase { - - @SuppressWarnings("deprecation") - @Rule - public final ExpectedException exceptionRule = ExpectedException.none(); - - @Test - public void throws_exception_when_unsupported_complex_fields_have_struct_field_attributes() throws IOException, SAXException { - exceptionRule.expect(IllegalArgumentException.class); - exceptionRule.expectMessage(getExpectedMessage("struct_array (struct_array.f1), struct_map (struct_map.value.f1)")); - createModelAndValidate(joinLines("search test {", - " document test {", - " struct s { field f1 type array {} }", - " field struct_array type array {", - " struct-field f1 { indexing: attribute }", - " }", - " field struct_map type map {", - " struct-field key { indexing: attribute }", - " struct-field value.f1 { indexing: attribute }", - " }", - " }", - "}")); - } - - @Test - public void throws_exception_when_nested_struct_array_is_specified_as_struct_field_attribute() throws IOException, SAXException { - exceptionRule.expect(IllegalArgumentException.class); - exceptionRule.expectMessage(getExpectedMessage("docTopics (docTopics.topics)")); - createModelAndValidate(joinLines( - "schema test {", - "document test {", - "struct topic {", - " field id type string {}", - " field label type string {}", - "}", - "struct docTopic {", - " field id type string {}", - " field topics type array {}", - "}", - "field docTopics type array {", - " indexing: summary", - " struct-field id { indexing: attribute }", - " struct-field topics { indexing: attribute }", - "}", - "}", - "}")); - } - - private String getExpectedMessage(String unsupportedFields) { - return "For cluster 'mycluster', search 'test': " + - "The following complex fields do not support using struct field attributes: " + - unsupportedFields + ". " + - "Only supported for the following complex field types: array or map of struct with primitive types, map of primitive types"; - } - - @Test - public void validation_passes_when_only_supported_struct_field_attributes_are_used() throws IOException, SAXException { - createModelAndValidate(joinLines("search test {", - " document test {", - " struct s1 {", - " field f1 type string {}", - " field f2 type int {}", - " }", - " struct s2 {", - " field f3 type string {}", - " field f4 type array {}", - " field f5 type array {}", - " }", - " field struct_array type array {", - " struct-field f3 { indexing: attribute }", - " }", - " field struct_map type map {", - " struct-field key { indexing: attribute }", - " struct-field value.f3 { indexing: attribute }", - " }", - " }", - "}")); - } - - private static void createModelAndValidate(String schema) throws IOException, SAXException { - DeployState deployState = createDeployState(servicesXml(), schema); - VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); - ValidationParameters validationParameters = new ValidationParameters(CheckRouting.FALSE); - new Validation().validate(model, validationParameters, deployState); - } - - private static DeployState createDeployState(String servicesXml, String schema) { - ApplicationPackage app = new MockApplicationPackage.Builder() - .withServices(servicesXml) - .withSchemas(List.of(schema)) - .build(); - return new DeployState.Builder().applicationPackage(app).build(); - } - - private static String servicesXml() { - return joinLines("", - new ContentClusterBuilder().getXml(), - ""); - } -} diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexFieldsValidatorTestCase.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexFieldsValidatorTestCase.java new file mode 100644 index 00000000000..11e4ae855a5 --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexFieldsValidatorTestCase.java @@ -0,0 +1,148 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.model.application.validation; + +import com.yahoo.config.application.api.ApplicationPackage; +import com.yahoo.config.model.NullConfigModelRegistry; +import com.yahoo.config.model.api.ValidationParameters; +import com.yahoo.config.model.api.ValidationParameters.CheckRouting; +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.config.model.test.MockApplicationPackage; +import com.yahoo.vespa.model.VespaModel; +import com.yahoo.vespa.model.content.utils.ContentClusterBuilder; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.xml.sax.SAXException; + +import java.io.IOException; +import java.util.List; + +import static com.yahoo.config.model.test.TestUtil.joinLines; + +/** + * @author geirst + */ +public class ComplexFieldsValidatorTestCase { + + @SuppressWarnings("deprecation") + @Rule + public final ExpectedException exceptionRule = ExpectedException.none(); + + @Test + public void throws_exception_when_unsupported_complex_fields_have_struct_field_attributes() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage(getExpectedMessage("struct_array (struct_array.f1), struct_map (struct_map.value.f1)")); + createModelAndValidate(joinLines("search test {", + " document test {", + " struct s { field f1 type array {} }", + " field struct_array type array {", + " struct-field f1 { indexing: attribute }", + " }", + " field struct_map type map {", + " struct-field key { indexing: attribute }", + " struct-field value.f1 { indexing: attribute }", + " }", + " }", + "}")); + } + + @Test + public void throws_exception_when_nested_struct_array_is_specified_as_struct_field_attribute() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage(getExpectedMessage("docTopics (docTopics.topics)")); + createModelAndValidate(joinLines( + "schema test {", + "document test {", + "struct topic {", + " field id type string {}", + " field label type string {}", + "}", + "struct docTopic {", + " field id type string {}", + " field topics type array {}", + "}", + "field docTopics type array {", + " indexing: summary", + " struct-field id { indexing: attribute }", + " struct-field topics { indexing: attribute }", + "}", + "}", + "}")); + } + + private String getExpectedMessage(String unsupportedFields) { + return "For cluster 'mycluster', search 'test': " + + "The following complex fields do not support using struct field attributes: " + + unsupportedFields + ". " + + "Only supported for the following complex field types: array or map of struct with primitive types, map of primitive types"; + } + + @Test + public void throws_when_complex_fields_have_struct_fields_with_index() throws IOException, SAXException { + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage("For cluster 'mycluster', schema 'test': " + + "The following complex fields have struct fields with 'indexing: index' which is not supported: " + + "topics (topics.id, topics.label). Change to 'indexing: attribute' instead"); + createModelAndValidate(joinLines( + "schema test {", + "document test {", + "struct topic {", + " field id type string {}", + " field label type string {}", + " field desc type string {}", + "}", + "field topics type array {", + " indexing: summary", + " struct-field id { indexing: index }", + " struct-field label { indexing: index | attribute }", + " struct-field desc { indexing: attribute }", + "}", + "}", + "}")); + } + + @Test + public void validation_passes_when_only_supported_struct_field_attributes_are_used() throws IOException, SAXException { + createModelAndValidate(joinLines("search test {", + " document test {", + " struct s1 {", + " field f1 type string {}", + " field f2 type int {}", + " }", + " struct s2 {", + " field f3 type string {}", + " field f4 type array {}", + " field f5 type array {}", + " }", + " field struct_array type array {", + " struct-field f3 { indexing: attribute }", + " }", + " field struct_map type map {", + " struct-field key { indexing: attribute }", + " struct-field value.f3 { indexing: attribute }", + " }", + " }", + "}")); + } + + private static void createModelAndValidate(String schema) throws IOException, SAXException { + DeployState deployState = createDeployState(servicesXml(), schema); + VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); + ValidationParameters validationParameters = new ValidationParameters(CheckRouting.FALSE); + new Validation().validate(model, validationParameters, deployState); + } + + private static DeployState createDeployState(String servicesXml, String schema) { + ApplicationPackage app = new MockApplicationPackage.Builder() + .withServices(servicesXml) + .withSchemas(List.of(schema)) + .build(); + return new DeployState.Builder().applicationPackage(app).build(); + } + + private static String servicesXml() { + return joinLines("", + new ContentClusterBuilder().getXml(), + ""); + } +} -- cgit v1.2.3