Kibanaのダッシュボードで、標準の「Lens」では表現できない複雑なチャート(サンキーチャートやカスタムマップ、特殊な散布図など)を作りたいとき、強力な武器になるのが Vega / Vega-Lite です。
しかし、これまでは重厚な Elasticsearch の Query DSL(JSON)を手書きしてデータを集計する必要があり、記述の複雑さに頭を悩ませた方も多いのではないでしょうか?
そんな開発者・アナリストに朗報です。バージョン 9.4+(Serverless Stack)以降、直感的で強力な新しいクエリ言語 ES|QL(Elasticsearch Query Language) を Vega のデータソースとして直接利用できるようになりました!
今回は、この ES|QL × Vega の組み合わせがもたらすメリットと、具体的な実装方法をコード例付きで解説します。
なぜ ES|QL × Vega なのか? 3つのメリット
1. 複雑な Query DSL からの解放
パイプライン演算子( | )を使ってデータを段階的に加工できる ES|QL を使うことで、ネストの深い Elasticsearch 独自の集計クエリを書く必要がなくなります。クエリの可読性が劇的に向上します。
2. データ形式の自動変換
ES|QL のクエリ結果は本来「列指向(Columnar)」ですが、Kibana の Vega インテグレーターが自動的に Vega が期待する「行ベース(Row-based / 1行1オブジェクト)」のJSON形式へ変換 してくれます。手動でのパース処理( format.property などの指定)の手間が大幅に減ります。
3. ダッシュボードの「時間範囲」や「フィルター」と完全同期
Kibana の拡張トークンを利用することで、ユーザーがダッシュボード上で操作した時間フィルターや検索条件を、ES|QL クエリ内に動的にマッピングできます。
ES|QL を呼び出すための設定パラメータ
Vega の data.url オブジェクト内に以下のパラメータを指定することで、ES|QL モードが有効になります。
| パラメータ | 必須/任意 | 概要 |
|---|---|---|
"%type%" | 必須 | "esql" を指定します。 |
"query" | 必須 | 実行したい ES|QL (1行で書きます。クエリ内の ” は、\” とエスケープする必要があります。) |
"%context%" | 任意 | true に設定すると、ダッシュボードのグローバルフィルターがクエリに自動適用されます。 |
"%timefield%" | 任意 | タイムスタンプのフィールド名を指定。これを設定すると、クエリ内で名前付きパラメータ ?_tstart と ?_tend が利用可能になります(ダッシュボードの時間範囲が代入されます)。 |
"dropNullColumns" | 任意 | true (デフォルト)の場合、null 値しか含まれない列をレスポンスから自動で除外します。 |
"params" | 任意 | クエリに動的に代入したい名前付きパラメータの配列を指定します。 |
Vega / Vega-Lite を使った Visualization の作成手順
Kibanaにおけるカスタム可視化の作成手順は以下の通りです(従来通り)。
1: Dashboard を新規作成するか、既存の Dashboard を編集モードにします。
2: 右上の [Add panel] プルダウンメニューから [New panel] を選択します。

3: パネル選択画面から [</> Custom visualization] を選択します。

4: Vega / Vega-Lite の作成画面が表示されるので、右側のエディタに JSON を記述していきます。

5: デバッグしたい場合は、上部の Inspect をクリックしてください。
【実践】サンプルコードで見る実装例1:折れ線グラフ(Vega-Lite)
以下は、サンプルデータ(Webログ)を使用し、時間の経過に伴うイベント数を2時間おきに集計して折れ線グラフ(Line Chart)で描画する Vega-Lite (v6) の定義例です。
{
"$schema": "https://vega.github.io/schema/vega-lite/v6.json",
"title": "Event counts per 2 hours",
"data": {
"url": {
"%type%": "esql",
"%context%": true,
"%timefield%": "@timestamp",
"query": "FROM kibana_sample_data_logs | WHERE @timestamp >= ?_tstart AND @timestamp < ?_tend | STATS doc_count=COUNT() BY tbucket=TBUCKET(2 hour) | SORT tbucket"
}
},
"mark": "line",
"encoding": {
"x": {
"field": "tbucket",
"type": "temporal",
"axis": { "title": false }
},
"y": {
"field": "doc_count",
"type": "quantitative",
"axis": { "title": "Document count" }
}
}
}💡 コードの解説
- **
%timefield%: "@timestamp"と?_tstart/?_tend**
ES|QL クエリ内の WHERE @timestamp >= ?_tstart AND @timestamp < ?_tend に注目してください。これにより、Kibana ダッシュボードの右上にある時間セレクター(例: 「過去24時間」など)の範囲が、自動的にこのプレースホルダーに流し込まれます。
STATS ... BY tbucket=TBUCKET(...)
ES|QL の強力な関数 TBUCKET を使い、2時間ごとのバケットに丸めてカウント( COUNT() )しています。従来の Query DSL で date_histogram アグリゲーションを書くよりも圧倒的にシンプルです。
mark : "line"
"mark": "line" を指定することで、集計データを折れ線グラフとして描画するよう指示しています。
encodingセクション
ES|QL で集計した結果の列名( tbucket と doc_count )が、そのまま Vega-Lite の field として直感的にマッピングされているのがわかります。
🖼️ 実行結果

【実践】サンプルコードで見る実装例2:時間帯×日付のヒートマップ(Vega-Lite)
次に、1時間ごとのイベント数を「曜日や日付×時間帯」のマトリクスで可視化し、アクセスの時間帯トレンドを一目で把握できるヒートマップ(パンチカード)の例をご紹介します。
{
"$schema": "https://vega.github.io/schema/vega-lite/v6.json",
"title": "kibana_sample_data_logs 内の時間ごとのログ数",
"data": {
"url": {
"%type%": "esql",
"%context%": true,
"%timefield%": "@timestamp",
"query": "FROM kibana_sample_data_logs | WHERE @timestamp >= ?_tstart AND @timestamp < ?_tend | STATS count=COUNT() BY datetime=TBUCKET(1 hour) | LIMIT 10000 | SORT datetime"
}
},
"config": {
"view": {
"strokeWidth": 0,
"step": 13
},
"axis": {
"domain": false
}
},
"mark": "rect",
"encoding": {
"x": {
"field": "datetime",
"timeUnit": "hours",
"type": "ordinal",
"title": "時"
},
"y": {
"field": "datetime",
"timeUnit": "date",
"type": "ordinal",
"title": "日"
},
"color": {
"field": "count",
"type": "quantitative",
"legend": {
"title": "カウント"
}
}
}
}💡 コードの解説
mark : "rect"によるグリッド描画
Vega-Lite でヒートマップを作る際は、タイル(四角形)を描画する "rect" マークを使用します。
timeUnit を使った「時」と「日」の切り出し
ES|QL 側からは TBUCKET(1 hour) で丸められた一連のタイムスタンプが返ってきます。それを Vega-Lite 側の timeUnit プロパティを使って、X軸には「時(hours)」、Y軸には「日(date)」として切り出してマッピングしています。これにより、複雑なクエリを書くことなく、フロントエンド側で綺麗な2次元マトリクスを表現できます。
color による密度の可視化
集計したイベント数( count )を color の quantitative(量的データ)として指定することで、ログ件数に応じた色の濃淡が自動的に適用されます。
🖼️ 実行結果

まとめ:データ分析と表現の幅を広げよう
これまでの Kibana カスタム可視化は、「Query DSL が難解で手が出せない」というエンジニアも多かったかと思います。しかし、ES|QL の登場によって、SQL ライクな直感的な記述でバックエンドのデータを引き出し、Vega の表現力をフルに活かせるようになりました。
ダッシュボードの表現力をもう一段階引き上げたい方は、ぜひ公式ドキュメントの Custom visualizations with Vega | Elastic Docs を参考に、ES|QL × Vega の強力なタッグを試してみてください!
その他の参考URL


