Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Query Engine

The query engine lets you search, filter, and project over all blocks in a document using a concise pipeline syntax. Queries can appear anywhere an expression is expected — inside let bindings, attribute values, and validation blocks.

Pipeline Syntax

query(selector | filter | filter | ... | projection?)

A pipeline is a selector followed by zero or more filter steps and an optional trailing projection. Each step is separated by |.

Selectors

Selectors determine the initial set of blocks the pipeline operates on.

SelectorMatches
serviceAll blocks of kind service
service#svc-authThe block of kind service with ID svc-auth
config.server.listenerA nested attribute path — the listener attribute inside server inside config
..health_checkRecursive descent — all blocks named health_check at any depth
.The document root
*All top-level blocks
table#idThe table with the given ID

Examples

// All services
let all_services = query(service)

// A specific service by ID
let auth = query(service#svc-auth)

// All tables
let all_tables = query(table.*)

// Every health_check block anywhere in the document
let checks = query(..health_check)

Filters

Filters narrow the result set. Multiple filters are AND-combined — every filter in the chain must match.

Attribute Comparison

query(service | .port > 8080)
query(service | .env == "production")
query(service | .env != "staging")
query(service | .workers >= 4)

Regex Match

query(service | .name =~ "^api-")
query(config  | .region =~ "us-.*")

Existence Check: has

// Blocks that have an 'auth' attribute
query(service | has(.auth))

// Blocks that carry a specific decorator
query(service | has(@sensitive))
query(service | has(@validate))

Decorator Argument Filtering

Filter on the arguments of a decorator:

// Services whose @validate decorator has min > 0
query(service | @validate.min > 0)

// Blocks with @retry where attempts >= 3
query(service | @retry.attempts >= 3)

Compound Filters

Chain multiple filters to AND them together:

query(service | .env == "production" | .port > 8000 | has(@tls))

Projections

A projection extracts a single attribute value from each matched block, producing a list of values instead of a list of block references. The projection step must be the last step in the pipeline.

// List of port numbers from all production services
let prod_ports = query(service | .env == "production" | .port)

// List of names from all services
let names = query(service | .name)

Without a projection, query() returns a list(block_ref). With a projection, it returns a list(value).

Result Types

PipelineReturn type
query(service)list(block_ref)
query(service | .port)list(value) (the projected attribute values)

Aggregate Functions

Aggregate functions operate on query results:

FunctionDescription
len(query(...))Number of matched items
sum(query(... | .attr))Sum of numeric projection
avg(query(... | .attr))Average of numeric projection
distinct(query(... | .attr))Deduplicated list of projected values
let service_count  = len(query(service))
let total_workers  = sum(query(service | .workers))
let avg_port       = avg(query(service | .port))
let all_envs       = distinct(query(service | .env))

Using Queries in Validation

validation "all-prod-services-have-tls" {
    let prod = query(service | .env == "production")
    check   = every(prod, s => has(s.tls))
    message = "all production services must have TLS configured"
}

validation "unique-ports" {
    let ports = query(service | .port)
    check   = len(ports) == len(distinct(ports))
    message = "each service must use a unique port"
}

CLI Usage

The wcl query subcommand runs a pipeline against a file from the command line:

wcl query file.wcl 'service'
wcl query file.wcl 'service | .env == "prod" | .port'
wcl query file.wcl 'service | has(@tls)'

Output Formats

FlagOutput
--format jsonJSON array
--format textOne item per line (default)
--format csvComma-separated values
--format wclWCL block representation

Additional Flags

FlagDescription
--countPrint the number of results instead of the results themselves
--recursiveDescend into imported files

Examples

# Count production services
wcl query --count infra.wcl 'service | .env == "production"'

# Export port list as JSON
wcl query --format json infra.wcl 'service | .env == "prod" | .port'

# Find all services without TLS
wcl query infra.wcl 'service | !has(@tls)'

# Recursively find all health_check blocks across imports
wcl query --recursive infra.wcl '..health_check'