aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeir Storli <geirstorli@yahoo.no>2018-11-27 17:18:12 +0100
committerGitHub <noreply@github.com>2018-11-27 17:18:12 +0100
commit68c4f2c38295ed2cda8a5be06f6ab60e2c6218e7 (patch)
tree98b591160829d893274b829ec2380f8fa2536588
parent95323a3a983e3b87f83ea7eaee990ce6070ed699 (diff)
parenta92d0b2c764aa4d3e0acc99955ca1abc23a96f9d (diff)
Merge pull request #7779 from vespa-engine/toregge/derive-config-for-imported-struct-fields
Handle limited import of array of struct/map of struct/map of primitives
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/derived/ImportedFields.java18
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/document/ImportedFields.java5
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java10
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java122
-rw-r--r--config-model/src/main/java/com/yahoo/searchdefinition/processing/SummaryFieldsMustHaveValidSource.java1
-rw-r--r--config-model/src/test/derived/imported_struct_fields/attributes.cfg168
-rw-r--r--config-model/src/test/derived/imported_struct_fields/child.sd19
-rw-r--r--config-model/src/test/derived/imported_struct_fields/imported-fields.cfg35
-rw-r--r--config-model/src/test/derived/imported_struct_fields/index-info.cfg37
-rw-r--r--config-model/src/test/derived/imported_struct_fields/indexschema.cfg0
-rw-r--r--config-model/src/test/derived/imported_struct_fields/parent.sd43
-rw-r--r--config-model/src/test/derived/imported_struct_fields/summary.cfg31
-rw-r--r--config-model/src/test/derived/imported_struct_fields/summarymap.cfg16
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/derived/ImportedFieldsTestCase.java5
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java2
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ImportedFieldsTestCase.java186
-rw-r--r--config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java2
17 files changed, 670 insertions, 30 deletions
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/derived/ImportedFields.java b/config-model/src/main/java/com/yahoo/searchdefinition/derived/ImportedFields.java
index cfe33ef019f..82b56f9c961 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/derived/ImportedFields.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/derived/ImportedFields.java
@@ -3,6 +3,7 @@ package com.yahoo.searchdefinition.derived;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchdefinition.document.Attribute;
+import com.yahoo.searchdefinition.document.ImmutableSDField;
import com.yahoo.searchdefinition.document.ImportedField;
import com.yahoo.vespa.config.search.ImportedFieldsConfig;
@@ -38,9 +39,22 @@ public class ImportedFields extends Derived implements ImportedFieldsConfig.Prod
}
}
+ private static boolean isNestedFieldName(String fieldName) {
+ return fieldName.indexOf('.') != -1;
+ }
+
private static void considerField(ImportedFieldsConfig.Builder builder, ImportedField field) {
- if (field.targetField().doesAttributing()) {
- builder.attribute.add(createAttributeBuilder(field));
+ ImmutableSDField targetField = field.targetField();
+ String targetFieldName = targetField.getName();
+ if (!isNestedFieldName(targetFieldName)) {
+ if (targetField.doesAttributing()) {
+ builder.attribute.add(createAttributeBuilder(field));
+ }
+ } else {
+ Attribute attribute = targetField.getAttributes().get(targetFieldName);
+ if (attribute != null) {
+ builder.attribute.add(createAttributeBuilder(field));
+ }
}
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/document/ImportedFields.java b/config-model/src/main/java/com/yahoo/searchdefinition/document/ImportedFields.java
index 2192a7e7bb1..7c67fc422d4 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/document/ImportedFields.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/document/ImportedFields.java
@@ -12,12 +12,15 @@ import java.util.Map;
public class ImportedFields {
private final Map<String, ImportedField> fields;
+ private final Map<String, ImportedField> complexFields;
- public ImportedFields(Map<String, ImportedField> fields) {
+ public ImportedFields(Map<String, ImportedField> fields, Map<String, ImportedField> complexFields) {
this.fields = fields;
+ this.complexFields = complexFields;
}
public Map<String, ImportedField> fields() {
return Collections.unmodifiableMap(fields);
}
+ public Map<String, ImportedField> complexFields() { return Collections.unmodifiableMap(complexFields); }
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java
index fefb54a7fe3..c10b52eb1e6 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFields.java
@@ -8,6 +8,7 @@ import com.yahoo.searchdefinition.document.ImmutableSDField;
import com.yahoo.vespa.documentmodel.SummaryField;
import com.yahoo.vespa.documentmodel.SummaryTransform;
import com.yahoo.vespa.model.container.search.QueryProfiles;
+import com.yahoo.searchdefinition.document.ImmutableImportedSDField;
import java.util.stream.Stream;
@@ -31,6 +32,11 @@ public class AddAttributeTransformToSummaryOfImportedFields extends Processor {
search.allImportedFields()
.flatMap(this::getSummaryFieldsForImportedField)
.forEach(AddAttributeTransformToSummaryOfImportedFields::setAttributeTransform);
+ search.importedFields().map(fields -> fields.complexFields().values().stream()).
+ orElse(Stream.empty()).
+ map(ImmutableImportedSDField::new).
+ flatMap(this::getSummaryFieldsForImportedField).
+ forEach(AddAttributeTransformToSummaryOfImportedFields::setAttributeCombinerTransform);
}
private Stream<SummaryField> getSummaryFieldsForImportedField(ImmutableSDField importedField) {
@@ -40,4 +46,8 @@ public class AddAttributeTransformToSummaryOfImportedFields extends Processor {
private static void setAttributeTransform(SummaryField summaryField) {
summaryField.setTransform(SummaryTransform.ATTRIBUTE);
}
+
+ private static void setAttributeCombinerTransform(SummaryField summaryField) {
+ summaryField.setTransform(SummaryTransform.ATTRIBUTECOMBINER);
+ }
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java
index a3efd086c6a..bfb2fa9c120 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/ImportedFieldsResolver.java
@@ -2,11 +2,15 @@
package com.yahoo.searchdefinition.processing;
import com.yahoo.config.application.api.DeployLogger;
+import com.yahoo.document.ArrayDataType;
import com.yahoo.document.DataType;
+import com.yahoo.document.MapDataType;
+import com.yahoo.document.StructDataType;
import com.yahoo.searchdefinition.DocumentReference;
import com.yahoo.searchdefinition.DocumentReferences;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.Search;
+import com.yahoo.searchdefinition.document.Attribute;
import com.yahoo.searchdefinition.document.ImmutableSDField;
import com.yahoo.searchdefinition.document.ImportedField;
import com.yahoo.searchdefinition.document.ImportedFields;
@@ -17,6 +21,10 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
+import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isArrayOfSimpleStruct;
+import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfPrimitiveType;
+import static com.yahoo.searchdefinition.document.ComplexAttributeFieldUtils.isMapOfSimpleStruct;
+
/**
* Iterates all imported fields from SD-parsing and validates and resolves them into concrete fields from referenced document types.
*
@@ -25,6 +33,7 @@ import java.util.Optional;
public class ImportedFieldsResolver extends Processor {
private final Map<String, ImportedField> importedFields = new LinkedHashMap<>();
+ private final Map<String, ImportedField> importedComplexFields = new LinkedHashMap<>();
private final Optional<DocumentReferences> references;
public ImportedFieldsResolver(Search search, DeployLogger deployLogger, RankProfileRegistry rankProfileRegistry, QueryProfiles queryProfiles) {
@@ -35,12 +44,86 @@ public class ImportedFieldsResolver extends Processor {
@Override
public void process(boolean validate, boolean documentsOnly) {
search.temporaryImportedFields().get().fields().forEach((name, field) -> resolveImportedField(field, validate));
- search.setImportedFields(new ImportedFields(importedFields));
+ search.setImportedFields(new ImportedFields(importedFields, importedComplexFields));
}
private void resolveImportedField(TemporaryImportedField importedField, boolean validate) {
DocumentReference reference = validateDocumentReference(importedField);
- ImmutableSDField targetField = validateTargetField(importedField, reference, validate);
+ ImmutableSDField targetField = getTargetField(importedField, reference);
+ if (isArrayOfSimpleStruct(targetField)) {
+ resolveImportedArrayOfStructField(importedField, reference, targetField, validate);
+ } else if (isMapOfSimpleStruct(targetField)) {
+ resolveImportedMapOfStructField(importedField, reference, targetField, validate);
+ } else if (isMapOfPrimitiveType(targetField)) {
+ resolveImportedMapOfPrimitiveField(importedField, reference, targetField, validate);
+ } else {
+ resolveImportedNormalField(importedField, reference, targetField, validate);
+ }
+ }
+
+ private void resolveImportedArrayOfStructField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField, boolean validate) {
+ resolveImportedNestedStructField(importedField, reference, targetField, validate);
+ makeImportedComplexField(importedField, reference, targetField);
+ }
+
+ private void resolveImportedMapOfStructField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField, boolean validate) {
+ resolveImportedNestedField(importedField, reference, targetField.getStructField("key"), validate);
+ resolveImportedNestedStructField(importedField, reference, targetField.getStructField("value"), validate);
+ makeImportedComplexField(importedField, reference, targetField);
+ }
+
+ private void makeImportedComplexField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField) {
+ String name = importedField.fieldName();
+ importedComplexFields.put(name, new ImportedField(name, reference, targetField));
+ }
+
+ private static String makeImportedNestedFieldName(TemporaryImportedField importedField, ImmutableSDField targetNestedField) {
+ return importedField.fieldName() + targetNestedField.getName().substring(importedField.targetFieldName().length());
+ }
+
+ private boolean resolveImportedNestedField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetNestedField, boolean requireAttribute) {
+ Attribute attribute = targetNestedField.getAttributes().get(targetNestedField.getName());
+ String importedNestedFieldName = makeImportedNestedFieldName(importedField, targetNestedField);
+ if (attribute != null) {
+ importedFields.put(importedNestedFieldName, new ImportedField(importedNestedFieldName, reference, targetNestedField));
+ } else if (requireAttribute) {
+ fail(importedField, importedNestedFieldName, targetFieldAsString(targetNestedField.getName(), reference) +
+ ": Is not an attribute field. Only attribute fields supported");
+ }
+ return attribute != null;
+ }
+
+ private void resolveImportedNestedStructField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetNestedField, boolean validate) {
+ boolean foundAttribute = false;
+ for (ImmutableSDField targetStructField : targetNestedField.getStructFields()) {
+ if (resolveImportedNestedField(importedField, reference, targetStructField, false)) {
+ foundAttribute = true;
+ };
+ }
+ if (validate && !foundAttribute) {
+ String importedNestedFieldName = makeImportedNestedFieldName(importedField, targetNestedField);
+ fail(importedField, importedNestedFieldName, targetFieldAsString(targetNestedField.getName(), reference) +
+ ": Is not a struct containing an attribute field.");
+ }
+ }
+
+ private void resolveImportedMapOfPrimitiveField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField, boolean validate) {
+ resolveImportedNestedField(importedField, reference, targetField.getStructField("key"), validate);
+ resolveImportedNestedField(importedField, reference, targetField.getStructField("value"), validate);
+ makeImportedComplexField(importedField, reference, targetField);
+ }
+
+ private void resolveImportedNormalField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField, boolean validate) {
+ if (validate) {
+ validateTargetField(importedField, targetField, reference);
+ }
importedFields.put(importedField.fieldName(), new ImportedField(importedField.fieldName(), reference, targetField));
}
@@ -53,30 +136,31 @@ public class ImportedFieldsResolver extends Processor {
return reference;
}
- private ImmutableSDField validateTargetField(TemporaryImportedField importedField,
- DocumentReference reference,
- boolean validate) {
+ private ImmutableSDField getTargetField(TemporaryImportedField importedField,
+ DocumentReference reference) {
String targetFieldName = importedField.targetFieldName();
Search targetSearch = reference.targetSearch();
ImmutableSDField targetField = targetSearch.getField(targetFieldName);
if (targetField == null) {
fail(importedField, targetFieldAsString(targetFieldName, reference) + ": Not found");
}
- if (validate) {
- if (!targetField.doesAttributing()) {
- fail(importedField, targetFieldAsString(targetFieldName, reference) +
- ": Is not an attribute field. Only attribute fields supported");
- } else if (targetField.doesIndexing()) {
- fail(importedField, targetFieldAsString(targetFieldName, reference) +
- ": Is an index field. Not supported");
- } else if (targetField.getDataType().equals(DataType.PREDICATE)) {
- fail(importedField, targetFieldAsString(targetFieldName, reference) +
- ": Is of type 'predicate'. Not supported");
- }
- }
return targetField;
}
+ private void validateTargetField(TemporaryImportedField importedField,
+ ImmutableSDField targetField, DocumentReference reference) {
+ if (!targetField.doesAttributing()) {
+ fail(importedField, targetFieldAsString(targetField.getName(), reference) +
+ ": Is not an attribute field. Only attribute fields supported");
+ } else if (targetField.doesIndexing()) {
+ fail(importedField, targetFieldAsString(targetField.getName(), reference) +
+ ": Is an index field. Not supported");
+ } else if (targetField.getDataType().equals(DataType.PREDICATE)) {
+ fail(importedField, targetFieldAsString(targetField.getName(), reference) +
+ ": Is of type 'predicate'. Not supported");
+ }
+ }
+
private static String targetFieldAsString(String targetFieldName, DocumentReference reference) {
return "Field '" + targetFieldName + "' via reference field '" + reference.referenceField().getName() + "'";
}
@@ -85,4 +169,8 @@ public class ImportedFieldsResolver extends Processor {
throw new IllegalArgumentException("For search '" + search.getName() + "', import field '" + importedField.fieldName() + "': " + msg);
}
+ private void fail(TemporaryImportedField importedField, String importedNestedFieldName, String msg) {
+ throw new IllegalArgumentException("For search '" + search.getName() + "', import field '" +
+ importedField.fieldName() + "' (nested to '" + importedNestedFieldName + "'): " + msg);
+ }
}
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/processing/SummaryFieldsMustHaveValidSource.java b/config-model/src/main/java/com/yahoo/searchdefinition/processing/SummaryFieldsMustHaveValidSource.java
index c87801685bb..008b3182d8f 100644
--- a/config-model/src/main/java/com/yahoo/searchdefinition/processing/SummaryFieldsMustHaveValidSource.java
+++ b/config-model/src/main/java/com/yahoo/searchdefinition/processing/SummaryFieldsMustHaveValidSource.java
@@ -51,6 +51,7 @@ public class SummaryFieldsMustHaveValidSource extends Processor {
return isDocumentField(source) ||
(isNotInThisSummaryClass(summary, source) && isSummaryField(source)) ||
(isInThisSummaryClass(summary, source) && !source.equals(summaryField.getName())) ||
+ (search.importedFields().map(fields -> fields.complexFields().get(source) != null).orElse(false)) ||
(SummaryClass.DOCUMENT_ID_FIELD.equals(source));
}
diff --git a/config-model/src/test/derived/imported_struct_fields/attributes.cfg b/config-model/src/test/derived/imported_struct_fields/attributes.cfg
new file mode 100644
index 00000000000..ce6ff5e54ae
--- /dev/null
+++ b/config-model/src/test/derived/imported_struct_fields/attributes.cfg
@@ -0,0 +1,168 @@
+attribute[0].name "parent_ref"
+attribute[0].datatype REFERENCE
+attribute[0].collectiontype SINGLE
+attribute[0].removeifzero false
+attribute[0].createifnonexistent false
+attribute[0].fastsearch false
+attribute[0].huge false
+attribute[0].ismutable false
+attribute[0].sortascending true
+attribute[0].sortfunction UCA
+attribute[0].sortstrength PRIMARY
+attribute[0].sortlocale ""
+attribute[0].enablebitvectors false
+attribute[0].enableonlybitvector false
+attribute[0].fastaccess false
+attribute[0].arity 8
+attribute[0].lowerbound -9223372036854775808
+attribute[0].upperbound 9223372036854775807
+attribute[0].densepostinglistthreshold 0.4
+attribute[0].tensortype ""
+attribute[0].imported false
+attribute[1].name "my_elem_array.name"
+attribute[1].datatype STRING
+attribute[1].collectiontype SINGLE
+attribute[1].removeifzero false
+attribute[1].createifnonexistent false
+attribute[1].fastsearch true
+attribute[1].huge false
+attribute[1].ismutable false
+attribute[1].sortascending true
+attribute[1].sortfunction UCA
+attribute[1].sortstrength PRIMARY
+attribute[1].sortlocale ""
+attribute[1].enablebitvectors false
+attribute[1].enableonlybitvector false
+attribute[1].fastaccess false
+attribute[1].arity 8
+attribute[1].lowerbound -9223372036854775808
+attribute[1].upperbound 9223372036854775807
+attribute[1].densepostinglistthreshold 0.4
+attribute[1].tensortype ""
+attribute[1].imported true
+attribute[2].name "my_elem_array.weight"
+attribute[2].datatype INT32
+attribute[2].collectiontype SINGLE
+attribute[2].removeifzero false
+attribute[2].createifnonexistent false
+attribute[2].fastsearch false
+attribute[2].huge false
+attribute[2].ismutable false
+attribute[2].sortascending true
+attribute[2].sortfunction UCA
+attribute[2].sortstrength PRIMARY
+attribute[2].sortlocale ""
+attribute[2].enablebitvectors false
+attribute[2].enableonlybitvector false
+attribute[2].fastaccess false
+attribute[2].arity 8
+attribute[2].lowerbound -9223372036854775808
+attribute[2].upperbound 9223372036854775807
+attribute[2].densepostinglistthreshold 0.4
+attribute[2].tensortype ""
+attribute[2].imported true
+attribute[3].name "my_elem_map.key"
+attribute[3].datatype STRING
+attribute[3].collectiontype SINGLE
+attribute[3].removeifzero false
+attribute[3].createifnonexistent false
+attribute[3].fastsearch true
+attribute[3].huge false
+attribute[3].ismutable false
+attribute[3].sortascending true
+attribute[3].sortfunction UCA
+attribute[3].sortstrength PRIMARY
+attribute[3].sortlocale ""
+attribute[3].enablebitvectors false
+attribute[3].enableonlybitvector false
+attribute[3].fastaccess false
+attribute[3].arity 8
+attribute[3].lowerbound -9223372036854775808
+attribute[3].upperbound 9223372036854775807
+attribute[3].densepostinglistthreshold 0.4
+attribute[3].tensortype ""
+attribute[3].imported true
+attribute[4].name "my_elem_map.value.name"
+attribute[4].datatype STRING
+attribute[4].collectiontype SINGLE
+attribute[4].removeifzero false
+attribute[4].createifnonexistent false
+attribute[4].fastsearch true
+attribute[4].huge false
+attribute[4].ismutable false
+attribute[4].sortascending true
+attribute[4].sortfunction UCA
+attribute[4].sortstrength PRIMARY
+attribute[4].sortlocale ""
+attribute[4].enablebitvectors false
+attribute[4].enableonlybitvector false
+attribute[4].fastaccess false
+attribute[4].arity 8
+attribute[4].lowerbound -9223372036854775808
+attribute[4].upperbound 9223372036854775807
+attribute[4].densepostinglistthreshold 0.4
+attribute[4].tensortype ""
+attribute[4].imported true
+attribute[5].name "my_elem_map.value.weight"
+attribute[5].datatype INT32
+attribute[5].collectiontype SINGLE
+attribute[5].removeifzero false
+attribute[5].createifnonexistent false
+attribute[5].fastsearch false
+attribute[5].huge false
+attribute[5].ismutable false
+attribute[5].sortascending true
+attribute[5].sortfunction UCA
+attribute[5].sortstrength PRIMARY
+attribute[5].sortlocale ""
+attribute[5].enablebitvectors false
+attribute[5].enableonlybitvector false
+attribute[5].fastaccess false
+attribute[5].arity 8
+attribute[5].lowerbound -9223372036854775808
+attribute[5].upperbound 9223372036854775807
+attribute[5].densepostinglistthreshold 0.4
+attribute[5].tensortype ""
+attribute[5].imported true
+attribute[6].name "my_str_int_map.key"
+attribute[6].datatype STRING
+attribute[6].collectiontype SINGLE
+attribute[6].removeifzero false
+attribute[6].createifnonexistent false
+attribute[6].fastsearch true
+attribute[6].huge false
+attribute[6].ismutable false
+attribute[6].sortascending true
+attribute[6].sortfunction UCA
+attribute[6].sortstrength PRIMARY
+attribute[6].sortlocale ""
+attribute[6].enablebitvectors false
+attribute[6].enableonlybitvector false
+attribute[6].fastaccess false
+attribute[6].arity 8
+attribute[6].lowerbound -9223372036854775808
+attribute[6].upperbound 9223372036854775807
+attribute[6].densepostinglistthreshold 0.4
+attribute[6].tensortype ""
+attribute[6].imported true
+attribute[7].name "my_str_int_map.value"
+attribute[7].datatype INT32
+attribute[7].collectiontype SINGLE
+attribute[7].removeifzero false
+attribute[7].createifnonexistent false
+attribute[7].fastsearch false
+attribute[7].huge false
+attribute[7].ismutable false
+attribute[7].sortascending true
+attribute[7].sortfunction UCA
+attribute[7].sortstrength PRIMARY
+attribute[7].sortlocale ""
+attribute[7].enablebitvectors false
+attribute[7].enableonlybitvector false
+attribute[7].fastaccess false
+attribute[7].arity 8
+attribute[7].lowerbound -9223372036854775808
+attribute[7].upperbound 9223372036854775807
+attribute[7].densepostinglistthreshold 0.4
+attribute[7].tensortype ""
+attribute[7].imported true \ No newline at end of file
diff --git a/config-model/src/test/derived/imported_struct_fields/child.sd b/config-model/src/test/derived/imported_struct_fields/child.sd
new file mode 100644
index 00000000000..8453031022b
--- /dev/null
+++ b/config-model/src/test/derived/imported_struct_fields/child.sd
@@ -0,0 +1,19 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+search child {
+ document child {
+ field parent_ref type reference<parent> {
+ indexing: attribute | summary
+ }
+ }
+ import field parent_ref.elem_array as my_elem_array {}
+ import field parent_ref.elem_map as my_elem_map {}
+ import field parent_ref.str_int_map as my_str_int_map {}
+
+ document-summary mysummary {
+ summary documentid type string {}
+ summary my_elem_array type array<elem> {}
+ summary my_elem_map type map<string, elem> {}
+ summary my_str_int_map type map<string, int> {}
+ }
+}
+
diff --git a/config-model/src/test/derived/imported_struct_fields/imported-fields.cfg b/config-model/src/test/derived/imported_struct_fields/imported-fields.cfg
new file mode 100644
index 00000000000..772e5cffec9
--- /dev/null
+++ b/config-model/src/test/derived/imported_struct_fields/imported-fields.cfg
@@ -0,0 +1,35 @@
+attribute[0].name "my_elem_array.name"
+attribute[0].referencefield "parent_ref"
+attribute[0].targetfield "elem_array.name"
+attribute[0].datatype NONE
+attribute[0].collectiontype SINGLE
+attribute[1].name "my_elem_array.weight"
+attribute[1].referencefield "parent_ref"
+attribute[1].targetfield "elem_array.weight"
+attribute[1].datatype NONE
+attribute[1].collectiontype SINGLE
+attribute[2].name "my_elem_map.key"
+attribute[2].referencefield "parent_ref"
+attribute[2].targetfield "elem_map.key"
+attribute[2].datatype NONE
+attribute[2].collectiontype SINGLE
+attribute[3].name "my_elem_map.value.name"
+attribute[3].referencefield "parent_ref"
+attribute[3].targetfield "elem_map.value.name"
+attribute[3].datatype NONE
+attribute[3].collectiontype SINGLE
+attribute[4].name "my_elem_map.value.weight"
+attribute[4].referencefield "parent_ref"
+attribute[4].targetfield "elem_map.value.weight"
+attribute[4].datatype NONE
+attribute[4].collectiontype SINGLE
+attribute[5].name "my_str_int_map.key"
+attribute[5].referencefield "parent_ref"
+attribute[5].targetfield "str_int_map.key"
+attribute[5].datatype NONE
+attribute[5].collectiontype SINGLE
+attribute[6].name "my_str_int_map.value"
+attribute[6].referencefield "parent_ref"
+attribute[6].targetfield "str_int_map.value"
+attribute[6].datatype NONE
+attribute[6].collectiontype SINGLE \ No newline at end of file
diff --git a/config-model/src/test/derived/imported_struct_fields/index-info.cfg b/config-model/src/test/derived/imported_struct_fields/index-info.cfg
new file mode 100644
index 00000000000..66448099e84
--- /dev/null
+++ b/config-model/src/test/derived/imported_struct_fields/index-info.cfg
@@ -0,0 +1,37 @@
+indexinfo[0].name "child"
+indexinfo[0].command[0].indexname "sddocname"
+indexinfo[0].command[0].command "index"
+indexinfo[0].command[1].indexname "sddocname"
+indexinfo[0].command[1].command "word"
+indexinfo[0].command[2].indexname "parent_ref"
+indexinfo[0].command[2].command "index"
+indexinfo[0].command[3].indexname "parent_ref"
+indexinfo[0].command[3].command "attribute"
+indexinfo[0].command[4].indexname "parent_ref"
+indexinfo[0].command[4].command "word"
+indexinfo[0].command[5].indexname "documentid"
+indexinfo[0].command[5].command "index"
+indexinfo[0].command[6].indexname "rankfeatures"
+indexinfo[0].command[6].command "index"
+indexinfo[0].command[7].indexname "summaryfeatures"
+indexinfo[0].command[7].command "index"
+indexinfo[0].command[8].indexname "my_elem_array.name"
+indexinfo[0].command[8].command "index"
+indexinfo[0].command[9].indexname "my_elem_array.weight"
+indexinfo[0].command[9].command "index"
+indexinfo[0].command[10].indexname "my_elem_array.weight"
+indexinfo[0].command[10].command "numerical"
+indexinfo[0].command[11].indexname "my_elem_map.key"
+indexinfo[0].command[11].command "index"
+indexinfo[0].command[12].indexname "my_elem_map.value.name"
+indexinfo[0].command[12].command "index"
+indexinfo[0].command[13].indexname "my_elem_map.value.weight"
+indexinfo[0].command[13].command "index"
+indexinfo[0].command[14].indexname "my_elem_map.value.weight"
+indexinfo[0].command[14].command "numerical"
+indexinfo[0].command[15].indexname "my_str_int_map.key"
+indexinfo[0].command[15].command "index"
+indexinfo[0].command[16].indexname "my_str_int_map.value"
+indexinfo[0].command[16].command "index"
+indexinfo[0].command[17].indexname "my_str_int_map.value"
+indexinfo[0].command[17].command "numerical" \ No newline at end of file
diff --git a/config-model/src/test/derived/imported_struct_fields/indexschema.cfg b/config-model/src/test/derived/imported_struct_fields/indexschema.cfg
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/config-model/src/test/derived/imported_struct_fields/indexschema.cfg
diff --git a/config-model/src/test/derived/imported_struct_fields/parent.sd b/config-model/src/test/derived/imported_struct_fields/parent.sd
new file mode 100644
index 00000000000..7419cb465fd
--- /dev/null
+++ b/config-model/src/test/derived/imported_struct_fields/parent.sd
@@ -0,0 +1,43 @@
+# Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+search parent {
+ document parent {
+ struct elem {
+ field name type string {}
+ field weight type int {}
+ }
+ field elem_array type array<elem> {
+ indexing: summary
+ struct-field name {
+ indexing: attribute
+ attribute: fast-search
+ }
+ struct-field weight {
+ indexing: attribute
+ }
+ }
+ field elem_map type map<string, elem> {
+ indexing: summary
+ struct-field key {
+ indexing: attribute
+ attribute: fast-search
+ }
+ struct-field value.name {
+ indexing: attribute
+ attribute: fast-search
+ }
+ struct-field value.weight {
+ indexing: attribute
+ }
+ }
+ field str_int_map type map<string, int> {
+ indexing: summary
+ struct-field key {
+ indexing: attribute
+ attribute: fast-search
+ }
+ struct-field value {
+ indexing: attribute
+ }
+ }
+ }
+}
diff --git a/config-model/src/test/derived/imported_struct_fields/summary.cfg b/config-model/src/test/derived/imported_struct_fields/summary.cfg
new file mode 100644
index 00000000000..666da5bbc76
--- /dev/null
+++ b/config-model/src/test/derived/imported_struct_fields/summary.cfg
@@ -0,0 +1,31 @@
+defaultsummaryid 1570252291
+classes[0].id 1570252291
+classes[0].name "default"
+classes[0].fields[0].name "parent_ref"
+classes[0].fields[0].type "longstring"
+classes[0].fields[1].name "rankfeatures"
+classes[0].fields[1].type "featuredata"
+classes[0].fields[2].name "summaryfeatures"
+classes[0].fields[2].type "featuredata"
+classes[0].fields[3].name "documentid"
+classes[0].fields[3].type "longstring"
+classes[1].id 2126652894
+classes[1].name "mysummary"
+classes[1].fields[0].name "documentid"
+classes[1].fields[0].type "longstring"
+classes[1].fields[1].name "my_elem_array"
+classes[1].fields[1].type "jsonstring"
+classes[1].fields[2].name "my_elem_map"
+classes[1].fields[2].type "jsonstring"
+classes[1].fields[3].name "my_str_int_map"
+classes[1].fields[3].type "jsonstring"
+classes[1].fields[4].name "rankfeatures"
+classes[1].fields[4].type "featuredata"
+classes[1].fields[5].name "summaryfeatures"
+classes[1].fields[5].type "featuredata"
+classes[2].id 1274088866
+classes[2].name "attributeprefetch"
+classes[2].fields[0].name "rankfeatures"
+classes[2].fields[0].type "featuredata"
+classes[2].fields[1].name "summaryfeatures"
+classes[2].fields[1].type "featuredata" \ No newline at end of file
diff --git a/config-model/src/test/derived/imported_struct_fields/summarymap.cfg b/config-model/src/test/derived/imported_struct_fields/summarymap.cfg
new file mode 100644
index 00000000000..0b869417aef
--- /dev/null
+++ b/config-model/src/test/derived/imported_struct_fields/summarymap.cfg
@@ -0,0 +1,16 @@
+defaultoutputclass -1
+override[0].field "my_elem_array"
+override[0].command "attributecombiner"
+override[0].arguments ""
+override[1].field "my_elem_map"
+override[1].command "attributecombiner"
+override[1].arguments ""
+override[2].field "my_str_int_map"
+override[2].command "attributecombiner"
+override[2].arguments ""
+override[3].field "rankfeatures"
+override[3].command "rankfeatures"
+override[3].arguments ""
+override[4].field "summaryfeatures"
+override[4].command "summaryfeatures"
+override[4].arguments "" \ No newline at end of file
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/derived/ImportedFieldsTestCase.java b/config-model/src/test/java/com/yahoo/searchdefinition/derived/ImportedFieldsTestCase.java
index eecd03cbe5b..f7641f97df6 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/derived/ImportedFieldsTestCase.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/derived/ImportedFieldsTestCase.java
@@ -15,4 +15,9 @@ public class ImportedFieldsTestCase extends AbstractExportingTestCase {
public void configs_for_imported_fields_are_derived() throws IOException, ParseException {
assertCorrectDeriving("importedfields", "child");
}
+
+ @Test
+ public void configs_for_imported_struct_fields_are_derived() throws IOException, ParseException {
+ assertCorrectDeriving("imported_struct_fields", "child");
+ }
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java
index 48adc0eefc5..3735b997073 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/AddAttributeTransformToSummaryOfImportedFieldsTest.java
@@ -54,7 +54,7 @@ public class AddAttributeTransformToSummaryOfImportedFieldsTest {
SDField targetField = new SDField("target_field", DataType.INT);
DocumentReference documentReference = new DocumentReference(new Field("reference_field"), targetSearch);
ImportedField importedField = new ImportedField(fieldName, documentReference, targetField);
- return new ImportedFields(Collections.singletonMap(fieldName, importedField));
+ return new ImportedFields(Collections.singletonMap(fieldName, importedField), Collections.emptyMap());
}
private static DocumentSummary createDocumentSummary(String fieldName) {
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 9b63c1cafe9..4167e534653 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
@@ -12,6 +12,7 @@ import org.junit.rules.ExpectedException;
import static org.junit.Assert.assertEquals;
import static com.yahoo.config.model.test.TestUtil.joinLines;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
/**
* @author geirst
@@ -20,14 +21,14 @@ public class ImportedFieldsTestCase {
@Test
public void fields_can_be_imported_from_referenced_document_types() throws ParseException {
- Search search = buildAdSearch(joinLines(
- "search ad {",
- " document ad {",
- " 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 {}",
+ Search search = buildAdSearch(joinLines(
+ "search ad {",
+ " document ad {",
+ " 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 {}",
"}"));
assertEquals(2, search.importedFields().get().fields().size());
assertSearchContainsImportedField("my_budget", "campaign_ref", "campaign", "budget", search);
@@ -65,6 +66,166 @@ public class ImportedFieldsTestCase {
return builder.getSearch("ad");
}
+ private static void checkStructImport(ParentSdBuilder parentBuilder) throws ParseException {
+ Search search = buildChildSearch(parentBuilder.build(), joinLines("search child {",
+ " document child {",
+ " field parent_ref type reference<parent> {",
+ " indexing: attribute | summary",
+ " }",
+ " }",
+ " import field parent_ref.elem_array as my_elem_array {}",
+ " import field parent_ref.elem_map as my_elem_map {}",
+ " import field parent_ref.str_int_map as my_str_int_map {}",
+ "}"));
+ assertEquals(parentBuilder.countAttrs(), search.importedFields().get().fields().size());
+ checkImportedField("my_elem_array.name", "parent_ref", "parent", "elem_array.name", search, parentBuilder.elem_array_name_attr);
+ checkImportedField("my_elem_array.weight", "parent_ref", "parent", "elem_array.weight", search, parentBuilder.elem_array_weight_attr);
+ checkImportedField("my_elem_map.key", "parent_ref", "parent", "elem_map.key", search, parentBuilder.elem_map_key_attr);
+ checkImportedField("my_elem_map.value.name", "parent_ref", "parent", "elem_map.value.name", search, parentBuilder.elem_map_value_name_attr);
+ checkImportedField("my_elem_map.value.weight", "parent_ref", "parent", "elem_map.value.weight", search, parentBuilder.elem_map_value_weight_attr);
+ checkImportedField("my_str_int_map.key", "parent_ref", "parent", "str_int_map.key", search, parentBuilder.str_int_map_key_attr);
+ checkImportedField("my_str_int_map.value", "parent_ref", "parent", "str_int_map.value", search, parentBuilder.str_int_map_value_attr);
+ }
+
+ @Test
+ public void check_struct_import() throws ParseException {
+ checkStructImport(new ParentSdBuilder());
+ checkStructImport(new ParentSdBuilder().elem_array_weight_attr(false).elem_map_value_weight_attr(false));
+ checkStructImport(new ParentSdBuilder().elem_array_name_attr(false).elem_map_value_name_attr(false));
+ }
+
+ @Test
+ public void check_illegal_struct_import_missing_array_of_struct_attributes() throws ParseException {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("For search 'child', import field 'my_elem_array' (nested to 'my_elem_array'): Field 'elem_array' via reference field 'parent_ref': Is not a struct containing an attribute field.");
+ checkStructImport(new ParentSdBuilder().elem_array_name_attr(false).elem_array_weight_attr(false));
+ }
+
+ @Test
+ public void check_illegal_struct_import_missing_map_of_struct_key_attribute() throws ParseException {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("For search 'child', import field 'my_elem_map' (nested to 'my_elem_map.key'): Field 'elem_map.key' via reference field 'parent_ref': Is not an attribute field. Only attribute fields supported");
+ checkStructImport(new ParentSdBuilder().elem_map_key_attr(false));
+ }
+
+ @Test
+ public void check_illegal_struct_import_missing_map_of_struct_value_attributes() throws ParseException {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("For search 'child', import field 'my_elem_map' (nested to 'my_elem_map.value'): Field 'elem_map.value' via reference field 'parent_ref': Is not a struct containing an attribute field.");
+ checkStructImport(new ParentSdBuilder().elem_map_value_name_attr(false).elem_map_value_weight_attr(false));
+ }
+
+ @Test
+ public void check_illegal_struct_import_missing_map_of_primitive_key_attribute() throws ParseException {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("For search 'child', import field 'my_str_int_map' (nested to 'my_str_int_map.key'): Field 'str_int_map.key' via reference field 'parent_ref': Is not an attribute field. Only attribute fields supported");
+ checkStructImport(new ParentSdBuilder().str_int_map_key_attr(false));
+ }
+
+ @Test
+ public void check_illegal_struct_import_missing_map_of_primitive_value_attribute() throws ParseException {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("For search 'child', import field 'my_str_int_map' (nested to 'my_str_int_map.value'): Field 'str_int_map.value' via reference field 'parent_ref': Is not an attribute field. Only attribute fields supported");
+ checkStructImport(new ParentSdBuilder().str_int_map_value_attr(false));
+ }
+
+ private static class ParentSdBuilder {
+ private boolean elem_array_name_attr;
+ private boolean elem_array_weight_attr;
+ private boolean elem_map_key_attr;
+ private boolean elem_map_value_name_attr;
+ private boolean elem_map_value_weight_attr;
+ private boolean str_int_map_key_attr;
+ private boolean str_int_map_value_attr;
+
+ public ParentSdBuilder() {
+ elem_array_name_attr = true;
+ elem_array_weight_attr = true;
+ elem_map_key_attr = true;
+ elem_map_value_name_attr = true;
+ elem_map_value_weight_attr = true;
+ str_int_map_key_attr = true;
+ str_int_map_value_attr = true;
+ }
+
+ public ParentSdBuilder elem_array_name_attr(boolean v) { elem_array_name_attr = v; return this; }
+ public ParentSdBuilder elem_array_weight_attr(boolean v) { elem_array_weight_attr = v; return this; }
+ public ParentSdBuilder elem_map_key_attr(boolean v) { elem_map_key_attr = v; return this; }
+ public ParentSdBuilder elem_map_value_name_attr(boolean v) { elem_map_value_name_attr = v; return this; }
+ public ParentSdBuilder elem_map_value_weight_attr(boolean v) { elem_map_value_weight_attr = v; return this; }
+ public ParentSdBuilder str_int_map_key_attr(boolean v) { str_int_map_key_attr = v; return this; }
+ public ParentSdBuilder str_int_map_value_attr(boolean v) { str_int_map_value_attr = v; return this; }
+
+ public String build() {
+ return joinLines("search parent {",
+ " document parent {",
+ " struct elem {",
+ " field name type string {}",
+ " field weight type int {}",
+ " }",
+ " field elem_array type array<elem> {",
+ " indexing: summary",
+ " struct-field name {",
+ structFieldSpec(elem_array_name_attr),
+ " }",
+ " struct-field weight {",
+ structFieldSpec(elem_array_weight_attr),
+ " }",
+ " }",
+ " field elem_map type map<string, elem> {",
+ " indexing: summary",
+ " struct-field key {",
+ structFieldSpec(elem_map_key_attr),
+ " }",
+ " struct-field value.name {",
+ structFieldSpec(elem_map_value_name_attr),
+ " }",
+ " struct-field value.weight {",
+ structFieldSpec(elem_map_value_weight_attr),
+ " }",
+ " }",
+ " field str_int_map type map<string, int> {",
+ " indexing: summary",
+ " struct-field key {",
+ structFieldSpec(str_int_map_key_attr),
+ " }",
+ " struct-field value {",
+ structFieldSpec(str_int_map_value_attr),
+ " }",
+ " }",
+ " }",
+ "}");
+ }
+
+ private static String structFieldSpec(boolean isAttribute) {
+ return isAttribute ? " indexing: attribute" : "";
+ }
+
+ private static int b2i(boolean b) {
+ return b ? 1 : 0;
+ }
+
+ public int countAttrs() {
+ int elem_array_attr_count = b2i(elem_array_name_attr) + b2i(elem_array_weight_attr);
+ int elem_map_attr_count = b2i(elem_map_key_attr) + b2i(elem_map_value_name_attr) + b2i(elem_map_value_weight_attr);
+ int str_int_map_attr_count = b2i(str_int_map_key_attr) + b2i(str_int_map_value_attr);
+ return elem_array_attr_count + elem_map_attr_count + str_int_map_attr_count;
+ }
+ }
+
+ private static Search buildChildSearch(String parentSdContent, String sdContent) throws ParseException {
+ SearchBuilder builder = new SearchBuilder();
+ builder.importString(parentSdContent);
+ builder.importString(sdContent);
+ builder.build();
+ return builder.getSearch("child");
+ }
+
+ private static void assertSearchNotContainsImportedField(String fieldName, Search search) {
+ ImportedField importedField = search.importedFields().get().fields().get(fieldName);
+ assertNull(importedField);
+ }
+
private static void assertSearchContainsImportedField(String fieldName,
String referenceFieldName,
String referenceDocType,
@@ -77,4 +238,13 @@ public class ImportedFieldsTestCase {
assertEquals(referenceDocType, importedField.reference().targetSearch().getName());
assertEquals(targetFieldName, importedField.targetField().getName());
}
+
+ private static void checkImportedField(String fieldName, String referenceFieldName, String referenceDocType,
+ String targetFieldName, Search search, boolean present) {
+ if (present) {
+ assertSearchContainsImportedField(fieldName, referenceFieldName, referenceDocType, targetFieldName, search);
+ } else {
+ assertSearchNotContainsImportedField(fieldName, search);
+ }
+ }
}
diff --git a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java
index d0b6524a7e1..e4c23f407c8 100644
--- a/config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java
+++ b/config-model/src/test/java/com/yahoo/searchdefinition/processing/ValidateFieldTypesTest.java
@@ -55,7 +55,7 @@ public class ValidateFieldTypesTest {
SDField targetField = new SDField("target_field", dataType);
DocumentReference documentReference = new DocumentReference(new Field("reference_field"), targetSearch);
ImportedField importedField = new ImportedField(fieldName, documentReference, targetField);
- return new ImportedFields(Collections.singletonMap(fieldName, importedField));
+ return new ImportedFields(Collections.singletonMap(fieldName, importedField), Collections.emptyMap());
}
private static DocumentSummary createDocumentSummary(String fieldName, DataType dataType) {