diff options
author | Henning Baldersheim <balder@yahoo-inc.com> | 2018-06-14 22:34:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-14 22:34:23 +0200 |
commit | 7fa7d9146c62043423348f11a56ae2d1722e38d2 (patch) | |
tree | 00b912b67a548b3394de3cf05e24de9a85e9c3d7 | |
parent | 24a36eeaab02a86f8da2c1565c287ea2c72481bd (diff) | |
parent | d99c687658dfc89522d6af953fb74abfbc820078 (diff) |
Merge pull request #6213 from vespa-engine/geirst/validate-complex-attribute-fields-in-config-model
Geirst/validate complex attribute fields in config model
7 files changed, 356 insertions, 70 deletions
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java index 6070aeb0ee1..55f3a94bb70 100644 --- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java +++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/AttributeFields.java @@ -8,6 +8,7 @@ import com.yahoo.searchdefinition.Search; import com.yahoo.searchdefinition.document.Attribute; import com.yahoo.searchdefinition.document.ImmutableSDField; import com.yahoo.searchdefinition.document.Ranking; +import com.yahoo.searchdefinition.document.SDDocumentType; import com.yahoo.searchdefinition.document.Sorting; import com.yahoo.vespa.config.search.AttributesConfig; import com.yahoo.vespa.indexinglanguage.expressions.ToPositionExpression; @@ -23,6 +24,7 @@ import java.util.stream.Collectors; import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isArrayOfSimpleStruct; import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfPrimitiveType; import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfSimpleStruct; +import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isSupportedComplexField; /** * The set of all attribute fields defined by a search definition @@ -46,14 +48,14 @@ public class AttributeFields extends Derived implements AttributesConfig.Produce /** Derives everything from a field */ @Override protected void derive(ImmutableSDField field, Search search) { - if (unsupportedFieldType(field)) { + if (unsupportedFieldType(field, search.getDocument())) { return; // Ignore complex struct and map fields for indexed search (only supported for streaming search) } if (field.isImportedField()) { deriveImportedAttributes(field); - } else if (isArrayOfSimpleStruct(field)) { + } else if (isArrayOfSimpleStruct(field, search.getDocument())) { deriveArrayOfSimpleStruct(field); - } else if (isMapOfSimpleStruct(field)) { + } else if (isMapOfSimpleStruct(field, search.getDocument())) { deriveMapOfSimpleStruct(field); } else if (isMapOfPrimitiveType(field)) { deriveMapOfPrimitiveType(field); @@ -62,11 +64,9 @@ public class AttributeFields extends Derived implements AttributesConfig.Produce } } - private static boolean unsupportedFieldType(ImmutableSDField field) { + private static boolean unsupportedFieldType(ImmutableSDField field, SDDocumentType docType) { return (field.usesStructOrMap() && - !isArrayOfSimpleStruct(field) && - !isMapOfSimpleStruct(field) && - !isMapOfPrimitiveType(field) && + !isSupportedComplexField(field, docType) && !field.getDataType().equals(PositionDataType.INSTANCE) && !field.getDataType().equals(DataType.getArray(PositionDataType.INSTANCE))); } diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java index a56364a6975..72eb1c96e0f 100644 --- a/config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java +++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtils.java @@ -7,6 +7,11 @@ import com.yahoo.document.Field; import com.yahoo.document.MapDataType; import com.yahoo.document.PositionDataType; import com.yahoo.document.StructDataType; +import com.yahoo.document.TemporaryStructuredDataType; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; /** * Utils used to check whether a complex field supports being represented as struct field attributes. @@ -14,33 +19,54 @@ import com.yahoo.document.StructDataType; * Currently we support: * - array of simple struct * - map of primitive type to simple struct + * - map of primitive type to primitive type * * @author geirst */ public class ComplexAttributeFieldUtils { - public static boolean isArrayOfSimpleStruct(ImmutableSDField field) { - return isArrayOfSimpleStruct(field.getDataType()); + public static boolean isSupportedComplexField(ImmutableSDField field, SDDocumentType docType) { + return (isArrayOfSimpleStruct(field, docType) || + isMapOfSimpleStruct(field, docType) || + isMapOfPrimitiveType(field)); + } + + public static boolean isSupportedComplexField(DataType fieldType) { + return (isArrayOfSimpleStruct(fieldType) || + isMapOfSimpleStruct(fieldType) || + isMapOfPrimitiveType(fieldType)); + } + + public static boolean isArrayOfSimpleStruct(ImmutableSDField field, SDDocumentType docType) { + return isArrayOfSimpleStruct(field.getDataType(), Optional.of(docType)); } public static boolean isArrayOfSimpleStruct(DataType fieldType) { + return isArrayOfSimpleStruct(fieldType, Optional.empty()); + } + + private static boolean isArrayOfSimpleStruct(DataType fieldType, Optional<SDDocumentType> docType) { if (fieldType instanceof ArrayDataType) { ArrayDataType arrayType = (ArrayDataType)fieldType; - return isSimpleStruct(arrayType.getNestedType()); + return isSimpleStruct(arrayType.getNestedType(), docType); } else { return false; } } - public static boolean isMapOfSimpleStruct(ImmutableSDField field) { - return isMapOfSimpleStruct(field.getDataType()); + public static boolean isMapOfSimpleStruct(ImmutableSDField field, SDDocumentType docType) { + return isMapOfSimpleStruct(field.getDataType(), Optional.of(docType)); } public static boolean isMapOfSimpleStruct(DataType fieldType) { + return isMapOfSimpleStruct(fieldType, Optional.empty()); + } + + private static boolean isMapOfSimpleStruct(DataType fieldType, Optional<SDDocumentType> docType) { if (fieldType instanceof MapDataType) { MapDataType mapType = (MapDataType)fieldType; return isPrimitiveType(mapType.getKeyType()) && - isSimpleStruct(mapType.getValueType()); + isSimpleStruct(mapType.getValueType(), docType); } else { return false; } @@ -60,11 +86,15 @@ public class ComplexAttributeFieldUtils { } } - private static boolean isSimpleStruct(DataType type) { + private static boolean isSimpleStruct(DataType type, Optional<SDDocumentType> docType) { if (type instanceof StructDataType && !(type.equals(PositionDataType.INSTANCE))) { StructDataType structType = (StructDataType) type; - for (Field innerField : structType.getFields()) { + Collection<Field> structFields = getStructFields(structType, docType); + if (structFields.isEmpty()) { + return false; + } + for (Field innerField : structFields) { if (!isPrimitiveType(innerField.getDataType())) { return false; } @@ -75,6 +105,19 @@ public class ComplexAttributeFieldUtils { } } + private static Collection<Field> getStructFields(StructDataType structType, Optional<SDDocumentType> docType) { + // The struct data type might be unresolved at this point. If so we use the document type to resolve it. + if (docType.isPresent() && (structType instanceof TemporaryStructuredDataType)) { + SDDocumentType realStructType = docType.get().getOwnedType(structType.getName()); + if (structType != null) { + return realStructType.getDocumentType().getFields(); + } + return Collections.emptyList(); + } else { + return structType.getFields(); + } + } + private static boolean isPrimitiveType(DataType dataType) { return dataType.equals(DataType.BYTE) || dataType.equals(DataType.INT) || @@ -84,10 +127,10 @@ public class ComplexAttributeFieldUtils { dataType.equals(DataType.STRING); } - public static boolean isComplexFieldWithOnlyStructFieldAttributes(ImmutableSDField field) { - if (isArrayOfSimpleStruct(field)) { + public static boolean isComplexFieldWithOnlyStructFieldAttributes(ImmutableSDField field, SDDocumentType docType) { + if (isArrayOfSimpleStruct(field, docType)) { return hasOnlyStructFieldAttributes(field); - } else if (isMapOfSimpleStruct(field)) { + } else if (isMapOfSimpleStruct(field, docType)) { return hasSingleAttribute(field.getStructField("key")) && hasOnlyStructFieldAttributes(field.getStructField("value")); } else if (isMapOfPrimitiveType(field)) { diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java index b51524b7e62..2d2fc10d9c5 100644 --- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java +++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImplicitSummaries.java @@ -83,7 +83,7 @@ public class ImplicitSummaries extends Processor { } } - if (addedSummaryField != null && isComplexFieldWithOnlyStructFieldAttributes(field)) { + if (addedSummaryField != null && isComplexFieldWithOnlyStructFieldAttributes(field, search.getDocument())) { addedSummaryField.setTransform(SummaryTransform.ATTRIBUTECOMBINER); } 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 new file mode 100644 index 00000000000..005c1dd8b2e --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidator.java @@ -0,0 +1,85 @@ +// Copyright 2018 Yahoo Holdings. 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.document.DataType; +import com.yahoo.document.PositionDataType; +import com.yahoo.searchdefinition.Search; +import com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils; +import com.yahoo.searchdefinition.document.ImmutableSDField; +import com.yahoo.vespa.model.VespaModel; +import com.yahoo.vespa.model.search.AbstractSearchCluster; +import com.yahoo.vespa.model.search.SearchCluster; +import org.apache.commons.lang.StringUtils; + +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<AbstractSearchCluster> searchClusters = model.getSearchClusters(); + for (AbstractSearchCluster cluster : searchClusters) { + if (cluster.isStreaming()) { + continue; + } + SearchCluster searchCluster = (SearchCluster) cluster; + for (AbstractSearchCluster.SearchDefinitionSpec spec : searchCluster.getLocalSDS()) { + validateComplexFields(searchCluster.getClusterName(), spec.getSearchDefinition().getSearch()); + } + } + } + + private static void validateComplexFields(String clusterName, Search search) { + String unsupportedFields = search.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", + clusterName, search.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.getDataType()) || + field.getDataType().equals(PositionDataType.INSTANCE) || + field.getDataType().equals(DataType.getArray(PositionDataType.INSTANCE))); + } + + private static String toString(ImmutableSDField field) { + return field.getName() + " (" + StringUtils.join(getStructFieldAttributes(field.getStructFields()), ", ") + ")"; + } + + private static boolean hasStructFieldAttributes(Collection<? extends ImmutableSDField> structFields) { + return !getStructFieldAttributes(structFields).isEmpty(); + } + + private static List<String> getStructFieldAttributes(Collection<? extends ImmutableSDField> structFields) { + List<String> result = new ArrayList<>(); + for (ImmutableSDField structField : structFields) { + structField.getAttributes().values().forEach(attr -> result.add(attr.getName())); + result.addAll(getStructFieldAttributes(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 523ced52306..c08e81b250f 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 @@ -52,6 +52,7 @@ public class Validation { } new ComponentValidator().validate(model, deployState); new SearchDataTypeValidator().validate(model, deployState); + new ComplexAttributeFieldsValidator().validate(model, deployState); new StreamingValidator().validate(model, deployState); new RankSetupValidator(force).validate(model, deployState); new NoPrefixForIndexes().validate(model, deployState); diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java index 4cdc48b330e..91a89c204c9 100644 --- a/config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java +++ b/config-model/src/test/java/com/yahoo/searchdefinition/document/ComplexAttributeFieldUtilsTestCase.java @@ -1,3 +1,4 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.searchdefinition.document; import com.yahoo.searchdefinition.Search; @@ -6,126 +7,214 @@ import com.yahoo.searchdefinition.parser.ParseException; import org.junit.Test; import static com.yahoo.config.model.test.TestUtil.joinLines; -import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isArrayOfSimpleStruct; -import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isComplexFieldWithOnlyStructFieldAttributes; -import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfPrimitiveType; -import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfSimpleStruct; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class ComplexAttributeFieldUtilsTestCase { - private static ImmutableSDField createField(String fieldName, String sdFieldContent) throws ParseException { - String sdContent = joinLines("search test {", - " document test {", - " struct elem {", - " field name type string {}", - " field weight type string {}", - " }", - sdFieldContent, - " }", - "}"); - Search search = SearchBuilder.createFromString(sdContent).getSearch(); - return search.getConcreteField(fieldName); + private static class FixtureBase { + private final Search search; + private final ImmutableSDField field; + + public FixtureBase(String fieldName, String sdContent) throws ParseException { + search = SearchBuilder.createFromString(sdContent).getSearch(); + field = search.getConcreteField(fieldName); + } + + public ImmutableSDField field() { + return field; + } + + public SDDocumentType docType() { + return search.getDocument(); + } + + public boolean isSupportedComplexField() { + return ComplexAttributeFieldUtils.isSupportedComplexField(field(), docType()); + } + + public boolean isArrayOfSimpleStruct() { + return ComplexAttributeFieldUtils.isArrayOfSimpleStruct(field(), docType()); + } + + public boolean isMapOfSimpleStruct() { + return ComplexAttributeFieldUtils.isMapOfSimpleStruct(field(), docType()); + } + + public boolean isMapOfPrimitiveType() { + return ComplexAttributeFieldUtils.isMapOfPrimitiveType(field()); + } + + public boolean isComplexFieldWithOnlyStructFieldAttributes() { + return ComplexAttributeFieldUtils.isComplexFieldWithOnlyStructFieldAttributes(field(), docType()); + } + } + + private static class Fixture extends FixtureBase { + + public Fixture(String fieldName, String sdFieldContent) throws ParseException { + super(fieldName, joinLines("search test {", + " document test {", + " struct elem {", + " field name type string {}", + " field weight type string {}", + " }", + sdFieldContent, + " }", + "}")); + } + } + + private static class ComplexFixture extends FixtureBase { + + public ComplexFixture(String fieldName, String sdFieldContent) throws ParseException { + super(fieldName, joinLines("search test {", + " document test {", + " struct elem {", + " field name type string {}", + " field weight type array<string> {}", + " }", + sdFieldContent, + " }", + "}")); + } } @Test public void array_of_struct_with_only_struct_field_attributes_is_tagged_as_such() throws ParseException { - ImmutableSDField field = createField("elem_array", + Fixture f = new Fixture("elem_array", joinLines("field elem_array type array<elem> {", " indexing: summary", " struct-field name { indexing: attribute }", " struct-field weight { indexing: attribute }", "}")); - assertTrue(isArrayOfSimpleStruct(field)); - assertTrue(isComplexFieldWithOnlyStructFieldAttributes(field)); + assertTrue(f.isSupportedComplexField()); + assertTrue(f.isArrayOfSimpleStruct()); + assertTrue(f.isComplexFieldWithOnlyStructFieldAttributes()); } @Test public void array_of_struct_with_some_struct_field_attributes_is_tagged_as_such() throws ParseException { - ImmutableSDField field = createField("elem_array", + Fixture f = new Fixture("elem_array", joinLines("field elem_array type array<elem> {", " indexing: summary", " struct-field weight { indexing: attribute }", "}")); - assertTrue(isArrayOfSimpleStruct(field)); - assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field)); + assertTrue(f.isSupportedComplexField()); + assertTrue(f.isArrayOfSimpleStruct()); + assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes()); } @Test public void map_of_struct_with_only_struct_field_attributes_is_tagged_as_such() throws ParseException { - ImmutableSDField field = createField("elem_map", + Fixture f = new Fixture("elem_map", joinLines("field elem_map type map<string, elem> {", " indexing: summary", " struct-field key { indexing: attribute }", " struct-field value.name { indexing: attribute }", " struct-field value.weight { indexing: attribute }", "}")); - assertTrue(isMapOfSimpleStruct(field)); - assertFalse(isMapOfPrimitiveType(field)); - assertTrue(isComplexFieldWithOnlyStructFieldAttributes(field)); + assertTrue(f.isSupportedComplexField()); + assertTrue(f.isMapOfSimpleStruct()); + assertFalse(f.isMapOfPrimitiveType()); + assertTrue(f.isComplexFieldWithOnlyStructFieldAttributes()); } @Test public void map_of_struct_with_some_struct_field_attributes_is_tagged_as_such() throws ParseException { { - ImmutableSDField field = createField("elem_map", + Fixture f = new Fixture("elem_map", joinLines("field elem_map type map<int, elem> {", " indexing: summary", " struct-field value.name { indexing: attribute }", " struct-field value.weight { indexing: attribute }", "}")); - assertTrue(isMapOfSimpleStruct(field)); - assertFalse(isMapOfPrimitiveType(field)); - assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field)); + assertTrue(f.isSupportedComplexField()); + assertTrue(f.isMapOfSimpleStruct()); + assertFalse(f.isMapOfPrimitiveType()); + assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes()); } { - ImmutableSDField field = createField("elem_map", + Fixture f = new Fixture("elem_map", joinLines("field elem_map type map<int, elem> {", " indexing: summary", " struct-field key { indexing: attribute }", " struct-field value.weight { indexing: attribute }", "}")); - assertTrue(isMapOfSimpleStruct(field)); - assertFalse(isMapOfPrimitiveType(field)); - assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field)); + assertTrue(f.isSupportedComplexField()); + assertTrue(f.isMapOfSimpleStruct()); + assertFalse(f.isMapOfPrimitiveType()); + assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes()); } } @Test public void map_of_primitive_type_with_only_struct_field_attributes_is_tagged_as_such() throws ParseException { - ImmutableSDField field = createField("str_map", - joinLines("field str_map type map<string, string> {", - " indexing: summary", - " struct-field key { indexing: attribute }", - " struct-field value { indexing: attribute }", - "}")); - assertTrue(isMapOfPrimitiveType(field)); - assertFalse(isMapOfSimpleStruct(field)); - assertTrue(isComplexFieldWithOnlyStructFieldAttributes(field)); + Fixture f = new Fixture("str_map", + joinLines("field str_map type map<string, string> {", + " indexing: summary", + " struct-field key { indexing: attribute }", + " struct-field value { indexing: attribute }", + "}")); + assertTrue(f.isSupportedComplexField()); + assertTrue(f.isMapOfPrimitiveType()); + assertFalse(f.isMapOfSimpleStruct()); + assertTrue(f.isComplexFieldWithOnlyStructFieldAttributes()); } @Test public void map_of_primitive_type_with_some_struct_field_attributes_is_tagged_as_such() throws ParseException { { - ImmutableSDField field = createField("int_map", + Fixture f = new Fixture("int_map", joinLines("field int_map type map<int, int> {", " indexing: summary", " struct-field key { indexing: attribute }", "}")); - assertTrue(isMapOfPrimitiveType(field)); - assertFalse(isMapOfSimpleStruct(field)); - assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field)); + assertTrue(f.isSupportedComplexField()); + assertTrue(f.isMapOfPrimitiveType()); + assertFalse(f.isMapOfSimpleStruct()); + assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes()); } { - ImmutableSDField field = createField("int_map", + Fixture f = new Fixture("int_map", joinLines("field int_map type map<int, int> {", " indexing: summary", " struct-field value { indexing: attribute }", "}")); - assertTrue(isMapOfPrimitiveType(field)); - assertFalse(isMapOfSimpleStruct(field)); - assertFalse(isComplexFieldWithOnlyStructFieldAttributes(field)); + assertTrue(f.isSupportedComplexField()); + assertTrue(f.isMapOfPrimitiveType()); + assertFalse(f.isMapOfSimpleStruct()); + assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes()); + } + } + + @Test + public void unsupported_complex_field_is_tagged_as_such() throws ParseException { + { + ComplexFixture f = new ComplexFixture("elem_array", + joinLines("field elem_array type array<elem> {", + " struct-field name { indexing: attribute }", + " struct-field weight { indexing: attribute }", + "}")); + assertFalse(f.isSupportedComplexField()); + assertFalse(f.isArrayOfSimpleStruct()); + assertFalse(f.isMapOfSimpleStruct()); + assertFalse(f.isMapOfPrimitiveType()); + assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes()); + } + { + ComplexFixture f = new ComplexFixture("elem_map", + joinLines("field elem_map type map<int, elem> {", + " indexing: summary", + " struct-field key { indexing: attribute }", + " struct-field value.weight { indexing: attribute }", + "}")); + assertFalse(f.isSupportedComplexField()); + assertFalse(f.isArrayOfSimpleStruct()); + assertFalse(f.isMapOfSimpleStruct()); + assertFalse(f.isMapOfPrimitiveType()); + assertFalse(f.isComplexFieldWithOnlyStructFieldAttributes()); } } + } 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 new file mode 100644 index 00000000000..146369d1620 --- /dev/null +++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/ComplexAttributeFieldsValidatorTestCase.java @@ -0,0 +1,68 @@ +// Copyright 2018 Yahoo Holdings. 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.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 static com.yahoo.config.model.test.TestUtil.joinLines; + +/** + * @author geirst + */ +public class ComplexAttributeFieldsValidatorTestCase { + + @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("For cluster 'mycluster', search 'test': " + + "The following complex fields do not support using struct field attributes: " + + "struct_array (struct_array.s1), struct_map (struct_map.key, struct_map.value.s1). " + + "Only supported for the following complex field types: array or map of struct with primitive types, map of primitive types"); + + createModelAndValidate(joinLines("search test {", + " document test {", + " struct s { field s1 type array<int> {} }", + " field struct_array type array<s> {", + " struct-field s1 { indexing: attribute }", + " }", + " field struct_map type map<string,s> {", + " struct-field key { indexing: attribute }", + " struct-field value.s1 { indexing: attribute }", + " }", + " }", + "}")); + } + + private static void createModelAndValidate(String searchDefinition) throws IOException, SAXException { + DeployState deployState = createDeployState(servicesXml(), searchDefinition); + VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState); + Validation.validate(model, false, false, deployState); + } + + private static DeployState createDeployState(String servicesXml, String searchDefinition) { + ApplicationPackage app = new MockApplicationPackage.Builder() + .withServices(servicesXml) + .withSearchDefinition(searchDefinition) + .build(); + return new DeployState.Builder().applicationPackage(app).build(true); + } + + private static String servicesXml() { + return joinLines("<services version='1.0'>", + new ContentClusterBuilder().getXml(), + "</services>"); + } +} |