Skip to content

Commit ed90972

Browse files
committed
updated to add duckdb
1 parent 68ec51f commit ed90972

File tree

5 files changed

+4378
-76
lines changed

5 files changed

+4378
-76
lines changed

src/components/layout/header.tsx

Lines changed: 35 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { AppBar, Paper, Button, Stack, Alert, Snackbar } from "@mui/material";
2-
32
import PlayCircleFilledWhiteIcon from "@mui/icons-material/PlayCircleFilledWhite";
43
import DataObjectIcon from "@mui/icons-material/DataObject";
4+
import DatabaseIcon from "@mui/icons-material/Storage"; // New icon for DuckDB
55
import GitHubIcon from "@mui/icons-material/GitHub";
66
import IconButton from "@mui/material/IconButton";
77
import "../../styles/Home.module.css";
@@ -14,13 +14,14 @@ import { useQueryContext } from "../../contexts/queryContext/useQueryContext";
1414
import MuiAlert, { AlertProps } from "@mui/material/Alert";
1515
import { AddUrlButton } from "./add-url-button";
1616
import { AlertMessage } from "./alert";
17-
import { fetchQuery } from "../../fetch";
17+
import { fetchQuery, fetchDuckDBQuery } from "../../fetch"; // Import new fetch function
1818

1919
const Header = () => {
2020
const { query, setQueryResults, setQueryRunning, queryRunning, serverUrl } =
2121
useQueryContext();
2222
const [errorMessage, setErrorMessage] = useState("");
2323
const [queryError, setQueryError] = useState(false);
24+
2425
const handleToggle = async (dts = false) => {
2526
setQueryRunning(true);
2627
try {
@@ -40,77 +41,55 @@ const Header = () => {
4041
}
4142
};
4243

43-
const handleClose = (
44-
_event?: React.SyntheticEvent | Event,
45-
reason?: string
46-
) => {
44+
// New function for handling DuckDB query execution
45+
const handleDuckDBQuery = async () => {
46+
setQueryRunning(true);
47+
try {
48+
const response = await fetchDuckDBQuery();
49+
setQueryRunning(false);
50+
51+
const resJson = await response.json();
52+
if (response.status !== 200) {
53+
setErrorMessage(resJson.error);
54+
setQueryError(true);
55+
return;
56+
}
57+
setQueryResults(resJson);
58+
} catch (error) {
59+
console.log("error is %o", error);
60+
setQueryRunning(false);
61+
}
62+
};
63+
64+
const handleClose = (_event?: React.SyntheticEvent | Event, reason?: string) => {
4765
if (reason === "clickaway") {
4866
return;
4967
}
50-
5168
setQueryError(false);
5269
};
53-
const QueryButtonText = "Run Query";
54-
const TypeButtonText = "Get Types";
5570

5671
return (
5772
<>
58-
<AlertMessage
59-
open={queryError}
60-
handleClose={handleClose}
61-
severity="error"
62-
errorMessage={errorMessage}
63-
/>
64-
<Backdrop
65-
className="w-screen ml-0"
66-
sx={{ color: "#fff", zIndex: (theme) => theme.zIndex.drawer + 1 }}
67-
open={queryRunning}
68-
>
73+
<AlertMessage open={queryError} handleClose={handleClose} severity="error" errorMessage={errorMessage} />
74+
<Backdrop className="w-screen ml-0" sx={{ color: "#fff", zIndex: (theme) => theme.zIndex.drawer + 1 }} open={queryRunning}>
6975
<CircularProgress color="inherit" />
7076
</Backdrop>
7177
<div className="header-left-group">
72-
<div className="flex-col col-span-1 tablet:col-span-2 mobile:col-span-2 justify-center pt-2 pr-2">
78+
<div className="flex-col col-span-1 tablet:col-span-2 mobile:col-span-2 justify-center pt-2 pr-2">
7379
<Image alt="logo" src="/logo-original.svg" width={162} height={32} />
7480
</div>
7581
<div className="header-left-stack">
76-
<Button
77-
onClick={async () => {
78-
await handleToggle();
79-
}}
80-
variant="outlined"
81-
className="button-primary"
82-
startIcon={<PlayCircleFilledWhiteIcon />}
83-
>
84-
{QueryButtonText}
82+
<Button onClick={async () => await handleToggle()} variant="outlined" className="button-primary" startIcon={<PlayCircleFilledWhiteIcon />}>
83+
Run Query
84+
</Button>
85+
<Button onClick={async () => await handleDuckDBQuery()} variant="outlined" className="button-primary" startIcon={<DatabaseIcon />}>
86+
Run DuckDB Query
8587
</Button>
86-
<IconButton
87-
onClick={async () => {
88-
await handleToggle();
89-
}}
90-
className="icon-button-primary"
91-
>
92-
<PlayCircleFilledWhiteIcon />
93-
</IconButton>
94-
<Button
95-
variant="outlined"
96-
className="button-primary"
97-
startIcon={<DataObjectIcon />}
98-
onClick={async () => {
99-
await handleToggle(true);
100-
}}
101-
>
102-
{TypeButtonText}
88+
<Button variant="outlined" className="button-primary" startIcon={<DataObjectIcon />} onClick={async () => await handleToggle(true)}>
89+
Get Types
10390
</Button>
104-
<IconButton
105-
onClick={async () => {
106-
await handleToggle(true);
107-
}}
108-
className="icon-button-primary"
109-
>
110-
<DataObjectIcon />
111-
</IconButton>
11291
</div>
113-
<div className="col-start-8 mobile:col-start-9 col-span-2 mobile:col-span-1 regular:col-start-7 regular:col-span-3 flex justify-end space-x-2 mobile:space-x-0 items-center">
92+
<div className="col-start-8 mobile:col-start-9 col-span-2 mobile:col-span-1 regular:col-start-7 regular:col-span-3 flex justify-end space-x-2 mobile:space-x-0 items-center">
11493
<AddUrlButton />
11594
<a href="https://github.com/stackql/stackql-playground">
11695
<IconButton aria-label="GitHub repository">

src/fetch.ts

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
1-
//TODO: turn this to custom hook so can access context
21
const fetchData = async (url: string) => {
32
const response = await fetch(url);
43
const data = await response.json();
54
if (response.ok) return data;
65
else throw data;
76
};
87

9-
export const fetchExplorer = async ({
10-
path,
11-
serverUrl,
12-
}: {
13-
path?: string;
14-
serverUrl?: string;
15-
}) => {
8+
export const fetchExplorer = async ({ path, serverUrl }: { path?: string; serverUrl?: string }) => {
169
let url = "/api/explorer";
1710
const params: string[] = [];
1811
if (serverUrl) {
@@ -21,22 +14,13 @@ export const fetchExplorer = async ({
2114
if (path) {
2215
params.push(`path=${path}`);
2316
}
24-
// get the data from the api
2517
if (params.length) {
2618
url = `${url}?${params.join("&")}`;
2719
}
2820
return fetchData(url);
2921
};
3022

31-
export const fetchQuery = async ({
32-
query,
33-
dts,
34-
serverUrl,
35-
}: {
36-
query: string;
37-
dts?: boolean;
38-
serverUrl?: string;
39-
}) => {
23+
export const fetchQuery = async ({ query, dts, serverUrl }: { query: string; dts?: boolean; serverUrl?: string }) => {
4024
let url = "/api/stackql";
4125
const params: string[] = [];
4226

@@ -46,13 +30,36 @@ export const fetchQuery = async ({
4630
if (serverUrl) {
4731
params.push(`serverUrl=${serverUrl}`);
4832
}
49-
if (params) {
33+
if (params.length) {
5034
url = `${url}?${params.join("&")}`;
5135
}
5236
const request = new Request(url, {
53-
body: query,
37+
body: JSON.stringify({ query }),
38+
method: "POST",
39+
headers: { "Content-Type": "application/json" }
40+
});
41+
return fetch(request);
42+
};
43+
44+
// New function for DuckDB queries
45+
export const fetchDuckDBQuery = async () => {
46+
const url = "/api/duckdb"; // Change this if the actual endpoint is different
47+
48+
const query = `SELECT country_region AS country,
49+
CAST(SUM(confirmed) AS INTEGER) AS total_confirmed,
50+
CAST(SUM(deaths) AS INTEGER) AS total_deaths,
51+
COUNT(*) AS record_count
52+
FROM read_csv_auto('s3://covid19-lake/archived/enigma-jhu/csv/Enigma-JHU.csv.gz')
53+
GROUP BY country_region
54+
ORDER BY total_confirmed DESC
55+
LIMIT 5`;
56+
57+
const request = new Request(url, {
58+
body: JSON.stringify({ query, showMetadata: true }),
5459
method: "POST",
60+
headers: { "Content-Type": "application/json" }
5561
});
62+
5663
return fetch(request);
5764
};
5865

src/pages/api/duckdb.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { NextApiRequest, NextApiResponse } from "next";
2+
import {
3+
getDataFromResponse,
4+
defaultMiddlewareUrl,
5+
validateAddress,
6+
} from "./_common";
7+
8+
export default async function handler(
9+
req: NextApiRequest,
10+
res: NextApiResponse
11+
) {
12+
let url = defaultMiddlewareUrl;
13+
const queryParams = req.query;
14+
let returnText = false;
15+
16+
const serverUrl = queryParams.serverUrl as string;
17+
if (serverUrl && validateAddress(serverUrl)) url = serverUrl;
18+
url = `${url}/duckdb`; // Update to point to DuckDB API
19+
20+
url = url + "?showMetadata=";
21+
if (queryParams.dts) {
22+
url = url + "&dts=";
23+
returnText = true;
24+
}
25+
26+
try {
27+
let query = req.body as string;
28+
query = query.replace(/\s+/g, " ").trim();
29+
const body = {
30+
query,
31+
showMetadata: true, // Ensure metadata is included
32+
};
33+
34+
const result = await getDataFromResponse<any>(
35+
url,
36+
JSON.stringify(body),
37+
"POST",
38+
returnText
39+
);
40+
41+
res.status(200).json({
42+
data: returnText ? result : result.data,
43+
returnText,
44+
metadata: result.metadata,
45+
});
46+
} catch (error) {
47+
res.status(400).json({ error: "DuckDB query failed", details: error });
48+
}
49+
}

src/pages/api/stackql.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// pages/api/stackql.ts
12
import { NextApiRequest, NextApiResponse } from "next";
23
import {
34
getDataFromResponse,

0 commit comments

Comments
 (0)