Pipelines
A pipeline starts at a collection and threads stages with |>.
users
|> filter active == true
|> select { name, email, balance }Studio tip: type | after a complete segment — it expands to |> and opens the stage menu (you rarely type the digraph by hand).
Stages
filter
Keep rows where the predicate is Bool.
users |> filter status == .paid && balance >= 50.00$usdselect
Project fields or paths. After a join, nested paths work:
orders |> join listing |> select { listing.title, total, placed_at }join
Follow a ref field (inner join). There is no SQL-style multi-collection join syntax — the right-hand side is the ref name on the left row.
orders |> join listingsort / take / skip
users |> sort balance desc |> take 10 |> skip 0Sort is stable. take / skip expect Int.
group … aggregate
users |> group status aggregate { n: count(), balance: sum(balance) }Aggregates: count(), sum(expr), min(expr), max(expr), avg(expr).
distinct
users |> select { status } |> distinctasof
Pin reads to the nearest named snapshot at or before the given time (create snapshots first — Studio and /ops can).
listings |> asof now() - 2d |> select { title, stock }Raw page-version history (without a named snapshot) is not wired yet.
Mutations
users |> insert { name: "alice", email: "a@x.com", active: true, balance: 49.99$usd, status: .paid }
users |> filter email == "a@x.com" |> update { status: .paid }
users |> filter active == false |> delete- Missing
idon insert → executor assigns a ULID. deletetombstones the row.- Mutations return affected rows and can compose with later stages.
atomic
See Transactions.
Parameters
Typed holes ${name} are filled by the host (named-query params or executor bindings):
users |> filter balance >= ${min_balance} |> select { name, balance }Sources
| Source | Status |
|---|---|
collection | Executes — current tenant |
across tenants(pred) collection | Parses only |
Worked scene
examples/storefront.eel shows checkout atomics, seller dashboard pipelines, asof, and a tenant fork in one file.