Definição
A árvore completa da definição de um filtro de eventos — blocos de consulta, projeção, filtros de coluna, de registro e de tabela relacionada, janela temporal e agregação.
O que é esta página
A definição de um filtro de eventos é a árvore que descreve quais eventos ele seleciona. É idêntica em todos os endpoints que a recebem ou a retornam, então está documentada uma única vez aqui:
| Endpoint | Onde a definição aparece |
|---|---|
POST /event-criterias | No corpo, sob queryBlocks |
PATCH /event-criterias/:id | No corpo, sob queryBlocks (opcional) |
GET /event-criterias/:id | Na resposta, sob queryBlocks |
POST /generate-sql | No corpo, sob queryBlocks |
POST /validate-from-body | No corpo, sob queryBlocks |
POST /preview-from-body | No corpo, sob queryBlocks |
Os nomes de tabelas e colunas que aparecem nos exemplos vêm do catálogo, que é a fonte do que pode ser filtrado.
queryBlocks[] — bloco de consulta
Mínimo 1 elemento. Cada bloco (QueryBlockDto) é um statement SQL completo; os blocos
se combinam entre si via setOperator.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
order | number (inteiro) | Sim | Posição do bloco; inteiro ≥ 0. Único dentro do filtro. |
setOperator | SetOperatorEnum | Não | Combina este bloco com o anterior. Ausente no bloco order = 0; obrigatório nos demais. |
from | FromDto | Sim | Fonte de dados: tabela principal + joins. |
select | SelectDto | Sim | Projeção (colunas e/ou agregações). |
timeWindow | TimeWindowDto | Não | Janela temporal do bloco (união discriminada por timeOperator). |
where | WhereDto | Sim | Árvore de filtros. Obrigatório mesmo que seja um grupo vazio. |
groupBy | ColumnRefDto[] | Não | Colunas de agrupamento. |
having | HavingDto | Não | Filtro sobre agregados. |
Um filtro de eventos não se compõe com outros filtros: não existe composedWith. O
setOperator de um bloco combina blocos dentro do mesmo filtro, e é o único nível
de operação de conjunto que existe.
ColumnRefDto — referência a coluna
Usado em toda a árvore (select, filtros de coluna, group by, joins, agregados, janela temporal):
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
table | string | Sim | Tabela do catálogo. Não vazio. |
column | string | Sim | Coluna. Não vazio. Pode ser um caminho STRUCT não repetido (ex.: "pricing.final_amount"). |
Toda coluna é sempre qualificada com sua table para evitar ambiguidade. A exceção são as
sub-árvores relativas —o interior de um filtro de registro ou de um filtro de tabela
relacionada—, onde o operando é um field solto sem tabela (ver
Sub-árvore relativa).
from — fonte de dados (FromDto)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
table | string | Sim | Tabela principal do catálogo. Não vazio. |
joins | JoinDto[] | Não | Cruzamentos com outras tabelas. Padrão []. |
Cada JoinDto:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
type | JoinTypeEnum | Sim | INNER | LEFT | RIGHT | FULL. |
table | string | Sim | Tabela a unir (do catálogo). Não vazio. |
on | JoinPredicateDto[] | Sim | Condições de junção. |
Cada JoinPredicateDto é um par de colunas: { left: ColumnRefDto, right: ColumnRefDto }.
Ambos os lados devem ser colunas escalares.
Somente as tabelas que
GET /tables retorna podem participar do
from. A tabela de destino de um semi-join não é uma tabela do catálogo: não entra
no from nem é destino de um join, e só é alcançada com um
filtro de tabela relacionada.
select — projeção (SelectDto)
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
distinct | boolean | Sim | Aplica SELECT DISTINCT. |
items | (SelectColumnItem | SelectAggregateItem)[] | Sim | Não vazio. União discriminada pelo campo kind. |
Os items são discriminados por kind:
-
Coluna —
kind: "column":Campo Tipo Obrigatório Descrição kind"column"Sim Discriminador. columnColumnRefDtoSim Coluna projetada. Deve ser escalar. aliasstring Não Deve corresponder a ^[A-Za-z_][A-Za-z0-9_]*$. -
Agregado —
kind: "aggregate":Campo Tipo Obrigatório Descrição kind"aggregate"Sim Discriminador. aggregateAggregateDtoSim Expressão de agregação. aliasstring Sim Aqui o alias é obrigatório. Mesmo regex de identificador.
AggregateDto:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
aggregationOperator | AggregationOperatorEnum | Sim | COUNT | SUM | AVG | MIN | MAX. |
operand | object | Sim | O que é agregado. União discriminada por kind: column, star ou arithmetic (veja abaixo). |
distinct | boolean | Não | Aplica DISTINCT dentro do agregado. COUNT(DISTINCT *) não é permitido. |
operand —o que é agregado— é discriminado por kind:
kind | Campos adicionais | Significado |
|---|---|---|
column | column: ColumnRefDto | Uma coluna do catálogo. |
star | (nenhum) | *: conta linhas. Só é válido com COUNT. |
arithmetic | arithmeticOperator: ArithmeticOperatorEnum, left, right | Expressão binária. Cada lado é outro operando: uma column ou um arithmetic aninhado (nunca star). |
ArithmeticOperatorEnum: ADD | SUBTRACT | MULTIPLY | SAFE_DIVIDE (uma divisão que retorna
NULL quando o denominador é zero, em vez de fazer a consulta falhar). As colunas de uma expressão
aritmética precisam ser numéricas (INT64, NUMERIC, BIGNUMERIC, FLOAT64), e as expressões se
aninham até 3 níveis de arithmetic, contando o externo.
Uma coluna RECORD REPEATED (attributes, items) não pode ser projetada, agrupada,
agregada nem usada no ON de um join: não se compara por si mesma. Tentar isso responde
400 com La columna "<tabla>.<columna>" es una columna repetida y no puede usarse en <contexto>.
Seu lugar é o filtro de registro.
where — árvore de filtros (WhereDto)
O where é uma árvore recursiva. O nó raiz é sempre um grupo, e suas folhas são de
três tipos:
kind | O que filtra |
|---|---|
group | Não é uma folha: agrupa filhos com AND / OR. Grupos podem ser aninhados. |
predicate | Uma coluna escalar de uma tabela do from. |
recordPredicate | Uma coluna RECORD REPEATED: existe ≥1 elemento que cumpre o sub-filtro. |
semiJoinPredicate | Um semi-join do catálogo: existe ≥1 correspondência na tabela relacionada. |
Grupo — kind: "group"
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
kind | "group" | Sim | Discriminador. |
logicalOperator | LogicalOperatorEnum (AND | OR) | Sim | Conectivo entre os filhos. |
children | (Group | Leaf)[] | Sim | Filhos do grupo; podem ser grupos aninhados. |
logicalOperator é sempre obrigatório em um grupo, mas só tem efeito com 2 ou mais
filhos. Com menos de 2 filhos seu valor é irrelevante (gera o mesmo SQL), mas ainda
assim deve ser enviado. Um where sem filtros é expresso como um grupo vazio:
{ "kind": "group", "logicalOperator": "AND", "children": [] }.
Folha de coluna — kind: "predicate"
Compara uma coluna escalar qualificada por sua tabela.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
kind | "predicate" | Sim | Discriminador. |
column | ColumnRefDto | Sim | Coluna do predicado. |
comparisonOperator | ComparisonOperatorEnum | Sim | Operador de comparação. |
value | PredicateValue | Depende | Seu formato depende do operador. |
{
"kind": "predicate",
"column": { "table": "event_transactions", "column": "status" },
"comparisonOperator": "EQUALS",
"value": "Facturada"
}Folha de registro — kind: "recordPredicate"
Filtra por uma coluna RECORD REPEATED (um array de structs, como attributes ou
items). Significa existe ao menos um elemento do array que cumpre o sub-filtro.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
kind | "recordPredicate" | Sim | Discriminador. |
column | ColumnRefDto | Sim | Coluna RECORD REPEATED de uma tabela do from. |
subWhere | Sub-árvore relativa | Sim | Condições sobre os sub-campos do elemento. Não pode ficar vazio. |
{
"kind": "recordPredicate",
"column": { "table": "event_transactions", "column": "attributes" },
"subWhere": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{ "kind": "predicate", "field": "key", "comparisonOperator": "EQUALS", "value": "documento_tipo" },
{ "kind": "predicate", "field": "value", "comparisonOperator": "EQUALS", "value": "factura" }
]
}
}As condições do subWhere são avaliadas sobre um mesmo elemento do array, não sobre
o array inteiro. O exemplo acima seleciona eventos que têm um atributo cuja key é
documento_tipo e cujo value é factura — não eventos que tenham um atributo com
essa key e outro, diferente, com esse value.
Folha de tabela relacionada — kind: "semiJoinPredicate"
Filtra os eventos pela existência de uma correspondência em outra tabela, seguindo um semi-join que o catálogo declara. O caminho entre as duas tabelas é resolvido pelo catálogo: a requisição só nomeia a tabela dona e o semi-join.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
kind | "semiJoinPredicate" | Sim | Discriminador. |
table | string | Sim | Tabela dona do semi-join. Deve participar do from do bloco. |
semiJoin | string | Sim | Nome do semi-join que essa tabela declara no catálogo. |
subWhere | Sub-árvore relativa | Sim | Condições sobre os campos da tabela relacionada. Não pode ficar vazio. |
{
"kind": "semiJoinPredicate",
"table": "event_transactions",
"semiJoin": "products",
"subWhere": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{
"kind": "predicate",
"field": "categorization.category_name",
"comparisonOperator": "IN",
"value": ["Food Service", "HORECA"]
}
]
}
}Um semi-join não pode ser aninhado dentro de outro: o subWhere de uma folha de
tabela relacionada aceita folhas escalares e de registro, mas não outra folha
semiJoinPredicate.
Sub-árvore relativa
O subWhere de um filtro de registro e o de um filtro de tabela relacionada têm o mesmo
formato: um grupo AND/OR cujas folhas são resolvidas contra os campos que o escopo
declara —os sub-campos do registro, ou os campos da tabela relacionada.
A diferença com a árvore de primeiro nível é uma só, e é a fonte de erro mais comum:
Dentro de uma sub-árvore relativa os operandos não levam tabela: escreve-se
"field": "item_id", não "column": { "table": "items", "column": "item_id" }. A folha
usa a chave field (uma string), não column.
Folhas aceitas dentro de uma sub-árvore relativa:
- Folha escalar —
{ "kind": "predicate", "field": string, "comparisonOperator": …, "value"?: … }. Ofielddeve ser um sub-campo escalar do escopo. - Registro aninhado —
{ "kind": "recordPredicate", "field": string, "subWhere": … }. Um registro dentro de outro registro (por exemploitems[].attributes[]); aninha-se sem limite declarado, desde que o catálogo declare cada nível. - Grupo — o mesmo formato de grupo, para combinar as folhas anteriores com
AND/OR.
Formato de value conforme comparisonOperator
| Operadores | Formato de value | Exemplo |
|---|---|---|
IS_NULL, IS_NOT_NULL | Sem value (omitido) | (omitido) |
IN, NOT_IN | Lista não vazia ScalarValue[] | ["Food Service", "HORECA"] |
BETWEEN | Par [min, max] | [10, 20] |
O resto (EQUALS, NOT_EQUALS, GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH) | Escalar único | "Facturada" · 42 |
ScalarValue = string | number | boolean. A regra vale igualmente para as folhas
de coluna e para as folhas escalares de uma sub-árvore relativa.
Operadores aceitos por tipo de coluna
Cada operador deve, além disso, ser compatível com o tipo da coluna que o catálogo
declara. Um operador fora desta tabela responde 400 com
Operator <op> is not allowed for <type> operands.
| Tipo de coluna | Operadores aceitos |
|---|---|
STRING | EQUALS · NOT_EQUALS · CONTAINS · NOT_CONTAINS · STARTS_WITH · ENDS_WITH · IN · NOT_IN · IS_NULL · IS_NOT_NULL |
BOOLEAN | EQUALS · NOT_EQUALS · IS_NULL · IS_NOT_NULL |
FLOAT64, INT64, NUMERIC, BIGNUMERIC | EQUALS · NOT_EQUALS · GREATER_THAN · LESS_THAN · GREATER_THAN_OR_EQUAL · LESS_THAN_OR_EQUAL · BETWEEN · IN · NOT_IN · IS_NULL · IS_NOT_NULL |
TIMESTAMP | EQUALS · NOT_EQUALS · GREATER_THAN · LESS_THAN · GREATER_THAN_OR_EQUAL · LESS_THAN_OR_EQUAL · BETWEEN · IS_NULL · IS_NOT_NULL |
O filtro por intervalo de datas não passa por estes operadores: é coberto pelo campo
timeWindow.
groupBy — colunas de agrupamento
Lista opcional de ColumnRefDto[]. Quando o bloco agrega (declara groupBy ou tem um
agregado no select), o groupBy deve corresponder exatamente às colunas não
agregadas do select.
having — filtro sobre agregados (HavingDto)
Mesma estrutura recursiva que o where —grupos AND/OR aninhados e a mesma regra de
value por operador—, mas a folha opera sobre um agregado, não sobre uma coluna:
- Grupo — idêntico ao do
where. - Folha —
{ "kind": "predicate", "aggregate": AggregateDto, "comparisonOperator": …, "value"?: … }.
having exige contexto de agregação: o bloco deve declarar groupBy ou ter um
agregado no select. Sem isso responde 400 com
HAVING requires an aggregation context (GROUP BY or an aggregate in the SELECT).
timeWindow — janela temporal (TimeWindowDto)
União discriminada por timeOperator. Restringe o bloco a um período de tempo sobre uma
coluna TIMESTAMP do catálogo (reference). É um campo irmão do where, não um nó da
árvore de filtros.
timeOperator | Campos adicionais |
|---|---|
ALL_TIME | (nenhum) — sem restrição de tempo. |
IN_THE_LAST | reference: ColumnRefDto, unit: TimeUnitEnum, value: inteiro > 0 |
SINCE | reference: ColumnRefDto, from: string (YYYY-MM-DD) |
BETWEEN | reference: ColumnRefDto, from: string (YYYY-MM-DD), to: string (YYYY-MM-DD) |
TimeUnitEnum: DAYS | WEEKS | MONTHS | YEARS.
Semântica de cada operador:
ALL_TIME— sem janela; considera todo o histórico.IN_THE_LAST— janela rolante relativa a "agora": os últimosvalueunit(ex.: últimos30 DAYS). Recalculada a cada execução.SINCE— a partir da data civilfrom(inclusive) em diante.BETWEEN— entrefrometo, ambas as datas civis inclusivas de seu dia completo.
IN_THE_FIRST é um membro válido de TimeOperatorEnum, mas não é suportado neste
recurso: mede a vida de um sujeito e um filtro de eventos não tem um. Enviá-lo responde
400 com
timeWindow IN_THE_FIRST is not supported: the catalog does not declare a lifetime origin.
As datas civis YYYY-MM-DD (SINCE, BETWEEN) são resolvidas para o início do dia no
fuso horário do tenant. O detalhe de fuso horário é interno; basta enviar a data
civil.
Regras de validação
A definição passa por duas camadas de validação, ambas retornando 400 Bad Request:
- Validação do DTO (estrutura, tipos, enums, campos obrigatórios).
messageé um array de strings e propriedades desconhecidas são rejeitadas. - Validação de negócio (a definição deve gerar SQL válido e coerente com o catálogo).
messageé uma string única com a mensagem da regra violada, e para no primeiro invariante violado.
Regras de negócio verificadas, com a mensagem exata da API:
Catálogo e referências
| Regra | Mensagem |
|---|---|
| A tabela deve pertencer ao catálogo. | Table "<t>" is not in the catalog |
Cada (table, column) deve pertencer ao catálogo. | Column "<t>.<c>" is not in the catalog |
Toda coluna referenciada deve pertencer a uma tabela do from. | Column "<t>.<c>" references a table that is not in the query block FROM |
| Uma coluna repetida não pode ser usada fora de um filtro de registro. | La columna "<t>.<c>" es una columna repetida y no puede usarse en <contexto> |
| Uma folha da árvore deve declarar seu operando. | Una hoja del filtro no declara una columna |
Projeção e agregação
| Regra | Mensagem |
|---|---|
O operando star (*) só é válido com COUNT. | '*' is only valid with COUNT, not <op> |
COUNT(DISTINCT *) não é permitido. | COUNT(DISTINCT *) is not allowed |
star não pode ser lado de uma expressão aritmética. | '*' sólo vale como operando directo de COUNT, no como lado de una expresión aritmética |
| As colunas de uma expressão aritmética precisam ser numéricas. | El operando "<t>.<c>" es de tipo <type>; una expresión aritmética sólo admite operandos numéricos (INT64, NUMERIC, BIGNUMERIC, FLOAT64) |
| Uma expressão aritmética se aninha até no máximo 3 níveis. | La expresión aritmética supera la profundidad máxima de 3 niveles |
Ao agregar, toda coluna não agregada do select deve estar no groupBy. | Column "<t>.<c>" must appear in GROUP BY when the operation aggregates |
Toda coluna do groupBy deve ser uma coluna não agregada do select. | Column "<t>.<c>" in GROUP BY must be a non-aggregated column of the SELECT |
having exige um contexto de agregação. | HAVING requires an aggregation context (GROUP BY or an aggregate in the SELECT) |
Blocos de consulta
| Regra | Mensagem |
|---|---|
| Deve haver ao menos um bloco. | A criteria must declare at least one operation (query block) |
Os valores de order são únicos. | Two operations declare the same order=<n>: each operation order must be unique |
O bloco order = 0 deve existir. | Missing the first operation: one operation must declare order=0 |
O primeiro bloco não leva setOperator. | First operation (order=0) must not have a setOperator |
Todo bloco posterior deve levar setOperator. | Operation with order=<n> must have a setOperator |
Operadores e valores
| Regra | Mensagem |
|---|---|
| O operador deve ser compatível com o tipo da coluna/agregado. | Operator <op> is not allowed for <type> operands |
Operador escalar com value ausente ou uma lista. | Operator <op> requires a single value |
IN/NOT_IN com value que não é lista ou está vazio. | Operator <op> requires a non-empty list of values |
BETWEEN sem exatamente dois valores. | Operator <op> requires exactly two values |
Filtros de registro e de tabela relacionada
| Regra | Mensagem |
|---|---|
| Um filtro de registro exige uma coluna repetida. | La columna "<t>.<c>" no es una columna repetida: no admite un filtro de registro |
| Um filtro de coluna exige uma coluna escalar. | La columna "<t>.<c>" es una columna repetida: no admite un filtro de columna |
| O sub-campo deve existir no registro. | El sub-campo "<f>" no existe en el registro |
| O sub-campo deve existir na tabela relacionada. | El sub-campo "<f>" no existe en la tabla relacionada "<t>" |
| Uma folha escalar não pode apontar para um sub-campo repetido. | El sub-campo "<f>" es una columna repetida: usá un filtro de registro anidado |
| Um registro aninhado exige um sub-campo repetido. | El sub-campo "<f>" no es una columna repetida |
| A sub-árvore de um filtro de registro não pode ficar vazia. | Un filtro de registro no puede quedar sin condiciones |
| A sub-árvore de um filtro de tabela relacionada não pode ficar vazia. | Un filtro sobre la tabla relacionada "<t>" no puede quedar sin condiciones |
A tabela dona do semi-join deve participar do from. | El semi-join "<s>" se declara sobre la tabla "<t>", que no participa del FROM del bloque |
| O semi-join deve estar declarado por essa tabela no catálogo. | El semi-join "<s>" no está declarado en la tabla "<t>" |
Janela temporal
| Regra | Mensagem |
|---|---|
A reference deve estar no catálogo. | timeWindow reference "<t>.<c>" is not in the catalog |
A reference deve ser uma coluna TIMESTAMP. | timeWindow reference "<t>.<c>" must be a TIMESTAMP column |
A reference não pode ser uma coluna repetida. | La referencia temporal "<t>.<c>" es una columna repetida y no puede anclar una ventana de tiempo |
value deve ser um inteiro positivo. | timeWindow value must be a positive integer |
unit deve ser um membro de TimeUnitEnum. | timeWindow unit "<u>" is not a valid time unit |
Em BETWEEN, from deve ser anterior ou igual a to. | timeWindow BETWEEN requires "from" to be on or before "to" |
| As datas devem ser datas de calendário válidas. | timeWindow date "<d>" is not a valid calendar date |
IN_THE_FIRST não é suportado. | timeWindow IN_THE_FIRST is not supported: the catalog does not declare a lifetime origin |