where - the free filter¶
Keep rows matching a deterministic predicate. Free - where never calls a
model. Streams, preserves input order, and emits matching rows byte-for-byte.
tail -f app.log \
| smartpipe where 'text has "ERROR"'
cat orders.jsonl \
| smartpipe where 'total > 1000'
Rule of thumb: where for facts, filter for judgment - the
filter-early habit is Learn chapter 4.
The predicate language¶
| Form | Meaning |
|---|---|
FIELD has "word" |
word-bounded match, case-insensitive |
FIELD contains "text" |
substring, case-insensitive |
FIELD matches /re/ |
Python regex search, case-sensitive |
FIELD == VALUE / != |
numeric when both sides are numbers, temporal when both are ISO dates/datetimes, else exact string |
FIELD > >= < <= |
numeric, or temporal when both sides are ISO dates/datetimes - anything else never matches (and is disclosed) |
and · or · not · ( ) |
not binds tightest, then and, then or |
FIELD is a record field name, or text for the whole line (on plain-text
input, text is the only field there is). It can also be a
field path
into nested records - where 'user.plan has "pro" and items[0].total >= 100' -
where a literal flat column named user.plan wins first and a path miss
counts as an ordinary missing field. Booleans compare by their JSON
spelling: pass == true. Temporal comparison (due >= "2026-01-01") kicks
in when both sides read as ISO dates/datetimes - a bare date counts as its
midnight, so a {due date} field extracted upstream compares as time, not
text.
Missing fields¶
Missing fields evaluate false - streams keep flowing - but the run ends with a disclosure noting which fields were missing:
where: 2,114 of 104,882 matched
note: field 'level' missing on 12,004 rows
Zero matches exits 0 (an empty result is a valid result, like filter).
A predicate the grammar can't parse exits 64 with the full operator menu,
before any input is read.