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
Stable sort by a field. Optional asc (default) or desc.
users |> sort balance desctake / skip
users |> sort balance desc |> take 10 |> skip 0take / skip expect Int.
group … aggregate
users |> group status aggregate { n: count(), balance: sum(balance) }Multiple keys are allowed:
orders |> group status, listing aggregate { n: count(), total: sum(total) }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 — see Status.
insert
users |> insert { name: "alice", email: "a@x.com", active: true, balance: 49.99$usd, status: .paid }Missing id → executor assigns a ULID.
update
users |> filter email == "a@x.com" |> update { status: .paid }delete
users |> filter active == false |> deletedelete tombstones the row. Mutations return affected rows and can compose with later stages.
atomic
Tenant-scoped transactions — see Transactions.
atomic {
listings |> filter id == ${lid} && stock > 0 |> update { stock: stock - 1 }
orders |> insert { listing: ${lid}, buyer_ref: ${bref}, total: 129.00$, placed_at: now(), status: .pending }
}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 — not executed |
Operators
- Equality:
==!= - Ordering:
<<=>>=(Int, Float, Text, Id, Time, Duration, Money) - Boolean:
&&||! - Arithmetic:
+-— Time ± Duration → Time; Time − Time → Duration; Money ± Money (same currency) - Multiply / divide:
*/(Int / Float only)
Cross-currency Money arithmetic and comparison are type errors.
Builtins
| Call | Result |
|---|---|
now() | Time — wall clock (injectable in tests / wasm) |
count() | Int — aggregate |
sum(expr) | Same type as expr |
min(expr) / max(expr) | Same type as expr |
avg(expr) | Money stays Money; Int/Float → Float |
Worked scene
examples/storefront.eel shows checkout atomics, seller dashboard pipelines, asof, and a tenant fork in one file.