From c5ffc3dd76611e6892c7ee8c491fc3ca5c4fe21f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=9B=A7=E5=9B=A7?=
Date: Wed, 1 Nov 2023 15:01:14 +0800
Subject: [PATCH] Allow query return size to be limited at API side & add
loading animation for query execution (#45)
* Add option to limit query size
* Add loader
---
src/components/MainLayout.vue | 9 ++++----
.../SchemaView/SchemaPropertyEditCell.vue | 2 +-
src/components/ShellView/CypherEditor.vue | 7 ++++++-
src/components/ShellView/ShellCell.vue | 21 +++++++++++++++++++
src/server/Cypher.js | 18 +++++++++++++++-
src/server/Datasets.js | 2 +-
src/server/utils/Database.js | 6 +++---
7 files changed, 53 insertions(+), 12 deletions(-)
diff --git a/src/components/MainLayout.vue b/src/components/MainLayout.vue
index 5e755c9..b574550 100644
--- a/src/components/MainLayout.vue
+++ b/src/components/MainLayout.vue
@@ -111,14 +111,13 @@
scale factor 0.1 dataset. Please run KùzuExplorer locally to load a
different dataset (see the
-
- documentation here
-
- ).
+ documentation here).
You can visualize the schema of LDBC SNB in the Schema tab and execute
- interactive Cypher queries in the Shell tab.
+ interactive Cypher queries in the Shell tab. If your query returns more than
+ 1000 rows, it will be truncated.
KùzuExplorer is running in read-only mode. In this mode, you cannot load a
diff --git a/src/components/SchemaView/SchemaPropertyEditCell.vue b/src/components/SchemaView/SchemaPropertyEditCell.vue
index 17de50e..d25583d 100644
--- a/src/components/SchemaView/SchemaPropertyEditCell.vue
+++ b/src/components/SchemaView/SchemaPropertyEditCell.vue
@@ -309,7 +309,7 @@ export default {
return `${type.memberType}[${type.size}]`;
case DATA_TYPES.VAR_LIST: {
let result = type.memberType;
- for (let i = 0; i < type.dim; i++) {
+ for (let i = 0; i < type.dim; ++i) {
result += '[]';
}
return result;
diff --git a/src/components/ShellView/CypherEditor.vue b/src/components/ShellView/CypherEditor.vue
index 66e6aec..af0e24b 100644
--- a/src/components/ShellView/CypherEditor.vue
+++ b/src/components/ShellView/CypherEditor.vue
@@ -17,7 +17,7 @@
-
+
@@ -39,6 +48,7 @@ export default {
errorMessage: "",
isEvaluated: false,
isMaximized: false,
+ isLoading: false,
}),
props: {
@@ -66,6 +76,7 @@ export default {
evaluateCypher(query) {
this.queryResult = null;
this.errorMessage = "";
+ this.isLoading = true;
Axios.post("/api/cypher", { query })
.then((res) => {
this.queryResult = res.data;
@@ -98,6 +109,8 @@ export default {
this.$refs.resultContainer.handleDataChange(this.schema, null, this.errorMessage);
});
}
+ }).finally(() => {
+ this.isLoading = false;
});
if (!this.isEvaluated) {
this.$emit("addCell");
@@ -133,4 +146,12 @@ export default {
.shell-cell__wrapper {
display: block;
}
+
+div.d-flex.align-items-center {
+ margin: 20px;
+ margin-top: 0;
+ padding: 16px;
+ border: 2px solid $gray-300;
+ border-top: 0;
+}
diff --git a/src/server/Cypher.js b/src/server/Cypher.js
index a9ea0f1..c3b7798 100644
--- a/src/server/Cypher.js
+++ b/src/server/Cypher.js
@@ -1,8 +1,15 @@
const database = require("./utils/Database");
const express = require("express");
const router = express.Router();
+const logger = require("./utils/Logger");
+
const DEMO_MODE = "DEMO";
+let querySizeLimit = parseInt(process.env.KUZU_QUERY_SIZE_LIMIT);
+querySizeLimit = isNaN(querySizeLimit) ? null : querySizeLimit;
+if (querySizeLimit) {
+ logger.info(`Query size limit: ${querySizeLimit}`);
+}
let schema = null;
router.post("/", async (req, res) => {
@@ -39,7 +46,16 @@ router.post("/", async (req, res) => {
const preparedStatment = await conn.prepare(query);
result = await conn.execute(preparedStatment, params);
}
- const rows = await result.getAll();
+ let rows;
+ const resultSize = result.getNumTuples();
+ if (!querySizeLimit || resultSize <= querySizeLimit) {
+ rows = await result.getAll();
+ } else {
+ rows = [];
+ for (let i = 0; i < querySizeLimit; ++i) {
+ rows.push(await result.getNext());
+ }
+ }
const columnTypes = await result.getColumnDataTypes();
const columnNames = await result.getColumnNames();
const dataTypes = {};
diff --git a/src/server/Datasets.js b/src/server/Datasets.js
index 45f21ba..8a6cf7f 100644
--- a/src/server/Datasets.js
+++ b/src/server/Datasets.js
@@ -97,7 +97,7 @@ router.get("/:dataset/copy", async (req, res) => {
}
const conn = database.getConnection();
try {
- for (let i = 0; i < commands.length; i++) {
+ for (let i = 0; i < commands.length; ++i) {
const command = commands[i];
res.write("Executing: " + command + "\n");
const result = await conn.query(command);
diff --git a/src/server/utils/Database.js b/src/server/utils/Database.js
index f600608..fd55204 100644
--- a/src/server/utils/Database.js
+++ b/src/server/utils/Database.js
@@ -75,7 +75,7 @@ class Database {
}
this.db = new kuzu.Database(dbPath, bufferPoolSize, true, accessMode);
this.connectionPool = [];
- for (let i = 0; i < numberConnections; i++) {
+ for (let i = 0; i < numberConnections; ++i) {
const conn = {
connection: new kuzu.Connection(this.db, coresPerConnection),
useCount: 0,
@@ -99,7 +99,7 @@ class Database {
getConnection() {
let minUseCount = Number.MAX_SAFE_INTEGER;
let minUseCountIndex = -1;
- for (let i = 0; i < this.connectionPool.length; i++) {
+ for (let i = 0; i < this.connectionPool.length; ++i) {
if (this.connectionPool[i].useCount < minUseCount) {
minUseCount = this.connectionPool[i].useCount;
minUseCountIndex = i;
@@ -115,7 +115,7 @@ class Database {
}
releaseConnection(connection) {
- for (let i = 0; i < this.connectionPool.length; i++) {
+ for (let i = 0; i < this.connectionPool.length; ++i) {
if (this.connectionPool[i].connection === connection) {
this.connectionPool[i].useCount--;
return true;