Query Language
A complete reference for the Pomavo query DSL, covering filtering, sorting, aggregation, references, date math, and bulk mutations with worked examples.
Overview
The Pomavo query language, or DSL, is a structured way to find and act on tickets. The same language powers Search, Saved Queries, reports, and bulk mutations. A query reads left to right as a filter, optionally followed by sorting, grouping/projection, or a mutation clause.
template = "Bug" and status != "Done"
order by created_at descThe same editor with autocompletion is available everywhere the DSL is accepted, and these examples render with the exact highlighting you see in the product.
Anatomy of a Query
A query is built from a few kinds of pieces:
- Fields: what you filter on. Built-in fields like
status,template,assignee,created_at, or any custom field by name. - Operators: how you compare, including
=,contains,in,between, and more. - Values: strings (
"In Progress"), numbers (5), dates (d'today'), references (@me), or another field (quot;Story Points"). - Clauses:
order by,group by,return,limit/offset, and mutation clauses.
priority = "High" and assignee = @me
order by created_at desc
limit 25Field Names
Reference a field by its name. Names with spaces or special characters must be quoted:
status = "In Progress" and "Story Points" >= 5Field names are case-insensitive. Built-in fields such as status, template, created_at, assignee, and id are recognized everywhere.
Comparison Operators
| Operator | Meaning |
|---|---|
= | Equal to |
!= | Not equal to |
> >= < <= | Greater / less than for numbers and dates |
between ... and ... | Inclusive range |
priority = "High""Story Points" >= 3 and "Story Points" <= 8created_at between d'-30d' and d'today'Text Matching
| Operator | Matches |
|---|---|
contains / not contains | Substring anywhere in the value |
starts with | Value begins with the text |
ends with | Value ends with the text |
like | Wildcard match: * matches any run and ? matches a single character |
title contains "login" and description not contains "duplicate"title starts with "[URGENT]""Branch Name" like "release/*"Set Membership
status in ("To Do", "In Progress", "In Review")assignee not in (@me, @alice, @bob)Presence
Check whether a field has a value at all:
assignee is empty"Due Date" is not empty and status != "Done"Logical Operators & Grouping
Combine conditions with and, or, and not. Use parentheses to control precedence:
(template = "Bug" or template = "Incident") and assignee = @me and status not in ("Done", "Closed")not ("Due Date" is empty) and priority in ("High", "Critical")References
Instead of a literal value, you can reference a user or another field:
@me: the current user, resolved at query time, which is great for saved queries shared across a team@username: a specific user by username$fieldorquot;Field Name": the value of another field on the same ticket
assignee = @me and author != @me"Actual Hours" > quot;Estimated Hours"Date Literals
Dates are written with the d'...' form:
- Absolute:
d'2025-01-15' - Keywords:
d'today',d'now' - Relative:
d'-7d'for seven days ago,d'now-1w',d'2y3m4d'to combineyears,months,weeks, anddays
created_at >= d'-7d' and created_at < d'today'"Due Date" between d'today' and d'now+2w'Sorting & Pagination
status = "In Progress"
order by priority desc, created_at asc
limit 50
offset 100order by <field> [asc|desc]: sort by one or more fieldsorder by <field> numeric: sort a text field as numbersorder by <field> custom(a, b, c): sort by an explicit order, e.g. a workflow sequencelimit N/offset N: page through results
status
order by status custom("To Do", "In Progress", "In Review", "Done")Aggregation & Projection
Go beyond filtering to compute summaries with group by and return:
status != "Done"
group by assignee
return assignee, count() as open_tickets
order by open_tickets descgroup by <field>[, <field>]: bucket tickets by one or more fieldsreturn <expr> [as <alias>][, ...]: choose each row's output; supports arithmetic (+ - * /) and functions
group by template
return template, count() as total, avg(quot;Story Points") as avg_pointsFunctions
| Function | Returns |
|---|---|
now() | The current date and time |
today() | The current date |
startOfDay(date) | The start of the given day |
startOfWeek(date) | The start of the given week |
length(value) | The length of a value |
count() / count(expr) | Number of tickets in a group |
sum(expr) | Sum of a numeric expression |
avg(expr) | Average of a numeric expression |
min(expr) / max(expr) | Minimum / maximum of an expression |
group by assignee
return assignee, sum(quot;Story Points") as committed, max(created_at) as latestComments
Annotate long queries with # or -- line comments:
# Open, unassigned bugs raised this week
template = "Bug" and assignee is empty and created_at >= d'-7d' -- last 7 days onlyMore Worked Examples
A grab-bag of everyday queries:
# Everything assigned to me that isn't finished
assignee = @me and status not in ("Done", "Closed")
order by priority desc# Stale tickets: untouched for over 30 days and still open
status not in ("Done", "Closed") and updated_at < d'-30d'
order by updated_at asc# High-priority work due this week
priority in ("High", "Critical") and "Due Date" between d'today' and d'now+1w'# Bugs whose estimate was exceeded
template = "Bug" and "Actual Hours" > quot;Estimated Hours"# Workload by assignee for the current sprint
"Sprint" = "Sprint 24"
group by assignee
return assignee, count() as tickets, sum(quot;Story Points") as points
order by points descBulk Mutations
The DSL can also change the tickets a query matches. A filter selects the tickets; a mutation clause applies the change to all of them. Mutations run as a background job, so large updates do not block the UI.
Mutations apply to every ticket the filter matches. Run the query as a plain search first to confirm the result set before turning it into a mutation.
| Clause | Effect |
|---|---|
set <field> = <value> | Update field values on matched tickets |
create <template> set ... | Create a new ticket from a template |
link <value> to (<query>) | Link matched tickets to the tickets in a sub-query |
unlink <value> / unlink all | Remove links |
comment <value> | Add a comment to matched tickets |
Updating Fields
Append a set clause to any filter to update the matches:
status = "Done" and resolution is empty
set resolution = "Fixed"Set multiple fields at once, and compute values from existing ones:
template = "Bug" and priority = "Critical"
set status = "In Progress", assignee = @me"Sprint" = "Sprint 23" and status != "Done"
set "Sprint" = "Sprint 24"Creating Tickets
create "Bug"
set title = "Investigate flaky checkout test", priority = "High", assignee = @meLinking & Unlinking
Link every matched ticket to the tickets returned by a sub-query:
template = "Bug" and "Sprint" = "Sprint 24"
link "blocks" to (template = "Release" and title = "v2.0")status = "Closed"
unlink allCommenting in Bulk
status = "In Review" and updated_at < d'-3d'
comment "Friendly nudge: this has been waiting for review for over 3 days."Where to Next
- Search & Saved Queries: run the DSL from the UI and save frequent queries
- Search Internals: how queries are parsed and executed