Lattice Grid Buy a licence

demo D232

Live pushdown to DuckDB, query by query

Every sort, filter and page becomes one SQL statement DuckDB answers, with only the window coming back and the generated SQL and timing on screen

createPushdownSource · duckdbAdapter · lastPlan()

Building…
Loading a live grid…

The configuration

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/lattice-grid.min.css">

<div id="app"></div>

<script type="module">
  import React from 'https://esm.sh/react@18';
  import { createRoot } from 'https://esm.sh/react-dom@18/client';
  import * as Lattice from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/lattice-grid.esm.min.js';
  import createLatticeGrid from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/modules/react.esm.min.js';

  const { createGrid, createPushdownSource, duckdbAdapter } = Lattice;
  const LatticeGrid = createLatticeGrid({ React, createGrid });

  const columns = [
    { field: 'reading_id', title: '#', type: 'number', layout: { width: 110 } },
    { field: 'plant', title: 'Plant', filter: { type: 'set' } },
    { field: 'line', title: 'Line', filter: { type: 'set' } },
    { field: 'product', title: 'Product' },
    { field: 'bore_mm', title: 'Bore', type: 'millimetres', spec: { lower: 9.95, upper: 10.05, target: 10 } },
    { field: 'cycle_seconds', title: 'Cycle', type: 'seconds' },
    { field: 'in_spec', title: 'In spec', type: 'boolean' },
    { field: 'taken_at', title: 'Taken', type: 'dateString' },
  ];

  function App() {
    const [source, setSource] = React.useState(null);
    const [status, setStatus] = React.useState('Starting DuckDB in the browser...');
    const [sql, setSql] = React.useState('');

    // DuckDB is the caller's engine, loaded from a CDN; the grid loads none of
    // it. duckdbAdapter takes the connection you made, so a hundred thousand
    // rows answer each filter and sort in SQL while the grid stays dependency-free.
    React.useEffect(() => {
      let disposed = false, connection, worker;
      (async () => {
        try {
          setStatus('Loading DuckDB from a CDN...');
          const duckdb = await import('https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.0/+esm');
          const bundle = await duckdb.selectBundle(duckdb.getJsDelivrBundles());
          worker = await duckdb.createWorker(bundle.mainWorker);
          const db = new duckdb.AsyncDuckDB(new duckdb.ConsoleLogger(duckdb.LogLevel.ERROR), worker);
          await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
          if (disposed) return;
          connection = await db.connect();

          // The engine resolves the URL, so it must be absolute. read_parquet
          // streams the file, so a host that honours range requests lets DuckDB
          // read only the bytes a query touches.
          const file = new URL('/demo-data/readings.parquet', location.origin).href;
          const adapter = duckdbAdapter({ connection, from: `read_parquet('${file}')` });

          // Wrap execute to show the statement DuckDB ran and how long it took.
          const inner = adapter.execute.bind(adapter);
          adapter.execute = async (query, request) => {
            const started = performance.now();
            const result = await inner(query, request);
            const ms = Math.round(performance.now() - started);
            const f = adapter.sqlFor(query);
            if (!disposed) setSql(f.sql + '\n-- ' + ms + ' ms, ' + result.rows.length + ' of ' + (result.total ?? '?') + ' rows');
            return result;
          };

          setSource(createPushdownSource({
            adapter, compute: Lattice, pageSize: 200,   // only the visible window comes back, per interaction
          }));
          setStatus('100,000 rows in DuckDB. Filter a line or sort a column and the exact statement it ran shows below.');
        } catch (err) {
          setStatus('DuckDB could not start here. It loads from a CDN and needs a browser with WebAssembly and cross-origin isolation.');
        }
      })();
      return () => { disposed = true; try { connection?.close?.(); } catch (e) {} try { worker?.terminate?.(); } catch (e) {} };
    }, []);

    return (
      <>
        <div style={{ fontSize: '13px', color: 'var(--ink-2)', lineHeight: 1.5 }}>{status}</div>
        {source && (
          <LatticeGrid
            rowKey="reading_id"
            toolPanel={{ side: 'left', panels: ['filters', 'columns'] }}
            columns={columns}
            source={source}
            style={{ height: '560px', marginTop: '12px' }}
          />
        )}
        {sql && <pre style={{ marginTop: '12px' }}>{sql}</pre>}
      </>
    );
  }

  createRoot(document.getElementById('app')).render(<App />);
</script>