Skip to content

Nested Keys

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 = 'prod'

Each dot-separated segment traverses one level into the nested structure. The SQL generators translate these paths into the correct syntax for each database dialect (JSON path access, Map lookups, etc.).

When a key segment itself contains a dot, wrap it in quotes so the dot is not treated as a separator:

"foo.bar".baz = value

Here:

  • "foo.bar" is a single key segment (the dot is literal)
  • baz is a child of "foo.bar"

Without quotes, foo.bar.baz would be three segments: foobarbaz.

Both single and double quotes work for key segments:

"foo.bar".baz = value
'foo.bar'.baz = value

Only the segments containing dots need to be quoted:

metadata."dotted.key".value = 123

Quoted identifiers also let you use reserved words as column names — for example, "not" = 1.

There is no limit on nesting depth:

a.b.c.d.e = value

Each segment is traversed in order when generating SQL or evaluating in-memory.

Nested keys work with all operators:

request.status >= 400
response.body ~ "error.*"
metadata.tags in ['prod', 'staging']
request.headers.content_type != "text/html"
not request.authenticated

For columns typed as arrays, use zero-indexed numeric path segments to access elements:

tags.0 = 'important'
tags.1 = 'second'

FlyQL array indexing is 0-based across all SQL dialects and the in-memory matcher. The generators translate the flyql index into each target’s native SQL convention — ClickHouse, StarRocks, and PostgreSQL arrays are all 1-indexed at the SQL layer, so flyql tags.0 emits tags[1] (with dialect-specific identifier quoting and function shape) in every dialect. This means the same flyql expression produces different SQL strings per dialect but evaluates to the same result against the same underlying data.

request.method = 'GET'
response.headers.content_type = "application/json"
"app.config".debug = true
request.status >= 400 and response.body ~ "error" and not request.internal