Expression query language - Enterprise
The expression language is a small, dependency-free query language your users author through the UI. It is the connective tissue behind alerts (and, in time, styled and calculated columns): one model for predicates, scalar maths, and change detection, evaluated the same way everywhere.
It ships in @svgrid/enterprise. Leaf comparisons delegate to the grid's own
applyExcelFilter, so an expression's operators mean exactly what the column
filter menu means.
The editor
<SvExpressionEditor> offers two modes over the same value:
- Builder (default) - a list of conditions (column + operator + value)
combined with all (AND) or any (OR), just like the filter row. Set
operators show a token input;
betweenshows two fields; blank operators show none. - Text - a free-text box for power users, with live validation and column maths.
Both show a live "matches N of M" count against a sample rows array.
<script lang="ts">
import { SvExpressionEditor, type ExprColumn, type PredicateExpr } from '@svgrid/enterprise'
const columns: ExprColumn[] = [
{ id: 'price', name: 'Price', type: 'number' },
{ id: 'region', name: 'Region', type: 'text' },
]
let expr = $state<PredicateExpr>({ kind: 'const', value: true })
</script>
<SvExpressionEditor {columns} bind:value={expr} rows={sample} />
Text syntax
Column references are a bare id (price) or a bracketed label ([Unit Price])
when the name has spaces.
price > 100 AND region IN ("EU", "US")
name CONTAINS "widget" OR name STARTSWITH "gadget"
price BETWEEN 10 AND 20
name ISBLANK
price / qty >= 40 -- cross-column maths
SUM(amount) > 10000 -- aggregate over the rows in scope
- Logic:
AND,OR,NOT, parentheses. - Text/set/range operators:
CONTAINS,STARTSWITH,ENDSWITH,MATCHES(regex),IN (…),BETWEEN … AND …,ISBLANK,ISNOTBLANK. - Symbolic comparators:
=,!=,>,<,>=,<=(these also drive cross-column maths). - Arithmetic:
+,-,*,/,%, unary-. - Aggregates:
SUM(col),AVG(col),COUNT(col). - Functions:
ABS,ROUND,FLOOR,CEIL,MIN,MAX,IF,COALESCE,CONCAT,LOWER,UPPER,LEN.
A column = literal comparison parses to a grid-filter cmp node (so it folds
accents/case like the filter row); general comparisons and maths parse to a
scalarCmp.
The model
The canonical form is a JSON AST - no parser is needed at runtime. Three families:
PredicateExpr- boolean:and/or/not/cmp(a filter-operator test) /scalarCmp(compare two scalar expressions) /const.ScalarExpr- a single value:col/lit/bin(arithmetic) /agg/func.ChangeExpr-changed/delta/percentChange/crossed, for the "relative change" alert trigger.
Evaluating expressions
import { evaluatePredicate, parsePredicate, validateExpression } from '@svgrid/enterprise'
const expr = parsePredicate('price > 100 AND region = "EU"', columns)
validateExpression(expr, columns) // [] when sound
evaluatePredicate(expr, { row: { price: 120, region: 'EU' } }) // true
evaluateScalar and evaluateChange cover the other two families. All three are
pure functions - safe to unit-test and to run in a Web Worker.
Advanced scalar functions can be injected per evaluation via the context's
functions map (for example, to delegate a formula to HyperFormula) without
making it a hard dependency.
Try it
Build a predicate in the visual editor and watch the AST underneath. The same value round-trips through the text mode, which is what makes it storable.
<script lang="ts">
import { SvExpressionEditor } from '@svgrid/enterprise'
import type { ExprColumn, PredicateExpr } from '@svgrid/enterprise'
const columns: ExprColumn[] = [
{ id: 'name', name: 'Name', type: 'text' },
{ id: 'department', name: 'Department', type: 'text' },
{ id: 'age', name: 'Age', type: 'number' },
{ id: 'salary', name: 'Salary', type: 'number' },
]
const rows = [
{ name: 'Ada Lovelace', department: 'Engineering', age: 36, salary: 142000 },
{ name: 'Grace Hopper', department: 'Engineering', age: 45, salary: 168000 },
{ name: 'Linus Torvalds', department: 'Platform', age: 54, salary: 155000 },
]
let expr = $state<PredicateExpr>({ kind: 'const', value: true })
</script>
<SvExpressionEditor {columns} {rows} bind:value={expr} />
<pre>{JSON.stringify(expr, null, 2)}</pre>
The AST is plain JSON, so a rule authored here is something you can put in a database and evaluate later - which is exactly what alerts do with it.
See also
- Alerts - the flagship consumer of this language.
- Filtering - the column filter operators the
cmpnodes reuse.
Related articles
- Migrating from DataTables.net to a Svelte Data Grid - A practical migration guide from jQuery DataTables to SvGrid - how column definitions, server-side data, custom rendering, and selection map across, with working code for each step.
- Using SvGrid with TanStack Query in Svelte - Wire TanStack Query's caching and background refetch into SvGrid for a server-driven grid that pages instantly and never shows a blank screen.
- What's New in SvGrid Enterprise - Export, Pivot, Import, Print, and AI - A practical look at @svgrid/enterprise - pivot tables, Excel and PDF export, data import, printing, and an AI assistant. Covers when each feature earns its place and what to watch out for in production.