diff options
Diffstat (limited to 'config-model/src')
10 files changed, 124 insertions, 40 deletions
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java index db22d0b88e9..e583ea3d9b1 100644 --- a/config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java +++ b/config-model/src/main/java/com/yahoo/searchdefinition/DocumentReferenceResolver.java @@ -4,6 +4,7 @@ package com.yahoo.searchdefinition; import com.yahoo.document.Field; import com.yahoo.document.ReferenceDataType; import com.yahoo.searchdefinition.document.SDDocumentType; +import com.yahoo.searchdefinition.document.SDField; import java.util.List; import java.util.Map; @@ -40,6 +41,12 @@ public class DocumentReferenceResolver { } private DocumentReference createDocumentReference(Field field) { + if (!isAttribute(field)) { + throw new IllegalArgumentException( + String.format( + "The field '%s' is an invalid document reference. The field must be an attribute.", + field.getName())); + } ReferenceDataType reference = (ReferenceDataType) field.getDataType(); String targetDocumentName = getTargetDocumentName(reference); Search search = searchMapping.get(targetDocumentName); @@ -51,6 +58,11 @@ public class DocumentReferenceResolver { return new DocumentReference(field, search); } + private static boolean isAttribute(Field field) { + SDField sdField = (SDField) field; // Ugly, but SDDocumentType only expose the fields as the super class Field + return sdField.doesAttributing(); + } + private static Map<String, Search> createDocumentNameToSearchMapping(List<Search> searchDefintions) { return searchDefintions.stream() .filter(search -> search.getDocument() != null) diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java index bc52aa27f4c..a93aeb6914b 100644 --- a/config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java +++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/Attribute.java @@ -1,12 +1,31 @@ // Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.searchdefinition.document; -import com.yahoo.document.*; -import com.yahoo.document.datatypes.*; +import com.yahoo.document.ArrayDataType; +import com.yahoo.document.CollectionDataType; +import com.yahoo.document.DataType; +import com.yahoo.document.DocumentType; +import com.yahoo.document.PrimitiveDataType; +import com.yahoo.document.ReferenceDataType; +import com.yahoo.document.StructuredDataType; +import com.yahoo.document.TemporaryStructuredDataType; +import com.yahoo.document.TensorDataType; +import com.yahoo.document.WeightedSetDataType; +import com.yahoo.document.datatypes.ByteFieldValue; +import com.yahoo.document.datatypes.DoubleFieldValue; +import com.yahoo.document.datatypes.FieldValue; +import com.yahoo.document.datatypes.FloatFieldValue; +import com.yahoo.document.datatypes.IntegerFieldValue; +import com.yahoo.document.datatypes.LongFieldValue; +import com.yahoo.document.datatypes.PredicateFieldValue; +import com.yahoo.document.datatypes.Raw; +import com.yahoo.document.datatypes.StringFieldValue; +import com.yahoo.document.datatypes.TensorFieldValue; import com.yahoo.tensor.TensorType; import java.io.Serializable; import java.util.LinkedHashSet; +import java.util.Objects; import java.util.Optional; import java.util.Set; @@ -37,10 +56,13 @@ public final class Attribute implements Cloneable, Serializable { private long lowerBound = BooleanIndexDefinition.DEFAULT_LOWER_BOUND; private long upperBound = BooleanIndexDefinition.DEFAULT_UPPER_BOUND; private double densePostingListThreshold = BooleanIndexDefinition.DEFAULT_DENSE_POSTING_LIST_THRESHOLD; - + /** This is set if the type of this is TENSOR */ private Optional<TensorType> tensorType = Optional.empty(); + /** This is set if the type of this is REFERENCE */ + private final Optional<StructuredDataType> referenceDocumentType; + private boolean isPosition = false; private final Sorting sorting = new Sorting(); @@ -63,7 +85,8 @@ public final class Attribute implements Cloneable, Serializable { DOUBLE("double", "DOUBLE"), STRING("string", "STRING"), PREDICATE("predicate", "PREDICATE"), - TENSOR("tensor", "TENSOR"); + TENSOR("tensor", "TENSOR"), + REFERENCE("reference", "REFERENCE"); private final String myName; // different from what name() returns. private final String exportAttributeTypeName; @@ -103,20 +126,25 @@ public final class Attribute implements Cloneable, Serializable { /** Creates an attribute with default settings */ public Attribute(String name, DataType fieldType) { - this(name, convertDataType(fieldType), convertCollectionType(fieldType), convertTensorType(fieldType)); + this(name, convertDataType(fieldType), convertCollectionType(fieldType), convertTensorType(fieldType), convertTargetType(fieldType)); setRemoveIfZero(fieldType instanceof WeightedSetDataType ? ((WeightedSetDataType)fieldType).removeIfZero() : false); setCreateIfNonExistent(fieldType instanceof WeightedSetDataType ? ((WeightedSetDataType)fieldType).createIfNonExistent() : false); } public Attribute(String name, Type type, CollectionType collectionType) { - this(name, type, collectionType, Optional.empty()); + this(name, type, collectionType, Optional.empty(), Optional.empty()); } - public Attribute(String name, Type type, CollectionType collectionType, Optional<TensorType> tensorType) { + public Attribute(String name, + Type type, + CollectionType collectionType, + Optional<TensorType> tensorType, + Optional<StructuredDataType> referenceDocumentType) { this.name=name; setType(type); setCollectionType(collectionType); this.tensorType = tensorType; + this.referenceDocumentType = referenceDocumentType; } /** @@ -207,6 +235,8 @@ public final class Attribute implements Cloneable, Serializable { return Type.TENSOR; } else if (fieldType instanceof CollectionDataType) { return convertDataType(((CollectionDataType) fieldType).getNestedType()); + } else if (fieldType instanceof ReferenceDataType) { + return Type.REFERENCE; } else { throw new IllegalArgumentException("Don't know which attribute type to " + "convert " + fieldType + " to"); @@ -223,6 +253,8 @@ public final class Attribute implements Cloneable, Serializable { return CollectionType.SINGLE; } else if (fieldType instanceof PrimitiveDataType) { return CollectionType.SINGLE; + } else if (fieldType instanceof ReferenceDataType) { + return CollectionType.SINGLE; } else { throw new IllegalArgumentException("Field " + fieldType + " not supported in convertCollectionType"); } @@ -233,6 +265,13 @@ public final class Attribute implements Cloneable, Serializable { return Optional.of(((TensorDataType)fieldType).getTensorType()); } + private static Optional<StructuredDataType> convertTargetType(DataType fieldType) { + return Optional.of(fieldType) + .filter(ReferenceDataType.class::isInstance) + .map(ReferenceDataType.class::cast) + .map(ReferenceDataType::getTargetType); + } + /** Converts to the right field type from an attribute type */ private DataType toDataType(Type attributeType) { switch (attributeType) { @@ -244,10 +283,23 @@ public final class Attribute implements Cloneable, Serializable { case BYTE: return DataType.BYTE; case PREDICATE: return DataType.PREDICATE; case TENSOR: return DataType.getTensor(tensorType.orElseThrow(IllegalStateException::new)); + case REFERENCE: return createReferenceDataType(); default: throw new IllegalArgumentException("Unknown attribute type " + attributeType); } } + private DataType createReferenceDataType() { + if (!referenceDocumentType.isPresent()) { + throw new IllegalStateException("Referenced document type is not set!"); + } + StructuredDataType type = referenceDocumentType.get(); + if (type instanceof DocumentType) { + return ReferenceDataType.createWithInferredId((DocumentType) type); + } else { + return ReferenceDataType.createWithInferredId((TemporaryStructuredDataType) type); + } + } + public DataType getDataType() { DataType dataType = toDataType(type); if (collectionType.equals(Attribute.CollectionType.ARRAY)) { @@ -259,22 +311,14 @@ public final class Attribute implements Cloneable, Serializable { } } + @Override public int hashCode() { - return name.hashCode() + - type.hashCode() + - collectionType.hashCode() + - sorting.hashCode() + - (isPrefetch() ? 13 : 0) + - (fastSearch ? 17 : 0) + - (removeIfZero ? 47 : 0) + - (createIfNonExistent ? 53 : 0) + - (isPosition ? 61 : 0) + - (huge ? 67 : 0) + - (enableBitVectors ? 71 : 0) + - (enableOnlyBitVector ? 73 : 0) + - tensorType.hashCode(); + return Objects.hash( + name, type, collectionType, sorting, isPrefetch(), fastAccess, removeIfZero, createIfNonExistent, + isPosition, huge, enableBitVectors, enableOnlyBitVector, tensorType, referenceDocumentType); } + @Override public boolean equals(Object object) { if (! (object instanceof Attribute)) return false; @@ -297,11 +341,13 @@ public final class Attribute implements Cloneable, Serializable { if (this.huge != other.huge) return false; if ( ! this.sorting.equals(other.sorting)) return false; if (!this.tensorType.equals(other.tensorType)) return false; + if (!this.referenceDocumentType.equals(other.referenceDocumentType)) return false; return true; } - public @Override Attribute clone() { + @Override + public Attribute clone() { try { return (Attribute)super.clone(); } diff --git a/config-model/src/test/cfg/application/validation/global_distribution_validation/searchdefinitions/simple.sd b/config-model/src/test/cfg/application/validation/global_distribution_validation/searchdefinitions/simple.sd index 49572be1cae..49223fabb76 100644 --- a/config-model/src/test/cfg/application/validation/global_distribution_validation/searchdefinitions/simple.sd +++ b/config-model/src/test/cfg/application/validation/global_distribution_validation/searchdefinitions/simple.sd @@ -1,6 +1,6 @@ # Copyright 2017 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. search simple { document simple { - field my_reference type reference<parent> { indexing: summary } + field my_reference type reference<parent> { indexing: summary | attribute } } } diff --git a/config-model/src/test/cfg/application/validation/search_alltypes/searchdefinitions/simple.sd b/config-model/src/test/cfg/application/validation/search_alltypes/searchdefinitions/simple.sd index 05d8b735249..36d8012b38e 100644 --- a/config-model/src/test/cfg/application/validation/search_alltypes/searchdefinitions/simple.sd +++ b/config-model/src/test/cfg/application/validation/search_alltypes/searchdefinitions/simple.sd @@ -12,6 +12,6 @@ search simple { field my_byte type byte { indexing: summary } field my_predicate type predicate { indexing: summary } field my_tensor type tensor(x{}) { indexing: summary } - field my_reference type reference<parent> { indexing: summary } + field my_reference type reference<parent> { indexing: summary | attribute } } } diff --git a/config-model/src/test/derived/importedfields/ad.sd b/config-model/src/test/derived/importedfields/ad.sd index ceb7acc7df4..fbc4925c542 100644 --- a/config-model/src/test/derived/importedfields/ad.sd +++ b/config-model/src/test/derived/importedfields/ad.sd @@ -1,7 +1,7 @@ search ad { document ad { - field campaign_ref type reference<campaign> {} - field person_ref type reference<person> {} + field campaign_ref type reference<campaign> { indexing: attribute } + field person_ref type reference<person> { indexing: attribute } } import field campaign_ref.budget as my_budget {} import field person_ref.name as my_person_name {} diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java index c5e1df77210..da9680aa641 100644 --- a/config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java +++ b/config-model/src/test/java/com/yahoo/searchdefinition/DocumentReferenceResolverTest.java @@ -35,6 +35,7 @@ public class DocumentReferenceResolverTest { // Create foo document with document reference to bar and add another field SDField fooRefToBarField = new SDField ("bar_ref", ReferenceDataType.createWithInferredId(barDocument.getDocumentType())); + fooRefToBarField.parseIndexingScript("{ attribute }"); SDField irrelevantField = new SDField("irrelevant_stuff", DataType.INT); Search fooSearch = new Search(); SDDocumentType fooDocument = new SDDocumentType("foo", fooSearch); @@ -57,6 +58,7 @@ public class DocumentReferenceResolverTest { // Create foo document with document reference to non-existing document bar SDField fooRefToBarField = new SDField( "bar_ref", ReferenceDataType.createWithInferredId(TemporaryStructuredDataType.create("bar"))); + fooRefToBarField.parseIndexingScript("{ attribute }"); Search fooSearch = new Search(); SDDocumentType fooDocument = new SDDocumentType("foo", fooSearch); fooDocument.addField(fooRefToBarField); @@ -71,4 +73,26 @@ public class DocumentReferenceResolverTest { resolver.resolveReferences(fooDocument); } + @Test + public void throws_exception_if_reference_is_not_an_attribute() { + // Create bar document with no fields + Search barSearch = new Search(); + SDDocumentType barDocument = new SDDocumentType("bar", barSearch); + barSearch.addDocument(barDocument); + + // Create foo document with document reference to bar and add another field + SDField fooRefToBarField = new SDField + ("bar_ref", ReferenceDataType.createWithInferredId(barDocument.getDocumentType())); + Search fooSearch = new Search(); + SDDocumentType fooDocument = new SDDocumentType("foo", fooSearch); + fooDocument.addField(fooRefToBarField); + fooSearch.addDocument(fooDocument); + + DocumentReferenceResolver resolver = new DocumentReferenceResolver(asList(fooSearch, barSearch)); + exceptionRule.expect(IllegalArgumentException.class); + exceptionRule.expectMessage( + "The field 'bar_ref' is an invalid document reference. The field must be an attribute."); + resolver.resolveReferences(fooDocument); + } + } diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryTestCase.java index 54d10327de1..cdc4cbce77f 100644 --- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryTestCase.java +++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/SummaryTestCase.java @@ -105,9 +105,11 @@ public class SummaryTestCase extends SearchDefinitionTestCase { builder.importString(joinLines("search ad {", " document ad {", " field campaign_ref type reference<campaign> {", - " indexing: summary", + " indexing: summary | attribute", + " }", + " field other_campaign_ref type reference<campaign> {", + " indexing: summary | attribute", " }", - " field other_campaign_ref type reference<campaign> {}", " }", " document-summary my_summary {", " summary other_campaign_ref type reference<campaign> {}", diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsTestCase.java index 44b30e3419a..4222a4ef66a 100644 --- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsTestCase.java +++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsTestCase.java @@ -23,8 +23,8 @@ public class ImportedFieldsTestCase { Search search = buildAdSearch(joinLines( "search ad {", " document ad {", - " field campaign_ref type reference<campaign> {}", - " field person_ref type reference<person> {}", + " field campaign_ref type reference<campaign> { indexing: attribute }", + " field person_ref type reference<person> { indexing: attribute }", " }", " import field campaign_ref.budget as my_budget {}", " import field person_ref.name as my_name {}", @@ -39,7 +39,7 @@ public class ImportedFieldsTestCase { Search search = buildAdSearch(joinLines("search ad {", " document ad {", " field title type string { indexing: attribute }", - " field self_ref type reference<ad> {}", + " field self_ref type reference<ad> { indexing: attribute }", " }", " import field self_ref.title as my_title {}", "}")); diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReferenceFieldTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReferenceFieldTestCase.java index 016ae8b8e67..42f8c302346 100644 --- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReferenceFieldTestCase.java +++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ReferenceFieldTestCase.java @@ -41,8 +41,8 @@ public class ReferenceFieldTestCase { String adSdContent = "search ad {\n" + " document ad {\n" + - " field campaign_ref type reference<campaign> {}\n" + - " field salesperson_ref type reference<salesperson> {}\n" + + " field campaign_ref type reference<campaign> { indexing: attribute }\n" + + " field salesperson_ref type reference<salesperson> { indexing: attribute }\n" + " }\n" + "}"; builder.importString(campaignSdContent); @@ -60,13 +60,13 @@ public class ReferenceFieldTestCase { String campaignSdContent = "search campaign {\n" + " document campaign {\n" + - " field ad_ref type reference<ad> {}\n" + + " field ad_ref type reference<ad> { indexing: attribute }\n" + " }\n" + "}"; String adSdContent = "search ad {\n" + " document ad {\n" + - " field campaign_ref type reference<campaign> {}\n" + + " field campaign_ref type reference<campaign> { indexing: attribute }\n" + " }\n" + "}"; builder.importString(campaignSdContent); diff --git a/config-model/src/test/java/com/yahoo/vespa/documentmodel/DocumentModelBuilderReferenceTypeTestCase.java b/config-model/src/test/java/com/yahoo/vespa/documentmodel/DocumentModelBuilderReferenceTypeTestCase.java index d2e41d0ec6d..bb115ff43e2 100644 --- a/config-model/src/test/java/com/yahoo/vespa/documentmodel/DocumentModelBuilderReferenceTypeTestCase.java +++ b/config-model/src/test/java/com/yahoo/vespa/documentmodel/DocumentModelBuilderReferenceTypeTestCase.java @@ -26,8 +26,8 @@ public class DocumentModelBuilderReferenceTypeTestCase extends SearchDefinitionT assertDocumentConfigs(new TestDocumentModelBuilder().addCampaign().addPerson().build(joinLines( "search ad {", " document ad {", - " field campaign_ref type reference<campaign> {}", - " field person_ref type reference<person> {}", + " field campaign_ref type reference<campaign> { indexing: attribute }", + " field person_ref type reference<person> { indexing: attribute }", " }", "}")), "refs_to_other_types"); @@ -38,8 +38,8 @@ public class DocumentModelBuilderReferenceTypeTestCase extends SearchDefinitionT assertDocumentConfigs(new TestDocumentModelBuilder().addCampaign().build(joinLines( "search ad {", " document ad {", - " field campaign_ref type reference<campaign> {}", - " field other_campaign_ref type reference<campaign> {}", + " field campaign_ref type reference<campaign> { indexing: attribute }", + " field other_campaign_ref type reference<campaign> { indexing: attribute }", " }", "}")), "refs_to_same_type"); @@ -50,7 +50,7 @@ public class DocumentModelBuilderReferenceTypeTestCase extends SearchDefinitionT assertDocumentConfigs(new TestDocumentModelBuilder().build(joinLines( "search ad {", " document ad {", - " field self_ref type reference<ad> {}", + " field self_ref type reference<ad> { indexing: attribute }", " }", "}")), "ref_to_self_type"); @@ -61,7 +61,7 @@ public class DocumentModelBuilderReferenceTypeTestCase extends SearchDefinitionT DocumentModel model = new TestDocumentModelBuilder().addCampaign().build(joinLines( "search ad {", " document ad {", - " field campaign_ref type reference<campaign> {}", + " field campaign_ref type reference<campaign> { indexing: attribute }", " }", "}")); NewDocumentType campaignType = model.getDocumentManager().getDocumentType("campaign"); |