use jetro::Jetro;
let j = Jetro::from_bytes(bytes)?;
let out = j.collect(
"$.orders
.map({ id: @.id, total: @.items.map(@.price * @.qty).sum() })
.filter(@.total > 100)
.map(@.id)
.first()"
)?;
In many query languages, this kind of chain naturally becomes: parse/materialize all orders
compute totals for every order
filter all shaped orders
extract all ids
then take the first one
Jetro tries to read the chain from the end backward and ask what is
actually needed. Then elements stream through the pipeline only until that need is satisfied: need one id
<- need one matching order
<- pull orders one at a time
<- compute total for the current candidate
<- if it matches, emit the id and stop
So the pipeline is not "finish every stage, then move to the next." It is more like:
pull one item, shape it, test it, maybe emit it, and stop as soon as the query has enough. let out = j.collect(
"{
errors: $.events
.drop_while(@.level != 'error')
.filter(@.service == 'checkout')
.map(match @ with {
{ level: 'error', message: msg, timestamp: ts } -> {
kind: 'error',
ts: ts,
msg: msg
},
{ level: 'warn', message: msg } -> {
kind: 'warning',
msg: msg
},
_ -> {
kind: 'other'
}
})
.take(20),
slow_orders: $.orders
.filter(@.latency_ms > 500)
.map({ id: @.id, latency: @.latency_ms })
.take(10),
first_vip: $.customers
.filter(@.tier == 'vip')
.map({ id: @.id, region: @.region })
.first()
}"
)?;
There’s also jetrocli for terminal, a book for learning the language.