private <T> void findFirstMatchesImpl(NodeType node, List<SearchResult<T>> result, Set<NodeType> visited, Function<NodeType, T> predicate) {
        if (!parentsOfNode.containsKey(node)) return;
        if (visited.contains(node)) return;

        visited.add(node);

        T match = predicate.apply(node);

        if (match != null) {
            result.add(new SearchResult<>(node, match));
            return;
        }

        for (NodeType parent : parentsOfNode.get(node)) {
            findFirstMatchesImpl(parent, result, visited, predicate);
        }
    }
Searches upwards for nodes where predicate(node) returns non null.
If a match is found, parents of that node are not checked (unless they are reachable by some other path without matches).
Return values store both the node and the result of running predicate on them
integration/schema-language-server/language-server/src/main/java/ai/vespa/schemals/index/InheritanceGraph.java