Reten Docs
Filtros de eventos

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:

EndpointOnde a definição aparece
POST /event-criteriasNo corpo, sob queryBlocks
PATCH /event-criterias/:idNo corpo, sob queryBlocks (opcional)
GET /event-criterias/:idNa resposta, sob queryBlocks
POST /generate-sqlNo corpo, sob queryBlocks
POST /validate-from-bodyNo corpo, sob queryBlocks
POST /preview-from-bodyNo 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.

CampoTipoObrigatórioDescrição
ordernumber (inteiro)SimPosição do bloco; inteiro ≥ 0. Único dentro do filtro.
setOperatorSetOperatorEnumNãoCombina este bloco com o anterior. Ausente no bloco order = 0; obrigatório nos demais.
fromFromDtoSimFonte de dados: tabela principal + joins.
selectSelectDtoSimProjeção (colunas e/ou agregações).
timeWindowTimeWindowDtoNãoJanela temporal do bloco (união discriminada por timeOperator).
whereWhereDtoSimÁrvore de filtros. Obrigatório mesmo que seja um grupo vazio.
groupByColumnRefDto[]NãoColunas de agrupamento.
havingHavingDtoNãoFiltro 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):

CampoTipoObrigatórioDescrição
tablestringSimTabela do catálogo. Não vazio.
columnstringSimColuna. 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)

CampoTipoObrigatórioDescrição
tablestringSimTabela principal do catálogo. Não vazio.
joinsJoinDto[]NãoCruzamentos com outras tabelas. Padrão [].

Cada JoinDto:

CampoTipoObrigatórioDescrição
typeJoinTypeEnumSimINNER | LEFT | RIGHT | FULL.
tablestringSimTabela a unir (do catálogo). Não vazio.
onJoinPredicateDto[]SimCondiçõ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)

CampoTipoObrigatórioDescrição
distinctbooleanSimAplica SELECT DISTINCT.
items(SelectColumnItem | SelectAggregateItem)[]SimNão vazio. União discriminada pelo campo kind.

Os items são discriminados por kind:

  • Coluna — kind: "column":

    CampoTipoObrigatórioDescrição
    kind"column"SimDiscriminador.
    columnColumnRefDtoSimColuna projetada. Deve ser escalar.
    aliasstringNãoDeve corresponder a ^[A-Za-z_][A-Za-z0-9_]*$.
  • Agregado — kind: "aggregate":

    CampoTipoObrigatórioDescrição
    kind"aggregate"SimDiscriminador.
    aggregateAggregateDtoSimExpressão de agregação.
    aliasstringSimAqui o alias é obrigatório. Mesmo regex de identificador.

AggregateDto:

CampoTipoObrigatórioDescrição
aggregationOperatorAggregationOperatorEnumSimCOUNT | SUM | AVG | MIN | MAX.
operandobjectSimO que é agregado. União discriminada por kind: column, star ou arithmetic (veja abaixo).
distinctbooleanNãoAplica DISTINCT dentro do agregado. COUNT(DISTINCT *) não é permitido.

operand —o que é agregado— é discriminado por kind:

kindCampos adicionaisSignificado
columncolumn: ColumnRefDtoUma coluna do catálogo.
star(nenhum)*: conta linhas. Só é válido com COUNT.
arithmeticarithmeticOperator: ArithmeticOperatorEnum, left, rightExpressã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:

kindO que filtra
groupNão é uma folha: agrupa filhos com AND / OR. Grupos podem ser aninhados.
predicateUma coluna escalar de uma tabela do from.
recordPredicateUma coluna RECORD REPEATED: existe ≥1 elemento que cumpre o sub-filtro.
semiJoinPredicateUm semi-join do catálogo: existe ≥1 correspondência na tabela relacionada.

Grupo — kind: "group"

CampoTipoObrigatórioDescrição
kind"group"SimDiscriminador.
logicalOperatorLogicalOperatorEnum (AND | OR)SimConectivo entre os filhos.
children(Group | Leaf)[]SimFilhos 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.

CampoTipoObrigatórioDescrição
kind"predicate"SimDiscriminador.
columnColumnRefDtoSimColuna do predicado.
comparisonOperatorComparisonOperatorEnumSimOperador de comparação.
valuePredicateValueDependeSeu 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.

CampoTipoObrigatórioDescrição
kind"recordPredicate"SimDiscriminador.
columnColumnRefDtoSimColuna RECORD REPEATED de uma tabela do from.
subWhereSub-árvore relativaSimCondiçõ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.

CampoTipoObrigatórioDescrição
kind"semiJoinPredicate"SimDiscriminador.
tablestringSimTabela dona do semi-join. Deve participar do from do bloco.
semiJoinstringSimNome do semi-join que essa tabela declara no catálogo.
subWhereSub-árvore relativaSimCondiçõ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"?: … }. O field deve ser um sub-campo escalar do escopo.
  • Registro aninhado — { "kind": "recordPredicate", "field": string, "subWhere": … }. Um registro dentro de outro registro (por exemplo items[].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

OperadoresFormato de valueExemplo
IS_NULL, IS_NOT_NULLSem value (omitido)(omitido)
IN, NOT_INLista não vazia ScalarValue[]["Food Service", "HORECA"]
BETWEENPar [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 colunaOperadores aceitos
STRINGEQUALS · NOT_EQUALS · CONTAINS · NOT_CONTAINS · STARTS_WITH · ENDS_WITH · IN · NOT_IN · IS_NULL · IS_NOT_NULL
BOOLEANEQUALS · NOT_EQUALS · IS_NULL · IS_NOT_NULL
FLOAT64, INT64, NUMERIC, BIGNUMERICEQUALS · NOT_EQUALS · GREATER_THAN · LESS_THAN · GREATER_THAN_OR_EQUAL · LESS_THAN_OR_EQUAL · BETWEEN · IN · NOT_IN · IS_NULL · IS_NOT_NULL
TIMESTAMPEQUALS · 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.

timeOperatorCampos adicionais
ALL_TIME(nenhum) — sem restrição de tempo.
IN_THE_LASTreference: ColumnRefDto, unit: TimeUnitEnum, value: inteiro > 0
SINCEreference: ColumnRefDto, from: string (YYYY-MM-DD)
BETWEENreference: 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 últimos value unit (ex.: últimos 30 DAYS). Recalculada a cada execução.
  • SINCE — a partir da data civil from (inclusive) em diante.
  • BETWEEN — entre from e to, 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:

  1. Validação do DTO (estrutura, tipos, enums, campos obrigatórios). message é um array de strings e propriedades desconhecidas são rejeitadas.
  2. 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

RegraMensagem
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

RegraMensagem
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

RegraMensagem
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

RegraMensagem
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

RegraMensagem
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

RegraMensagem
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