aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/schema/processing/ImportedFieldsResolver.java
diff options
context:
space:
mode:
Diffstat (limited to 'config-model/src/main/java/com/yahoo/schema/processing/ImportedFieldsResolver.java')
-rw-r--r--config-model/src/main/java/com/yahoo/schema/processing/ImportedFieldsResolver.java207
1 files changed, 207 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/schema/processing/ImportedFieldsResolver.java b/config-model/src/main/java/com/yahoo/schema/processing/ImportedFieldsResolver.java
new file mode 100644
index 00000000000..ee465be44f2
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/schema/processing/ImportedFieldsResolver.java
@@ -0,0 +1,207 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.schema.processing;
+
+import com.yahoo.config.application.api.DeployLogger;
+import com.yahoo.document.DataType;
+import com.yahoo.document.PositionDataType;
+import com.yahoo.schema.DocumentReference;
+import com.yahoo.schema.DocumentReferences;
+import com.yahoo.schema.RankProfileRegistry;
+import com.yahoo.schema.Schema;
+import com.yahoo.schema.document.Attribute;
+import com.yahoo.schema.document.GeoPos;
+import com.yahoo.schema.document.ImmutableSDField;
+import com.yahoo.schema.document.ImportedComplexField;
+import com.yahoo.schema.document.ImportedField;
+import com.yahoo.schema.document.ImportedFields;
+import com.yahoo.schema.document.ImportedSimpleField;
+import com.yahoo.schema.document.TemporaryImportedField;
+import com.yahoo.vespa.model.container.search.QueryProfiles;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.yahoo.schema.document.ComplexAttributeFieldUtils.isArrayOfSimpleStruct;
+import static com.yahoo.schema.document.ComplexAttributeFieldUtils.isMapOfPrimitiveType;
+import static com.yahoo.schema.document.ComplexAttributeFieldUtils.isMapOfSimpleStruct;
+
+/**
+ * Iterates all imported fields from schema parsing and validates and resolves them into concrete fields from referenced document types.
+ *
+ * @author geirst
+ */
+public class ImportedFieldsResolver extends Processor {
+
+ private final Map<String, ImportedField> importedFields = new LinkedHashMap<>();
+ private final Optional<DocumentReferences> references;
+
+ public ImportedFieldsResolver(Schema schema, DeployLogger deployLogger, RankProfileRegistry rankProfileRegistry, QueryProfiles queryProfiles) {
+ super(schema, deployLogger, rankProfileRegistry, queryProfiles);
+ references = schema.getDocument().getDocumentReferences();
+ }
+
+ @Override
+ public void process(boolean validate, boolean documentsOnly) {
+ schema.temporaryImportedFields().get().fields().forEach((name, field) -> resolveImportedField(field, validate));
+ schema.setImportedFields(new ImportedFields(importedFields));
+ }
+
+ private void resolveImportedField(TemporaryImportedField importedField, boolean validate) {
+ DocumentReference reference = validateDocumentReference(importedField);
+ ImmutableSDField targetField = getTargetField(importedField, reference);
+ if (GeoPos.isAnyPos(targetField)) {
+ resolveImportedPositionField(importedField, reference, targetField, validate);
+ } else 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 resolveImportedPositionField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField, boolean validate) {
+ TemporaryImportedField importedZCurveField = new TemporaryImportedField(PositionDataType.getZCurveFieldName(importedField.fieldName()),
+ reference.referenceField().getName(), PositionDataType.getZCurveFieldName(targetField.getName()));
+ ImmutableSDField targetZCurveField = getTargetField(importedZCurveField, reference);
+ resolveImportedNormalField(importedZCurveField, reference, targetZCurveField, validate);
+ ImportedComplexField importedStructField = new ImportedComplexField(importedField.fieldName(), reference, targetField);
+ registerImportedField(importedField, null, importedStructField);
+ }
+
+ private void resolveImportedArrayOfStructField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField, boolean validate) {
+ ImportedComplexField importedStructField = new ImportedComplexField(importedField.fieldName(), reference, targetField);
+ resolveImportedNestedStructField(importedField, reference, importedStructField, targetField, validate);
+ registerImportedField(importedField, null, importedStructField);
+ }
+
+ private void resolveImportedMapOfStructField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField, boolean validate) {
+ ImportedComplexField importedMapField = new ImportedComplexField(importedField.fieldName(), reference, targetField);
+ ImportedComplexField importedStructField = new ImportedComplexField(importedField.fieldName() + ".value", reference, targetField.getStructField("value"));
+ importedMapField.addNestedField(importedStructField);
+ resolveImportedNestedField(importedField, reference, importedMapField, targetField.getStructField("key"), validate);
+ resolveImportedNestedStructField(importedField, reference, importedStructField, importedStructField.targetField(), validate);
+ registerImportedField(importedField, null, importedMapField);
+ }
+
+ private void makeImportedNormalField(TemporaryImportedField importedField, ImportedComplexField owner, String name, DocumentReference reference, ImmutableSDField targetField) {
+ ImportedField importedSimpleField = new ImportedSimpleField(name, reference, targetField);
+ registerImportedField(importedField, owner, importedSimpleField);
+ }
+
+ private void registerImportedField(TemporaryImportedField temporaryImportedField, ImportedComplexField owner, ImportedField importedField) {
+ if (owner != null) {
+ owner.addNestedField(importedField);
+ } else {
+ if (importedFields.get(importedField.fieldName()) != null) {
+ fail(temporaryImportedField, importedField.fieldName(), targetFieldAsString(importedField.targetField().getName(), importedField.reference()) + ": Field already imported");
+ }
+ importedFields.put(importedField.fieldName(), importedField);
+ }
+ }
+
+ private static String makeImportedNestedFieldName(TemporaryImportedField importedField, ImmutableSDField targetNestedField) {
+ return importedField.fieldName() + targetNestedField.getName().substring(importedField.targetFieldName().length());
+ }
+
+ private boolean resolveImportedNestedField(TemporaryImportedField importedField, DocumentReference reference,
+ ImportedComplexField owner, ImmutableSDField targetNestedField, boolean requireAttribute) {
+ Attribute attribute = targetNestedField.getAttribute();
+ String importedNestedFieldName = makeImportedNestedFieldName(importedField, targetNestedField);
+ if (attribute != null) {
+ makeImportedNormalField(importedField, owner, 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,
+ ImportedComplexField ownerField, ImmutableSDField targetNestedField, boolean validate) {
+ boolean foundAttribute = false;
+ for (ImmutableSDField targetStructField : targetNestedField.getStructFields()) {
+ if (resolveImportedNestedField(importedField, reference, ownerField, 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) {
+ ImportedComplexField importedMapField = new ImportedComplexField(importedField.fieldName(), reference, targetField);
+ resolveImportedNestedField(importedField, reference, importedMapField, targetField.getStructField("key"), validate);
+ resolveImportedNestedField(importedField, reference, importedMapField, targetField.getStructField("value"), validate);
+ registerImportedField(importedField, null, importedMapField);
+ }
+
+ private void resolveImportedNormalField(TemporaryImportedField importedField, DocumentReference reference,
+ ImmutableSDField targetField, boolean validate) {
+ if (validate) {
+ validateTargetField(importedField, targetField, reference);
+ }
+ makeImportedNormalField(importedField, null, importedField.fieldName(), reference, targetField);
+ }
+
+ private DocumentReference validateDocumentReference(TemporaryImportedField importedField) {
+ String referenceFieldName = importedField.referenceFieldName();
+ DocumentReference reference = references.get().referenceMap().get(referenceFieldName);
+ if (reference == null) {
+ fail(importedField, "Reference field '" + referenceFieldName + "' not found");
+ }
+ return reference;
+ }
+
+ private ImmutableSDField getTargetField(TemporaryImportedField importedField,
+ DocumentReference reference) {
+ String targetFieldName = importedField.targetFieldName();
+ Schema targetSchema = reference.targetSearch();
+ ImmutableSDField targetField = targetSchema.getField(targetFieldName);
+ if (targetField == null) {
+ fail(importedField, targetFieldAsString(targetFieldName, reference) + ": Not found");
+ }
+ 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() + "'";
+ }
+
+ private void fail(TemporaryImportedField importedField, String msg) {
+ throw new IllegalArgumentException("For " + schema + ", import field '" +
+ importedField.fieldName() + "': " + msg);
+ }
+
+ private void fail(TemporaryImportedField importedField, String importedNestedFieldName, String msg) {
+ if (importedField.fieldName().equals(importedNestedFieldName)) {
+ fail(importedField, msg);
+ }
+ throw new IllegalArgumentException("For " + schema + ", import field '" +
+ importedField.fieldName() + "' (nested to '" + importedNestedFieldName + "'): " + msg);
+ }
+}