Skip to content

Commit

Permalink
perf(svelte-ui): use drizzle select API
Browse files Browse the repository at this point in the history
This patch uses the Drizzle select API instead of the query API to
reduce overhead. We only select the first look for each collection and
use a single query with two LEFT JOINs to accomplish a nearly identical
UX that is 10x faster.
  • Loading branch information
nicholaschiang committed Nov 24, 2024
1 parent e22ccf9 commit 4c4cd88
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 104 deletions.
5 changes: 2 additions & 3 deletions svelte-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
"@supabase/sql-formatter": "^4.0.3",
"@tailwindcss/forms": "^0.5.9",
"drizzle-orm": "^0.36.4",
"highlight.js": "^11.10.0",
"pg": "^8.13.1",
"svelte-highlight": "^7.7.0"
"lucide-svelte": "^0.460.1",
"pg": "^8.13.1"
}
}
41 changes: 11 additions & 30 deletions svelte-ui/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 21 additions & 46 deletions svelte-ui/src/routes/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,29 @@
import { asc, ilike, sql } from "drizzle-orm"
import { desc, ilike, and, eq, sql } from "drizzle-orm"
import * as tables from "$lib/server/db/schema/tables"
import { db } from "$lib/server/db"

const collectionsPageQuery = db.query.collection
.findMany({
columns: { id: true, name: true, level: true, location: true, sex: true },
with: {
brand: {
columns: {
id: true,
name: true,
},
},
season: {
columns: { id: true, year: true, name: true },
},
looks: {
columns: { id: true, number: true },
with: {
images: {
limit: 1,
columns: { id: true, url: true },
},
},
orderBy: [asc(tables.look.number)],
},
},
where: ilike(tables.collection.name, sql.placeholder("search")),
orderBy: [
asc(tables.collection.name),
asc(tables.collection.level),
asc(tables.collection.sex),
],
limit: sql.placeholder("limit"),
const query = db
.selectDistinctOn([tables.collection.name], {
collectionId: tables.collection.id,
collectionName: tables.collection.name,
collectionDate: tables.collection.date,
collectionLocation: tables.collection.location,
lookNumber: tables.look.number,
lookImageUrl: tables.image.url,
})
.prepare("collectionsPageQuery")
.from(tables.collection)
.leftJoin(tables.look, eq(tables.look.collectionId, tables.collection.id))
.leftJoin(tables.image, eq(tables.image.lookId, tables.look.id))
.orderBy(desc(tables.collection.name))
.where(
and(
eq(tables.look.number, 1),
ilike(tables.collection.name, sql.placeholder("search")),
),
)
.prepare("collections")

export async function load({ url }) {
const search = url.searchParams.get("q") ?? ""
const query = collectionsPageQuery.getQuery()
let sql = query.sql
for (let i = 0; i < query.params.length; i++) {
sql = sql.replace(`$${i + 1}`, `${query.params[i]}`)
}
return {
search,
sql,
collections: collectionsPageQuery.execute({
search: `%${search}%`,
limit: search ? 100_000 : 100,
}),
}
return { collections: query.execute({ search: `%${search}%` }) }
}
63 changes: 38 additions & 25 deletions svelte-ui/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
<script lang="ts">
import { Highlight } from "svelte-highlight";
import sql from "svelte-highlight/languages/sql";
import "svelte-highlight/styles/github-dark.css";
import { format } from "@supabase/sql-formatter"
import { Search } from "lucide-svelte"
let { data } = $props()
let value = $state("")
let code = $derived(format(data.sql))
let form: HTMLFormElement
const { format } = new Intl.DateTimeFormat(undefined, {
dateStyle: "long",
timeStyle: "short",
})
type Collection = Awaited<typeof data.collections>[number]
const sort = (a: Collection, b: Collection) =>
new Date(b.collectionDate ?? new Date()).valueOf() -
new Date(a.collectionDate ?? new Date()).valueOf()
</script>

<header
Expand All @@ -14,21 +21,26 @@
<h1 class="text-lg">collections</h1>
</header>
<div class="flex flex-col gap-6 p-6">
<form method="get">
<form
data-sveltekit-replacestate
data-sveltekit-keepfocus
bind:this={form}
class="flex h-10 items-center gap-3 border-b border-gray-200 bg-gray-50 px-3 dark:border-gray-800 dark:bg-gray-900"
>
<Search class="h-4 w-4" />
<input
name="q"
type="search"
class="block w-full border-gray-200 bg-transparent focus:ring-0 dark:border-gray-700"
placeholder="Search collections"
bind:value
class="w-0 grow border-0 bg-transparent px-0 focus:ring-0"
placeholder="Search..."
oninput={() => form.requestSubmit()}
/>
</form>
<div class="flex flex-col gap-2">
{#await data.collections}
<p>Executing query...</p>
<Highlight class="text-xs" language={sql} {code} />
<p>Loading...</p>
{:then collections}
<p>{collections.length} collections found</p>
<p>Found {collections.length} results</p>
{:catch error}
<p>Error: {error.message}</p>
{/await}
Expand All @@ -37,18 +49,18 @@
class="grid gap-x-2 gap-y-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-6"
>
{#await data.collections then collections}
{#each collections as collection (collection.id)}
{#each collections.sort(sort) as collection (collection.collectionId)}
<li class="flex flex-col gap-2 text-xs">
<div
class="flex aspect-person w-full items-center justify-center bg-gray-100 dark:bg-gray-800"
>
{#if collection.looks[0]?.images[0]?.url}
{#if collection.lookImageUrl}
<img
src={collection.looks[0].images[0].url}
alt={collection.name}
class="h-full w-full object-cover flex items-center justify-center"
loading="lazy"
decoding="async"
src={collection.lookImageUrl}
alt="Look {collection.lookNumber} of {collection.collectionName}"
class="flex h-full w-full items-center justify-center object-cover"
/>
{:else}
<span class="text-gray-200 dark:text-gray-700"
Expand All @@ -57,19 +69,20 @@
{/if}
</div>
<div>
<h2 class="font-semibold uppercase">{collection.brand.name}</h2>
<h3>{collection.season.name}</h3>
{#if collection.location}
<h2>{collection.collectionName}</h2>
{#if collection.collectionDate}
<p class="text-gray-400 dark:text-gray-500">
{format(new Date(collection.collectionDate))}
</p>
{/if}
{#if collection.collectionLocation}
<p class="text-gray-400 dark:text-gray-500">
{collection.location}
{collection.collectionLocation}
</p>
{/if}
</div>
</li>
{/each}
{#if collections.length === 0}
<li>No collections found.</li>
{/if}
{/await}
</ul>
</div>

0 comments on commit 4c4cd88

Please sign in to comment.