From 6053453722ae5b904a31256b3f219f4ef4aebf91 Mon Sep 17 00:00:00 2001 From: RobinTF <83676088+RobinTF@users.noreply.github.com> Date: Sat, 7 Oct 2023 00:02:04 +0200 Subject: [PATCH] Allow updating values by websocket --- backend/static/js/helper.js | 35 +++++++++++++++++++----------- backend/static/js/qleverUI.js | 41 +++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/backend/static/js/helper.js b/backend/static/js/helper.js index 16e24d56..f8e27d98 100644 --- a/backend/static/js/helper.js +++ b/backend/static/js/helper.js @@ -36,28 +36,36 @@ function normalizeQuery(query, escapeQuotes = false) { // // NOTE: A click on "Analysis" will show the runtime information from the last // query. See runtimeInfoForTreant in qleverUI.js. -function appendRuntimeInformation(runtime_info, query, time) { +function appendRuntimeInformation(runtime_info, query, time, queryUpdate) { // Backwards compatability hack in case the info on the execution tree is // not in a separate "query_execution_tree" element yet. if (runtime_info["query_execution_tree"] == undefined) { console.log("BACKWARDS compatibility hack: adding runtime_info[\"query_execution_tree\"]"); runtime_info["query_execution_tree"] = structuredClone(runtime_info); - runtime_info["meta"] = { } + runtime_info["meta"] = {}; } // Add query time to meta info. runtime_info["meta"]["total_time_computing"] = - parseInt(time["computeResult"].toString().replace(/ms/, "")) + parseInt(time["computeResult"].toString().replace(/ms/, ""), 10); runtime_info["meta"]["total_time"] = - parseInt(time["total"].toString().replace(/ms/, "")) - - // Append to log and shorten log if too long (FIFO). - runtime_log[runtime_log.length] = runtime_info; - query_log[query_log.length] = query; - if (runtime_log.length - 10 >= 0) { - runtime_log[runtime_log.length - 10] = null; - query_log[query_log.length - 10] = null; + parseInt(time["total"].toString().replace(/ms/, ""), 10); + + if (queryUpdate.queryId === lastQueryUpdate.queryId) { + if (queryUpdate.updateTimeStamp > lastQueryUpdate.updateTimeStamp) { + runtime_log[runtime_log.length - 1] = runtime_info; + query_log[query_log.length - 1] = query; + } + } else { + // Append to log and shorten log if too long (FIFO). + runtime_log[runtime_log.length] = runtime_info; + query_log[query_log.length] = query; + if (runtime_log.length - 10 >= 0) { + runtime_log[runtime_log.length - 10] = null; + query_log[query_log.length - 10] = null; + } } + lastQueryUpdate = queryUpdate; } // Add "text" field to given `tree_node`, for display using Treant.js (in @@ -744,7 +752,7 @@ function expandEditor() { } } -function displayError(response, statusWithText = undefined) { +function displayError(queryId, response, statusWithText = undefined) { console.error("Either the GET request failed or the backend returned an error:", response); if (response["exception"] == undefined || response["exception"] == "") { response["exception"] = "Unknown error"; @@ -787,7 +795,8 @@ function displayError(response, statusWithText = undefined) { // console.log("DEBUG: Error response with runtime information found!"); appendRuntimeInformation(response.runtimeInformation, response.query, - response.time); + response.time, + { queryId, updateTimeStamp: Date.now() }); } } diff --git a/backend/static/js/qleverUI.js b/backend/static/js/qleverUI.js index e04a5e41..0c7e41ea 100755 --- a/backend/static/js/qleverUI.js +++ b/backend/static/js/qleverUI.js @@ -8,6 +8,7 @@ var high_query_time_ms = 100; var very_high_query_time_ms = 1000; var runtime_log = []; var query_log = []; +var lastQueryUpdate = { queryId: null, updateTimeStamp: 0 }; $(window).resize(function (e) { if (e.target == window) { @@ -392,12 +393,43 @@ async function processQuery(sendLimit=0, element=$("#exebtn")) { params["cmd"] = "clear-cache"; nothingToShow = true; } + const queryId = crypto.randomUUID(); + const ws = new WebSocket(`${BASEURL.replaceAll(/^http/g, "ws")}/watch/${queryId}`); + const startTimeStamp = Date.now(); + ws.onopen = () => { + log("Waiting for live updates", "other"); + }; + ws.onmessage = (message) => { + if (typeof message.data !== "string") { + log("Unexpected message format", "other"); + } else { + appendRuntimeInformation( + { + query_execution_tree: JSON.parse(message.data), + meta: {} + }, + params["query"], + { + computeResult: Date.now() - startTimeStamp, + total: Date.now() - startTimeStamp + }, + { + queryId, + updateTimeStamp: Date.now() + } + ); + } + }; + ws.onerror = () => { + log("Live updates not supported", "other"); + }; $.ajax({ method: "POST", url: BASEURL, data: $.param(params), headers: { "Content-type": "application/x-www-form-urlencoded", - "Accept": "application/qlever-results+json" + "Accept": "application/qlever-results+json", + "Query-Id": queryId, }, success: function (result) { log('Evaluating and displaying results...', 'other'); @@ -411,7 +443,7 @@ async function processQuery(sendLimit=0, element=$("#exebtn")) { return; } - if (result.status == "ERROR") { displayError(result); return; } + if (result.status == "ERROR") { displayError(queryId, result); return; } if (result["warnings"].length > 0) { displayWarning(result); } // Show some statistics (on top of the table). @@ -567,7 +599,8 @@ async function processQuery(sendLimit=0, element=$("#exebtn")) { scrollTop: $("#resTable").scrollTop() + 500 }, 500); - appendRuntimeInformation(result.runtimeInformation, result.query, result.time); + // MAX_VALUE ensures this always has priority over the websocket updates + appendRuntimeInformation(result.runtimeInformation, result.query, result.time, { queryId, updateTimeStamp: Number.MAX_VALUE }); }}) .fail(function (jqXHR, textStatus, errorThrown) { $(element).find('.glyphicon').removeClass('glyphicon-spin glyphicon-refresh'); @@ -586,7 +619,7 @@ async function processQuery(sendLimit=0, element=$("#exebtn")) { } var statusWithText = jqXHR.status && jqXHR.statusText ? (jqXHR.status + " (" + jqXHR.statusText + ")") : undefined; - displayError(jqXHR.responseJSON, statusWithText); + displayError(queryId, jqXHR.responseJSON, statusWithText); }); }