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">

<script type="module">
  import { defineLatticeGrid } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/modules/webcomponent.esm.js';
  defineLatticeGrid();
</script>

<lattice-grid row-key="reading_id" style="display: block; height: 560px"></lattice-grid>
<pre id="sql" style="margin-top: 12px"></pre>

<script type="module">
  import { duckdbAdapter, createPushdownSource } from 'https://cdn.jsdelivr.net/npm/@toclocoinc/lattice-grid@1.28.0/modules/webcomponent.esm.js';

  // The web component module carries the grid; DuckDB is your own engine. The
  // page starts DuckDB-Wasm itself from a CDN and hands the adapter a live
  // connection, and the module imports none of it.
  const duckdb = await import('https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.0/+esm');
  const bundle = await duckdb.selectBundle(duckdb.getJsDelivrBundles());
  const 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);
  const connection = await db.connect();

  // from is any SQL table expression. read_parquet streams the file, so a host
  // that honours range requests lets DuckDB read only the bytes a query touches.
  const adapter = duckdbAdapter({
    connection,
    from: "read_parquet('/demo-data/readings.parquet')",
  });

  // The adapter answers every filter and sort on this grid in SQL, so nothing is
  // left to finish in the browser. Only the visible window comes back, per
  // interaction.
  const source = createPushdownSource({ adapter, pageSize: 200 });

  // Wrap execute to show the statement DuckDB ran and how long it took.
  const sqlOut = document.getElementById('sql');
  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);
    sqlOut.textContent = f.sql + '\n-- ' + ms + ' ms, ' + result.rows.length + ' of ' + (result.total ?? '?') + ' rows';
    return result;
  };

  const el = document.querySelector('lattice-grid');
  el.config = {
    toolPanel: { side: 'left', panels: ['filters', 'columns'] },
    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' },
    ],
    source,   // a hundred thousand rows; DuckDB answers each filter and sort in SQL
  };
</script>