Skip to content

Pipelines

Operator entries live in the same data.sources: map as leaf sources, each with a type:. An operator reads one or more other entries (leaf or operator) and transforms their output. Components and wrangl address an operator by name exactly as they would a leaf, so a source can be reshaped or swapped without touching what consumes it.

Operator Reads Does Streams
passthrough 1 gives an entry a second, stable name yes
filter 1 drops items not matching a predicate yes
project 1 rebuilds each item from declared keys yes
derive 1 adds computed fields to each item yes
sort 1 reorders items no
union N concatenates homogeneous lists, tagging rows when every child streams
compose N bundles different shapes into one object no
join 1 + lookups enriches each row with per-row fetches no
cache 1 holds one snapshot for a TTL passes through

Expressions (where, keep, compute, by, on) use the expression language described in templating.md. Each item's top-level fields are in scope unprefixed (status.phase); for a plain text line the line is item.

Names starting with _ are hidden from wrangl --list (--all shows them) but stay usable everywhere. Inline pipe: stages use this for the intermediate entries they create.

Validation: every name an operator reads must be defined, the graph must have no cycles, and no operator may read a windowed source.

Fields shared by operators

Field Type Default Valid on/with What it does
type string — required passthrough, filter, project, derive, sort, union, compose, join or cache.
from string — passthrough, filter, project, derive, sort, cache The entry this operator reads.
parameters map — — Typed inputs, available in this operator's expressions as params.NAME. See parameters.
pipe list — passthrough only Further stages applied to this entry's output. Other operators chain with from: instead. See pipe.

passthrough

Delegates unchanged. Useful as a stable public name in front of an entry you expect to swap, and as the carrier for a pipe: chain.

pods:
  type: passthrough
  from: _pods_raw

filter

Keeps items for which where is true. On a single value, returns it or nothing. On a stream, drops events that don't match.

Field Type Default Valid on/with What it does
where expr — required The predicate. A missing field evaluates to nil, so metadata.namespace == 'default' is simply false on an item without metadata.
running_pods:
  type: filter
  from: pods
  where: "status.phase == 'Running'"

errors_only:
  type: filter
  from: app_logs          # a streamed text source
  where: "item contains 'ERROR'"

project

Rebuilds each item with only the keys in keep. The left side is the output key and the right side an expression, so one entry can rename, flatten and compute. Non-map items are dropped.

Field Type Default Valid on/with What it does
keep map of expr — required Output key → expression over the input item.
user_summary:
  type: project
  from: users
  keep:
    username: username
    city: address.city
    domain: "lower(website)"

derive

Copies each item and adds the fields in compute. Every original field survives; a computed key that already exists replaces it. Non-map items pass through.

Field Type Default Valid on/with What it does
compute map of expr — required New field → expression over the item.
labelled_users:
  type: derive
  from: users
  compute:
    is_biz: "hasSuffix(website, '.biz')"
    name_len: "len(username)"

sort

Reorders a list by a key expression. Stable on ties. Numbers compare numerically, strings lexically, false before true, times chronologically; mixed types fall back to their string form; missing keys sort first in ascending order. A non-list passes through. Not available on streams.

Field Type Default Valid on/with What it does
by expr — required Evaluated per item to produce its sort key.
order string asc — asc or desc.
by_restarts:
  type: sort
  from: pods
  by: "status.containerStatuses[0].restartCount"
  order: desc

union

Concatenates several entries' lists into one, tagging each row with where it came from. Like the merge source, but its children can be operators as well as leaves, so each one can be filtered or reshaped first.

Field Type Default Valid on/with What it does
sources list of string — not with children Child entries, by name.
tag_field string — sources Each row gets a tag under this key whose value is the child's name.
children list — not with sources Children with per-child tags (source, tags), as on merge.
meta_key string _meta — Where tags are written on each row. "" writes them at the top level.
on_error string fail — fail aborts on any child error; skip drops failed children.
all_running:
  type: union
  sources: [prod_running, staging_running]
  tag_field: cluster
  on_error: skip

compose

Bundles several entries into one object, one key per entry, keeping their different shapes apart. Children are fetched in parallel.

Field Type Default Valid on/with What it does
parts map of string — required Output key → entry name.
on_error string fail — fail aborts on any child error; skip leaves that key out (and errors only if every child fails).
fleet:
  type: compose
  parts:
    pods: pods_all
    deployments: deployments_all
  # → {pods: [...], deployments: [...]}

join

For each row of a driver list, runs one or more lookup sources with parameters computed from that row, and attaches the results. Lookups run in parallel per row and are cached by parameter values (see cache).

Field Type Default Valid on/with What it does
driver map — required from: the entry whose rows drive the join.
lookups map — required, at least one Output name → lookup. Each lookup has from (a leaf source that declares parameters:) and on (that source's parameter → expression over the driver row).
emit string separate — separate gives {row: <driver row>, <lookup>: <result>, …} per row; merged flattens lookup fields into the row (both must be maps; colliding keys become <lookup>_<key>).
on_error string fail — fail aborts the join on any lookup error; skip drops that row.

Validation: each lookup's from is a leaf source with parameters:, and every on: key is one of its parameters.

users_with_posts:
  type: join
  driver: {from: users}
  lookups:
    posts:
      from: user_posts     # declares parameters: user_id
      on: {user_id: id}
  emit: separate

cache

Holds one snapshot of an entry for a TTL, so several consumers of the same upstream within the window cause one fetch. Errors aren't cached. Streams pass through. For per-parameter caching on a leaf source, use the cache: field.

Field Type Default Valid on/with What it does
ttl duration — required How long the snapshot stays fresh.
cached_pods:
  type: cache
  from: pods
  ttl: 30s

Parameters

An operator can declare parameters: (same fields as source parameters). Bound values are available in its expressions as params.NAME, separate from the item's fields. They apply only to that operator's own expressions and are not forwarded to the entries it reads. wrangl --param binds an operator's parameters when it declares some, and otherwise passes through to the underlying source.

long_usernames:
  type: filter
  from: users
  parameters:
    min: {type: int, default: "8"}
  where: "len(username) >= int(params.min)"

pipe

A chain of single-input stages applied to an entry's output, without naming each step. Each stage is a filter, project, derive, sort or cache entry written without from: (its input is the previous stage).

Field Type Default Valid on/with What it does
pipe list — leaf kinds except merge; passthrough The stages, in order.

At load, a leaf with pipe: becomes a hidden _NAME_raw entry plus the chain, and the chain's final stage takes the original name. Intermediate stages are hidden _NAME_stepN entries. parameters: on the leaf still bind to the raw fetch.

Validation: pipe: is only accepted on a leaf or a passthrough; on any other operator it fails to load, so chain those with from:. Stages have a type: from the five allowed and no from:. Multi-input operators (union, compose, join) can't be stages; declare them as named entries instead.

biz_users:
  type: http
  url: https://jsonplaceholder.typicode.com/users
  pipe:
    - {type: filter, where: "hasSuffix(website, '.biz')"}
    - {type: project, keep: {name: name, site: website}}
    - {type: sort, by: name}