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();
}
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();
}
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);
}
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);
}
}
Aggregations