aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/schema/derived/SearchOrderer.java
diff options
context:
space:
mode:
Diffstat (limited to 'config-model/src/main/java/com/yahoo/schema/derived/SearchOrderer.java')
-rw-r--r--config-model/src/main/java/com/yahoo/schema/derived/SearchOrderer.java123
1 files changed, 123 insertions, 0 deletions
diff --git a/config-model/src/main/java/com/yahoo/schema/derived/SearchOrderer.java b/config-model/src/main/java/com/yahoo/schema/derived/SearchOrderer.java
new file mode 100644
index 00000000000..3bab808beff
--- /dev/null
+++ b/config-model/src/main/java/com/yahoo/schema/derived/SearchOrderer.java
@@ -0,0 +1,123 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.schema.derived;
+
+import com.yahoo.document.DataTypeName;
+import com.yahoo.schema.DocumentReference;
+import com.yahoo.schema.DocumentReferences;
+import com.yahoo.schema.Schema;
+import com.yahoo.schema.document.SDDocumentType;
+
+import java.util.*;
+
+/**
+ * <p>A class which can reorder a list of search definitions such that any supertype
+ * always preceed any subtype. Subject to this condition the given order
+ * is preserved (the minimal reordering is done).</p>
+ *
+ * <p>This class is <b>not</b> multithread safe. Only one ordering must be done
+ * at the time in any instance.</p>
+ *
+ * @author bratseth
+ * @author bjorncs
+ */
+public class SearchOrderer {
+
+ /** A map from DataTypeName to the Search defining them */
+ private final Map<DataTypeName, Schema> documentNameToSearch = new HashMap<>();
+
+ /**
+ * Reorders the given list of search definitions such that any supertype
+ * always preceed any subtype. Subject to this condition the given order
+ * is preserved (the minimal reordering is done).
+ *
+ * @return a new list containing the same search instances in the right order
+ */
+ public List<Schema> order(List<Schema> unordered) {
+ // Description above state that the original order should be preserved, except for the dependency constraint.
+ // Yet we botch that guarantee by sorting the list...
+ unordered.sort(Comparator.comparing(Schema::getName));
+
+ // No, this is not a fast algorithm...
+ indexOnDocumentName(unordered);
+ List<Schema> ordered = new ArrayList<>(unordered.size());
+ List<Schema> moveOutwards = new ArrayList<>();
+ for (Schema schema : unordered) {
+ if (allDependenciesAlreadyEmitted(ordered, schema)) {
+ addOrdered(ordered, schema, moveOutwards);
+ }
+ else {
+ moveOutwards.add(schema);
+ }
+ }
+
+ // Any leftovers means we have search definitions with undefined inheritants.
+ // This is warned about elsewhere.
+ ordered.addAll(moveOutwards);
+
+ documentNameToSearch.clear();
+ return ordered;
+ }
+
+ private void addOrdered(List<Schema> ordered, Schema schema, List<Schema> moveOutwards) {
+ ordered.add(schema);
+ Schema eligibleMove;
+ do {
+ eligibleMove = removeFirstEntryWithFullyEmittedDependencies(moveOutwards, ordered);
+ if (eligibleMove != null) {
+ ordered.add(eligibleMove);
+ }
+ } while (eligibleMove != null);
+ }
+
+ /** Removes and returns the first search from the move list which can now be added, or null if none */
+ private Schema removeFirstEntryWithFullyEmittedDependencies(List<Schema> moveOutwards, List<Schema> ordered) {
+ for (Schema move : moveOutwards) {
+ if (allDependenciesAlreadyEmitted(ordered, move)) {
+ moveOutwards.remove(move);
+ return move;
+ }
+ }
+ return null;
+ }
+
+ private boolean allDependenciesAlreadyEmitted(List<Schema> alreadyOrdered, Schema schema) {
+ if (schema.getDocument() == null) {
+ return true;
+ }
+ SDDocumentType document = schema.getDocument();
+ return allInheritedDependenciesEmitted(alreadyOrdered, document) && allReferenceDependenciesEmitted(alreadyOrdered, document);
+ }
+
+ private boolean allInheritedDependenciesEmitted(List<Schema> alreadyOrdered, SDDocumentType document) {
+ for (SDDocumentType sdoc : document.getInheritedTypes() ) {
+ DataTypeName inheritedName = sdoc.getDocumentName();
+ if ("document".equals(inheritedName.getName())) {
+ continue;
+ }
+ Schema inheritedSchema = documentNameToSearch.get(inheritedName);
+ if (!alreadyOrdered.contains(inheritedSchema)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean allReferenceDependenciesEmitted(List<Schema> alreadyOrdered, SDDocumentType document) {
+ DocumentReferences documentReferences = document.getDocumentReferences()
+ .orElseThrow(() -> new IllegalStateException("Missing document references. Should have been processed by now."));
+ return documentReferences.stream()
+ .map(Map.Entry::getValue)
+ .map(DocumentReference::targetSearch)
+ .allMatch(alreadyOrdered::contains);
+ }
+
+ private void indexOnDocumentName(List<Schema> schemas) {
+ documentNameToSearch.clear();
+ for (Schema schema : schemas) {
+ if (schema.getDocument() != null) {
+ documentNameToSearch.put(schema.getDocument().getDocumentName(), schema);
+ }
+ }
+ }
+
+}