Merge pull request #26067 from ClickHouse/play-graphs

Render pipelines in Play UI
This commit is contained in:
alexey-milovidov 2021-07-08 17:13:56 +03:00 committed by GitHub
commit 72e3abfef7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 86 additions and 1 deletions

View File

@ -53,5 +53,6 @@ macro(clickhouse_embed_binaries)
set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${ASSEMBLY_FILE_NAME}" APPEND PROPERTY INCLUDE_DIRECTORIES "${EMBED_RESOURCE_DIR}") set_property(SOURCE "${CMAKE_CURRENT_BINARY_DIR}/${ASSEMBLY_FILE_NAME}" APPEND PROPERTY INCLUDE_DIRECTORIES "${EMBED_RESOURCE_DIR}")
target_sources("${EMBED_TARGET}" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${ASSEMBLY_FILE_NAME}") target_sources("${EMBED_TARGET}" PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${ASSEMBLY_FILE_NAME}")
set_target_properties("${EMBED_TARGET}" PROPERTIES OBJECT_DEPENDS "${RESOURCE_FILE}")
endforeach() endforeach()
endmacro() endmacro()

View File

@ -283,6 +283,29 @@
color: var(--link-color); color: var(--link-color);
text-decoration: none; text-decoration: none;
} }
/* This is for graph in svg */
text
{
font-size: 14px;
fill: var(--text-color);
}
.node rect
{
fill: var(--element-background-color);
filter: drop-shadow(.2rem .2rem .2rem var(--shadow-color));
}
.edgePath path
{
stroke: var(--text-color);
}
marker
{
fill: var(--text-color);
}
</style> </style>
</head> </head>
@ -305,6 +328,7 @@
<table class="monospace shadow" id="data-table"></table> <table class="monospace shadow" id="data-table"></table>
<pre class="monospace shadow" id="data-unparsed"></pre> <pre class="monospace shadow" id="data-unparsed"></pre>
</div> </div>
<svg id="graph" fill="none"></svg>
<p id="error" class="monospace shadow"> <p id="error" class="monospace shadow">
</p> </p>
</body> </body>
@ -447,6 +471,12 @@
table.removeChild(table.lastChild); table.removeChild(table.lastChild);
} }
let graph = document.getElementById('graph');
while (graph.firstChild) {
graph.removeChild(graph.lastChild);
}
graph.style.display = 'none';
document.getElementById('data-unparsed').innerText = ''; document.getElementById('data-unparsed').innerText = '';
document.getElementById('data-unparsed').style.display = 'none'; document.getElementById('data-unparsed').style.display = 'none';
@ -461,12 +491,21 @@
function renderResult(response) function renderResult(response)
{ {
//console.log(response);
clear(); clear();
let stats = document.getElementById('stats'); let stats = document.getElementById('stats');
stats.innerText = 'Elapsed: ' + response.statistics.elapsed.toFixed(3) + " sec, read " + response.statistics.rows_read + " rows."; stats.innerText = 'Elapsed: ' + response.statistics.elapsed.toFixed(3) + " sec, read " + response.statistics.rows_read + " rows.";
/// We can also render graphs if user performed EXPLAIN PIPELINE graph=1.
if (response.data.length > 3 && response.data[0][0] === "digraph" && document.getElementById('query').value.match(/^\s*EXPLAIN/i)) {
renderGraph(response);
} else {
renderTable(response);
}
}
function renderTable(response)
{
let thead = document.createElement('thead'); let thead = document.createElement('thead');
for (let idx in response.meta) { for (let idx in response.meta) {
let th = document.createElement('th'); let th = document.createElement('th');
@ -559,6 +598,51 @@
document.getElementById('error').style.display = 'block'; document.getElementById('error').style.display = 'block';
} }
/// Huge JS libraries should be loaded only if needed.
function loadJS(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.addEventListener('load', function() { resolve(true); });
document.head.appendChild(script);
});
}
let load_dagre_promise;
function loadDagre() {
if (load_dagre_promise) { return load_dagre_promise; }
load_dagre_promise = Promise.all([
loadJS('https://dagrejs.github.io/project/dagre/v0.8.5/dagre.min.js'),
loadJS('https://dagrejs.github.io/project/graphlib-dot/v0.6.4/graphlib-dot.min.js'),
loadJS('https://dagrejs.github.io/project/dagre-d3/v0.6.4/dagre-d3.min.js'),
loadJS('https://cdn.jsdelivr.net/npm/d3@7.0.0'),
]);
return load_dagre_promise;
}
async function renderGraph(response)
{
await loadDagre();
/// https://github.com/dagrejs/dagre-d3/issues/131
const dot = response.data.reduce((acc, row) => acc + '\n' + row[0].replace(/shape\s*=\s*box/g, 'shape=rect'));
let graph = graphlibDot.read(dot);
graph.graph().rankdir = 'TB';
let render = new dagreD3.render();
let svg = document.getElementById('graph');
svg.style.display = 'block';
render(d3.select("#graph"), graph);
svg.style.width = graph.graph().width;
svg.style.height = graph.graph().height;
}
function setColorTheme(theme) function setColorTheme(theme)
{ {
window.localStorage.setItem('theme', theme); window.localStorage.setItem('theme', theme);