Skip to content

QM Syntax Manual

Download this document

QM (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:

javascript
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

FieldTypeRequiredDescription
namestringYesUnique query model identifier
captionstringNoDisplay name
modelstring/arrayYesAssociated TM model (single or multiple)
columnGroupsarrayNoColumn group definitions
ordersarrayNoDefault sorting

2. Single Model Association

The most common case is QM associating with a single TM:

javascript
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.

javascript
// 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

FieldTypeRequiredDescription
namestring/proxyYesTM model name or loadTableModel proxy
aliasstringYesTable alias to distinguish fields from different models
onBuilderfunctionNoJOIN 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:

javascript
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:

javascript
{ ref: fo.customer }
// Equivalent to auto-generating:
// customer$id
// customer$caption

4.2 Column Group Fields

FieldTypeRequiredDescription
captionstringNoGroup name
namestringNoGroup identifier
itemsarrayYesColumn item list

4.3 Column Item Fields

FieldTypeRequiredDescription
refobjectYesField reference (using loadTableModel proxy)
captionstringNoOverride display name from TM
aliasstringNoOutput column alias
uiobjectNoUI configuration

4.4 UI Configuration

FieldTypeDescription
fixedstringFixed position: left / right
widthnumberColumn width (pixels)
alignstringAlignment: left / center / right
visiblebooleanDefault visibility

5. Field Reference Format

After loading models with loadTableModel, reference fields via proxy objects:

javascript
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$caption

5.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:

javascript
// 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., product$category$caption) — the parser treats everything after the first $ as the property name, causing lookup failure.

Path syntax explanation:

  • fo.product → Level 1 dimension
  • fo.product.category → Level 2 dimension (sub-dimension of product)
  • fo.product.category$caption → caption attribute of level 2 dimension
  • fo.product.category$id → id of level 2 dimension
  • fo.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 ReferenceOutput Column Name
fo.product$captionproduct$caption
fo.product.category$captionproduct_category$caption
fo.product.category.group$captionproduct_category_group$caption

6. Default Sorting (orders)

Define default sorting rules for queries:

javascript
orders: [
    { name: 'orderTime', order: 'desc' },
    { name: 'orderId', order: 'asc' }
]

6.1 Sorting Fields

FieldTypeRequiredDescription
namestringYesSort field name
orderstringYesSort direction: asc (ascending) / desc (descending)

7. Calculated Fields

You can define calculated fields in QM:

javascript
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

FieldTypeRequiredDescription
namestringYesCalculated field name
captionstringNoDisplay name
formulastringYesCalculation formula
typestringNoResult data type

8. Complete Examples

8.1 Basic Query Model

javascript
// 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

javascript
// 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

javascript
// 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