QM Syntax Manual
Download this documentQM (Query Model) defines query views based on TM, including queryable fields and UI configurations.
1. Basic Structure
QM files use JavaScript syntax, exporting a queryModel object:
export const queryModel = {
name: 'FactOrderQueryModel', // Query model name (required)
caption: 'Order Query', // Display name
model: 'FactOrderModel', // Associated TM model name (required)
columnGroups: [...], // Column group definitions
orders: [...] // Default sorting
};1.1 Basic Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique query model identifier |
caption | string | No | Display name |
model | string/array | Yes | Associated TM model (single or multiple) |
columnGroups | array | No | Column group definitions |
orders | array | No | Default sorting |
2. Single Model Association
The most common case is QM associating with a single TM:
export const queryModel = {
name: 'FactOrderQueryModel',
model: 'FactOrderModel', // Directly use TM name
columnGroups: [...]
};3. Multi-Model Association
When associating multiple fact tables, use loadTableModel to load models and reference fields via ref.
// Load models
const fo = loadTableModel('FactOrderModel');
const fp = loadTableModel('FactPaymentModel');
export const queryModel = {
name: 'OrderPaymentJoinQueryModel',
caption: 'Order Payment Join Query',
// Multi-model configuration
model: [
{
name: fo,
alias: 'fo' // Table alias
},
{
name: fp,
alias: 'fp',
onBuilder: () => { // JOIN condition
return 'fo.order_id = fp.order_id';
}
}
],
columnGroups: [
{
caption: 'Order Info',
items: [
{ ref: fo.orderId }, // V2: Use ref for reference
{ ref: fo.orderStatus },
{ ref: fo.customer } // Dimension ref, auto-expands to $id and $caption
]
},
{
caption: 'Payment Info',
items: [
{ ref: fp.paymentId },
{ ref: fp.payAmount }
]
}
]
};3.1 Multi-Model Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string/proxy | Yes | TM model name or loadTableModel proxy |
alias | string | Yes | Table alias to distinguish fields from different models |
onBuilder | function | No | JOIN condition builder (required for 2nd model onwards) |
4. Column Group Definition (columnGroups)
Column groups organize query fields for UI display. After loading a model with loadTableModel, reference fields via ref:
const fo = loadTableModel('FactOrderModel');
columnGroups: [
{
caption: 'Order Info',
items: [
{ ref: fo.orderId },
{ ref: fo.orderStatus }
]
},
{
caption: 'Customer Dimension',
items: [
{ ref: fo.customer }, // Auto-expands to $id + $caption
{ ref: fo.customer$customerType } // Dimension attribute
]
}
]Advantages of ref syntax:
- IDE support for code completion and type checking
- Auto-updates references during refactoring
- Compile-time error detection
4.1 Auto-Expansion of Dimension References
When ref points to a dimension (without $ suffix), it auto-expands to two columns:
{ ref: fo.customer }
// Equivalent to auto-generating:
// customer$id
// customer$caption4.2 Column Group Fields
| Field | Type | Required | Description |
|---|---|---|---|
caption | string | No | Group name |
name | string | No | Group identifier |
items | array | Yes | Column item list |
4.3 Column Item Fields
| Field | Type | Required | Description |
|---|---|---|---|
ref | object | Yes | Field reference (using loadTableModel proxy) |
caption | string | No | Override display name from TM |
alias | string | No | Output column alias |
ui | object | No | UI configuration |
4.4 UI Configuration
| Field | Type | Description |
|---|---|---|
fixed | string | Fixed position: left / right |
width | number | Column width (pixels) |
align | string | Alignment: left / center / right |
visible | boolean | Default visibility |
5. Field Reference Format
After loading models with loadTableModel, reference fields via proxy objects:
const fo = loadTableModel('FactOrderModel');
// Fact table properties
fo.orderId
fo.orderStatus
// Measures
fo.totalAmount
// Dimensions (auto-expand to $id + $caption)
fo.customer
// Dimension attributes
fo.customer$customerType
fo.customer$province
// Nested dimensions (using . path syntax)
fo.product.category$caption
fo.product.category.group$caption5.1 Nested Dimension References
Syntax rule: . handles dimension path navigation, $ handles property access — each has a distinct role:
fo.product.category$caption
├─────────────┘ └──┘
│ dimension path (.sep) property ($sep)Three reference formats:
// Format 1: Full path (precise, no alias needed)
{ ref: fo.product.category$caption }
{ ref: fo.product.category.group$caption }
// Format 2: Alias (recommended, requires alias defined in TM)
{ ref: fo.productCategory$caption } // alias: 'productCategory'
{ ref: fo.categoryGroup$caption } // alias: 'categoryGroup'
// Format 3: Underscore format in DSL queries (output column name format)
columns: ["product_category$caption", "product_category_group$caption"]Note: Do NOT use multiple
$instead of.(e.g.,) — the parser treats everything after the firstproduct$category$caption$as the property name, causing lookup failure.
Path syntax explanation:
fo.product→ Level 1 dimensionfo.product.category→ Level 2 dimension (sub-dimension of product)fo.product.category$caption→ caption attribute of level 2 dimensionfo.product.category$id→ id of level 2 dimensionfo.product.category.group$caption→ caption of level 3 dimension
Output column name format:
Dots in paths are automatically converted to underscores in output, avoiding JavaScript property name conflicts:
| QM Reference | Output Column Name |
|---|---|
fo.product$caption | product$caption |
fo.product.category$caption | product_category$caption |
fo.product.category.group$caption | product_category_group$caption |
6. Default Sorting (orders)
Define default sorting rules for queries:
orders: [
{ name: 'orderTime', order: 'desc' },
{ name: 'orderId', order: 'asc' }
]6.1 Sorting Fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Sort field name |
order | string | Yes | Sort direction: asc (ascending) / desc (descending) |
7. Calculated Fields
You can define calculated fields in QM:
columnGroups: [
{
caption: 'Calculated Fields',
items: [
{
name: 'profitRate',
caption: 'Profit Rate',
formula: 'profitAmount / salesAmount * 100',
type: 'NUMBER'
},
{
name: 'avgPrice',
caption: 'Average Price',
formula: 'totalAmount / totalQuantity',
type: 'MONEY'
}
]
}
]8.1 Calculated Field Configuration
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Calculated field name |
caption | string | No | Display name |
formula | string | Yes | Calculation formula |
type | string | No | Result data type |
8. Complete Examples
8.1 Basic Query Model
// FactOrderQueryModel.qm
const fo = loadTableModel('FactOrderModel');
export const queryModel = {
name: 'FactOrderQueryModel',
caption: 'Order Query',
model: fo,
columnGroups: [
{
caption: 'Order Info',
items: [
{ ref: fo.orderId },
{ ref: fo.orderStatus },
{ ref: fo.orderTime }
]
},
{
caption: 'Customer Info',
items: [
{ ref: fo.customer },
{ ref: fo.customer$customerType },
{ ref: fo.customer$province }
]
},
{
caption: 'Product Info',
items: [
{ ref: fo.product },
{ ref: fo.product$category },
{ ref: fo.product$unitPrice }
]
},
{
caption: 'Measures',
items: [
{ ref: fo.totalQuantity },
{ ref: fo.totalAmount },
{ ref: fo.profitAmount }
]
}
],
orders: [
{ name: 'orderTime', order: 'desc' }
]
};8.2 Multi-Fact Table Association
// OrderPaymentQueryModel.qm
const order = loadTableModel('FactOrderModel');
const payment = loadTableModel('FactPaymentModel');
export const queryModel = {
name: 'OrderPaymentQueryModel',
caption: 'Order Payment Query',
model: [
{
name: order,
alias: 'order'
},
{
name: payment,
alias: 'payment',
onBuilder: () => 'order.order_id = payment.order_id'
}
],
columnGroups: [
{
caption: 'Order Info',
items: [
{ ref: order.orderId },
{ ref: order.orderStatus },
{ ref: order.totalAmount }
]
},
{
caption: 'Payment Info',
items: [
{ ref: payment.paymentId },
{ ref: payment.paymentMethod },
{ ref: payment.paymentAmount },
{ ref: payment.paymentTime }
]
},
{
caption: 'Customer Info',
items: [
{ ref: order.customer },
{ ref: order.customer$customerType }
]
}
],
orders: [
{ name: 'payment.paymentTime', order: 'desc' }
]
};8.3 Query Model with Calculated Fields
// SalesAnalysisQueryModel.qm
const fs = loadTableModel('FactSalesModel');
export const queryModel = {
name: 'SalesAnalysisQueryModel',
caption: 'Sales Analysis',
model: fs,
columnGroups: [
{
caption: 'Dimensions',
items: [
{ ref: fs.salesDate$year },
{ ref: fs.salesDate$month },
{ ref: fs.product$category },
{ ref: fs.customer$customerType }
]
},
{
caption: 'Base Measures',
items: [
{ ref: fs.salesQuantity },
{ ref: fs.salesAmount },
{ ref: fs.costAmount },
{ ref: fs.profitAmount }
]
},
{
caption: 'Calculated Metrics',
items: [
{
name: 'profitRate',
caption: 'Profit Rate (%)',
formula: 'profitAmount / salesAmount * 100',
type: 'NUMBER'
},
{
name: 'avgOrderAmount',
caption: 'Average Order Value',
formula: 'salesAmount / COUNT(*)',
type: 'MONEY'
}
]
}
],
orders: [
{ name: 'salesDate$year', order: 'desc' },
{ name: 'salesDate$month', order: 'desc' }
]
};9. Naming Conventions
9.1 File Naming
- QM files:
{TMModelName}QueryModel.qm - Example:
FactOrderQueryModel.qm
9.2 Model Naming
- Query model name:
{TMModelName}QueryModel - Example:
FactOrderQueryModel
Next Steps
- TM Syntax Manual - Table model definitions
- JSON Query DSL - Complete DSL query syntax (recommended)
- Parent-Child Dimensions - Hierarchical dimensions
- Calculated Fields - Calculated field details
- Query API - HTTP API reference
- Row-Level Security - Row-level data isolation
