Loading...
Loading...
Compare original and translation side by side
┌─────────────────────────────────────────────────────────────────────┐
│ STEP 1: Get table schemas via get_table_details(catalog, schema) │
├─────────────────────────────────────────────────────────────────────┤
│ STEP 2: Write SQL queries for each dataset │
├─────────────────────────────────────────────────────────────────────┤
│ STEP 3: TEST EVERY QUERY via execute_sql() ← DO NOT SKIP! │
│ - If query fails, FIX IT before proceeding │
│ - Verify column names match what widgets will reference │
│ - Verify data types are correct (dates, numbers, strings) │
├─────────────────────────────────────────────────────────────────────┤
│ STEP 4: Build dashboard JSON using ONLY verified queries │
├─────────────────────────────────────────────────────────────────────┤
│ STEP 5: Deploy via create_or_update_dashboard() │
└─────────────────────────────────────────────────────────────────────┘┌─────────────────────────────────────────────────────────────────────┐
│ 步骤1:通过get_table_details(catalog, schema)获取表结构 │
├─────────────────────────────────────────────────────────────────────┤
│ 步骤2:为每个数据集编写SQL查询 │
├─────────────────────────────────────────────────────────────────────┤
│ 步骤3:通过execute_sql()测试所有查询 ← 请勿跳过! │
│ - 如果查询失败,修复后再继续 │
│ - 验证列名称与小部件引用的名称一致 │
│ - 验证数据类型正确(日期、数字、字符串) │
├─────────────────────────────────────────────────────────────────────┤
│ 步骤4:仅使用已验证的查询构建仪表板JSON │
├─────────────────────────────────────────────────────────────────────┤
│ 步骤5:通过create_or_update_dashboard()部署 │
└─────────────────────────────────────────────────────────────────────┘| Tool | Description |
|---|---|
| STEP 1: Get table schemas for designing queries |
| STEP 3: Test SQL queries - MANDATORY before deployment! |
| Get available warehouse ID |
| STEP 5: Deploy dashboard JSON (only after validation!) |
| Get dashboard details by ID |
| List dashboards in workspace |
| Move dashboard to trash |
| Publish dashboard for viewers |
| Unpublish a dashboard |
| 工具 | 说明 |
|---|---|
| 步骤1:获取表结构以设计查询 |
| 步骤3:测试SQL查询 - 部署前必须执行! |
| 获取可用的仓库ID |
| 步骤5:部署仪表板JSON(仅在验证后执行!) |
| 通过ID获取仪表板详情 |
| 列出工作区中的仪表板 |
| 将仪表板移至回收站 |
| 发布仪表板供查看者访问 |
| 取消发布仪表板 |
;catalog.schema.table_nameASfieldName;catalog.schema.table_nameASfieldNameCRITICAL: Field Name Matching Rule TheinnameMUST exactly match thequery.fieldsinfieldName. If they don't match, the widget shows "no selected fields to visualize" error!encodings
// In query.fields:
{"name": "sum(spend)", "expression": "SUM(`spend`)"}
// In encodings (must match!):
{"fieldName": "sum(spend)", "displayName": "Total Spend"}// In query.fields:
{"name": "spend", "expression": "SUM(`spend`)"} // name is "spend"
// In encodings:
{"fieldName": "sum(spend)", ...} // ERROR: "sum(spend)" ≠ "spend"{"name": "sum(revenue)", "expression": "SUM(`revenue`)"}
{"name": "avg(price)", "expression": "AVG(`price`)"}
{"name": "count(orders)", "expression": "COUNT(`order_id`)"}
{"name": "countdistinct(customers)", "expression": "COUNT(DISTINCT `customer_id`)"}
{"name": "min(date)", "expression": "MIN(`order_date`)"}
{"name": "max(date)", "expression": "MAX(`order_date`)"}{"name": "daily(date)", "expression": "DATE_TRUNC(\"DAY\", `date`)"}
{"name": "weekly(date)", "expression": "DATE_TRUNC(\"WEEK\", `date`)"}
{"name": "monthly(date)", "expression": "DATE_TRUNC(\"MONTH\", `date`)"}{"name": "category", "expression": "`category`"}重要提示:字段名称匹配规则中的query.fields必须与name中的encodings完全匹配。 如果不匹配,小部件将显示“没有选定的可视化字段”错误!fieldName
// 在query.fields中:
{"name": "sum(spend)", "expression": "SUM(`spend`)"}
// 在encodings中(必须匹配!):
{"fieldName": "sum(spend)", "displayName": "总支出"}// 在query.fields中:
{"name": "spend", "expression": "SUM(`spend`)"} // 名称是"spend"
// 在encodings中:
{"fieldName": "sum(spend)", ...} // 错误:"sum(spend)" ≠ "spend"{"name": "sum(revenue)", "expression": "SUM(`revenue`)"}
{"name": "avg(price)", "expression": "AVG(`price`)"}
{"name": "count(orders)", "expression": "COUNT(`order_id`)"}
{"name": "countdistinct(customers)", "expression": "COUNT(DISTINCT `customer_id`)"}
{"name": "min(date)", "expression": "MIN(`order_date`)"}
{"name": "max(date)", "expression": "MAX(`order_date`)"}{"name": "daily(date)", "expression": "DATE_TRUNC(\"DAY\", `date`)"}
{"name": "weekly(date)", "expression": "DATE_TRUNC(\"WEEK\", `date`)"}
{"name": "monthly(date)", "expression": "DATE_TRUNC(\"MONTH\", `date`)"}{"name": "category", "expression": "`category`"}date_sub(current_date(), N)add_months(current_date(), -N)DATE_TRUNC('DAY'|'WEEK'|'MONTH'|'QUARTER'|'YEAR', column)INTERVALdate_sub(current_date(), N)add_months(current_date(), -N)DATE_TRUNC('DAY'|'WEEK'|'MONTH'|'QUARTER'|'YEAR', column)INTERVAL{"x": 0, "y": 0, "width": 2, "height": 4}| Widget Type | Width | Height | Notes |
|---|---|---|---|
| Text header | 6 | 1 | Full width; use SEPARATE widgets for title and subtitle |
| Counter/KPI | 2 | 3-4 | NEVER height=2 - too cramped! |
| Line/Bar chart | 3 | 5-6 | Pair side-by-side to fill row |
| Pie chart | 3 | 5-6 | Needs space for legend |
| Full-width chart | 6 | 5-7 | For detailed time series |
| Table | 6 | 5-8 | Full width for readability |
y=0: Title (w=6, h=1) - Dashboard title (use separate widget!)
y=1: Subtitle (w=6, h=1) - Description (use separate widget!)
y=2: KPIs (w=2 each, h=3) - 3 key metrics side-by-side
y=5: Section header (w=6, h=1) - "Trends" or similar
y=6: Charts (w=3 each, h=5) - Two charts side-by-side
y=11: Section header (w=6, h=1) - "Details"
y=12: Table (w=6, h=6) - Detailed data{"x": 0, "y": 0, "width": 2, "height": 4}| 小部件类型 | 宽度 | 高度 | 说明 |
|---|---|---|---|
| 文本标题 | 6 | 1 | 全屏宽度;标题和副标题使用单独的小部件 |
| 计数器/KPI | 2 | 3-4 | 绝对不要用高度=2 - 太拥挤! |
| 折线图/柱状图 | 3 | 5-6 | 并排摆放以填满整行 |
| 饼图 | 3 | 5-6 | 需要预留图例空间 |
| 全屏宽度图表 | 6 | 5-7 | 用于详细的时间序列 |
| 表格 | 6 | 5-8 | 全屏宽度以提升可读性 |
y=0: 标题(w=6, h=1)- 仪表板标题(使用单独的小部件!)
y=1: 副标题(w=6, h=1)- 描述(使用单独的小部件!)
y=2: KPIs(每个w=2, h=3)- 3个关键指标并排显示
y=5: 章节标题(w=6, h=1)- 如“趋势”等
y=6: 图表(每个w=3, h=5)- 两个图表并排显示
y=11: 章节标题(w=6, h=1)- 如“详情”
y=12: 表格(w=6, h=6)- 详细数据| Dimension Type | Max Values | Examples |
|---|---|---|
| Chart color/groups | 3-8 | 4 regions, 5 product lines, 3 tiers |
| Filters | 4-10 | 8 countries, 5 channels |
| High cardinality | Table only | customer_id, order_id, SKU |
get_table_details| 维度类型 | 最大值 | 示例 |
|---|---|---|
| 图表颜色/分组 | 3-8 | 4个地区、5个产品线、3个层级 |
| 过滤器 | 4-10 | 8个国家、5个渠道 |
| 高基数 | 仅用于表格 | customer_id、order_id、SKU |
get_table_detailswidget.nameframe.titlewidget.queries[0].name"main_query"| Widget Type | Version |
|---|---|
| counter | 2 |
| table | 2 |
| filter-multi-select | 2 |
| filter-single-select | 2 |
| filter-date-range-picker | 2 |
| bar | 3 |
| line | 3 |
| pie | 3 |
| text | N/A (no spec block) |
multilineTextboxSpec######**bold***italic*lines// CORRECT: Separate widgets for title and subtitle
{
"widget": {
"name": "title",
"multilineTextboxSpec": {
"lines": ["## Dashboard Title"]
}
},
"position": {"x": 0, "y": 0, "width": 6, "height": 1}
},
{
"widget": {
"name": "subtitle",
"multilineTextboxSpec": {
"lines": ["Description text here"]
}
},
"position": {"x": 0, "y": 1, "width": 6, "height": 1}
}
// WRONG: Multiple lines concatenate into one line!
{
"widget": {
"name": "title-widget",
"multilineTextboxSpec": {
"lines": ["## Dashboard Title", "Description text here"] // Becomes "## Dashboard TitleDescription text here"
}
},
"position": {"x": 0, "y": 0, "width": 6, "height": 2}
}versionwidgetType"disaggregated": truename{
"widget": {
"name": "total-revenue",
"queries": [{
"name": "main_query",
"query": {
"datasetName": "summary_ds",
"fields": [{"name": "revenue", "expression": "`revenue`"}],
"disaggregated": true
}
}],
"spec": {
"version": 2,
"widgetType": "counter",
"encodings": {
"value": {"fieldName": "revenue", "displayName": "Total Revenue"}
},
"frame": {"showTitle": true, "title": "Total Revenue"}
}
},
"position": {"x": 0, "y": 0, "width": 2, "height": 3}
}"disaggregated": falsenamefieldName"sum(spend)"{
"widget": {
"name": "total-spend",
"queries": [{
"name": "main_query",
"query": {
"datasetName": "by_category",
"fields": [{"name": "sum(spend)", "expression": "SUM(`spend`)"}],
"disaggregated": false
}
}],
"spec": {
"version": 2,
"widgetType": "counter",
"encodings": {
"value": {"fieldName": "sum(spend)", "displayName": "Total Spend"}
},
"frame": {"showTitle": true, "title": "Total Spend"}
}
},
"position": {"x": 0, "y": 0, "width": 2, "height": 3}
}versionwidgetTypefieldNamedisplayName"disaggregated": true{
"widget": {
"name": "details-table",
"queries": [{
"name": "main_query",
"query": {
"datasetName": "details_ds",
"fields": [
{"name": "name", "expression": "`name`"},
{"name": "value", "expression": "`value`"}
],
"disaggregated": true
}
}],
"spec": {
"version": 2,
"widgetType": "table",
"encodings": {
"columns": [
{"fieldName": "name", "displayName": "Name"},
{"fieldName": "value", "displayName": "Value"}
]
},
"frame": {"showTitle": true, "title": "Details"}
}
},
"position": {"x": 0, "y": 0, "width": 6, "height": 6}
}versionwidgetTypexycolorscale.type"temporal""quantitative""categorical""disaggregated": true"y": {
"scale": {"type": "quantitative"},
"fields": [
{"fieldName": "sum(orders)", "displayName": "Orders"},
{"fieldName": "sum(returns)", "displayName": "Returns"}
]
}"y": {"fieldName": "sum(revenue)", "scale": {"type": "quantitative"}},
"color": {"fieldName": "region", "scale": {"type": "categorical"}, "displayName": "Region"}mark"mark": {"layout": "group"}versionwidgetTypeanglecolorwidget.nameframe.titlewidget.queries[0].name"main_query"| 小部件类型 | 版本 |
|---|---|
| counter | 2 |
| table | 2 |
| filter-multi-select | 2 |
| filter-single-select | 2 |
| filter-date-range-picker | 2 |
| bar | 3 |
| line | 3 |
| pie | 3 |
| text | 无(不需要spec块) |
multilineTextboxSpec######**粗体***斜体*lines// 正确示例:标题和副标题使用单独的小部件
{
"widget": {
"name": "title",
"multilineTextboxSpec": {
"lines": ["## 仪表板标题"]
}
},
"position": {"x": 0, "y": 0, "width": 6, "height": 1}
},
{
"widget": {
"name": "subtitle",
"multilineTextboxSpec": {
"lines": ["描述文本"]
}
},
"position": {"x": 0, "y": 1, "width": 6, "height": 1}
}
// 错误示例:多行文本会被拼接成一行!
{
"widget": {
"name": "title-widget",
"multilineTextboxSpec": {
"lines": ["## 仪表板标题", "描述文本"] // 会变成"## 仪表板标题描述文本"
}
},
"position": {"x": 0, "y": 0, "width": 6, "height": 2}
}versionwidgetType"disaggregated": truename{
"widget": {
"name": "total-revenue",
"queries": [{
"name": "main_query",
"query": {
"datasetName": "summary_ds",
"fields": [{"name": "revenue", "expression": "`revenue`"}],
"disaggregated": true
}
}],
"spec": {
"version": 2,
"widgetType": "counter",
"encodings": {
"value": {"fieldName": "revenue", "displayName": "总收入"}
},
"frame": {"showTitle": true, "title": "总收入"}
}
},
"position": {"x": 0, "y": 0, "width": 2, "height": 3}
}"disaggregated": falsenamefieldName"sum(spend)"{
"widget": {
"name": "total-spend",
"queries": [{
"name": "main_query",
"query": {
"datasetName": "by_category",
"fields": [{"name": "sum(spend)", "expression": "SUM(`spend`)"}],
"disaggregated": false
}
}],
"spec": {
"version": 2,
"widgetType": "counter",
"encodings": {
"value": {"fieldName": "sum(spend)", "displayName": "总支出"}
},
"frame": {"showTitle": true, "title": "总支出"}
}
},
"position": {"x": 0, "y": 0, "width": 2, "height": 3}
}versionwidgetTypefieldNamedisplayName"disaggregated": true{
"widget": {
"name": "details-table",
"queries": [{
"name": "main_query",
"query": {
"datasetName": "details_ds",
"fields": [
{"name": "name", "expression": "`name`"},
{"name": "value", "expression": "`value`"}
],
"disaggregated": true
}
}],
"spec": {
"version": 2,
"widgetType": "table",
"encodings": {
"columns": [
{"fieldName": "name", "displayName": "名称"},
{"fieldName": "value", "displayName": "值"}
]
},
"frame": {"showTitle": true, "title": "详情"}
}
},
"position": {"x": 0, "y": 0, "width": 6, "height": 6}
}versionwidgetTypexycolorscale.type"temporal""quantitative""categorical""disaggregated": true"y": {
"scale": {"type": "quantitative"},
"fields": [
{"fieldName": "sum(orders)", "displayName": "订单数"},
{"fieldName": "sum(returns)", "displayName": "退货数"}
]
}"y": {"fieldName": "sum(revenue)", "scale": {"type": "quantitative"}},
"color": {"fieldName": "region", "scale": {"type": "categorical"}, "displayName": "地区"}mark"mark": {"layout": "group"}versionwidgetTypeanglecolorCRITICAL: Filter widgets use DIFFERENT widget types than charts!
- Valid types:
,filter-multi-select,filter-single-selectfilter-date-range-picker- DO NOT use
- this does not exist and will cause errorswidgetType: "filter"- Filters use
spec.version: 2- ALWAYS include
withframefor filter widgetsshowTitle: true
filter-date-range-pickerfilter-single-selectfilter-multi-select重要提示:过滤器小部件使用的类型与图表不同!
- 有效类型:
、filter-multi-select、filter-single-selectfilter-date-range-picker- 不要使用
- 该类型不存在,会导致错误widgetType: "filter"- 过滤器使用
spec.version: 2- 始终为过滤器小部件添加带
的showTitle: trueframe
filter-date-range-pickerfilter-single-selectfilter-multi-select| Type | Placement | Scope | Use Case |
|---|---|---|---|
| Global Filter | Dedicated page with | Affects ALL pages that have datasets with the filter field | Cross-dashboard filtering (e.g., date range, campaign) |
| Page-Level Filter | Regular page with | Affects ONLY widgets on that same page | Page-specific filtering (e.g., platform filter on breakdown page only) |
| 类型 | 位置 | 范围 | 使用场景 |
|---|---|---|---|
| 全局过滤器 | 专用页面,设置 | 影响所有包含该过滤器字段的数据集的页面 | 跨仪表板过滤(例如:日期范围、活动) |
| 页面级过滤器 | 普通页面,设置 | 仅影响同一页面上的小部件 | 页面专属过滤(例如:仅在细分页面上使用平台过滤器) |
CRITICAL: Do NOT use- it causes SQL errors! Use a simple field expression instead.associative_filter_predicate_group
{
"widget": {
"name": "filter_region",
"queries": [{
"name": "ds_data_region",
"query": {
"datasetName": "ds_data",
"fields": [
{"name": "region", "expression": "`region`"}
],
"disaggregated": false
}
}],
"spec": {
"version": 2,
"widgetType": "filter-multi-select",
"encodings": {
"fields": [{
"fieldName": "region",
"displayName": "Region",
"queryName": "ds_data_region"
}]
},
"frame": {"showTitle": true, "title": "Region"}
}
},
"position": {"x": 0, "y": 0, "width": 2, "height": 2}
}重要提示:不要使用- 会导致SQL错误! 改用简单的字段表达式。associative_filter_predicate_group
{
"widget": {
"name": "filter_region",
"queries": [{
"name": "ds_data_region",
"query": {
"datasetName": "ds_data",
"fields": [
{"name": "region", "expression": "`region`"}
],
"disaggregated": false
}
}],
"spec": {
"version": 2,
"widgetType": "filter-multi-select",
"encodings": {
"fields": [{
"fieldName": "region",
"displayName": "地区",
"queryName": "ds_data_region"
}]
},
"frame": {"showTitle": true, "title": "地区"}
}
},
"position": {"x": 0, "y": 0, "width": 2, "height": 2}
}{
"name": "filters",
"displayName": "Filters",
"pageType": "PAGE_TYPE_GLOBAL_FILTERS",
"layout": [
{
"widget": {
"name": "filter_campaign",
"queries": [{
"name": "ds_campaign",
"query": {
"datasetName": "overview",
"fields": [{"name": "campaign_name", "expression": "`campaign_name`"}],
"disaggregated": false
}
}],
"spec": {
"version": 2,
"widgetType": "filter-multi-select",
"encodings": {
"fields": [{
"fieldName": "campaign_name",
"displayName": "Campaign",
"queryName": "ds_campaign"
}]
},
"frame": {"showTitle": true, "title": "Campaign"}
}
},
"position": {"x": 0, "y": 0, "width": 2, "height": 2}
}
]
}{
"name": "filters",
"displayName": "过滤器",
"pageType": "PAGE_TYPE_GLOBAL_FILTERS",
"layout": [
{
"widget": {
"name": "filter_campaign",
"queries": [{
"name": "ds_campaign",
"query": {
"datasetName": "overview",
"fields": [{"name": "campaign_name", "expression": "`campaign_name`"}],
"disaggregated": false
}
}],
"spec": {
"version": 2,
"widgetType": "filter-multi-select",
"encodings": {
"fields": [{
"fieldName": "campaign_name",
"displayName": "营销活动",
"queryName": "ds_campaign"
}]
},
"frame": {"showTitle": true, "title": "营销活动"}
}
},
"position": {"x": 0, "y": 0, "width": 2, "height": 2}
}
]
}{
"name": "platform_breakdown",
"displayName": "Platform Breakdown",
"pageType": "PAGE_TYPE_CANVAS",
"layout": [
{
"widget": {
"name": "page-title",
"multilineTextboxSpec": {"lines": ["## Platform Breakdown"]}
},
"position": {"x": 0, "y": 0, "width": 4, "height": 1}
},
{
"widget": {
"name": "filter_platform",
"queries": [{
"name": "ds_platform",
"query": {
"datasetName": "platform_data",
"fields": [{"name": "platform", "expression": "`platform`"}],
"disaggregated": false
}
}],
"spec": {
"version": 2,
"widgetType": "filter-multi-select",
"encodings": {
"fields": [{
"fieldName": "platform",
"displayName": "Platform",
"queryName": "ds_platform"
}]
},
"frame": {"showTitle": true, "title": "Platform"}
}
},
"position": {"x": 4, "y": 0, "width": 2, "height": 2}
}
// ... other widgets on this page
]
}x=0width: 2, height: 2{
"name": "platform_breakdown",
"displayName": "平台细分",
"pageType": "PAGE_TYPE_CANVAS",
"layout": [
{
"widget": {
"name": "page-title",
"multilineTextboxSpec": {"lines": ["## 平台细分"]}
},
"position": {"x": 0, "y": 0, "width": 4, "height": 1}
},
{
"widget": {
"name": "filter_platform",
"queries": [{
"name": "ds_platform",
"query": {
"datasetName": "platform_data",
"fields": [{"name": "platform", "expression": "`platform`"}],
"disaggregated": false
}
}],
"spec": {
"version": 2,
"widgetType": "filter-multi-select",
"encodings": {
"fields": [{
"fieldName": "platform",
"displayName": "平台",
"queryName": "ds_platform"
}]
},
"frame": {"showTitle": true, "title": "平台"}
}
},
"position": {"x": 4, "y": 0, "width": 2, "height": 2}
}
// ... 该页面上的其他小部件
]
}x=0width: 2, height: 2namefieldName"sum(spend)"disaggregated: truedisaggregated: falseexecute_sqlnamefieldName"sum(spend)"disaggregated: truedisaggregated: falseexecute_sqlimport jsonimport jsontripsavg_fareavg_distancepickup_ziptrip_countpickup_ziptrip_counttripsavg_fareavg_distancepickup_ziptrip_countpickup_ziptrip_countundefinedundefinedimport jsonimport jsontotal_revenueregiontotal_revenueregionundefinedundefinednamequery.fieldsfieldNameencodings// WRONG - names don't match
"fields": [{"name": "spend", "expression": "SUM(`spend`)"}]
"encodings": {"value": {"fieldName": "sum(spend)", ...}} // ERROR!
// CORRECT - names match
"fields": [{"name": "sum(spend)", "expression": "SUM(`spend`)"}]
"encodings": {"value": {"fieldName": "sum(spend)", ...}} // OK!query.fieldsnameencodingsfieldName// 错误示例 - 名称不匹配
"fields": [{"name": "spend", "expression": "SUM(`spend`)"}]
"encodings": {"value": {"fieldName": "sum(spend)", ...}} // 错误!
// 正确示例 - 名称匹配
"fields": [{"name": "sum(spend)", "expression": "SUM(`spend`)"}]
"encodings": {"value": {"fieldName": "sum(spend)", ...}} // 正确!version: 2version: 2version: 2version: 3specmultilineTextboxSpecwidgetType: "text"version: 2fieldNamedisplayNametypenumberFormatversion: 2version: 2version: 2version: 2version: 3specmultilineTextboxSpecwidgetType: "text"version: 2fieldNamedisplayNametypenumberFormatversion: 2disaggregatedtruedisaggregatedtruewidgetTypefilter-multi-selectfilter-single-selectfilter-date-range-pickerwidgetType: "filter"spec.version2queryNamenamedisaggregated: falseframeshowTitle: truewidgetTypefilter-multi-selectfilter-single-selectfilter-date-range-pickerwidgetType: "filter"spec.version2queryNamenamedisaggregated: falseshowTitle: trueframePAGE_TYPE_GLOBAL_FILTERSPAGE_TYPE_CANVASPAGE_TYPE_GLOBAL_FILTERSPAGE_TYPE_CANVASassociative_filter_predicate_groupassociative_filter_predicate_groupCOUNT_IF(\{"name": "field", "expression": "\COUNT_IF(\{"name": "field", "expression": "\lineslines