jevql docs
playgroundGitHub ↗

SQL surface

Every jev function, its arguments, and where it may appear in a statement.

All functions take a row source as their first argument and a question as their second.

Row sources

Form Sends
jev(people, ...) every column of the people relation (or alias) in FROM
jev(p, ...) same, for FROM people p
jev((name, bio), ...) only those expressions, as a JSON object keyed by column name

Alias form refuses tables with bytea columns unless you narrow the columns with the list form or --columns name,bio. NULLs are sent as null. Timestamps are RFC 3339 in UTC.

Functions

Call Returns
jev(src, 'condition') boolean, p >= threshold
jev(src, 'condition', 0.7) boolean with an explicit threshold
jev_prob(src, 'condition') float8 in 0..1, the calibrated probability
jev_choice(src, 'question', ARRAY['a','b']) text, the chosen option
jev_score(src, 'question', ARRAY['lo','mid','hi']) float8, weighted index into the levels
jev_score_norm(src, 'question', ARRAY[...]) float8 in 0..1
jev_confidence(src, 'q' [, 'noul' | 'choice' | 'score', ARRAY[...]]) float8
jev_eval(src, 'q' [, kind, ARRAY[...]]) the raw answer as JSON

Threshold precedence: explicit third argument, then --threshold, then JEV_THRESHOLD, then 0.5.

Calls that share a row source, question and options are judged once. jev(p, 'x') and jev_prob(p, 'x') in the same statement cost one judgement per row.

Where a call may appear

WHERE. jev() as an AND term, including NOT jev():

WHERE jev(t, 'is urgent') AND status = 'open'
WHERE NOT jev(e, 'is a newsletter') AND received > now() - interval '1 day'

SELECT list. Any function, as the whole expression, with or without an alias:

SELECT subject, jev_choice(t, 'which team?', ARRAY['billing','technical']) AS team FROM tickets t

ORDER BY. By alias, position or the expression itself:

ORDER BY p DESC        -- alias of jev_prob(...)
ORDER BY jev_prob(people, 'x')

GROUP BY. With count(*), count(x), sum, avg, min, max. Aggregation happens in the client, so sum and avg need numeric columns:

SELECT jev_choice(t, 'which team?', ARRAY['billing','technical','sales']) AS team, count(*)
FROM tickets t
WHERE status = 'open'
GROUP BY 1
ORDER BY 2 DESC;

Joins. Judge one or several aliases:

SELECT p.name, c.country
FROM people p JOIN cities c ON c.name = p.city
WHERE jev(p, 'could work from home') AND c.country = 'PT';

What is rejected, on purpose

jevql errors with a specific message rather than guessing:

Statement Why
jev_* in INSERT, UPDATE, DELETE, DDL only SELECT is supported
jev_* inside a CTE or subquery run the inner query with jevql instead
HAVING jev_*(...), or any HAVING with jev present not in v1
window functions over jev_* not in v1
... OR jev(...) every row would need judging; split the query
jev_prob(...) > 0.7 in WHERE write jev(src, 'condition', 0.7)
round(jev_prob(...), 2) jev must be the whole SELECT expression
SELECT DISTINCT, UNION, FETCH ... WITH TIES not in v1
alias-form on a table with bytea narrow the columns first

Pushdown rules

ORDER BY, LIMIT and OFFSET are sent to Postgres only when the collected rows are exactly the final rows: no jev predicate in WHERE, no GROUP BY, and no jev sort key. Then LIMIT 10 judges ten rows. Otherwise they run in the client after judging, and --max-rows caps the collected set.

Edit this page on GitHub ↗