A lightweight, injection-proof query language that parses human-readable filter expressions into a portable AST, transpiles to SQL across ClickHouse, PostgreSQL, and StarRocks, evaluates in-memory, and ships with a Vue 3 editor — with full cross-language parity in Go, Python, and JavaScript.
| Intent | FlyQL Query |
|---|---|
| Filter by exact status | status = 200 |
| Active records, high priority or critical severity | active and (priority = 'high' or severity = 'critical') |
| Numeric range filter | price > 50.00 and quantity <= 10 |
| String equality, either of two values | event_type = 'login' or event_type = 'logout' |
| Truthy + negated truthy + truthy chain | enabled and not suspended and last_login |
| Case-insensitive regex match, exclude another | message ~ "(?i)error" and message !~ "(?i)warning" |
| Pending tasks past due or urgent | pending and (due_date < '2023-12-31' or priority = 'urgent') |
| Truthy + IN-list + numeric | in_stock and category in ['electronics', 'appliances'] and rating > 4.5 |
| Walk the AST to emit a custom (non-SQL) generator | see AST & Custom Generators |
| IN-list type consistency (homogeneous + heterogeneous) | status in [200, 'ok', 404] (validator is skipped — heterogeneous) |
Full examples with parse → generate → run for each pattern: Query Recipes.
Go:
go get github.com/iamtelescope/flyql/golangPython:
pip install flyql
# or
uv add flyqlJavaScript:
npm install flyqlPython:
from flyql import parse
from flyql.generators.clickhouse.generator import to_sql_where
from flyql.generators.clickhouse.column import Column
result = parse("status >= 400 and host like 'prod%'")
columns = {
"status": Column("status", "UInt32"),
"host": Column("host", "String"),
}
sql = to_sql_where(result.root, columns)JavaScript:
import { parse } from 'flyql'
import { generateWhere, newColumn } from 'flyql/generators/clickhouse'
const result = parse("status >= 400 and host like 'prod%'")
const columns = {
status: newColumn({ name: 'status', type: 'UInt32' }),
host: newColumn({ name: 'host', type: 'String' }),
}
const sql = generateWhere(result.root, columns)Go:
import (
flyql "github.com/iamtelescope/flyql/golang"
"github.com/iamtelescope/flyql/golang/generators/clickhouse"
)
result, err := flyql.Parse("status >= 400 and host like 'prod%'")
columns := map[string]*clickhouse.Column{
"status": clickhouse.NewColumn(clickhouse.ColumnDef{Name: "status", Type: "UInt32"}),
"host": clickhouse.NewColumn(clickhouse.ColumnDef{Name: "host", Type: "String"}),
}
sql, err := clickhouse.ToSQLWhere(result.Root, columns)A query consists of one or more conditions connected by boolean operators (and, or, not). Conditions can be comparisons or truthy checks.
status=200 and active and not archivedMore examples:
-- comparisons with or
service!=api or user="john doe"
-- regex match and negation
message~"error.*" and not debug
-- grouped conditions
(a=1 or b=2) and not (c=3 and d=4)
-- list membership
status in [200, 201] and method not in ['DELETE', 'PUT']
-- temporal functions
timestamp > ago(1h) and level = 'error'
-- timezone-aware date
date = today('Europe/Berlin')
-- start of week
created_at > startOf('week')
-- Date (calendar day) vs DateTime (instant) column types drive
-- schema-aware matcher coercion: ISO-8601 literals auto-parse and the
-- record value is compared at the declared granularity.
event_day = '2026-04-06' -- Type.Date column: Y/M/D compare
ts > '2026-04-06T21:00:00Z' -- Type.DateTime column: ms-granularity compareFor more examples, see the E2E report.
FlyQL supports the following comparison operators:
| Operator | Syntax |
|---|---|
| Equals | = |
| Not equals | != |
| Regex match | ~ |
| Not regex match | !~ |
| Greater than | > |
| Less than | < |
| Greater or equal | >= |
| Less or equal | <= |
| In list | in |
| Not in list | not in |
| Like pattern | like |
| Not like pattern | not like |
| Case-insensitive like | ilike |
| Case-insensitive not like | not ilike |
Use in and not in to check if a value is in a list:
status in [200, 201, 204]
env not in ['prod', 'staging']Rules:
- Values are enclosed in square brackets
[]and separated by commas - Values can be mixed types:
[200, 'ok', true, null] - String values must be quoted:
['a', 'b'] - Number values are unquoted:
[1, 2, 3] - Empty list
[]is allowed (in []is always false,not in []is always true)
Use like and ilike for SQL-style pattern matching:
-- starts with "error"
message like 'error%'
-- contains "prod"
host like '%prod%'
-- _ matches any single character
path like '/api/_/status'
-- case-insensitive
message ilike '%Error%'
-- negated
host not like 'test%'Wildcards:
%matches any sequence of characters (including empty)_matches exactly one character\%and\_match literal%and_
A standalone key without an operator checks if the field has a truthy value:
active
message and statusA value is considered falsy if it is:
null/None/ missing- Empty string
"" - Zero
0 - Boolean
false
Everything else is truthy.
Use not to negate any expression:
-- field is falsy
not active
-- status is not 200
not status=200
-- negates the grouped expression
not (a=1 and b=2)
-- combine with other conditions
active and not archivedDouble negation cancels out: not not active is equivalent to active.
- Boolean operators - Use
andto require all conditions to be true andorto allow for either condition. - Negation - Use
notbefore any expression to negate it. - Parentheses - Use
(and)to group conditions and set the precedence of operations (parentheses must be matched on both sides to avoid errors).
FlyQL supports temporal function calls as values for time-relative filters:
-- last 1 hour
timestamp > ago(1h)
-- compound: 1 hour 30 minutes
timestamp > ago(1h30m)
-- before current time
updated_at < now()
-- today's date
date = today()
-- today in timezone
date = today('Europe/Berlin')
-- start of today
created_at > startOf('day')
-- Monday 00:00
created_at > startOf('week')
-- first of month
created_at > startOf('month', 'UTC')- Duration units:
s(seconds),m(minutes),h(hours),d(days),w(weeks) - Timezones: IANA timezone names (e.g.,
'Europe/Berlin','Asia/Tokyo') - Operators: Only comparison operators (
=,!=,>,>=,<,<=) work with functions - Bare names:
field=ago(no parens) is a column reference, not a function
Use dot-separated paths to access nested fields in JSON, Map, or structured data:
request.url = "/api/users"
response.headers.content_type = "application/json"
metadata.labels.env = prodWhen a key segment itself contains a dot, wrap it in quotes so the dot is not treated as a separator:
"app.config".debug = true
metadata."dotted.key".value = 123Nested keys work with all operators:
request.status >= 400 and response.body ~ "error" and not request.internalTransformers modify column values before comparison using the pipe (|) syntax. They can be chained left-to-right:
-- convert to uppercase before comparing
message|upper = "ERROR"
-- chain multiple transformers
message|lower|len > 100Built-in transformers:
| Transformer | Input | Output | Example |
|---|---|---|---|
upper |
string | string | name|upper = "ALICE" |
lower |
string | string | name|lower = "alice" |
len |
string | int | message|len > 100 |
split(delimiter) |
string | array | path|split("/") |
- Standalone keys - A key without an operator is treated as a truthy check.
- Comparisons - A key with an operator must have a corresponding value.
- Spaces - Spaces around operators are allowed (
status=200andstatus = 200are equivalent).
- Without spaces - If the value contains no spaces, you can write it directly (e.g.,
status=200). - With spaces - If the value includes spaces, enclose it in single (
') or double (") quotes (e.g.,user="John Doe"oruser='John Doe'). - Escaping quotes - If the value itself contains quotes, these must be properly escaped (e.g.
user='John\'s Doe').
| Supported Versions | |
|---|---|
| Go | 1.21+ |
| Python | 3.10+ |
| Node.js | 16+ |
SQL Dialects: ClickHouse, PostgreSQL, StarRocks
Generators accept an opt-in format: true option that breaks WHERE at boolean operators and puts one SELECT column per line. Output is whitespace-only different from the default. See Formatted output.
FlyQL ships with a Vue 3 editor component that provides syntax highlighting, autocomplete, and schema-driven value suggestions:
import { FlyqlEditor } from 'flyql-vue'
import 'flyql-vue/flyql.css'<FlyqlEditor v-model="query" :columns="columns" :dark="true" @submit="onSubmit" />See the editor documentation for the full API reference.
Full documentation: docs.flyql.dev
See CONTRIBUTING.md for development guidelines and requirements.
See SECURITY.md for reporting vulnerabilities.
MIT