diff --git a/sql/core/src/main/resources/org/apache/spark/sql/execution/ui/static/spark-sql-viz.css b/sql/core/src/main/resources/org/apache/spark/sql/execution/ui/static/spark-sql-viz.css index dbdbf9fbf57b1..d6a498e93872c 100644 --- a/sql/core/src/main/resources/org/apache/spark/sql/execution/ui/static/spark-sql-viz.css +++ b/sql/core/src/main/resources/org/apache/spark/sql/execution/ui/static/spark-sql-viz.css @@ -57,3 +57,21 @@ .job-url { word-wrap: break-word; } + +#plan-viz-graph svg g.node rect.selected { + fill: #E25A1CFF; + stroke: #317EACFF; + stroke-width: 2px; +} + +#plan-viz-graph svg g.node rect.linked { + fill: #FFC106FF; + stroke: #317EACFF; + stroke-width: 2px; +} + +#plan-viz-graph svg path.linked { + fill: #317EACFF; + stroke: #317EACFF; + stroke-width: 2px; +} diff --git a/sql/core/src/main/resources/org/apache/spark/sql/execution/ui/static/spark-sql-viz.js b/sql/core/src/main/resources/org/apache/spark/sql/execution/ui/static/spark-sql-viz.js index 96a7a7a3cc0e2..d4cc45a1639ab 100644 --- a/sql/core/src/main/resources/org/apache/spark/sql/execution/ui/static/spark-sql-viz.js +++ b/sql/core/src/main/resources/org/apache/spark/sql/execution/ui/static/spark-sql-viz.js @@ -47,6 +47,7 @@ function renderPlanViz() { .attr("ry", "5"); setupLayoutForSparkPlanCluster(g, svg); + setupSelectionForSparkPlanNode(g); setupTooltipForSparkPlanNode(g); resizeSvg(svg); postprocessForAdditionalMetrics(); @@ -269,3 +270,45 @@ function togglePlanViz() { // eslint-disable-line no-unused-vars planVizContainer().style("display", "none"); } } + +/* + * Light up the selected node and its linked nodes and edges. + */ +function setupSelectionForSparkPlanNode(g) { + const linkedNodes = new Map(); + const linkedEdges = new Map(); + + g.edges().forEach(function (e) { + const edge = g.edge(e); + const from = g.node(e.v); + const to = g.node(e.w); + collectLinks(linkedNodes, from.id, to.id); + collectLinks(linkedNodes, to.id, from.id); + collectLinks(linkedEdges, from.id, edge.arrowheadId); + collectLinks(linkedEdges, to.id, edge.arrowheadId); + }); + + linkedNodes.forEach((linkedNodes, selectNode) => { + d3.select("#" + selectNode).on("click", () => { + planVizContainer().selectAll(".selected").classed("selected", false); + planVizContainer().selectAll(".linked").classed("linked", false); + d3.select("#" + selectNode + " rect").classed("selected", true); + linkedNodes.forEach((linkedNode) => { + d3.select("#" + linkedNode + " rect").classed("linked", true); + }); + linkedEdges.get(selectNode).forEach((linkedEdge) => { + const arrowHead = d3.select("#" + linkedEdge + " path"); + arrowHead.classed("linked", true); + const arrowShaft = $(arrowHead.node()).parents("g.edgePath").children("path"); + arrowShaft.addClass("linked"); + }); + }); + }); +} + +function collectLinks(map, key, value) { + if (!map.has(key)) { + map.set(key, new Set()); + } + map.get(key).add(value); +}