データ計算
Graphic Walkerは2つの計算モードをサポートしています: クライアント側(デフォルト)とサーバー側。データセットのサイズとアーキテクチャ要件に基づいて選択してください。
クライアント側計算
dataプロパティを渡すと、Graphic Walkerはクライアントのウェブワーカーですべての計算を実行します。これは最もシンプルな設定で、バックエンドは不要です。
<GraphicWalker data={myData} fields={fields} />利点:
- サーバー側のセットアップが不要
- オフラインで動作
- 即座のインタラクション
制限事項:
- データセットがブラウザのメモリに収まる必要がある
- すべてのデータをクライアントに初期転送
- パフォーマンスはクライアントのハードウェアに依存
推奨: 100K行未満のデータセット。
DuckDB WASM(オプション)
大規模データセットでのクライアント側パフォーマンスを向上させるために、Graphic WalkerはDuckDB WASMを使用できます。オプションパッケージをインストールしてください:
npm install @kanaries/graphic-walker-duckdbこれにより、ブラウザでのSQLベースの集計が有効になり、大規模データセットで大幅に高速化されます。
サーバー側計算
大規模データセットやデータがサーバーから離れることができない場合は、dataの代わりにcomputation関数を渡します。Graphic Walkerはクエリペイロードを関数に送信し、結果を返します。
import { GraphicWalker } from '@kanaries/graphic-walker';
import type { IComputationFunction } from '@kanaries/graphic-walker';
const computation: IComputationFunction = async (payload) => {
const response = await fetch('/api/data/query', {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
});
return response.json();
};
function App() {
return (
<GraphicWalker
computation={computation}
fields={fields}
/>
);
}利点:
- 任意のサイズのデータセットを処理
- データはサーバーに留まる
- サーバー側のデータベース(PostgreSQL、DuckDBなど)を活用
制限事項:
- クエリエンドポイントの実装が必要
- ネットワーク遅延がインタラクション速度に影響
計算関数のシグネチャ
type IComputationFunction = (payload: IDataQueryPayload) => Promise<IRow[]>;関数はIDataQueryPayloadを受け取ります。これは処理ステップのパイプラインであるworkflow配列を含みます:
interface IDataQueryPayload {
workflow: IDataQueryWorkflowStep[];
limit?: number;
offset?: number;
}ワークフローステップ
ワークフローは順序付けられたステップの配列です。サーバーはこれらを順番に処理します:
1. フィルターステップ
集計前に行レベルのフィルターを適用します:
{
"type": "filter",
"filters": [
{
"fid": "country",
"rule": { "type": "one of", "value": ["US", "UK", "DE"] }
},
{
"fid": "revenue",
"rule": { "type": "range", "value": [1000, null] }
}
]
}2. 変換ステップ
派生フィールドを計算します:
{
"type": "transform",
"transform": [
{
"key": "log_revenue",
"expression": {
"op": "log10",
"params": [{ "type": "field", "value": "revenue" }],
"as": "log_revenue"
}
}
]
}3. ビューステップ
データの集計または選択を行います。これは最も一般的なステップです:
集計クエリ:
{
"type": "view",
"query": [{
"op": "aggregate",
"groupBy": ["country", "product"],
"measures": [
{ "field": "revenue", "agg": "sum", "asFieldKey": "sum_revenue" },
{ "field": "revenue", "agg": "count", "asFieldKey": "count_records" }
]
}]
}生クエリ(集計なし):
{
"type": "view",
"query": [{
"op": "raw",
"fields": ["country", "product", "revenue", "date"]
}]
}フォールドクエリ(アンピボット):
{
"type": "view",
"query": [{
"op": "fold",
"foldBy": ["q1_sales", "q2_sales", "q3_sales", "q4_sales"],
"newFoldKeyCol": "quarter",
"newFoldValueCol": "sales"
}]
}ビンクエリ:
{
"type": "view",
"query": [{
"op": "bin",
"binBy": "age",
"newBinCol": "age_bin",
"binSize": 10
}]
}4. ソートステップ
結果をソートします:
{
"type": "sort",
"sort": "descending",
"by": ["sum_revenue"]
}サーバー実装例
SQLを使用した最小限のExpress.jsエンドポイントの例:
app.post('/api/data/query', async (req, res) => {
const { workflow, limit, offset } = req.body;
let query = 'SELECT * FROM dataset';
const params = [];
for (const step of workflow) {
if (step.type === 'filter') {
const conditions = step.filters.map(f => {
if (f.rule.type === 'range') {
const [min, max] = f.rule.value;
if (min !== null && max !== null) return `${f.fid} BETWEEN ${min} AND ${max}`;
if (min !== null) return `${f.fid} >= ${min}`;
if (max !== null) return `${f.fid} <= ${max}`;
}
if (f.rule.type === 'one of') {
return `${f.fid} IN (${f.rule.value.map(v => `'${v}'`).join(',')})`;
}
return '1=1';
});
query += ` WHERE ${conditions.join(' AND ')}`;
}
if (step.type === 'view') {
for (const q of step.query) {
if (q.op === 'aggregate') {
const groupCols = q.groupBy.join(', ');
const measureCols = q.measures.map(m =>
`${m.agg.toUpperCase()}(${m.field}) AS ${m.asFieldKey}`
).join(', ');
query = `SELECT ${groupCols}, ${measureCols} FROM (${query}) t GROUP BY ${groupCols}`;
}
if (q.op === 'raw') {
query = `SELECT ${q.fields.join(', ')} FROM (${query}) t`;
}
}
}
if (step.type === 'sort') {
query += ` ORDER BY ${step.by.join(', ')} ${step.sort === 'ascending' ? 'ASC' : 'DESC'}`;
}
}
if (limit) query += ` LIMIT ${limit}`;
if (offset) query += ` OFFSET ${offset}`;
const results = await db.query(query);
res.json(results);
});セキュリティ上の注意: 上記の例は簡略化されています。本番環境では、SQLインジェクションを防ぐためにパラメータ化クエリを使用してください。
計算タイムアウト
両方のモードでタイムアウト設定をサポートしています:
<GraphicWalker
data={data}
fields={fields}
computationTimeout={30000} // 30 seconds
/>適切なモードの選択
| 要因 | クライアント側 | サーバー側 |
|---|---|---|
| セットアップの複雑さ | 最小限 | バックエンドエンドポイントが必要 |
| データセットサイズ | 100K行未満 | 無制限 |
| データプライバシー | データがブラウザに送信される | データはサーバーに留まる |
| インタラクション速度 | 高速(ネットワーク不要) | ネットワーク+サーバーに依存 |
| オフラインサポート | あり | なし |