Skip to content

Commit

Permalink
Persist query history (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
mewim authored Feb 13, 2024
1 parent 889d334 commit dd0165c
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 13 deletions.
11 changes: 8 additions & 3 deletions src/components/ShellView/CypherEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default {
default: false,
},
},
emits: ['remove', 'evaluateCypher', 'toggleMaximize', 'generateAndEvaluateQuery'],
emits: ['remove', 'evaluateCypher', 'toggleMaximize', 'generateAndEvaluateQuery'],
data: () => {
return {
name: "CypherEditor",
Expand Down Expand Up @@ -229,10 +229,15 @@ emits: ['remove', 'evaluateCypher', 'toggleMaximize', 'generateAndEvaluateQuery'
removeCell() {
this.$emit("remove");
},
isActive(){
isActive() {
return (this.isQueryGenerationMode && this.$refs.gptQuestionTextArea === document.activeElement) ||
(!this.isQueryGenerationMode && this.editor && this.editor.hasTextFocus());
}
},
loadFromHistory(history) {
this.isQueryGenerationMode = history.isQueryGenerationMode;
this.gptQuestion = history.gptQuestion;
this.setEditorContent(history.cypherQuery);
},
},
}
</script>
Expand Down
13 changes: 12 additions & 1 deletion src/components/ShellView/ShellCell.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,21 @@ export default {
isActive() {
return this.$refs.editor.isActive();
},
loadEditorFromHistory(history) {
this.isEvaluated = true;
this.$refs.editor.loadFromHistory(history);
},
evaluateCypher(query) {
this.queryResult = null;
this.errorMessage = "";
this.isLoading = true;
this.loadingText = "Evaluating query...";
Axios.post("/api/cypher", { query })
Axios.post("/api/cypher",
{
query,
uuid: this.cellId,
isQueryGenerationMode: this.$refs.editor.isQueryGenerationMode
})
.then((res) => {
this.queryResult = res.data;
this.queryString = query;
Expand Down Expand Up @@ -158,6 +167,8 @@ export default {
question,
token,
model,
uuid: this.cellId,
isQueryGenerationMode: this.$refs.editor.isQueryGenerationMode
};
Axios.post(url, data)
.then((res) => {
Expand Down
42 changes: 42 additions & 0 deletions src/components/ShellView/ShellMainView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<script lang="js">
import ShellCell from "./ShellCell.vue";
import { v4 as uuidv4 } from 'uuid';
import Axios from "axios";
export default {
name: "ShellMainView",
components: {
Expand Down Expand Up @@ -58,6 +59,7 @@ export default {
});
window.addEventListener("resize", this.updateContainerHeight);
document.addEventListener("keydown", this.handleKeyDown);
this.loadCellsFromHistory();
},
beforeUnmount() {
Expand All @@ -75,12 +77,49 @@ export default {
this.containerHeight = window.innerHeight - this.navbarHeight;
},
removeCell(index) {
const uuid = this.shellCell[index].cellId;
this.shellCell.splice(index, 1);
this.$nextTick(() => {
if (this.shellCell.length === 0) {
this.shellCell.push(this.createCell());
}
});
if (!uuid) {
return;
}
try {
this.removeCellFromHistory(uuid);
} catch (e) {
// Ignore
}
},
removeCellFromHistory(uuid) {
return Axios.delete(`/api/session/history/${uuid}`);
},
loadCellHistoryFromServer() {
return Axios.get("/api/session/history").then(res => res.data);
},
async loadCellsFromHistory() {
const history = await this.loadCellHistoryFromServer();
history.map(cell => {
return {
cellId: cell.uuid,
};
}).forEach(cell => {
if (this.isCellAddedToTheEnd) {
this.shellCell.unshift(cell);
}
else {
this.shellCell.push(cell);
}
});
this.$nextTick(() => {
history.forEach((cell) => {
const uuid = cell.uuid;
const cellRef = this.$refs[this.getCellRefById(uuid)][0];
cellRef.loadEditorFromHistory(cell);
});
});
},
addCell() {
const cell = this.createCell();
Expand All @@ -103,6 +142,9 @@ export default {
getCellRef(index) {
return `shell-cell-${this.shellCell[index].cellId}`;
},
getCellRefById(uuid) {
return `shell-cell-${uuid}`;
},
handleKeyDown(event) {
if (event.shiftKey && event.key === "Enter") {
event.preventDefault();
Expand Down
22 changes: 18 additions & 4 deletions src/server/Cypher.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
const database = require("./utils/Database");
const express = require("express");
const router = express.Router();
const logger = require("./utils/Logger");
const MODES = require("./utils/Constants").MODES;
const database = require("./utils/Database");
const sessionDb = require("./utils/SessionDatabase");

const DEMO_MODE = MODES.DEMO;

Expand Down Expand Up @@ -67,16 +68,29 @@ router.post("/", async (req, res) => {
if (mode === MODES.READ_WRITE) {
const currentSchema = await database.getSchema();
isSchemaChanged =
JSON.stringify(schema) !== JSON.stringify(currentSchema);
JSON.stringify(schema) !== JSON.stringify(currentSchema);
}
// This is a workaround for the JSON stringify issue with INT128 values
const replacer = (key, value) => {
if (typeof value === 'bigint') {
if (typeof value === "bigint") {
return value.toString();
}
return value;
};
const responseBody = JSON.stringify({ rows, dataTypes, isSchemaChanged }, replacer);
const responseBody = JSON.stringify(
{ rows, dataTypes, isSchemaChanged },
replacer
);
try {
await sessionDb.upsertHistoryItem({
uuid: req.body.uuid,
isQueryGenerationMode: Boolean(req.body.isQueryGenerationMode),
cypherQuery: query,
});
} catch (err) {
// Ignore the error. It fails to record the history, but the query is
// still executed.
}
return res.send(responseBody);
} catch (err) {
return res.status(400).send({ error: err.message });
Expand Down
14 changes: 13 additions & 1 deletion src/server/Gpt.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const openai = require("openai").OpenAI;
const express = require("express");
const router = express.Router();
const database = require("./utils/Database");
const sessionDb = require("./utils/SessionDatabase");

const getPrompt = (question, schema) => {
const prompt = `Task:Generate Cypher statement for Kùzu Graph Database Mangagement System to query a graph database.
Expand Down Expand Up @@ -61,7 +62,18 @@ router.post("/", async (req, res) => {
let query;
try {
query = chatCompletion.choices[0].message.content;
query = query.split("\n").join(" ");
query = query.split("\n").join(" ");
try {
await sessionDb.upsertHistoryItem({
uuid: req.body.uuid,
isQueryGenerationMode: Boolean(req.body.isQueryGenerationMode),
gptQuestion: question,
cypherQuery: query,
});
} catch (err) {
// Ignore the error. It fails to record the history, but the query is
// still executed.
}
res.send({ query, prompt, schema, model });
} catch (err) {
return res
Expand Down
18 changes: 18 additions & 0 deletions src/server/Session.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,22 @@ router.post("/settings", async (req, res) => {
}
});

router.get("/history", async (_, res) => {
try {
const history = await sessionDb.getHistoryItems();
res.send(history);
} catch (err) {
return res.status(400).send({ error: err.message });
}
});

router.delete("/history/:uuid", async (req, res) => {
try {
await sessionDb.deleteHistoryItem(req.params.uuid);
res.send({ success: true });
} catch (err) {
return res.status(400).send({ error: err.message });
}
});

module.exports = router;
70 changes: 66 additions & 4 deletions src/server/utils/SessionDatabase.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ class SessionDatabase {
return this.sqlInitPromise;
}

isWritable() {
return this.isInitialized && !this.isReadOnly;
}

async createDbSchema() {
await this.db.exec(`
CREATE TABLE settings (
Expand Down Expand Up @@ -118,10 +122,7 @@ class SessionDatabase {

async setSetting(value, key = "allSettings") {
await this.initSqlite();
if (!this.isInitialized) {
return;
}
if (this.isReadOnly) {
if (!this.isWritable()) {
return;
}
await this.db.run(
Expand All @@ -130,6 +131,67 @@ class SessionDatabase {
JSON.stringify(value)
);
}

async upsertHistoryItem(historyItem) {
await this.initSqlite();
if (!this.isWritable()) {
return;
}
await this.db.run("BEGIN TRANSACTION;");
try {
let { uuid, isQueryGenerationMode, gptQuestion, cypherQuery } =
historyItem;
const currentRow = await this.db.get(
"SELECT * FROM history WHERE uuid = ?",
uuid
);
if (!currentRow) {
await this.db.run(
"INSERT INTO history (uuid, isQueryGenerationMode, gptQuestion, cypherQuery) VALUES (?, ?, ?, ?)",
uuid,
isQueryGenerationMode,
gptQuestion,
cypherQuery
);
} else {
if (!gptQuestion) {
gptQuestion = currentRow.gptQuestion;
}
if (!cypherQuery) {
cypherQuery = currentRow.cypherQuery;
}
await this.db.run(
"UPDATE history SET isQueryGenerationMode = ?, gptQuestion = ?, cypherQuery = ? WHERE uuid = ?",
isQueryGenerationMode,
gptQuestion,
cypherQuery,
uuid
);
}
await this.db.run("COMMIT;");
} catch (err) {
await this.db.run("ROLLBACK;");
}
}

async deleteHistoryItem(uuid) {
await this.initSqlite();
if (!this.isWritable()) {
return;
}
await this.db.run("DELETE FROM history WHERE uuid = ?", uuid);
}

async getHistoryItems() {
await this.initSqlite();
if (!this.isInitialized) {
return [];
}
const historyItems = await this.db.all(
"SELECT *, rowid FROM history ORDER BY rowid DESC"
);
return historyItems;
}
}

module.exports = new SessionDatabase();

0 comments on commit dd0165c

Please sign in to comment.