1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DuckDB-Wasm の HTMLファイルのみで試せる公式サンプルを書きかえたりもして試す

Last updated at Posted at 2025-05-15

はじめに

技術情報を色々見てる中で、ちょこちょこ以下の「DuckDB」をの情報を見かけます。

●DuckDB – An in-process SQL OLAP database management system
 https://duckdb.org/

また DuckDB関連では、以下の「DuckDB-Wasm」もわりと情報を見かける感じがして、こちらのほうがより気になっていました。

●DuckDB Wasm – DuckDB
 https://duckdb.org/docs/stable/clients/wasm/overview.html

image.png

今回、DuckDB-Wasm のほうを試してみます。

DuckDB-Wasm が気になったきっかけ

DuckDB-Wasm の情報をいくつか見かける中、特に以下の記事に関するものが気になりました。

●ブラウザでオフライン日本語インスタント全文検索を実現する
 https://voluntas.ghost.io/offline-japanese-full-text-search-in-browser/

●DuckDB-Wasmで簡単な地理空間情報分析アプリを作る - Qiita
 https://qiita.com/northprint/items/0bb2113814a8878fdfef

●DuckDB-Wasm が OPFS に対応した
 https://voluntas.ghost.io/duckdb-wasm-opfs/

今回の内容

今回、以下の HTMLファイルのみで試せる公式サンプルを動かしてみたり、少しだけ手を加えてみたりして「DuckDB-Wasm」を試していきます。

●duckdb-wasm/examples/plain-html/index.html at main · duckdb/duckdb-wasm
 https://github.com/duckdb/duckdb-wasm/blob/main/examples/plain-html/index.html

サンプルの HTMLファイル

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const getDb = async () => {
        const duckdb = window.duckdbduckdbWasm;
        // @ts-ignore
        if (window._db) return window._db;
        const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();

        // Select a bundle based on browser checks
        const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);

        const worker_url = URL.createObjectURL(
          new Blob([`importScripts("${bundle.mainWorker}");`], {
            type: "text/javascript",
          })
        );

        // Instantiate the asynchronus version of DuckDB-wasm
        const worker = new Worker(worker_url);
        // const logger = null //new duckdb.ConsoleLogger();
        const logger = new duckdb.ConsoleLogger();
        const db = new duckdb.AsyncDuckDB(logger, worker);
        await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
        URL.revokeObjectURL(worker_url);
        window._db = db;
        return db;
      };
    </script>
    <script type="module">
      import * as duckdbduckdbWasm from "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.28.1-dev106.0/+esm";
      window.duckdbduckdbWasm = duckdbduckdbWasm;
      getDb().then(async (db) => {
        // Create a new connection
        const conn = await db.connect();
        // Prepare query
        const stmt = await conn.prepare(
          `SELECT v + ? FROM generate_series(0, 10000) AS t(v);`
        );
        // ... and run the query with materialized results
        console.log((await stmt.query(234)).toArray());
      });
    </script>
  </body>
</html>

さっそく試す

早速、試していきます。

お試し1

まずは公式サンプルをほぼそのまま動かしてみます。その際、少しだけ内容を書きかえます。

読みこむライブラリのバージョンを変える

内容を書きかえるのにあたり、少し内容を見てみます。

上で掲載した公式サンプルでは、CDN からライブラリなどを読みこむ形になっています。具体的には以下の部分などです。

      import * as duckdbduckdbWasm from "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.28.1-dev106.0/+esm";

以下の jsDelivr上のページを見ると、さらに新しいバージョンが出ているようです。

●@duckdb/duckdb-wasm CDN by jsDelivr - A CDN for npm and GitHub
 https://www.jsdelivr.com/package/npm/@duckdb/duckdb-wasm

https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.1-dev132.0/+esm

読みこむライブラリのバージョンを、新しいものにしてみます。また、他もほんの少しだけ書きかえて、これを動かしてみます。

最初に試す HTMLファイルの内容

最初に試す内容は、以下のとおりです。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const getDb = async () => {
        const duckdb = window.duckdbduckdbWasm;
        if (window._db) return window._db;
        const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
        const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);

        const worker_url = URL.createObjectURL(
          new Blob([`importScripts("${bundle.mainWorker}");`], {
            type: "text/javascript",
          })
        );

        const worker = new Worker(worker_url);
        const logger = new duckdb.ConsoleLogger();
        const db = new duckdb.AsyncDuckDB(logger, worker);
        await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
        URL.revokeObjectURL(worker_url);
        window._db = db;
        return db;
      };
    </script>
    <script type="module">
      import * as duckdbduckdbWasm from "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.1-dev132.0/+esm";
      window.duckdbduckdbWasm = duckdbduckdbWasm;
      getDb().then(async (db) => {
        const conn = await db.connect();
        const stmt = await conn.prepare(
          `SELECT v + ? FROM generate_series(0, 10000) AS t(v);`
        );
        // ... and run the query with materialized results
        console.log((await stmt.query(234)).toArray());
      });
    </script>
  </body>
</html>

ブラウザで得られる出力

上記の内容をブラウザで動かすと、以下のような出力が確認できました。

image.png

中を展開して途中の部分を見てみると、こんな感じになっていたりします。

image.png

どうやら以下の「Proxy」が使われているようです。

●Proxy - JavaScript | MDN
 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Proxy

お試し2

先ほどのログ出力の部分を少しだけ書きかえてみます。

書きかえた部分とブラウザで得られた出力1

以下は書きかえた部分を取り出したものです。コメントアウトした部分が書きかえ前の内容で、その下が書きかえ後の内容です。

        // console.log((await stmt.query(234)).toArray());
        const rows = (await stmt.query(234)).toArray();
        console.log(rows[0]);

先ほどの書きかえ後の内容をブラウザで開いたところ、以下の出力が得られました。

image.png

この後、特定の部分を取り出してみます。

書きかえた部分とブラウザで得られた出力2・3

コメントアウトした部分が書きかえ前の内容で、それ以外が書きかえ後の内容です。

        const rows = (await stmt.query(234)).toArray();
        // console.log(rows[0]);
        console.log(JSON.stringify(rows.slice(0, 5), null, 2));

先ほどの書きかえ後の内容をブラウザで開いたところ、以下の出力が得られました。

image.png

以下のように変更した内容も試してみました。

        const rows = (await stmt.query(234)).toArray();
        // console.log(JSON.stringify(rows.slice(0, 5), null, 2));
        console.log(JSON.stringify(rows[0], null, 2));
        console.log(rows[0]["(v + $1)"]);
        console.log(Object.keys(rows[0]));
        console.log(Object.values(rows[0]));

出力は以下のとおりです。

image.png

部分的な内容を抽出もできました。

お試し3

先ほどまで試したものと、違ったデータの取り出しを試していきます。

その内容を準備するのにあたり、以下の内容や ChatGPT を使って進めました。

●DuckDB-Wasm: Efficient Analytical SQL in the Browser – DuckDB
 https://duckdb.org/2021/10/29/duckdb-wasm.html

●DuckDB-wasm×wllamaでJSONを可視化&要約する軽量ダッシュボードを作った
 https://zenn.dev/tesla/articles/e3ec9fe5e15c9a

そうやって作った実装内容は、以下のとおりです。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      const getDb = async () => {
        const duckdb = window.duckdbduckdbWasm;
        if (window._db) return window._db;
        const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
        const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);

        const worker_url = URL.createObjectURL(
          new Blob([`importScripts("${bundle.mainWorker}");`], {
            type: "text/javascript",
          })
        );

        const worker = new Worker(worker_url);
        const logger = new duckdb.ConsoleLogger();
        const db = new duckdb.AsyncDuckDB(logger, worker);
        await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
        URL.revokeObjectURL(worker_url);
        window._db = db;
        return db;
      };
    </script>
    <script type="module">
      import * as duckdbduckdbWasm from "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.29.1-dev132.0/+esm";
      window.duckdbduckdbWasm = duckdbduckdbWasm;
      getDb().then(async (db) => {
        const conn = await db.connect();

        await conn.query(`
        CREATE TABLE IF NOT EXISTS people(name VARCHAR, age INTEGER);
        INSERT INTO people VALUES
          ('Alice', 30),
          ('Bob', 25),
          ('Charlie', 35),
          ('Dave', 28),
          ('Eve', 22);
      `);

        const sql = `SELECT * FROM people WHERE age >= 30 ORDER BY age;`;
        const result = await conn.query(sql);

        console.log(result);
        console.log(result.toArray().length);

        for (const r of result.toArray()) {
          console.log(r.name, r.age);
        }
      });
    </script>
  </body>
</html>

出力は以下のとおりです。

image.png

出力結果を見ると、以下で用意していたデータに対して、age が 30以上のもののみ取り出すということができました。

        await conn.query(`
        CREATE TABLE IF NOT EXISTS people(name VARCHAR, age INTEGER);
        INSERT INTO people VALUES
          ('Alice', 30),
          ('Bob', 25),
          ('Charlie', 35),
          ('Dave', 28),
          ('Eve', 22);
      `);

とりあえず、今回のお試しは以上になります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?