Search in sources :

Example 1 with Node

use of com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node in project fdb-record-layer by FoundationDB.

the class PlannerGraphProperty method exportToDot.

/**
 * Creates a serialized format of this graph as a dot-compatible definition.
 * @param plannerGraph the planner graph we should export to dot
 * @param queryPlannerNodes set of nodes which is a subset of nodes of {@code plannerGraph} which represents the
 *        actual query graph
 * @param clusteringFunction function to partition the planner graph into clusters. Clusters are not isolated
 *        sub-graphs in this context. It is possible and often desirable for a use case to define edges between
 *        nodes of clusters. Clusters are used by the dot exporter to
 *        <ul>
 *            <li>assign common attributes to all nodes and edges, e.g. like a common gray background</li>
 *            <li>assign a name that is displayed displayed</li>
 *            <li>cause the layout algorithm to pack the nodes in a cluster if they were one big node</li>
 *        </ul>
 * @return the graph as string in dot format.
 */
@Nonnull
public static String exportToDot(@Nonnull final AbstractPlannerGraph<Node, Edge> plannerGraph, @Nonnull final Set<Node> queryPlannerNodes, @Nonnull final Function<GraphExporter.ClusterProvider<Node, Edge>, Collection<Cluster<Node, Edge>>> clusteringFunction) {
    final GraphExporter<Node, Edge> exporter = new DotExporter<>(new CountingIdProvider<>(), Node::getAttributes, Edge::getAttributes, ImmutableMap.of("fontname", Attribute.dot("courier"), "rankdir", Attribute.dot("BT"), "splines", Attribute.dot("false")), (network, nodes) -> {
        final ImmutableList.Builder<Cluster<Node, Edge>> clusterBuilder = ImmutableList.builder();
        clusterBuilder.addAll(clustersForGroups(plannerGraph.getNetwork(), queryPlannerNodes));
        clusterBuilder.addAll(clusteringFunction.apply(PlannerGraphProperty::clustersForGroups));
        return clusterBuilder.build();
    });
    // export as string
    final Writer writer = new StringWriter();
    exporter.exportGraph(plannerGraph.getNetwork(), writer);
    return writer.toString();
}
Also used : StringWriter(java.io.StringWriter) ImmutableList(com.google.common.collect.ImmutableList) Node(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node) Cluster(com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.Cluster) PartialMatchEdge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.PartialMatchEdge) Edge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Edge) PrintWriter(java.io.PrintWriter) StringWriter(java.io.StringWriter) Writer(java.io.Writer) Nonnull(javax.annotation.Nonnull)

Example 2 with Node

use of com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node in project fdb-record-layer by FoundationDB.

the class PlannerGraphProperty method exportToGml.

/**
 * Creates a serialized format of this graph as a gml-compatible definition.
 * @param plannerGraph the planner graph to be exported
 * @param additionalInfoMap a map used to generate names and descriptions for operators.
 * @return the graph as string in gml format.
 */
@Nonnull
public static String exportToGml(@Nonnull final PlannerGraph plannerGraph, @Nonnull final Map<String, Attribute> additionalInfoMap) {
    // Synthesize the set of NodeWithInfo nodes that are in the plan.
    final ImmutableSet<String> usedInfoIds = plannerGraph.getNetwork().nodes().stream().filter(n -> n instanceof PlannerGraph.WithInfoId).map(n -> (PlannerGraph.WithInfoId) n).map(PlannerGraph.WithInfoId::getInfoId).collect(ImmutableSet.toImmutableSet());
    final ImmutableMap.Builder<String, Attribute> infoMapBuilder = ImmutableMap.builder();
    infoMapBuilder.putAll(Maps.filterEntries(NodeInfo.getInfoAttributeMap(NodeInfo.getNodeInfos()), e -> usedInfoIds.contains(Objects.requireNonNull(e).getKey())));
    infoMapBuilder.putAll(Maps.filterEntries(additionalInfoMap, e -> usedInfoIds.contains(Objects.requireNonNull(e).getKey())));
    final ImmutableMap<String, Attribute> infoMap = infoMapBuilder.build();
    final GraphExporter<Node, Edge> exporter = createGmlExporter(infoMap);
    // export as string
    final Writer writer = new StringWriter();
    exporter.exportGraph(plannerGraph.getNetwork(), writer);
    return writer.toString();
}
Also used : ExpressionRefTraversal(com.apple.foundationdb.record.query.plan.temp.ExpressionRefTraversal) PartialMatchEdge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.PartialMatchEdge) Iterables(com.google.common.collect.Iterables) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) ComponentIdProvider(com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.ComponentIdProvider) Function(java.util.function.Function) PlannerProperty(com.apple.foundationdb.record.query.plan.temp.PlannerProperty) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) ImmutableList(com.google.common.collect.ImmutableList) CharStreams(com.google.common.io.CharStreams) Map(java.util.Map) ImmutableNetwork(com.google.common.graph.ImmutableNetwork) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) URI(java.net.URI) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Network(com.google.common.graph.Network) Edge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Edge) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) PrintWriter(java.io.PrintWriter) Desktop(java.awt.Desktop) Verify(com.google.common.base.Verify) ImmutableSet(com.google.common.collect.ImmutableSet) Cluster(com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.Cluster) ImmutableMap(com.google.common.collect.ImmutableMap) Node(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node) StringWriter(java.io.StringWriter) Collection(java.util.Collection) Debugger(com.apple.foundationdb.record.query.plan.temp.debug.Debugger) Throwables(com.google.common.base.Throwables) Set(java.util.Set) Maps(com.google.common.collect.Maps) InputStreamReader(java.io.InputStreamReader) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) File(java.io.File) StandardCharsets(java.nio.charset.StandardCharsets) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) Objects(java.util.Objects) List(java.util.List) Writer(java.io.Writer) Optional(java.util.Optional) Preconditions(com.google.common.base.Preconditions) InputStream(java.io.InputStream) Node(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node) ImmutableMap(com.google.common.collect.ImmutableMap) StringWriter(java.io.StringWriter) PartialMatchEdge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.PartialMatchEdge) Edge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Edge) PrintWriter(java.io.PrintWriter) StringWriter(java.io.StringWriter) Writer(java.io.Writer) Nonnull(javax.annotation.Nonnull)

Example 3 with Node

use of com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node in project fdb-record-layer by FoundationDB.

the class PlannerGraphProperty method show.

/**
 * Show the planner expression that and all the match candidates rendered in your default browser. This also
 * shows {@link PartialMatch}es between references if they exist.
 * @param renderSingleGroups iff true group references with just one member are not rendered
 * @param queryPlanRootReference the planner expression to be rendered.
 * @param matchCandidates a set of candidates for matching which should also be shown
 * @return the word "done" (IntelliJ really likes a return of String).
 */
@Nonnull
public static String show(final boolean renderSingleGroups, @Nonnull final GroupExpressionRef<? extends RelationalExpression> queryPlanRootReference, @Nonnull final Set<MatchCandidate> matchCandidates) {
    final PlannerGraph queryPlannerGraph = Objects.requireNonNull(queryPlanRootReference.acceptPropertyVisitor(forInternalShow(renderSingleGroups, true)));
    final PlannerGraph.InternalPlannerGraphBuilder graphBuilder = queryPlannerGraph.derived();
    final Map<MatchCandidate, PlannerGraph> matchCandidateMap = matchCandidates.stream().collect(ImmutableMap.toImmutableMap(Function.identity(), matchCandidate -> Objects.requireNonNull(matchCandidate.getTraversal().getRootReference().acceptPropertyVisitor(forInternalShow(renderSingleGroups)))));
    matchCandidateMap.forEach((matchCandidate, matchCandidateGraph) -> graphBuilder.addGraph(matchCandidateGraph));
    final ExpressionRefTraversal queryGraphTraversal = ExpressionRefTraversal.withRoot(queryPlanRootReference);
    final Set<ExpressionRef<? extends RelationalExpression>> queryGraphRefs = queryGraphTraversal.getRefs();
    queryGraphRefs.forEach(queryGraphRef -> {
        for (final MatchCandidate matchCandidate : Sets.intersection(matchCandidates, queryGraphRef.getMatchCandidates())) {
            final Set<PartialMatch> partialMatchesForCandidate = queryGraphRef.getPartialMatchesForCandidate(matchCandidate);
            final PlannerGraph matchCandidatePlannerGraph = Objects.requireNonNull(matchCandidateMap.get(matchCandidate));
            final Node queryRefNode = Objects.requireNonNull(queryPlannerGraph.getNodeForIdentity(queryGraphRef));
            for (final PartialMatch partialMatchForCandidate : partialMatchesForCandidate) {
                @Nullable final Node matchCandidateNode = matchCandidatePlannerGraph.getNodeForIdentity(partialMatchForCandidate.getCandidateRef());
                // should always be true but we don't want to bail out for corrupt graphs
                if (matchCandidateNode != null) {
                    graphBuilder.addEdge(queryRefNode, matchCandidateNode, new PartialMatchEdge());
                }
            }
        }
    });
    final String dotString = exportToDot(graphBuilder.build(), queryPlannerGraph.getNetwork().nodes(), nestedClusterProvider -> matchCandidateMap.entrySet().stream().map(entry -> new NamedCluster(entry.getKey().getName(), entry.getValue().getNetwork().nodes(), nestedClusterProvider)).collect(Collectors.toList()));
    return show(dotString);
}
Also used : ExpressionRefTraversal(com.apple.foundationdb.record.query.plan.temp.ExpressionRefTraversal) PartialMatchEdge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.PartialMatchEdge) Iterables(com.google.common.collect.Iterables) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) ComponentIdProvider(com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.ComponentIdProvider) Function(java.util.function.Function) PlannerProperty(com.apple.foundationdb.record.query.plan.temp.PlannerProperty) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) ImmutableList(com.google.common.collect.ImmutableList) CharStreams(com.google.common.io.CharStreams) Map(java.util.Map) ImmutableNetwork(com.google.common.graph.ImmutableNetwork) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) URI(java.net.URI) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Network(com.google.common.graph.Network) Edge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Edge) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) PrintWriter(java.io.PrintWriter) Desktop(java.awt.Desktop) Verify(com.google.common.base.Verify) ImmutableSet(com.google.common.collect.ImmutableSet) Cluster(com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.Cluster) ImmutableMap(com.google.common.collect.ImmutableMap) Node(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node) StringWriter(java.io.StringWriter) Collection(java.util.Collection) Debugger(com.apple.foundationdb.record.query.plan.temp.debug.Debugger) Throwables(com.google.common.base.Throwables) Set(java.util.Set) Maps(com.google.common.collect.Maps) InputStreamReader(java.io.InputStreamReader) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) File(java.io.File) StandardCharsets(java.nio.charset.StandardCharsets) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) Objects(java.util.Objects) List(java.util.List) Writer(java.io.Writer) Optional(java.util.Optional) Preconditions(com.google.common.base.Preconditions) InputStream(java.io.InputStream) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) PartialMatchEdge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.PartialMatchEdge) ExpressionRefTraversal(com.apple.foundationdb.record.query.plan.temp.ExpressionRefTraversal) Node(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) Nullable(javax.annotation.Nullable) Nonnull(javax.annotation.Nonnull)

Example 4 with Node

use of com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node in project fdb-record-layer by FoundationDB.

the class PlannerGraphProperty method evaluateAtRef.

@Nonnull
@Override
public PlannerGraph evaluateAtRef(@Nonnull final ExpressionRef<? extends RelationalExpression> ref, @Nonnull List<PlannerGraph> memberResults) {
    if (memberResults.isEmpty()) {
        // should not happen -- but we don't want to bail
        return PlannerGraph.builder(new PlannerGraph.ExpressionRefHeadNode(ref)).build();
    }
    if (removePlansIfPossible()) {
        final List<PlannerGraph> filteredMemberResults = memberResults.stream().filter(graph -> graph.getRoot() instanceof PlannerGraph.WithExpression).filter(graph -> {
            final RelationalExpression expression = ((PlannerGraph.WithExpression) graph.getRoot()).getExpression();
            return !(expression instanceof RecordQueryPlan);
        }).collect(Collectors.toList());
        // if we filtered down to empty it is better to just show the physical plan, otherwise try to avoid it
        if (!filteredMemberResults.isEmpty()) {
            memberResults = filteredMemberResults;
        }
    } else if (removeLogicalExpressions()) {
        final List<PlannerGraph> filteredMemberResults = memberResults.stream().filter(graph -> graph.getRoot() instanceof PlannerGraph.WithExpression).filter(graph -> ((PlannerGraph.WithExpression) graph.getRoot()).getExpression() instanceof RecordQueryPlan).collect(Collectors.toList());
        // if we filtered down to empty it is better to just show the physical plan, otherwise try to avoid it
        if (!filteredMemberResults.isEmpty()) {
            memberResults = filteredMemberResults;
        }
    }
    if (renderSingleGroups() || memberResults.size() > 1) {
        final Node head = new PlannerGraph.ExpressionRefHeadNode(ref);
        final PlannerGraph.InternalPlannerGraphBuilder plannerGraphBuilder = PlannerGraph.builder(head);
        final List<PlannerGraph> memberGraphs = memberResults.stream().map(childGraph -> {
            final Node root = childGraph.getRoot();
            final Optional<String> debugNameOptional = Debugger.mapDebugger(debugger -> {
                if (root instanceof PlannerGraph.WithExpression) {
                    final PlannerGraph.WithExpression withExpression = (PlannerGraph.WithExpression) root;
                    @Nullable final RelationalExpression expression = withExpression.getExpression();
                    return expression == null ? null : debugger.nameForObject(expression);
                }
                return null;
            });
            final Node member = debugNameOptional.map(PlannerGraph.ExpressionRefMemberNode::new).orElse(new PlannerGraph.ExpressionRefMemberNode());
            return PlannerGraph.builder(member).addGraph(childGraph).addEdge(root, member, new PlannerGraph.GroupExpressionRefEdge()).build();
        }).collect(Collectors.toList());
        memberGraphs.forEach(memberGraph -> {
            plannerGraphBuilder.addGraph(memberGraph);
            plannerGraphBuilder.addEdge(memberGraph.getRoot(), head, new PlannerGraph.GroupExpressionRefInternalEdge());
        });
        return plannerGraphBuilder.build();
    } else {
        // !renderSingleGroups && memberResults.size() == 1
        return Iterables.getOnlyElement(memberResults);
    }
}
Also used : ExpressionRefTraversal(com.apple.foundationdb.record.query.plan.temp.ExpressionRefTraversal) PartialMatchEdge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.PartialMatchEdge) Iterables(com.google.common.collect.Iterables) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) ComponentIdProvider(com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.ComponentIdProvider) Function(java.util.function.Function) PlannerProperty(com.apple.foundationdb.record.query.plan.temp.PlannerProperty) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) ImmutableList(com.google.common.collect.ImmutableList) CharStreams(com.google.common.io.CharStreams) Map(java.util.Map) ImmutableNetwork(com.google.common.graph.ImmutableNetwork) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) URI(java.net.URI) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Network(com.google.common.graph.Network) Edge(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Edge) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) PrintWriter(java.io.PrintWriter) Desktop(java.awt.Desktop) Verify(com.google.common.base.Verify) ImmutableSet(com.google.common.collect.ImmutableSet) Cluster(com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.Cluster) ImmutableMap(com.google.common.collect.ImmutableMap) Node(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node) StringWriter(java.io.StringWriter) Collection(java.util.Collection) Debugger(com.apple.foundationdb.record.query.plan.temp.debug.Debugger) Throwables(com.google.common.base.Throwables) Set(java.util.Set) Maps(com.google.common.collect.Maps) InputStreamReader(java.io.InputStreamReader) Collectors(java.util.stream.Collectors) Sets(com.google.common.collect.Sets) File(java.io.File) StandardCharsets(java.nio.charset.StandardCharsets) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) Objects(java.util.Objects) List(java.util.List) Writer(java.io.Writer) Optional(java.util.Optional) Preconditions(com.google.common.base.Preconditions) InputStream(java.io.InputStream) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) Optional(java.util.Optional) Node(com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node) ImmutableList(com.google.common.collect.ImmutableList) List(java.util.List) Nonnull(javax.annotation.Nonnull)

Aggregations

Cluster (com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.Cluster)4 Edge (com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Edge)4 Node (com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.Node)4 PartialMatchEdge (com.apple.foundationdb.record.query.plan.temp.explain.PlannerGraph.PartialMatchEdge)4 ImmutableList (com.google.common.collect.ImmutableList)4 PrintWriter (java.io.PrintWriter)4 StringWriter (java.io.StringWriter)4 Writer (java.io.Writer)4 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)3 ExpressionRef (com.apple.foundationdb.record.query.plan.temp.ExpressionRef)3 ExpressionRefTraversal (com.apple.foundationdb.record.query.plan.temp.ExpressionRefTraversal)3 GroupExpressionRef (com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef)3 MatchCandidate (com.apple.foundationdb.record.query.plan.temp.MatchCandidate)3 PartialMatch (com.apple.foundationdb.record.query.plan.temp.PartialMatch)3 PlannerProperty (com.apple.foundationdb.record.query.plan.temp.PlannerProperty)3 RelationalExpression (com.apple.foundationdb.record.query.plan.temp.RelationalExpression)3 Debugger (com.apple.foundationdb.record.query.plan.temp.debug.Debugger)3 ComponentIdProvider (com.apple.foundationdb.record.query.plan.temp.explain.GraphExporter.ComponentIdProvider)3 Preconditions (com.google.common.base.Preconditions)3 Throwables (com.google.common.base.Throwables)3