From 95b6cebf63f8c62a4a020073f525eb0b9a80c7bc Mon Sep 17 00:00:00 2001 From: David Valentine Date: Wed, 20 Aug 2025 09:57:28 -0700 Subject: [PATCH 01/12] Add panzoom functionality. Update documentation to include details on functionality (per claude). Add a query-analysis.md that can be the start of optimizing queries --- README.md | 12 +++- backend/static/css/style.css | 45 +++++++++++++++ backend/static/js/qleverUI.js | 22 ++++++++ backend/templates/partials/head.html | 8 +++ .../partials/modals/visualisation.html | 13 +++-- db/qleverui.sqlite3 | Bin 318464 -> 411648 bytes docs/install_qleverui.md | 10 +++- package-lock.json | 7 +++ package.json | 1 + panzoom/index.js | 52 ++++++++++++++++++ rollup.config.js | 40 +++++++++----- 11 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 panzoom/index.js diff --git a/README.md b/README.md index 24b3e7bb..2655f3c6 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,16 @@ # What is QLever UI? **QLever UI** is an easy-to-use interactive **user interface** for the **SPARQL search engine QLever** that helps to discover the scopes and information in very large knowledge bases by providing **context-sensitive suggestions** and **auto-completion** while adding helpful information and additional views to the various outputs of the queries. -QLever UI supports different types of results (e.g. geographical data, named instances, images, and more) and is highly customizable to the needs of its users and the structure of the underlying dataset. +QLever UI supports different types of results (e.g. geographical data, named instances, images, and more) and is highly customizable to the needs of its users and the structure of the underlying dataset. The interface includes **interactive query analysis** for exploring query execution and performance. + +## Key Features + +- **Context-sensitive SPARQL autocompletion** - Smart suggestions based on query context +- **Interactive query execution analysis** - Navigate through query trees to understand performance +- **Customizable result views** - Support for geographic data, images, and structured results +- **Real-time query monitoring** - Live query progress updates during execution +- **SPARQL formatting** - Automatic query formatting and syntax validation +- **Multi-backend support** - Configure and switch between different QLever instances ### What is QLever? QLever (pronounced "clever") is an efficient SPARQL engine that can handle very large datasets. For example, QLever can index the complete Wikidata (~ 18 billion triples) in less than 24 hours on a standard Linux machine using around 40 GB of RAM, with subsequent query times below 1 second even for relatively complex queries with large result sets. On top of the standard SPARQL functionality, QLever also supports SPARQL+Text search. @@ -45,6 +54,7 @@ Distributed under the Apache 2.0 License. See `LICENSE` for more information. * [Setting up the database manually](docs/install_qleverui.md#setting-up-the-database-manually) * [Running QLever UI without docker](docs/install_qleverui.md#running-qlever-ui-without-docker) * [Configure QLever UI](docs/configure_qleverui.md) +* [Query Analysis](docs/query-analysis.md) * [Extending QLever UI](#construct-and-theoretical-approach) * [Extending the language parser](docs/extending_parser.md) * [Extending the suggestions](docs/extending_suggestions.md) diff --git a/backend/static/css/style.css b/backend/static/css/style.css index 21b53489..6f59c08c 100644 --- a/backend/static/css/style.css +++ b/backend/static/css/style.css @@ -278,6 +278,51 @@ li.CodeMirror-hint-active { /* required LIB STYLES */ /* .Treant se automatski dodaje na svaki chart conatiner */ .Treant { position: relative; overflow: hidden; padding: 0 !important; } +/* Visualization modal styles */ +#visualisation .modal-dialog { + width: 80%; +} + +#visualisation .modal-body { + height: 80vh; + overflow-y: auto; +} + +#visualisation .modal-header .pull-right { + margin-right: 30px; + font-size: 12px; + color: #666; +} + +/* Tree viewport and panzoom styles */ +#tree-viewport { + width: 100%; + height: 600px; + border: 1px solid #ddd; + overflow: hidden !important; + position: relative; +} + +#result-tree { + position: absolute; + cursor: grab; + width: 100%; + height: 100%; + top: 0; + left: 0; + overflow: visible; +} + +#result-tree .Treant { + overflow: visible; + width: auto !important; + height: auto !important; +} + +/* Query history styles */ +#lastQueries { + margin: -25px 0px; +} .Treant > .node, .Treant > .pseudo { position: absolute; display: block; visibility: hidden; } .Treant.Treant-loaded .node, diff --git a/backend/static/js/qleverUI.js b/backend/static/js/qleverUI.js index 869c9e94..767884f4 100755 --- a/backend/static/js/qleverUI.js +++ b/backend/static/js/qleverUI.js @@ -84,6 +84,13 @@ $(document).ready(function () { entities.data('has-mouseenter-handler', 'true'); } + // Set up panzoom cleanup when visualization modal is closed + $('#visualisation').on('hidden.bs.modal', function () { + if (typeof window.destroyPanzoom === 'function') { + window.destroyPanzoom(); + } + }); + // Initialization done. log('Editor initialized', 'other'); @@ -954,6 +961,8 @@ function renderRuntimeInformationToDom(entry = undefined) { container: "#result-tree", rootOrientation: "NORTH", connectors: { type: "step" }, + // Allow unlimited canvas size for large trees + scrollbar: "resize" }, nodeStructure: runtime_info["query_execution_tree"] } @@ -965,9 +974,22 @@ function renderRuntimeInformationToDom(entry = undefined) { if (currentTree !== null) { currentTree.destroy(); } + // Destroy any existing panzoom instance + if (typeof window.destroyPanzoom === 'function') { + window.destroyPanzoom(); + } + currentTree = new Treant(treant_tree); $("#visualisation").scrollTop(scrollTop); $("#result-tree").scrollLeft(scrollLeft); + + // Initialize panzoom for the tree after it's created + if (typeof window.initializePanzoom === 'function') { + // Use setTimeout to ensure the tree is fully rendered + setTimeout(() => { + window.initializePanzoom(); + }, 100); + } $("div.node").each(function () { const details_childs = $(this).children(".node-details"); diff --git a/backend/templates/partials/head.html b/backend/templates/partials/head.html index c97c06f7..232463eb 100644 --- a/backend/templates/partials/head.html +++ b/backend/templates/partials/head.html @@ -79,6 +79,14 @@ window.determineOperationType = determineOperationType; + + + diff --git a/backend/templates/partials/modals/visualisation.html b/backend/templates/partials/modals/visualisation.html index 09efe6a8..6d382684 100644 --- a/backend/templates/partials/modals/visualisation.html +++ b/backend/templates/partials/modals/visualisation.html @@ -1,8 +1,11 @@