Skip to content

Commit

Permalink
Add a new vector search + Web UI example.
Browse files Browse the repository at this point in the history
  • Loading branch information
ignatz committed Nov 20, 2024
1 parent fb2c5b9 commit 6bb517a
Show file tree
Hide file tree
Showing 28 changed files with 2,764 additions and 1 deletion.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,3 @@ node_modules/

# Dev artifacts
public/
traildepot/
3 changes: 3 additions & 0 deletions examples/blog/traildepot/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ secrets/
uploads/

!migrations/

trailbase.js
trailbase.d.ts
24 changes: 24 additions & 0 deletions examples/coffeesearch/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
19 changes: 19 additions & 0 deletions examples/coffeesearch/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM trailbase/trailbase:0.2.1 AS base

COPY traildepot /app/traildepot
COPY dist /app/public

USER root

RUN mkdir -p /app/traildepot/data
RUN chown -R trailbase /app/traildepot/data
RUN chmod +w /app/traildepot/data


USER trailbase

EXPOSE 4000
ENTRYPOINT ["tini", "--"]
CMD ["/app/trail", "--data-dir", "/app/traildepot", "run", "--address", "0.0.0.0:4000", "--public-dir", "/app/public"]

HEALTHCHECK CMD curl --fail http://localhost:4000/api/healthcheck || exit 1
9 changes: 9 additions & 0 deletions examples/coffeesearch/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
all: init app

app: dist
pnpm build

init:
mkdir -p traildepot/data; cat import.sql | sqlite3 traildepot/data/main.db -

.PHONY: init
15 changes: 15 additions & 0 deletions examples/coffeesearch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Coffee Search

A small web application demonstrating the use of TrailBase and its vector
search to build a coffee search.

To import the coffee data from CSV, run:

```bash
mkdir -p traildepot/data
cat import.sql | sqlite3 traildepot/data/main.db -
```

## Reference

* Coffee data [source](https://github.com/jldbc/coffee-quality-database/blob/master/data/arabica_data_cleaned.csv)
1,318 changes: 1,318 additions & 0 deletions examples/coffeesearch/arabica_data_cleaned.csv

Large diffs are not rendered by default.

28 changes: 28 additions & 0 deletions examples/coffeesearch/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
35 changes: 35 additions & 0 deletions examples/coffeesearch/import.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
-- Create table if it doesn't exist.
CREATE TABLE IF NOT EXISTS coffee (
Species TEXT,
Owner TEXT,

Aroma REAL,
Flavor REAL,
Acidity REAL,
Sweetness REAL,

embedding BLOB
) STRICT;

-- Empty table for clean import.
DELETE FROM coffee;

-- Go on to import data.
DROP TABLE IF EXISTS temporary;

.mode csv
.import arabica_data_cleaned.csv temporary

INSERT INTO coffee (Species, Owner, Aroma, Flavor, Acidity, Sweetness)
SELECT
Species,
Owner,

CAST(Aroma AS REAL) AS Aroma,
CAST(Flavor AS REAL) AS Flavor,
CAST(Acidity AS REAL) AS Acidity,
CAST(Sweetness AS REAL) AS Sweetness
FROM temporary;

-- Clean up.
DROP TABLE temporary;
14 changes: 14 additions & 0 deletions examples/coffeesearch/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<title>Coffee Search</title>
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
31 changes: 31 additions & 0 deletions examples/coffeesearch/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "my-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"format": "prettier -w src traildepot/scripts",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@eslint/js": "^9.13.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"eslint": "^9.13.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.11.0",
"prettier": "^3.3.3",
"typescript": "~5.6.2",
"typescript-eslint": "^8.11.0",
"vite": "^5.4.10"
}
}
42 changes: 42 additions & 0 deletions examples/coffeesearch/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#root {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}

.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}

.card {
padding: 2em;
}

.read-the-docs {
color: #888;
}
97 changes: 97 additions & 0 deletions examples/coffeesearch/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { useState, useEffect } from "react";
import "./App.css";

type Data = Array<Array<object>>;

async function getData(v: {
aroma: number;
flavor: number;
acidity: number;
sweetness: number;
}): Promise<Data> {
const URL = import.meta.env.DEV ? "http://localhost:4000" : "";
const params = Object.entries(v)
.map(([k, v]) => `${k}=${v}`)
.join("&");
const response = await fetch(`${URL}/search?${params}`);
return await response.json();
}

function Input(props: {
label: string;
value: number;
update: (v: number) => void;
}) {
return (
<>
<label>{props.label}:</label>
<input
type="number"
step="0.1"
value={props.value}
onChange={(el) => props.update(el.target.valueAsNumber)}
/>
</>
);
}

function Table() {
const [aroma, setAroma] = useState(8);
const [flavor, setFlavor] = useState(8);
const [acidity, setAcidity] = useState(8);
const [sweetness, setSweetness] = useState(8);

const [data, setData] = useState<Data | undefined>();
useEffect(() => {
setData(undefined);
getData({ aroma, flavor, acidity, sweetness }).then((data) =>
setData(data),
);
}, [aroma, flavor, acidity, sweetness]);

const Row = (props: { row: Array<object> }) => (
<tr>
{props.row.map((d) => (
<td>{`${d}`}</td>
))}
</tr>
);

return (
<>
<div className="inputs">
<Input label="Aroma" value={aroma} update={setAroma} />
<Input label="Flavor" value={flavor} update={setFlavor} />
<Input label="Acidity" value={acidity} update={setAcidity} />
<Input label="Sweetness" value={sweetness} update={setSweetness} />
</div>

<div className="table">
<table>
<thead>
<tr>
<th scope="col">Owner</th>
<th scope="col">Aroma</th>
<th scope="col">Flavor</th>
<th scope="col">Acidity</th>
<th scope="col">Sweetness</th>
</tr>
</thead>

<tbody>
{(data ?? []).map((row) => (
<Row row={row} />
))}
</tbody>
</table>
</div>
</>
);
}

export const App = () => (
<>
<h1>Coffee Search</h1>
<Table />
</>
);
31 changes: 31 additions & 0 deletions examples/coffeesearch/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color: #213547;
}

body {
margin: 0;
display: flex;
min-width: 320px;
min-height: 100vh;
}

h1 {
font-size: 3.2em;
line-height: 1.1;
}

.inputs {
margin: 1em;
display: grid;
gap: 1rem 0px;
grid-template-columns: repeat(2, minmax(0, 1fr));
}

.table {
display: flex;
flex-direction: column;
justify-content: center;
}
10 changes: 10 additions & 0 deletions examples/coffeesearch/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import { App } from "./App.tsx";

createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
);
1 change: 1 addition & 0 deletions examples/coffeesearch/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
5 changes: 5 additions & 0 deletions examples/coffeesearch/traildepot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

backups/
data/
secrets/
uploads/
Loading

0 comments on commit 6bb517a

Please sign in to comment.