Skip to content

JSON Query DSL Syntax

Download this document

This document describes the complete JSON Query DSL (Domain Specific Language) syntax for Foggy Dataset Model.

1. Overview

JSON Query DSL is a declarative query language that describes query conditions, field selection, grouping, sorting, and other operations in JSON format. The system parses the DSL and converts it to SQL for execution.

1.1 Request Structure

json
{
    "page": 1,                          // Page number (starts from 1)
    "pageSize": 20,                     // Page size
    "param": {
        "columns": [...],               // Query columns
        "slice": [...],                 // Filter conditions
        "groupBy": [...],               // Grouping fields
        "orderBy": [...],               // Sorting fields
        "calculatedFields": [...],      // Dynamic calculated fields
        "returnTotal": true             // Whether to return totals
    }
}

2. Field Reference Format

2.1 Reference Types

FormatDescriptionExample
propertyNameFact table propertyorderId, orderStatus
measureNameMeasure fieldtotalAmount, quantity
dimension$idDimension IDcustomer$id
dimension$captionDimension display valuecustomer$caption
dimension$propertyDimension propertycustomer$customerType
dimension$hierarchy$idParent-child hierarchy viewteam$hierarchy$id
nested.dimension$propertyNested dimension propertyproduct.category$caption

2.2 Nested Dimension Reference

Nested dimension references use two separators: . for dimension path navigation, $ for property access.

json
{
    "columns": [
        "product$caption",                    // Level 1 dimension
        "product.category$caption",           // Level 2 dimension (. separates hierarchy)
        "product.category.group$caption"      // Level 3 dimension
    ]
}

If alias is defined in TM, you can also use alias references:

json
{
    "columns": [
        "product$caption",                    // Level 1 dimension
        "productCategory$caption",            // Level 2 dimension (via alias)
        "categoryGroup$caption"               // Level 3 dimension (via alias)
    ]
}

Note: Do NOT use multiple $ instead of . (e.g., product$category$caption) — $ is only for separating dimension from property.

Output column name mapping: Dots in paths are automatically converted to underscores in response data:

DSL ReferenceOutput Column Name
product$captionproduct$caption
product.category$captionproduct_category$caption
product.category.group$captionproduct_category_group$caption
productCategory$caption (alias)productCategory$caption

3. Filter Conditions (slice)

3.1 Basic Structure

Single Condition:

json
{
    "field": "fieldName",
    "op": "operator",
    "value": "value",
    "maxDepth": 2           // Hierarchy depth limit (hierarchy operators only)
}

OR Condition Group:

json
{
    "$or": [
        { "field": "field1", "op": "=", "value": "value1" },
        { "field": "field2", "op": "=", "value": "value2" }
    ]
}

AND Condition Group:

json
{
    "$and": [
        { "field": "field1", "op": ">", "value": 100 },
        { "field": "field2", "op": "<", "value": 1000 }
    ]
}

3.2 Operator List

Comparison Operators

OperatorDescriptionValue TypeExample
=Equalany{ "op": "=", "value": "A" }
!= / <>Not equalany{ "op": "!=", "value": "B" }
===Strict equal (no null special handling)any{ "op": "===", "value": "A" }
>Greater thannumber{ "op": ">", "value": 100 }
>=Greater or equalnumber{ "op": ">=", "value": 100 }
<Less thannumber{ "op": "<", "value": 1000 }
<=Less or equalnumber{ "op": "<=", "value": 1000 }

= vs ===: = automatically converts to IS NULL when value is null, while === always generates field = value without null special handling.

Set Operators

OperatorDescriptionValue TypeExample
inIn listarray{ "op": "in", "value": ["A", "B", "C"] }
not in / ninNot in listarray{ "op": "not in", "value": ["X", "Y"] }

Pattern Matching Operators

OperatorDescriptionWildcard HandlingExample
likePattern matchAuto adds %...%{ "op": "like", "value": "keyword" }
left_likeLeft matchAuto adds %...{ "op": "left_like", "value": "suffix" }
right_likeRight matchAuto adds ...%{ "op": "right_like", "value": "prefix" }
not likeNot matchAuto adds %...%{ "op": "not like", "value": "excluded" }
not left_likeNot left matchAuto adds %...{ "op": "not left_like", "value": "suffix" }
not right_likeNot right matchAuto adds ...%{ "op": "not right_like", "value": "prefix" }

Null Operators

OperatorDescriptionValueExample
is null / isNullIs nullnot needed{ "op": "is null" }
is not null / isNotNullIs not nullnot needed{ "op": "is not null" }
isNullAndEmptyIs null or empty stringnot needed{ "op": "isNullAndEmpty" }
isNotNullAndEmptyIs not null and not empty stringnot needed{ "op": "isNotNullAndEmpty" }

isNullAndEmpty generates (field IS NULL OR field = ''), useful when you need to check for both NULL and empty string values.

Range Operators

OperatorDescriptionBoundariesExample
[]Closed intervalIncludes both{ "op": "[]", "value": [100, 500] }
[)Left-closed right-openIncludes left{ "op": "[)", "value": ["2024-01-01", "2024-07-01"] }
(]Left-open right-closedIncludes right{ "op": "(]", "value": [0, 100] }
()Open intervalExcludes both{ "op": "()", "value": [0, 100] }

Hierarchy Operators (Parent-Child Dimensions)

For hierarchical queries on parent-child dimensions. See Parent-Child Dimensions for details.

OperatorDescriptionIncludes SelfExample
childrenOf / children_ofDirect childrenNo{ "op": "childrenOf", "value": "T001" }
descendantsOf / descendants_ofAll descendantsNo{ "op": "descendantsOf", "value": "T001" }
selfAndDescendantsOf / self_and_descendants_ofSelf and descendantsYes{ "op": "selfAndDescendantsOf", "value": "T001" }
ancestorsOf / ancestors_ofAll ancestorsNo{ "op": "ancestorsOf", "value": "T001" }
selfAndAncestorsOf / self_and_ancestors_ofSelf and ancestorsYes{ "op": "selfAndAncestorsOf", "value": "T001" }

Downward queries (childrenOf/descendantsOf) use closure.parentKey = value, upward queries (ancestorsOf/selfAndAncestorsOf) use closure.childKey = value.

Hierarchy Depth Limit:

Use the maxDepth parameter to limit query depth:

json
{
    "field": "team$id",
    "op": "descendantsOf",
    "value": "T001",
    "maxDepth": 2          // Only query descendants within 2 levels
}

Hierarchy Operator Example:

json
{
    "param": {
        "columns": ["team$caption", "salesAmount"],
        "slice": [
            {
                "field": "team$id",
                "op": "childrenOf",
                "value": "T001"
            }
        ]
    }
}

Generated SQL:

sql
SELECT dim_team.caption, SUM(fact.sales_amount)
FROM fact_team_sales fact
LEFT JOIN team_closure closure ON fact.team_id = closure.child_id
LEFT JOIN dim_team ON closure.child_id = dim_team.id
WHERE closure.parent_id = 'T001'
  AND closure.distance = 1
GROUP BY dim_team.caption

Vector Operators (Vector Models Only)

For semantic similarity search on vector models. Only vector fields (type=VECTOR) support these operators.

OperatorDescriptionExample
similarSimilarity search{ "op": "similar", "value": { "text": "..." } }
hybridHybrid search (vector + keyword){ "op": "hybrid", "value": { "text": "...", "keyword": "..." } }

similar Parameters:

ParameterTypeRequiredDescription
textstringYes*Search text (auto-converted to vector)
vectorfloat[]Yes*Direct vector input (alternative to text)
topKintNoNumber of results, default 10
minScorefloatNoMinimum similarity score (0-1)
groupBystringNoGroup by field for deduplication
radiusfloatNoMinimum score for range search

hybrid Parameters:

ParameterTypeRequiredDescription
textstringYesSearch text (auto-converted to vector)
keywordstringNoKeyword filter
topKintNoNumber of results, default 10
vectorWeightfloatNoVector weight, default 0.7
keywordWeightfloatNoKeyword weight, default 0.3

Vector Search Example:

json
{
    "param": {
        "columns": ["docId", "title", "content", "_score"],
        "slice": [
            {
                "field": "embedding",
                "op": "similar",
                "value": {
                    "text": "sales performance analysis",
                    "topK": 10,
                    "minScore": 0.6
                }
            },
            { "field": "category", "op": "=", "value": "report" }
        ]
    }
}

Hybrid Search Example:

json
{
    "param": {
        "columns": ["docId", "title", "_score"],
        "slice": [
            {
                "field": "embedding",
                "op": "hybrid",
                "value": {
                    "text": "sales analysis",
                    "keyword": "report",
                    "topK": 10
                }
            }
        ]
    }
}

Vector search results are sorted by similarity in descending order. The _score field indicates similarity (0-1).

3.3 Logical Combination ($or / $and)

Conditions within slice array are connected with AND by default. Use $or or $and operators to explicitly specify logical combinations.

MongoDB-style Operators:

OperatorDescriptionExample
$orOR logical group{ "$or": [cond1, cond2] } matches when any condition is true
$andAND logical group{ "$and": [cond1, cond2] } matches when all conditions are true

Example: Find orders where customer type is "VIP" OR order amount > 10000

json
{
    "param": {
        "slice": [
            {
                "$or": [
                    { "field": "customer$customerType", "op": "=", "value": "VIP" },
                    { "field": "totalAmount", "op": ">", "value": 10000 }
                ]
            }
        ]
    }
}

Example: Find orders where status is "COMPLETED" AND (customer type is "VIP" OR amount > 10000)

json
{
    "param": {
        "slice": [
            { "field": "orderStatus", "op": "=", "value": "COMPLETED" },
            {
                "$or": [
                    { "field": "customer$customerType", "op": "=", "value": "VIP" },
                    { "field": "totalAmount", "op": ">", "value": 10000 }
                ]
            }
        ]
    }
}

Nested Conditions:

$or and $and can be nested:

json
{
    "$or": [
        {
            "$and": [
                { "field": "region", "op": "=", "value": "East" },
                { "field": "totalAmount", "op": ">=", "value": 10000 }
            ]
        },
        {
            "$and": [
                { "field": "region", "op": "=", "value": "West" },
                { "field": "totalAmount", "op": ">=", "value": 5000 }
            ]
        }
    ]
}

3.4 Field-to-Field Comparison ($field / $expr)

Compare values between two fields directly without creating calculated fields. Two syntaxes are provided:

$field Reference

Use {"$field": "fieldName"} as value to reference another field instead of a literal value:

json
{
    "param": {
        "slice": [
            {
                "field": "salesAmount",
                "op": ">",
                "value": { "$field": "costAmount" }
            }
        ]
    }
}

Generated SQL:

sql
WHERE sales_amount > cost_amount

Supported comparison operators:

OperatorExample
={"field": "a", "op": "=", "value": {"$field": "b"}}
!={"field": "a", "op": "!=", "value": {"$field": "b"}}
>{"field": "a", "op": ">", "value": {"$field": "b"}}
>={"field": "a", "op": ">=", "value": {"$field": "b"}}
<{"field": "a", "op": "<", "value": {"$field": "b"}}
<={"field": "a", "op": "<=", "value": {"$field": "b"}}

$expr Expression

Use $expr for more complex field comparison expressions:

json
{
    "param": {
        "slice": [
            { "$expr": "salesAmount > costAmount" }
        ]
    }
}

Supports arithmetic operations:

json
{
    "param": {
        "slice": [
            { "$expr": "salesAmount > costAmount * 1.2" },
            { "$expr": "profitAmount >= discountAmount + 100" }
        ]
    }
}

Generated SQL:

sql
WHERE (sales_amount > (cost_amount * 1.2))
  AND (profit_amount >= (discount_amount + 100))

Combining with Other Conditions

$field and $expr can be combined with regular conditions and logical groups:

json
{
    "param": {
        "slice": [
            { "orderStatus": "COMPLETED" },
            { "field": "salesAmount", "op": ">", "value": { "$field": "costAmount" } },
            { "field": "quantity", "op": ">=", "value": 10 }
        ]
    }
}

Using within $or conditions:

json
{
    "param": {
        "slice": [
            {
                "$or": [
                    { "$expr": "salesAmount > costAmount * 1.5" },
                    { "field": "discountAmount", "op": ">", "value": 100 }
                ]
            }
        ]
    }
}

Use Cases

ScenarioRecommended SyntaxExample
Simple field comparison$fieldSales amount greater than cost
Comparison with arithmetic$exprProfit rate over 20% (salesAmount > costAmount * 1.2)
Multi-field arithmetic$exprNet amount greater than cost (salesAmount - discountAmount > costAmount)

4. Grouping (groupBy)

4.1 Basic Format

json
{
    "param": {
        "groupBy": [
            { "field": "customer$customerType" },
            { "field": "orderDate$year" },
            { "field": "orderDate$month" }
        ]
    }
}

4.2 Aggregation Types

TypeDescription
SUMSum
AVGAverage
COUNTCount
COUNTD / COUNT_DISTINCTDistinct count
MAXMaximum
MINMinimum
GROUP_CONCATString concatenation
STDDEV_POPPopulation standard deviation
STDDEV_SAMPSample standard deviation
VAR_POPPopulation variance
VAR_SAMPSample variance
NONENo aggregation (added to GROUP BY)

5. Sorting (orderBy)

json
{
    "param": {
        "orderBy": [
            { "field": "totalAmount", "dir": "desc" },
            { "field": "orderId", "dir": "asc" }
        ]
    }
}
ParameterTypeRequiredDescription
fieldstringYesSort field name
dirstringNoasc (ascending) / desc (descending), default asc
nullFirstbooleanNoNULL values first
nullLastbooleanNoNULL values last

6. Dynamic Calculated Fields (calculatedFields)

json
{
    "param": {
        "calculatedFields": [
            {
                "name": "profitRate",
                "caption": "Profit Rate",
                "expression": "profitAmount / salesAmount * 100",
                "agg": "SUM"
            }
        ],
        "columns": ["product$caption", "profitRate"]
    }
}

6.2 Parameters

ParameterTypeRequiredDescription
namestringYesCalculated field name (used for reference)
captionstringNoDisplay name
expressionstringYesCalculation expression
descriptionstringNoField description
aggstringNoAggregation type (SUM/AVG/COUNT/COUNTD/MAX/MIN etc.)
typestringNoReturn type (NUMBER/INTEGER/TEXT)
partitionBystring[]NoWindow function partition fields
windowOrderByobject[]NoWindow function ordering, format: [{"field": "xxx", "dir": "desc"}]
windowFramestringNoWindow frame definition, e.g. "ROWS BETWEEN 6 PRECEDING AND CURRENT ROW"

6.3 Supported Expressions

Arithmetic Operations

  • +, -, *, /, %

Math Functions

  • ABS(x) - Absolute value
  • ROUND(x, n) - Round to n decimal places
  • CEIL(x) / CEILING(x) - Round up
  • FLOOR(x) - Round down
  • MOD(x, y) - Modulo
  • POWER(x, y) / POW(x, y) - Exponentiation
  • SQRT(x) - Square root
  • SIGN(x) - Sign function (returns 1 for positive, -1 for negative, 0 for zero)
  • TRUNCATE(x, n) / TRUNC(x, n) - Truncate to n decimal places

Date Functions

  • YEAR(date) - Extract year
  • MONTH(date) - Extract month
  • DAY(date) - Extract day
  • HOUR(datetime) - Extract hour
  • MINUTE(datetime) - Extract minute
  • SECOND(datetime) - Extract second
  • DATE(datetime) - Extract date part
  • TIME(datetime) - Extract time part
  • NOW() - Current datetime
  • CURRENT_DATE() - Current date
  • CURRENT_TIME() - Current time
  • CURRENT_TIMESTAMP() - Current timestamp
  • DATE_ADD(date, interval) - Date addition
  • DATE_SUB(date, interval) - Date subtraction
  • DATEDIFF(date1, date2) - Date difference
  • TIMESTAMPDIFF(unit, date1, date2) - Timestamp difference
  • DATE_FORMAT(date, format) - Date formatting
  • STR_TO_DATE(str, format) - String to date
  • EXTRACT(unit FROM date) - Extract date part

String Functions

  • CONCAT(s1, s2, ...) - String concatenation
  • CONCAT_WS(sep, s1, s2, ...) - Concatenation with separator
  • SUBSTRING(s, start, len) / SUBSTR(s, start, len) - Extract substring
  • LEFT(s, n) - Left substring of n characters
  • RIGHT(s, n) - Right substring of n characters
  • UPPER(s) - Convert to uppercase
  • LOWER(s) - Convert to lowercase
  • TRIM(s) - Remove leading and trailing whitespace
  • LTRIM(s) - Remove leading whitespace
  • RTRIM(s) - Remove trailing whitespace
  • LENGTH(s) - Byte length
  • CHAR_LENGTH(s) - Character length
  • REPLACE(s, from, to) - String replacement
  • INSTR(s, substr) - Find substring position
  • LOCATE(substr, s) - Find substring position
  • LPAD(s, len, pad) - Left pad
  • RPAD(s, len, pad) - Right pad

Conditional and Type Functions

  • COALESCE(v1, v2, ...) - Return first non-null value
  • NULLIF(v1, v2) - Return NULL if equal
  • IFNULL(v, default) / NVL(v, default) / ISNULL(v) - Null handling
  • IF(condition, trueVal, falseVal) - Conditional expression
  • CASE WHEN ... THEN ... ELSE ... END - Case expression
  • CAST(v AS type) / CONVERT(v, type) - Type conversion

Window Functions

Window functions are used in calculated fields with partitionBy, windowOrderBy, and windowFrame parameters:

FunctionDescription
ROW_NUMBER()Row number (unique per row)
RANK()Rank (gaps on ties)
DENSE_RANK()Rank (no gaps on ties)
NTILE(n)Divide data into n buckets
LAG(field, offset)Value from preceding row
LEAD(field, offset)Value from following row
FIRST_VALUE(field)First value in window
LAST_VALUE(field)Last value in window

Window Function Example:

json
{
    "param": {
        "calculatedFields": [
            {
                "name": "salesRank",
                "caption": "Sales Rank",
                "expression": "RANK()",
                "partitionBy": ["region$caption"],
                "windowOrderBy": [{"field": "salesAmount", "dir": "desc"}]
            },
            {
                "name": "movingAvg",
                "caption": "7-Day Moving Average",
                "expression": "AVG(salesAmount)",
                "partitionBy": ["product$id"],
                "windowOrderBy": [{"field": "salesDate$caption", "dir": "asc"}],
                "windowFrame": "ROWS BETWEEN 6 PRECEDING AND CURRENT ROW"
            },
            {
                "name": "prevDaySales",
                "caption": "Previous Day Sales",
                "expression": "LAG(salesAmount, 1)",
                "partitionBy": ["product$id"],
                "windowOrderBy": [{"field": "salesDate$caption", "dir": "asc"}]
            }
        ],
        "columns": ["salesDate$caption", "product$caption", "salesAmount", "salesRank", "movingAvg", "prevDaySales"]
    }
}

Note: Window functions do not trigger GROUP BY; they compute independently on each row.


7. Pagination

ParameterTypeDefaultDescription
pageinteger1Page number (starts from 1)
pageSizeinteger10Page size
startinteger0Start record (alternative to page)
limitinteger10Record limit (alternative to pageSize)

8. Response Structure

json
{
    "code": 0,
    "data": {
        "items": [...],
        "total": 100,
        "totalData": {
            "total": 100,
            "totalAmount": 129900.00
        }
    },
    "msg": "success"
}

9. Complete Examples

Detail Query

json
{
    "page": 1,
    "pageSize": 20,
    "param": {
        "columns": ["orderId", "customer$caption", "product$caption", "totalAmount"],
        "slice": [
            { "field": "orderStatus", "op": "in", "value": ["COMPLETED", "SHIPPED"] },
            { "field": "orderTime", "op": "[)", "value": ["2024-01-01", "2024-07-01"] }
        ],
        "orderBy": [{ "field": "orderTime", "dir": "desc" }]
    }
}

Aggregation Query

json
{
    "page": 1,
    "pageSize": 100,
    "param": {
        "columns": ["customer$customerType", "totalQuantity", "totalAmount"],
        "groupBy": [{ "field": "customer$customerType" }],
        "orderBy": [{ "field": "totalAmount", "dir": "desc" }]
    }
}

Next Steps