Skip to content

Commit

Permalink
allow multiple queries
Browse files Browse the repository at this point in the history
  • Loading branch information
XiangpengHao committed Dec 27, 2024
1 parent 8e23728 commit 2081129
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 117 deletions.
17 changes: 17 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@
<!-- Cloudflare Web Analytics -->
<script defer src='https://static.cloudflareinsights.com/beacon.min.js'
data-cf-beacon='{"token": "cdf9b270eac24614a52f26d4b465b8ae"}'></script><!-- End Cloudflare Web Analytics -->
<style>
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-20px);
}

to {
opacity: 1;
transform: translateY(0);
}
}

.animate-slide-in {
animation: slideIn 0.3s ease-out forwards;
}
</style>
</head>

<body class="bg-gray-50"></body>
Expand Down
38 changes: 22 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,13 @@ fn App() -> impl IntoView {
}
}
set_query_results.update(|r| {
r.push(QueryResult::new(query, Arc::new(results), physical_plan));
let id = r.len();
r.push(QueryResult::new(
id,
query,
Arc::new(results),
physical_plan,
));
});
}
Err(e) => set_error_message.set(Some(e)),
Expand Down Expand Up @@ -374,7 +380,6 @@ fn App() -> impl IntoView {
set_parquet_table=set_parquet_table
/>

<div class="border-t border-gray-300 my-4"></div>

{move || {
error_message
Expand Down Expand Up @@ -423,20 +428,21 @@ fn App() -> impl IntoView {
}}
</div>

{move || {
view! {
<div class="space-y-4">
{query_results
.get()
.into_iter()
.rev()
.map(|result| {
view! { <QueryResultView result=result /> }
})
.collect_view()}
</div>
}
}}
<div class="space-y-4">
<For
each=move || query_results.get().into_iter().rev()
key=|result| result.id()
children=move |result| {
view! {
<div class="transform transition-all duration-300 ease-out animate-slide-in">
<QueryResultView result=result />
</div>
}
}
/>
</div>

<div class="border-t border-gray-300 my-4"></div>

<div class="mt-8">
{move || {
Expand Down
2 changes: 1 addition & 1 deletion src/query_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ pub fn QueryInput(
/>
</svg>
<div class="absolute bottom-full right-0 mb-2 w-64 p-2 bg-gray-800 text-white text-xs rounded shadow-lg opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none">
"Query starts with 'SELECT' run as SQL, otherwise it is a question to be answered by AI generated SQL"
"Query begins with 'SELECT' runs as SQL, otherwise it is a question to be answered by AI generated SQL"
</div>
</div>
</div>
Expand Down
199 changes: 99 additions & 100 deletions src/query_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,38 +100,43 @@ pub(crate) fn export_to_parquet_inner(query_result: &[RecordBatch]) {

#[derive(Debug, Clone)]
pub(crate) struct QueryResult {
id: usize,
sql_query: String,
query_result: Arc<Vec<RecordBatch>>,
physical_plan: Arc<dyn ExecutionPlan>,
}

impl QueryResult {
pub fn new(
id: usize,
sql_query: String,
query_result: Arc<Vec<RecordBatch>>,
physical_plan: Arc<dyn ExecutionPlan>,
) -> Self {
Self {
id,
sql_query,
query_result,
physical_plan,
}
}

pub(crate) fn id(&self) -> usize {
self.id
}
}

#[component]
pub fn QueryResultView(result: QueryResult) -> impl IntoView {
let (active_tab, set_active_tab) = signal("results".to_string());

let (show_plan, set_show_plan) = signal(false);
let query_result_clone1 = result.query_result.clone();
let query_result_clone2 = result.query_result.clone();

let sql = result.sql_query.clone();

view! {
<div class="mt-4 p-4 bg-white border border-gray-300 rounded-md">
<div class="mt-4 p-4 bg-white border border-gray-300 rounded-md hover:shadow-lg transition-shadow duration-200">
<div class="flex justify-between items-center mb-4">
<div class="p-3 bg-gray-50 rounded border border-gray-200 font-mono text-sm overflow-x-auto relative group flex-grow">
<div class="p-2 border border-gray-200 font-mono text-sm overflow-x-auto relative group flex-grow">
{sql.clone()}
</div>
<div class="flex items-center gap-2 ml-4">
Expand Down Expand Up @@ -220,110 +225,104 @@ pub fn QueryResultView(result: QueryResult) -> impl IntoView {
/>
</svg>
</button>
<button
class=move || {
format!(
"p-2 text-gray-500 hover:text-gray-700 relative group {}",
if show_plan() { "text-blue-600" } else { "" },
)
}
aria-label="Execution plan"
on:click=move |_| set_show_plan.update(|v| *v = !*v)
>
<span class="absolute bottom-full left-1/2 transform -translate-x-1/2 px-2 py-1 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 whitespace-nowrap pointer-events-none">
"Execution plan"
</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"
/>
</svg>
</button>
</div>
</div>
<div class="mb-4 border-b border-gray-300 flex items-center">
<button
class=move || {
format!(
"px-4 py-2 {} {}",
if active_tab() == "results" {
"border-b-2 border-blue-500 text-blue-600"
} else {
"text-gray-600"
},
"hover:text-blue-600",
)
}
on:click=move |_| set_active_tab("results".to_string())
>
"Query Results"
</button>
<button
class=move || {
format!(
"px-4 py-2 {} {}",
if active_tab() == "physical_plan" {
"border-b-2 border-blue-500 text-blue-600"
} else {
"text-gray-600"
},
"hover:text-blue-600",
)
}
on:click=move |_| set_active_tab("physical_plan".to_string())
>
"ExecutionPlan"
</button>

</div>
{move || {
show_plan()
.then(|| {
view! {
<div class="mb-4">
<PhysicalPlan physical_plan=result.physical_plan.clone() />
</div>
}
})
}}

// Always show query results
<div class="max-h-[32rem] overflow-auto relative">
<table class="min-w-full bg-white table-fixed">
<thead class="sticky top-0 z-10">
<tr class="bg-gray-100">
{result
.query_result[0]
.schema()
.fields()
.iter()
.map(|field| {
view! {
<th class="px-4 py-1 text-left w-48 min-w-48 bg-gray-100 leading-tight text-gray-700">
<div class="truncate" title=field.name().clone()>
{field.name().clone()}
</div>
<div
class="text-xs text-gray-600 truncate"
title=field.data_type().to_string()
>
{field.data_type().to_string()}
</div>
</th>
}
})
.collect::<Vec<_>>()}
</tr>
</thead>
<tbody>
{(0..result.query_result[0].num_rows())
.map(|row_idx| {
view! {
<tr class="hover:bg-gray-50">
{(0..result.query_result[0].num_columns())
.map(|col_idx| {
let column = result.query_result[0].column(col_idx);
let cell_value = if column.is_null(row_idx) {
"NULL".to_string()
} else {
column.as_ref().value_to_string(row_idx)
};

{move || match active_tab().as_str() {
"results" => {
view! {
<div class="max-h-[32rem] overflow-auto relative">
<table class="min-w-full bg-white border border-gray-300 table-fixed">
<thead class="sticky top-0 z-10">
<tr class="bg-gray-100">
{result
.query_result[0]
.schema()
.fields()
.iter()
.map(|field| {
view! {
<th class="px-4 py-1 text-left border-b w-48 min-w-48 bg-gray-100 leading-tight text-gray-700">
<div class="truncate" title=field.name().clone()>
{field.name().clone()}
</div>
<div
class="text-xs text-gray-600 truncate"
title=field.data_type().to_string()
>
{field.data_type().to_string()}
</div>
</th>
<td class="px-4 py-1 w-48 min-w-48 leading-tight text-gray-700">
<div title=cell_value.clone()>{cell_value.clone()}</div>
</td>
}
})
.collect::<Vec<_>>()}
</tr>
</thead>
<tbody>
{(0..result.query_result[0].num_rows())
.map(|row_idx| {
view! {
<tr class="hover:bg-gray-50">
{(0..result.query_result[0].num_columns())
.map(|col_idx| {
let column = result.query_result[0].column(col_idx);
let cell_value = if column.is_null(row_idx) {
"NULL".to_string()
} else {
column.as_ref().value_to_string(row_idx)
};

view! {
<td class="px-4 py-1 border-b w-48 min-w-48 leading-tight text-gray-700">
<div title=cell_value.clone()>{cell_value.clone()}</div>
</td>
}
})
.collect::<Vec<_>>()}
</tr>
}
})
.collect::<Vec<_>>()}
</tbody>
</table>
</div>
}
.into_any()
}
"physical_plan" => {
view! { <PhysicalPlan physical_plan=result.physical_plan.clone() /> }.into_any()
}
_ => view! { <p>"Invalid tab"</p> }.into_any(),
}}
}
})
.collect::<Vec<_>>()}
</tbody>
</table>
</div>
</div>
}
}
Expand Down

0 comments on commit 2081129

Please sign in to comment.