Search Internals
How the Pomavo query language is lexed, parsed, and translated into Elasticsearch queries and app-side aggregations.
Overview
Pomavo ships a query language that reads like a sentence but executes like a database query. This page explains the pipeline that turns a query string into results: lexing, parsing into an abstract syntax tree, translating into Elasticsearch, and computing aggregations and mutations on top.
The Pipeline
Lex
The raw query string is scanned into tokens, identifiers, operators, literals, keywords, dates, and mentions.
Parse
Tokens are parsed into an abstract syntax tree, or AST: a tree of comparisons, logical groupings, ordering, grouping, and projection clauses.
Translate
The AST is translated into an Elasticsearch query. Field comparisons become term/range queries; AND/OR/NOT become boolean clauses; text predicates become match/wildcard queries.
Aggregate / project
GROUP BY and RETURN clauses are computed by an aggregation engine after the matching documents come back, so the language can express arithmetic and functions that Elasticsearch alone cannot.
Modular Design
The language is a clean separation of concerns, each part living in its own module: the core lexer/parser/AST, the Elasticsearch translation layer, and the function library are independent of one another. The parser is store-agnostic, it produces an AST that the translation layer maps onto Elasticsearch term, range, match, and wildcard queries, while GROUP BY and RETURN are handled separately by the aggregation engine.
For the full operator, reference, date-math, and mutation surface, see the Query Language reference.
App-Side Aggregation
GROUP BY and RETURN are deliberately computed in the application rather than pushed entirely into Elasticsearch. This lets the language offer a consistent function library, including now, today, startOfDay, startOfWeek, length, count, sum, avg, min, and max, and arithmetic over projected values, independent of any single store's aggregation quirks. The matching/filtering work stays in Elasticsearch, where it scales; the shaping work happens over the result set.
Mutations
The same language can change data, not just read it. Mutation clauses such as SET, CREATE, LINK, UNLINK, and COMMENT are executed by a dedicated mutation executor that applies the change through the normal domain services, so every mutation respects permissions, multi-tenancy, and the same event/job pipeline as any other write. A bulk SET over a query, for example, updates each matching ticket and emits the corresponding events, which then index, record history, and fire automations like any manual edit would.
Mutations go through the full permission and event pipeline, they are not raw database writes. A query mutation is exactly as safe and as audited as doing the same edits by hand.
Saved Queries
Because a query is just text, it can be saved, shared, and re-evaluated live against the current index, powering filtered views and dashboard widgets. See Search & Saved Queries for the user-facing workflow.