Skip to content

Commit d11bb5a

Browse files
authored
Merge pull request #3 from CNAG-Biomedical-Informatics/codex/add-method-to-list-sqlite-files-and-display-data
Add dynamic tabs for GitHub sqlite files
2 parents 440592c + 78a5732 commit d11bb5a

File tree

3 files changed

+135
-46
lines changed

3 files changed

+135
-46
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
Provide the URL of any SQLite database file and edit the default SQL Query.
66

7-
[Read more](https://github.com/phiresky/sql.js-httpvfs)
7+
[Read more](https://github.com/phiresky/sql.js-httpvfs)

docs/dev.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
2+
# To run it locally
3+
1. Clone the repository:
4+
```bash
5+
git clone https://github.com/CNAG-Biomedical-Informatics/sql.js-httpvfs-playground.git
6+
```
7+
2. Navigate to the project directory:
8+
```bash
9+
cd sql.js-httpvfs-playground
10+
```
11+
3. Install the dependencies:
12+
```bash
13+
yarn install
14+
```
15+
4. Start the development server:
16+
```bash
17+
yarn dev
18+
```
19+
20+
# Most frequent bugs when running locally
21+
- `ReferenceError: fetch is not defined`, make sure you are using Node.js version 18 or higher.

src/App.svelte

Lines changed: 113 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { Spinner, Alert } from "flowbite-svelte";
44
import { Heading, P, A } from "flowbite-svelte";
55
import { Select, Input, Label, Helper } from "flowbite-svelte";
6+
import { onMount } from "svelte";
67
78
import { createDbWorker } from "sql.js-httpvfs";
89
import { PowerTable } from "@muonw/powertable";
@@ -30,13 +31,13 @@
3031
{ value: "16384", name: "16384" },
3132
{ value: "32768", name: "32768" },
3233
];
33-
let sqlQuery = `SELECT * FROM tcga_table WHERE "TAR-UUID" = '0011a67b-1ba9-4a32-a6b8-7850759a38cf';`;
34+
let sqlQuery = `SELECT * FROM tcga_table LIMIT 20';`;
3435
// let dbUrl = "https://nishad.github.io/sql.js-httpvfs-playground/db/imdb-titles-100000_1024_indexed.db";
3536
// let dbUrl =
3637
// "https://cnag-biomedical-informatics.github.io/sql.js-httpvfs-playground/db/tcga.db";
3738
let dbUrl =
38-
"https://raw.githubusercontent.com/CNAG-Biomedical-Informatics/cbi-datahub/refs/heads/main/sqlite/tcga.db"
39-
39+
"https://raw.githubusercontent.com/CNAG-Biomedical-Informatics/cbi-datahub/refs/heads/main/sqlite/tcga.db";
40+
4041
import Prism from "prismjs";
4142
import "prismjs/components/prism-sql";
4243
@@ -49,6 +50,9 @@
4950
);
5051
const wasmUrl = new URL("sql.js-httpvfs/dist/sql-wasm.wasm", import.meta.url);
5152
53+
let sqliteFiles = [];
54+
let activeFile = null;
55+
5256
let ptOptions = {
5357
footerText: false,
5458
footerFilters: false,
@@ -57,47 +61,97 @@
5761
parseAs: "unsafe-html",
5862
};
5963
60-
let ptInstructs = [
61-
{ key: "RANK", title: "RANK" },
62-
{ key: "REFERENCE(ID)", title: "REFERENCE(ID)" },
63-
{ key: "TARGET(ID)", title: "TARGET(ID)" },
64-
// { key: "FORMAT", title: "FORMAT" },
65-
{ key: "LENGTH", title: "LENGTH" },
66-
// { key: "WEIGHTED", title: "WEIGHTED" },
67-
{ key: "HAMMING-DISTANCE", title: "HAMMING-DISTANCE" },
68-
{ key: "DISTANCE-Z-SCORE", title: "DISTANCE-Z-SCORE" },
69-
{ key: "DISTANCE-P-VALUE", title: "DISTANCE-P-VALUE" },
70-
{ key: "DISTANCE-Z-SCORE(RAND)", title: "DISTANCE-Z-SCORE(RAND)" },
71-
{ key: "JACCARD-INDEX", title: "JACCARD-INDEX" },
72-
{ key: "JACCARD-Z-SCORE", title: "JACCARD-Z-SCORE" },
73-
{ key: "JACCARD-P-VALUE", title: "JACCARD-P-VALUE" },
74-
{ key: "REFERENCE-VARS", title: "REFERENCE-VARS" },
75-
{ key: "TARGET-VARS", title: "TARGET-VARS" },
76-
{ key: "INTERSECT", title: "INTERSECT" },
77-
{ key: "INTERSECT-RATE(%)", title: "INTERSECT-RATE(%)" },
78-
{ key: "COMPLETENESS(%)", title: "COMPLETENESS(%)" },
79-
{ key: "REF-UUID", title: "REF-UUID" },
80-
{ key: "TAR-UUID", title: "TAR-UUID" },
81-
{ key: "REF-UUID-URL", title: "REF-UUID-URL", parseAs: "unsafe-html" },
82-
{ key: "TAR-UUID-URL", title: "TAR-UUID-URL", parseAs: "unsafe-html" },
83-
];
64+
let ptInstructs = [];
65+
66+
function updateInstructs(data) {
67+
if (Array.isArray(data) && data.length > 0) {
68+
ptInstructs = Object.keys(data[0]).map((key) => ({
69+
key,
70+
title: key,
71+
...(key.includes("URL") ? { parseAs: "unsafe-html" } : {}),
72+
}));
73+
}
74+
return ptInstructs;
75+
}
76+
77+
async function getSqliteFiles() {
78+
const apiUrl =
79+
"https://api.github.com/repos/CNAG-Biomedical-Informatics/cbi-datahub/contents/sqlite";
80+
try {
81+
const res = await fetch(apiUrl);
82+
if (!res.ok) return [];
83+
const data = await res.json();
84+
return data
85+
.filter(
86+
(item) =>
87+
item.type === "file" &&
88+
(item.name.endsWith(".db") || item.name.endsWith(".sqlite"))
89+
)
90+
.map((item) => ({
91+
name: item.name,
92+
url:
93+
item.download_url ||
94+
`https://raw.githubusercontent.com/CNAG-Biomedical-Informatics/cbi-datahub/main/sqlite/${item.name}`,
95+
}));
96+
} catch (e) {
97+
console.error(e);
98+
return [];
99+
}
100+
}
101+
102+
async function loadDb(file) {
103+
const dbUrl = file.url;
104+
105+
// Map of table ➔ custom WHERE clauses
106+
const exampleQueries = {
107+
tcga_table: `"TAR-UUID" = '0011a67b-1ba9-4a32-a6b8-7850759a38cf'`,
108+
omim_table: `"TAR-UUID" = '100100'`,
109+
};
110+
111+
const tablesData = await queryDb(
112+
dbUrl,
113+
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
114+
);
115+
if (tablesData.result.length) {
116+
const table = tablesData.result[0].name;
117+
console.log("Selected table: ", table);
118+
119+
// Override if this table has a special filter
120+
if (Object.keys(exampleQueries).includes(table)) {
121+
console.log("Using example query for table:", table);
122+
sqlQuery = `SELECT * FROM "${table}" WHERE ${exampleQueries[table]};`;
123+
}
84124
85-
async function queryDb() {
125+
await runQuery(dbUrl, sqlQuery);
126+
} else {
127+
sqlQuery = "";
128+
result = [];
129+
}
130+
}
131+
132+
onMount(async () => {
133+
sqliteFiles = await getSqliteFiles();
134+
if (sqliteFiles.length) {
135+
await loadDb(sqliteFiles[0]);
136+
}
137+
});
138+
139+
async function queryDb(url = dbUrl, query = sqlQuery) {
86140
const worker = await createDbWorker(
87141
[
88142
{
89143
from: "inline",
90144
config: {
91145
serverMode: "full",
92-
url: dbUrl,
146+
url,
93147
requestChunkSize: Number(pageSize),
94148
},
95149
},
96150
],
97151
workerUrl.toString(),
98152
wasmUrl.toString()
99153
);
100-
const result = await worker.db.query(sqlQuery);
154+
const result = await worker.db.query(query);
101155
const bytesRead = await worker.worker.bytesRead;
102156
const stats = await worker.worker.getStats();
103157
return { result, bytesRead, stats };
@@ -113,14 +167,15 @@
113167
let totalBytes;
114168
let totalRequests;
115169
116-
async function runQuery() {
170+
async function runQuery(url = dbUrl, query = sqlQuery) {
117171
result = null;
118172
querying = true;
119173
error = false;
120-
let queryData = pTime(queryDb)();
174+
let queryData = pTime(() => queryDb(url, query))();
121175
await queryData
122176
.then((data) => {
123177
result = data.result;
178+
updateInstructs(result);
124179
timeTaken = queryData.time;
125180
bytesRead = data.bytesRead;
126181
totalRequests = data.stats.totalRequests;
@@ -137,6 +192,7 @@
137192
console.log("Query Error: ", queryError.message);
138193
console.log(queryError);
139194
querying = false;
195+
updateInstructs([]);
140196
jsonFile = null;
141197
});
142198
}
@@ -149,17 +205,10 @@
149205
>sql.js-httpvfs Playground</Heading
150206
>
151207
<P class="my-4 text-gray-500">
152-
<code>sql.js-httpvfs</code> is a fork of and wrapper around sql.js to provide
153-
a read-only HTTP-Range-request based virtual file system for SQLite. It allows
154-
hosting an SQLite database on a static file hoster and querying that database
155-
from the browser without fully downloading it.</P
156-
>
157-
<P class="mb-4"
158-
>Provide the URL of any SQLite database file and edit the default SQL
159-
Query.</P
160-
>
161-
<A href="https://github.com/phiresky/sql.js-httpvfs"
162-
>Read more
208+
This is a fork of nishad's sql.js-httpvfs playground
209+
</P>
210+
<A href="https://github.com/nishad/sql.js-httpvfs-playground"
211+
>Check it out here:
163212
<svg
164213
class="ml-1 w-6 h-6"
165214
fill="currentColor"
@@ -174,7 +223,26 @@
174223
</A>
175224
</div>
176225
177-
<div class="p-6">
226+
{#if sqliteFiles.length}
227+
<div class="border-b mb-4">
228+
<nav class="flex space-x-4" aria-label="Tabs">
229+
{#each sqliteFiles as f}
230+
<button
231+
class={`py-2 px-4 text-sm font-medium border-b-2 ${
232+
activeFile === f.name
233+
? "border-blue-600 text-blue-600"
234+
: "border-transparent text-gray-500"
235+
}`}
236+
on:click={() => loadDb(f)}
237+
>
238+
{f.name}
239+
</button>
240+
{/each}
241+
</nav>
242+
</div>
243+
{/if}
244+
245+
<!-- <div class="p-6">
178246
<Label class="space-y-2">
179247
<span>SQLite DB file URL</span>
180248
<Input type="url" placeholder="" size="md" bind:value={dbUrl} />
@@ -183,7 +251,7 @@
183251
>Select page size
184252
<Select class="mt-2" items={pageSizes} bind:value={pageSize} />
185253
</Label>
186-
</div>
254+
</div> -->
187255
<div class="p-6">
188256
<Label class="space-y-2">
189257
<span>Edit SQL Query</span>

0 commit comments

Comments
 (0)