| name | steedos-analytics |
| description | Analytics question (.question.yml) and dashboard (.dashboard.yml) files.
TRIGGER: .question.yml or .dashboard.yml files; analytics reports, charts,
MBQL queries, display types, visualization_settings, result_metadata;
dashboard grid layout, card placement, parameter_mappings;
@steedos-labs/analytics package; YAML seed data under <package>/main/default/.
SKIP: custom Amis page → steedos-pages; analytics API calls → steedos-server-api.
|
Steedos Analytics — Questions & Dashboards File Format Guide
Overview | 概述
Analytics in Steedos consists of two file types:
- Questions (
.question.yml) — individual report/chart definitions with MBQL queries
- Dashboards (
.dashboard.yml) — visual containers arranging multiple question cards in a grid
Workflow: Create questions first, then create dashboards that reference them.
基于 @steedos-labs/analytics 包。工作流程:先创建 question,再创建 dashboard 引用它们。
Part 1: Questions | 问题
File Location & Naming | 文件路径与命名
<package>/main/default/questions/<问题名称>.question.yml
Examples:
questions/单位总数.question.yml
questions/媒体类型统计.question.yml
File Structure | 文件结构
name: <问题名称>
created_at: <ISO8601 datetime>
creator_id: <MongoDB ObjectId>
database_id: 1
dataset_query:
database: 1
type: query
query:
source-table: <object_name>
display: <display_type>
enable_embedding: false
entity_id: <MongoDB ObjectId>
id: <MongoDB ObjectId>
parameter_mappings: []
parameters: []
query_type: query
result_metadata:
- ...
table_id: <object_name>
updated_at: <ISO8601 datetime>
visualization_settings: '{}'
ID Generation | ID 生成
All id and entity_id values are 24-character MongoDB-style hex ObjectIds.
entity_id and id are always the same value within one file.
Generate unique IDs — never reuse across different questions or dashboards.
Example: 68a5805bb74676d1c8cb3614
dataset_query — MBQL Query | MBQL 查询结构
Count (scalar) | 计数
dataset_query:
database: 1
type: query
query:
source-table: media_org_info
aggregation:
- - count
Count with filter | 带过滤的计数
dataset_query:
database: 1
type: query
query:
source-table: media_journalist
filter:
- '='
- - field
- media_journalist.open
- null
- true
aggregation:
- - count
Group by field (breakout) | 按字段分组
dataset_query:
database: 1
type: query
query:
source-table: media_infor
aggregation:
- - count
breakout:
- - field
- media_infor.type
- null
Table list with limit | 列表查询
dataset_query:
database: 1
type: query
query:
source-table: media_org_info
limit: 10000
Native SQL query | 原生 SQL 查询
dataset_query:
database: 1
type: native
native:
query: "SELECT status, COUNT(*) as cnt FROM contracts GROUP BY status"
Display Types | 显示类型
display value | Description |
|---|
scalar | Single number (计数/合计等单值) |
table | Data table (数据列表) |
bar | Bar chart (柱状图) |
line | Line chart (折线图) |
pie | Pie chart (饼图) |
area | Area chart (面积图) |
row | Horizontal bar (横向柱状图) |
smartscalar | Number with trend (带趋势的数值) |
funnel | Funnel chart (漏斗图) |
pivot | Pivot table (透视表) |
visualization_settings | 可视化设置
For simple questions, use '{}' (empty JSON string).
Common examples:
visualization_settings: '{"table.cell_column":"count"}'
visualization_settings: >-
{"graph.dimensions":["type"],"graph.metrics":["count"],"graph.show_values":true,"graph.x_axis.labels_enabled":false,"graph.y_axis.labels_enabled":false}
visualization_settings: >-
{"table.pivot_column":"status","table.cell_column":"count"}
result_metadata | 结果列元数据
For aggregation (count) | 聚合查询结果
result_metadata:
- base_type: type/BigInteger
display_name: 计数
effective_type: type/BigInteger
field_ref:
- aggregation
- 0
name: count
semantic_type: type/Quantity
source: aggregation
For breakout + count | 分组+计数结果
result_metadata:
- id: type
name: type
display_name: 媒体类型
base_type: type/Text
effective_type: type/Text
semantic_type: null
source: breakout
visibility_type: normal
table_id: media_infor
field_ref:
- field
- media_infor.type
- null
- base_type: type/BigInteger
display_name: 计数
effective_type: type/BigInteger
field_ref:
- aggregation
- 0
name: count
semantic_type: type/Quantity
source: aggregation
For table list query | 列表查询结果
Each output column needs an entry. id uses <table>.<field> format:
result_metadata:
- id: media_org_info.name
name: name
display_name: 单位名称
base_type: type/Text
effective_type: type/Text
semantic_type: null
field_ref:
- field
- media_org_info.name
- null
source: fields
visibility_type: normal
table_id: media_org_info
base_type mapping | 字段类型映射
| Steedos field type | base_type |
|---|
| text / select / lookup | type/Text |
| number / currency | type/Float |
| integer | type/Integer |
| date | type/Date |
| datetime | type/DateTime |
| boolean | type/Boolean |
| count result | type/BigInteger |
Question Examples | 问题示例
Scalar — count of records
name: 单位总数
created_at: 2025-08-20T07:59:23.569Z
creator_id: 689c34199c7714afa7502547
database_id: 1
dataset_query:
query:
source-table: media_org_info
aggregation:
- - count
type: query
database: 1
display: scalar
entity_id: 68a5805bb74676d1c8cb3614
id: 68a5805bb74676d1c8cb3614
parameter_mappings: []
parameters: []
query_type: query
result_metadata:
- base_type: type/BigInteger
display_name: 计数
effective_type: type/BigInteger
field_ref:
- aggregation
- 0
name: count
semantic_type: type/Quantity
source: aggregation
table_id: media_org_info
updated_at: 2025-08-20T07:59:23.569Z
visualization_settings: '{}'
Bar chart — grouped count
name: 媒体类型统计
created_at: 2025-08-20T08:06:22.163Z
creator_id: 689c34199c7714afa7502547
database_id: 1
dataset_query:
database: 1
type: query
query:
source-table: media_infor
aggregation:
- - count
breakout:
- - field
- media_infor.type
- null
display: bar
enable_embedding: false
entity_id: 68a581feb74676d1c8cb361e
id: 68a581feb74676d1c8cb361e
parameter_mappings: []
parameters: []
query_type: query
result_metadata:
- id: type
name: type
display_name: 媒体类型
base_type: type/Text
effective_type: type/Text
semantic_type: null
source: breakout
visibility_type: normal
table_id: media_infor
field_ref:
- field
- media_infor.type
- null
- base_type: type/BigInteger
display_name: 计数
effective_type: type/BigInteger
field_ref:
- aggregation
- 0
name: count
semantic_type: type/Quantity
source: aggregation
table_id: media_infor
updated_at: 2025-08-20T08:06:22.163Z
visualization_settings: >-
{"graph.x_axis.labels_enabled":false,"graph.y_axis.labels_enabled":false,"graph.dimensions":["type"],"graph.metrics":["count"],"graph.show_values":true}
Question Key Rules | 问题关键规则
- File name = question name:
单位总数.question.yml
id == entity_id: always the same 24-char hex value
table_id: the Steedos object name (e.g. media_org_info)
database_id and dataset_query.database: always 1
query_type: always "query" (even for native SQL)
parameter_mappings and parameters: use [] unless participating in dashboard filtering
visualization_settings: must be a JSON string (quoted), not an object
- Generate fresh unique ObjectIds — never copy IDs from other questions
Part 2: Dashboards | 仪表盘
File Location & Naming | 文件路径与命名
<package>/main/default/dashboards/<仪表盘名称>.dashboard.yml
Example: dashboards/日常看板.dashboard.yml
File Structure | 文件结构
name: <仪表盘名称>
archived: 'false'
auto_apply_filters: 'true'
enable_embedding: 'false'
entity_id: <MongoDB ObjectId>
id: <MongoDB ObjectId>
show_in_getting_started: 'false'
parameters:
- ...
cards:
- ...
Note: archived, auto_apply_filters, enable_embedding,
show_in_getting_started are string values 'true'/'false',
not YAML booleans.
Grid Layout | 网格布局
The dashboard uses an 18-column grid. Cards are positioned with:
| Field | Description |
|---|
row | Starting row (0-based) |
col | Starting column (0-based, max 17) |
size_x | Width in grid units (max 18) |
size_y | Height in grid units (typical: 3–6) |
Common layout patterns:
| Pattern | cards per row | size_x each | col values |
|---|
| 3 equal columns | 3 | 6 | 0, 6, 12 |
| 2 equal columns | 2 | 9 | 0, 9 |
| Full width | 1 | 18 | 0 |
| 1/3 + 2/3 | 2 | 6, 12 | 0, 6 |
cards — Card Placement | 卡片布局定义
Each entry in cards places one question onto the dashboard:
cards:
- card_id: <question id>
col: 0
row: 0
size_x: 6
size_y: 3
entity_id: <unique ObjectId>
id: <same as entity_id>
series: []
visualization_settings: '{}'
parameter_mappings: []
parameter_mappings for a card | 卡片的参数映射
When a dashboard parameter filters a card, add parameter_mappings:
parameter_mappings:
- parameter_id: <dashboard parameter id>
card_id: <question id>
target:
- dimension
- - field
- <object_name>.<field_name>
- null
parameters — Dashboard Filter Parameters | 仪表盘过滤参数
parameters:
- name: 单位名称
slug: '%E5%8D%95%E4%BD%8D%E5%90%8D%E7%A7%B0'
id: 9e2d576d
type: string/=
sectionId: string
values_source_type: card
values_source_config:
card_id: <question id>
value_field:
- field
- <object_name>.<field_name>
- null
filteringParameters:
- f82fb543
Parameter types | 参数类型
type | Description |
|---|
string/= | Exact string match |
string/contains | Contains string |
number/= | Exact number match |
number/between | Number range |
date/single | Single date |
date/range | Date range |
date/relative | Relative date (e.g. "last 30 days") |
category | Category selection |
values_source_type options | 数据来源类型
values_source_type | Description |
|---|
static-list | Hand-coded list in values_source_config.values |
card | Pull values from a question's result column |
connected-field | Pull values from a database field directly |
slug | 参数 slug
slug is the URL-encoded version of name. In Python:
urllib.parse.quote('单位名称') → '%E5%8D%95%E4%BD%8D%E5%90%8D%E7%A7%B0'
For ASCII names, slug equals the name itself (e.g. name: status → slug: status).
Dashboard Complete Example | 仪表盘完整示例
name: 日常看板
archived: 'false'
auto_apply_filters: 'true'
enable_embedding: 'false'
entity_id: 68a58065b74676d1c8cb3615
id: 68a58065b74676d1c8cb3615
show_in_getting_started: 'false'
parameters:
- name: 单位名称
slug: '%E5%8D%95%E4%BD%8D%E5%90%8D%E7%A7%B0'
id: 9e2d576d
type: string/=
sectionId: string
values_source_type: card
values_source_config:
card_id: 68e75b39ae8aaf0623f11fb1
value_field:
- field
- media_org_info.name
- null
filteringParameters:
- f82fb543
- name: 单位层级
slug: '%E5%8D%95%E4%BD%8D%E5%B1%82%E7%BA%A7'
id: f82fb543
type: string/=
sectionId: string
values_source_type: static-list
values_source_config:
values:
- 一级单位
- 二级单位
- 三级单位
cards:
- card_id: 68a5805bb74676d1c8cb3614
col: 0
row: 0
size_x: 6
size_y: 3
entity_id: 68a580ebb74676d1c8cb3618
id: 68a580ebb74676d1c8cb3618
series: []
visualization_settings: '{}'
parameter_mappings:
- parameter_id: 9e2d576d
card_id: 68a5805bb74676d1c8cb3614
target:
- dimension
- - field
- media_org_info.media_matrix
- null
- parameter_id: f82fb543
card_id: 68a5805bb74676d1c8cb3614
target:
- dimension
- - field
- media_org_info.organizational_level
- null
- card_id: 68a581feb74676d1c8cb361e
col: 0
row: 3
size_x: 18
size_y: 5
entity_id: 68a58206b74676d1c8cb361f
id: 68a58206b74676d1c8cb361f
series: []
visualization_settings: '{}'
parameter_mappings:
- parameter_id: 9e2d576d
card_id: 68a581feb74676d1c8cb361e
target:
- dimension
- - field
- media_infor.media_org_info
- null
- parameter_id: f82fb543
card_id: 68a581feb74676d1c8cb361e
target:
- dimension
- - field
- media_infor.organizational_level
- null
Dashboard Key Rules | 仪表盘关键规则
- File name = dashboard name:
日常看板.dashboard.yml
id == entity_id for both the dashboard and each card placement
- Each card placement gets its own unique ObjectId (different from the question's id)
card_id references the question's id field (defined in .question.yml)
- Grid is 18 columns wide —
col + size_x must not exceed 18
parameter_mappings on each card must reference parameter_id values defined in the top-level parameters list
- Boolean-like fields (
archived, auto_apply_filters, enable_embedding, show_in_getting_started) are strings: 'true'/'false'
visualization_settings on each card is a JSON string '{}'
series is always [] unless using combo/multi-series charts
Workflow | 创建步骤
- Create questions first: write all
.question.yml files, note their id values
- Plan the grid layout: decide rows, cols, sizes for each card
- Define parameters (if filtering is needed): assign 8-char hex
ids
- Write the dashboard file: reference question ids in
card_id, add parameter_mappings for each card that participates in filtering