【ThoughtSpotTips】MuzeStudio でテーブルを表現する

ThoughtSpotのMuzeStudioでテーブルを表現する方法をご紹介します

 

AkimasaKajitaniです。

ThoughtSpotでは、チャートタイプの一つとして「MuzeStudio」を選択することが可能です。これは、MuzeというJavaScriptベースのライブラリを使って高度なグラフを描画することができるようになっているものです。このMuzeライブラリをベースとしたネイティブチャート化が現在進んでおり、2026年6月時点でアーリーアクセスとして新チャートがいくつか提供されています。

ところで、MuzeStudioというチャートタイプでは、Muzeライブラリを使わなくても、その他のJavaScriptベースのライブラリを呼び出して使うことができます。そもそも、ライブラリを使わずHTMLを描画することも可能です。これは「Muze Studio」を単なるJavaScriptを呼び出すための基盤としても利用できる、ということです。これにより、従来はカスタムチャートで行っていたようなことも、このMuzeStudioのチャートタイプで実現することができます。

 

MuzeStudioで表形式は作れますか?

Muzeライブラリでは表形式はサポートしていません。そのため、JavaScriptでできる範囲で実装をする必要があります。

方法としては、HTMLタグを直接書く方法と、何らかのテーブル形式で表現を行うライブラリを使う必要があります。

 

まずおさらいとして、通常のThoughtSpotの表形式がどのようなものだったか改めて思い出してみましょう。以下のような表示となります。

 

さらに、新しいピボットテーブルを使うと以下のような表現も可能です。

 

この時、レイアウトは以下のようにしています。

 

これらの見た目や機能で不足している、ということであれば、Muze Studioで表形式を実装していくことになります。

 

HTMLタグを描画して表形式を作成する

まず最初に、JavaScriptではHTMLタグを作成して描画することができます。これによるJavaScriptのコードは以下のとおりです。

 

const { events, getDataFromSearchQuery } = viz;
const data = getDataFromSearchQuery();
const rawData = data.getData().data;

const chartContainer = document.getElementById('chart');
chartContainer.innerHTML = ''; // 既存の要素をクリアして初期化

// ==========================================
// 1. コンテナのスクロール設定
// ==========================================
chartContainer.style.height = '600px'; // お好みの高さに変更してください
chartContainer.style.overflowY = 'auto';

// ==========================================
// 2. 見栄えを良くするためのCSS(スタイル)を追加
// ==========================================
// ※ヘッダー固定や罫線などのデザインを定義します
const styleId = 'custom-table-style';
if (!document.getElementById(styleId)) {
    const style = document.createElement('style');
    style.id = styleId;
    style.textContent = `
        .custom-table {
            width: 100%;
            border-collapse: collapse;
            font-family: sans-serif;
            font-size: 14px;
            color: #333;
        }
        .custom-table th {
            position: sticky;
            top: 0; /* ヘッダーを上部に固定 */
            background-color: #f4f6f8;
            padding: 12px;
            border-bottom: 2px solid #ccc;
            text-align: left;
            z-index: 1; /* スクロール時に他のセルより上に表示 */
        }
        .custom-table td {
            padding: 10px 12px;
            border-bottom: 1px solid #eee;
        }
        .custom-table tr:hover {
            background-color: #f9f9f9; /* マウスオーバー時に色を変える */
        }
    `;
    document.head.appendChild(style);
}

// ==========================================
// 3. テーブル要素の組み立て
// ==========================================
const table = document.createElement('table');
table.className = 'custom-table';

// --- ヘッダー(thead)の作成 ---
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
const headers = ["注文月", "ジャンル", "商品名", "売上個数", "売上金額"];

headers.forEach(headerText => {
    const th = document.createElement('th');
    th.textContent = headerText;
    headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);

// --- データ(tbody)の作成 ---
const tbody = document.createElement('tbody');

rawData.forEach(row => {
    // UNIXタイムスタンプから YYYY/MM/DD への変換処理
    const unixTimeMs = Number(row["0"]);
    const date = new Date(unixTimeMs);
    const yyyy = date.getFullYear();
    const mm = String(date.getMonth() + 1).padStart(2, '0');
    const dd = String(date.getDate()).padStart(2, '0');
    const formattedDate = `${yyyy}/${mm}/${dd}`;

    // 1行分のデータ配列を作成
    const rowData = [
        formattedDate, // "注文月"
        row["1"],      // "ジャンル"
        row["2"],      // "商品名"
        row["3"],      // "売上個数"
        row["4"]       // "売上金額"
    ];

    // 行(tr)とセル(td)を生成して追加
    const tr = document.createElement('tr');
    rowData.forEach(cellData => {
        const td = document.createElement('td');
        // 数値データの場合はカンマ区切りにするなどの工夫もここで可能です
        td.textContent = cellData;
        tr.appendChild(td);
    });
    
    tbody.appendChild(tr);
});
table.appendChild(tbody);

// ==========================================
// 4. テーブルの表示と完了通知
// ==========================================
chartContainer.appendChild(table);
events.emitRenderCompletedEvent();

 

これにより、以下のように表現できます。

 

Answerの状態だと、右上のエディターがちょっと邪魔ですね・・・。ライブボードに貼り付ければこれは消えるのでご安心ください。

 

コードに関するちょっとした説明

また、ThoughtSpotからMuzeStudioの中で渡されるデータは、検索バーの順番のとおりとなります(厳密にはデータの型ごとの並び順に左右されるようです)。例えば今回は以下のような順番で検索キーワードが設定されています。

 

 

これらの検索キーワードがコード内のrow[0]、row[1]・・・に対応しています。

また、日時形式のデータについてはUNIXタイムで渡されるため、そのまま可視化しようとすると秒単位でのUNIXタイムになるのでご注意下さい。今回はこれをYYYY/MM/DD形式に変換するためのコードを追加し、人間が見やすい形式に変換しています。

スタイルなどはHTMLタグがわかれば比較的簡単に修正ができるのがこの方式の利点です。ただし、複雑なことをしようとすると自力でさらにコーディングする必要があります(その代わり、汎用的な技術が使えるので、AIを使えばカスタマイズも簡単かと思います)。

 

Grid.jsライブラリを使って表形式を表現する

次に、汎用のテーブル表示ライブラリを使ってみましょう。Grid.jsを使ったサンプルは以下のとおりです。

 

// Grid.jsライブラリとCSSを動的に読み込む
const loadGridJS = () => {
    return new Promise((resolve, reject) => {
        if (typeof gridjs !== 'undefined') {
            resolve(gridjs);
            return;
        }
        
        // CSSの読み込み
        const link = document.createElement('link');
        link.href = '<https://cdn.jsdelivr.net/npm/gridjs/dist/theme/mermaid.min.css>';
        link.rel = 'stylesheet';
        document.head.appendChild(link);

        // JSの読み込み
        const script = document.createElement('script');
        script.src = '<https://cdn.jsdelivr.net/npm/gridjs/dist/gridjs.umd.js>';
        script.onload = () => resolve(window.gridjs);
        script.onerror = reject;
        document.head.appendChild(script);
    });
};

loadGridJS().then(() => {
    const { events, getDataFromSearchQuery } = viz;
    const data = getDataFromSearchQuery();
    const rawData = data.getData().data;

    // Grid.jsが要求する二次元配列にデータを変換
    const tableData = rawData.map(row => {
        // --- UNIXタイムスタンプから YYYY/MM/DD への変換処理 ---
        const unixTimeMs = Number(row["0"]);
        const date = new Date(unixTimeMs);
        
        const yyyy = date.getFullYear();
        const mm = String(date.getMonth() + 1).padStart(2, '0'); // 月は0から始まるため+1
        const dd = String(date.getDate()).padStart(2, '0');
        
        const formattedDate = `${yyyy}/${mm}/${dd}`;
        // ----------------------------------------------------

        // 変換後の日付(formattedDate)と、その他のカラム(row["1"]〜["4"])を配列にして返す
        return [
            formattedDate, // "注文月" の位置に変換後の文字列を入れる
            row["1"],      // "ジャンル"
            row["2"],      // "商品名"
            row["3"],      // "売上個数"
            row["4"]       // "売上金額"
        ];
    });

    const chartContainer = document.getElementById('chart');
    chartContainer.innerHTML = ''; // 初期化

    // Grid.jsでテーブルを描画
    new gridjs.Grid({
        columns: ["注文月", "ジャンル", "商品名", "売上個数", "売上金額"], 
        data: tableData,
        sort: true,
        pagination: true
    }).render(chartContainer);

    events.emitRenderCompletedEvent();
}).catch(error => {
    console.error("Failed to load Grid.js:", error);
});

 

これで、以下のような表現が行われます。

 

このライブラリは、「pagination: true」にすると、自動的にページ分割してくれます。この部分のコードを変えると、単純なスクロール形式にすることもできます。ページ割りの方法は色々とできるようなので、好みの方法でやってみてください。

 

Tabulatorライブラリを使って表形式を表現する

次は、Tabulatorライブラリです。

 

// TabulatorライブラリとCSSを動的に読み込む
const loadTabulator = () => {
    return new Promise((resolve, reject) => {
        if (typeof Tabulator !== 'undefined') {
            resolve(Tabulator);
            return;
        }

        // CSSの読み込み(標準のスタイル)
        const link = document.createElement('link');
        link.href = '<https://unpkg.com/tabulator-tables@6.2.1/dist/css/tabulator.min.css>';
        link.rel = 'stylesheet';
        document.head.appendChild(link);

        // JSの読み込み
        const script = document.createElement('script');
        script.src = '<https://unpkg.com/tabulator-tables@6.2.1/dist/js/tabulator.min.js>';
        script.onload = () => resolve(window.Tabulator);
        script.onerror = reject;
        document.head.appendChild(script);
    });
};

loadTabulator().then(() => {
    const { events, getDataFromSearchQuery } = viz;
    const data = getDataFromSearchQuery();
    const rawData = data.getData().data;

    // Tabulatorは「オブジェクトの配列」を好むため、扱いやすい形に変換します
    const tableData = rawData.map((row, index) => {
        // --- UNIXタイムスタンプから YYYY/MM/DD への変換 ---
        const unixTimeMs = Number(row["0"]);
        const date = new Date(unixTimeMs);
        const yyyy = date.getFullYear();
        const mm = String(date.getMonth() + 1).padStart(2, '0');
        const dd = String(date.getDate()).padStart(2, '0');
        const formattedDate = `${yyyy}/${mm}/${dd}`;

        return {
            id: index, // 行を一意に識別するためのID
            orderDate: formattedDate,
            genre: row["1"],
            productName: row["2"],
            // ソートや計算が正しく動くように数値(Number)にキャストしておきます
            salesQty: Number(row["3"]),
            salesAmount: Number(row["4"])
        };
    });

    const chartContainer = document.getElementById('chart');
    chartContainer.innerHTML = ''; // 初期化

    // Tabulatorテーブルの生成
    const table = new Tabulator(chartContainer, {
        data: tableData, // 変換したデータ
        height: "600px", // 高さを指定すると自動的に縦スクロール&ヘッダー固定になります
        layout: "fitColumns", // 表の幅をコンテナにピッタリ合わせる設定
        
        // カラムの定義とフォーマット設定
        columns: [
            { title: "注文月", field: "orderDate", sorter: "string" },
            { title: "ジャンル", field: "genre", sorter: "string" },
            { title: "商品名", field: "productName", sorter: "string" },
            { 
                title: "売上個数", 
                field: "salesQty", 
                sorter: "number", 
                hozAlign: "right" // 数値を見やすく右寄せ
            },
            { 
                title: "売上金額", 
                field: "salesAmount", 
                sorter: "number", 
                hozAlign: "right",
                formatter: "money", // Tabulatorの標準機能で金額フォーマット
                formatterParams: {
                    decimal: ".",
                    thousand: ",",
                    symbol: "¥",
                    precision: false // 小数点以下は非表示
                }
            }
        ]
    });

    // Tabulatorの描画が完全に終わったタイミングでThoughtSpotに完了を通知
    table.on("tableBuilt", function(){
        events.emitRenderCompletedEvent();
    });

}).catch(error => {
    console.error("Failed to load Tabulator:", error);
});

 

見た目は以下のとおりです。これはかなり他のライブラリと見た目が異なりますね。どっちかというと古臭いスタイルかもしれませんが、なにげに私はこっちの方が見慣れています・・・。Tabulatorはビジュアルが良くて多機能な表が簡単に作れるようなので、色々とカスタマイズできそうです。

 

Excelにダウンロードしたい!

表があれば、当然Excelにダウンロードしたいですよね・・・。ということで、TabulatorとExcel出力用ライブラリのSheetJSでExcelファイルダウンロードできるようにしてみましょう。

 

// TabulatorとExcel出力用ライブラリ(SheetJS)を読み込む
const loadLibraries = () => {
    return new Promise((resolve) => {
        // 1. TabulatorのCSS
        const link = document.createElement('link');
        link.href = '<https://unpkg.com/tabulator-tables@6.2.1/dist/css/tabulator.min.css>';
        link.rel = 'stylesheet';
        document.head.appendChild(link);

        // 2. Excel生成ライブラリ (SheetJS)
        const sheetJs = document.createElement('script');
        sheetJs.src = '<https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js>';
        document.head.appendChild(sheetJs);

        // 3. TabulatorのJS
        const tabulatorJs = document.createElement('script');
        tabulatorJs.src = '<https://unpkg.com/tabulator-tables@6.2.1/dist/js/tabulator.min.js>';
        tabulatorJs.onload = () => resolve(window.Tabulator);
        document.head.appendChild(tabulatorJs);
    });
};

loadLibraries().then(() => {
    const { events, getDataFromSearchQuery } = viz;
    const data = getDataFromSearchQuery();
    const rawData = data.getData().data;

    const tableData = rawData.map((row, index) => {
        const unixTimeMs = Number(row["0"]);
        const date = new Date(unixTimeMs);
        const yyyy = date.getFullYear();
        const mm = String(date.getMonth() + 1).padStart(2, '0');
        const dd = String(date.getDate()).padStart(2, '0');

        return {
            id: index,
            orderDate: `${yyyy}/${mm}/${dd}`,
            genre: row["1"],
            productName: row["2"],
            salesQty: Number(row["3"]),
            salesAmount: Number(row["4"])
        };
    });

    const chartContainer = document.getElementById('chart');
    chartContainer.innerHTML = ''; // 初期化

    // ==========================================
    // ダウンロードボタンのUIを作成
    // ==========================================
    const btnContainer = document.createElement('div');
    btnContainer.style.marginBottom = '10px';
    
    const downloadBtn = document.createElement('button');
    downloadBtn.textContent = '📥 Excelダウンロード';
    downloadBtn.style.padding = '8px 16px';
    downloadBtn.style.backgroundColor = '#217346'; // Excelっぽい緑色
    downloadBtn.style.color = '#fff';
    downloadBtn.style.border = 'none';
    downloadBtn.style.borderRadius = '4px';
    downloadBtn.style.cursor = 'pointer';
    
    btnContainer.appendChild(downloadBtn);
    chartContainer.appendChild(btnContainer);

    // テーブルを描画する用のコンテナを作成
    const tableContainer = document.createElement('div');
    chartContainer.appendChild(tableContainer);

    // ==========================================
    // Tabulatorテーブルの生成
    // ==========================================
    const table = new Tabulator(tableContainer, {
        data: tableData,
        height: "400px",
        layout: "fitColumns",
        columns: [
            { title: "注文月", field: "orderDate", sorter: "string" },
            { title: "ジャンル", field: "genre", sorter: "string" },
            { title: "商品名", field: "productName", sorter: "string" },
            { title: "売上個数", field: "salesQty", sorter: "number", hozAlign: "right" },
            { 
                title: "売上金額", field: "salesAmount", sorter: "number", hozAlign: "right",
                formatter: "money",
                formatterParams: { symbol: "¥", precision: false }
            }
        ]
    });

    // ==========================================
    // ボタンクリック時の処理(Excelダウンロード)
    // ==========================================
    downloadBtn.onclick = () => {
        // 拡張子(.xlsx)とファイル名を指定するだけでダウンロードされます
        table.download("xlsx", "ThoughtSpot_Data.xlsx", {sheetName:"売上データ"});
    };

    table.on("tableBuilt", function(){
        events.emitRenderCompletedEvent();
    });

});

 

これにより、Excelにダウンロードできます。ただし、書式は反映されていないのでご注意ください。左上についているExcelダウンロードボタンでダウンロードが可能です。

 

まとめ

  • ThoughtSpotのMuzeStudioのチャートタイプ内で、いくつかの方法を使ってテーブル形式で表示する方法をご紹介しました。
    • HTMLをJavaScriptで直接生成する方法
    • Grid.js
    • Tabulator
  • 本ページのコードはほぼGeminiのPro3.1(コード特化のモデル)で作成しています。とはいえ、GeminiにMuzeStudioで、汎用的なライブラリを使って書いてくれ、と頼んでも書いてくれません(どうやら情報が古いようです)。結局、外部ライブラリを使った手元にあるサンプルコードをGeminiに食わせることでようやく理解してくれました。ということで、こちらのページにあるコードをGeminiなどに食わせてそこから指示した方が確実にコードを生成してくれます。
  • 生成AIがどんどん賢くなるので、何ができるかさえ分かっていればコードは生成AIが書いてくれるので楽な時代になりましたね・・・。

 

※ThoughtSpot バージョン26.6.0.cl-66時点の情報です

 

おすすめの記事