Reten Docs
Filtros de eventos

Formato compute

Representação plana de um filtro de eventos como lista de condições, em snake_case, para consumidores de analytics.

GET /api/event-criterias/:id?format=compute

Retorna um filtro de eventos como uma lista plana de condições, em vez da árvore completa. É o outro ramo do query param format de GET /event-criterias/:id.

Auth: Obrigatória — permissão event-criteria:view

Esta representação é mais estreita que a definição: transporta condições de linha sobre um único bloco de consulta. Um filtro cuja definição não cabe nela é rejeitado com 422 e um código que diz exatamente o que não cabe — nunca se retorna uma versão degradada do filtro.

Parâmetros de rota

ParâmetroTipoDescrição
idUUIDID do filtro de eventos.

Parâmetros de consulta

ParâmetroTipoObrigatórioDescrição
formatstringNãoEnvie compute para pedir esta representação. Omiti-lo —ou enviar detail— retorna o detalhe completo.

Exemplo

curl "https://api.reten.ai/api/event-criterias/9f3a1c22-4b7e-4c31-9a8d-2f5e6b7c8d90?format=compute" \
  -H "Authorization: Bearer <token>" \
  -H "x-tenant-id: <tenant-id>" \
  -H "x-project-id: <project-id>"
import axios from 'axios';

const id = '9f3a1c22-4b7e-4c31-9a8d-2f5e6b7c8d90';
const response = await axios.get(
  `https://api.reten.ai/api/event-criterias/${id}`,
  {
    headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' },
    params: { format: 'compute' },
  },
);
const computeCriteria = response.data;

Forma da resposta 200 OK

Os campos desta resposta usam snake_case (sub_filters, match_all), diferentemente do resto do recurso, que usa camelCase.

IComputeEventCriteria:

CampoTipoDescrição
idstring (UUID)ID do filtro.
namestringNome do filtro.
conditionsIComputeCondition[]Condições a aplicar. São combinadas com AND. [] é válido.

Cada elemento de conditions é uma de duas formas, distinguíveis pelo seu operator:

Condição escalar

CampoTipoDescrição
fieldstringCaminho da coluna dentro de source.
operatorOperador do subconjuntoQualquer um menos CONTAINS_RECORD.
valueescalar | arrayAusente —não null— para IS NULL e IS NOT NULL. Array para IN e NOT IN. Escalar para o resto.
sourcestringTabela de onde vem a condição.

Condição de registro

CampoTipoDescrição
fieldstringColuna RECORD REPEATED dentro de source.
operator"CONTAINS_RECORD"Só existe nesta forma.
sourcestringTabela de onde vem a condição.
match_allbooleanComo os sub_filters são combinados dentro de um elemento (ver abaixo).
sub_filtersarrayCondições sobre os sub-campos do elemento: field, operator, value — sem source.

Os quatro casos

1 · Condição escalar

Eventos cujo status é Facturada.

{
  "id": "9f3a1c22-4b7e-4c31-9a8d-2f5e6b7c8d90",
  "name": "Venta oficial — solo facturada",
  "conditions": [
    {
      "field": "status",
      "operator": "=",
      "value": "Facturada",
      "source": "event_transactions"
    }
  ]
}

2 · Registro com dois sub-filtros

Eventos com um atributo cuja key é documento_tipo e cujo value é factura.

{
  "id": "1c7d2e83-5a90-4f62-8b31-6d4e5f70a1b2",
  "name": "Documentos tipo factura",
  "conditions": [
    {
      "field": "attributes",
      "operator": "CONTAINS_RECORD",
      "source": "event_transactions",
      "match_all": true,
      "sub_filters": [
        { "field": "key", "operator": "=", "value": "documento_tipo" },
        { "field": "value", "operator": "=", "value": "factura" }
      ]
    }
  ]
}

3 · Escalar + registro

Vendas faturadas com pelo menos uma linha que não é uma devolução.

{
  "id": "4b8e0f15-2c76-4a03-9d58-7e1f2a3b4c5d",
  "name": "Ventas con ítems no-devolución",
  "conditions": [
    {
      "field": "status",
      "operator": "=",
      "value": "Facturada",
      "source": "event_transactions"
    },
    {
      "field": "items",
      "operator": "CONTAINS_RECORD",
      "source": "event_transactions",
      "match_all": true,
      "sub_filters": [
        { "field": "category", "operator": "NOT IN", "value": ["devolucion"] }
      ]
    }
  ]
}

4 · Escalar + tabela relacionada

Vendas faturadas com pelo menos uma linha cujo produto é da categoria Food Service ou HORECA. A condição sobre a tabela relacionada chega no mesmo nível que as demais, distinguida pelo seu source: o caminho até product_properties não viaja.

{
  "id": "6d2a9c41-7e35-48b0-b1c9-0f8e7d6c5b4a",
  "name": "Proyecto Food Service",
  "conditions": [
    {
      "field": "status",
      "operator": "=",
      "value": "Facturada",
      "source": "event_transactions"
    },
    {
      "field": "categorization.category_name",
      "operator": "IN",
      "value": ["Food Service", "HORECA"],
      "source": "product_properties"
    }
  ]
}

Um filtro de tabela relacionada com vários sub-filtros emite uma condição por sub-filtro, todas com o mesmo source. É a única folha cuja cardinalidade de saída não é 1.

O que significa match_all

É onde uma integração mais facilmente erra, então fica explícito:

  1. match_all combina os sub_filters dentro do elemento, não entre os elementos. match_all: true significa que existe um elemento do array que satisfaz todos os sub_filters. Um elemento que corresponde só em key ou só em value não o satisfaz.
  2. match_all: false é o mesmo existencial com OR dentro: existe um elemento que satisfaz pelo menos um dos sub_filters. Nunca significa "todos os elementos".
  3. Com um único sub_filter sempre viaja match_all: true. Com menos de dois sub-filtros o conectivo não muda o resultado, e true é a forma canônica.
  4. CONTAINS_RECORD é existencial também com operadores negativos. items com category NOT IN ["devolucion"] e match_all: true significa "existe um item que não é devolução", não "nenhum item é devolução". Esta representação não consegue expressar quantificação universal.
  5. Um array vazio ou NULL nunca satisfaz um CONTAINS_RECORD, e um sub-campo em NULL não corresponde, nem mesmo com um operador negativo.

O que significa field

O field é o caminho completo relativo ao seu escopo, e o escopo nunca se repete:

Onde apareceO que contémExemplo
Condição escalar de primeiro nívelCaminho da coluna dentro de source.pricing.final_amount
Condição de registroNome da coluna RECORD REPEATED dentro de source.attributes
Dentro de sub_filtersCaminho relativo ao registro, sem o seu prefixo.key — não attributes.key
Condição vinda de uma tabela relacionadaCaminho completo dentro dessa tabela, com source = a tabela relacionada.categorization.category_name

Subconjunto de operadores

Os operadores viajam como literais SQL, não como os membros do enum da definição:

comparisonOperator da definiçãooperator desta resposta
EQUALS=
NOT_EQUALS!=
GREATER_THAN>
GREATER_THAN_OR_EQUAL>=
LESS_THAN<
LESS_THAN_OR_EQUAL<=
ININ
NOT_INNOT IN
IS_NULLIS NULL
IS_NOT_NULLIS NOT NULL

CONTAINS_RECORD não traduz nenhum operador de comparação: quem o emite é a folha de registro.

Estes operadores não estão no subconjunto, e um filtro que os use é rejeitado com 422 e código OPERATOR_UNSUPPORTED:

  • CONTAINS · NOT_CONTAINS · STARTS_WITH · ENDS_WITH — os operadores de texto.
  • BETWEEN — emitir o par como lista o transformaria em um IN, que é outro filtro.

Tipo JSON do value

Com qual tipo JSON o valor viaja é fixado pelo tipo da coluna no catálogo, não pela forma em que a definição ficou salva:

Tipo da colunaTipo JSON do value
STRINGstring
TIMESTAMPstring
BOOLEANboolean
FLOAT64number
INT64number
NUMERICstring
BIGNUMERICstring

A definição aceita as duas formas para vários tipos —100 e "100" valem igual para um FLOAT64—, então emitir o que ficou salvo faria a mesma coluna viajar ora como número, ora como texto, dependendo de quem a escreveu, e deixaria um IN misturar as duas formas em uma única lista. NUMERIC e BIGNUMERIC viajam como string porque como número perderiam os dígitos exatos que o seu tipo garante.

Resposta 422 Unprocessable Entity

O filtro é válido e compila para SQL; o que não existe é a sua projeção para esta representação. O corpo traz o diagnóstico:

Este corpo de erro viaja sem envelope: é code, path e message puros, sem o statusCode nem o error que os demais erros da API trazem.

{
  "code": "TIME_WINDOW_UNSUPPORTED",
  "path": "queryBlocks[0].timeWindow",
  "message": "No se puede proyectar queryBlocks[0].timeWindow (TIME_WINDOW_UNSUPPORTED): el wire no transporta la ventana IN_THE_LAST"
}
CampoTipoDescrição
codestringUm dos doze códigos abaixo.
pathstringCaminho até o nó da árvore que não tem projeção, em notação de acesso (queryBlocks[0].where.children[1].subWhere). É o que precisa mudar.
messagestringDescrição legível da rejeição.

Os doze códigos

codeO que o disparapath
MULTIPLE_QUERY_BLOCKSO filtro tem mais de um bloco de consulta. Esta representação é de um único bloco.queryBlocks
AGGREGATION_UNSUPPORTEDO bloco agrega: groupBy não vazio, um agregado no select, ou um having com condições.queryBlocks[0]
TIME_WINDOW_UNSUPPORTEDO bloco declara uma janela temporal diferente de ALL_TIME.queryBlocks[0].timeWindow
JOIN_UNSUPPORTEDO bloco declara joins.queryBlocks[0].from.joins
TOP_LEVEL_OR_UNSUPPORTEDA árvore de filtros de primeiro nível é combinada com OR. A lista é unida com AND.queryBlocks[0].where
MIXED_LOGICAL_OPERATORS_UNSUPPORTEDUm mesmo escopo mistura AND e OR em grupos aninhados. Só se admite um conectivo por escopo.O grupo mais superficial onde o conflito é detectado
OPERATOR_UNSUPPORTEDUm operador fora do subconjunto (CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH, BETWEEN).A folha que o usa
NESTED_RECORD_UNSUPPORTEDUm filtro de registro aninhado dentro de outro. Os sub_filters são planos e escalares.A folha aninhada
SEMI_JOIN_OR_UNSUPPORTEDA sub-árvore de um filtro de tabela relacionada é combinada com OR. Achatada viraria AND: um filtro mais estrito.O subWhere da folha
MULTIPLE_SEMI_JOINS_SAME_SOURCEDois escopos diferentes emitiriam o mesmo source. São dois existenciais independentes que, achatados, se aplicariam à mesma linha.A segunda folha que reivindica esse source
EMPTY_PREDICATE_GROUPUma sub-árvore relativa sem condições: não restringiria nada.A sub-árvore vazia
VALUE_NOT_REPRESENTABLEUm operador que precisa de valor e não o traz, ou um INT64 fora do intervalo exato de um número JSON.A folha

Estes códigos dizem o que esta representação não consegue expressar, não o que a API não suporta: todas essas formas são definições válidas que compilam para SQL e funcionam em Gerar SQL, Validar e Pré-visualizar.

Relação com conditionCount

O conditionCount da listagem conta as folhas da árvore da definição e não coincide necessariamente com o tamanho de conditions. Um filtro com uma folha de tabela relacionada de três sub-filtros tem conditionCount: 1 e retorna três condições. Os dois números estão corretos e medem coisas diferentes.

Respostas de erro

StatusDescrição
400O id não é um UUID válido, format não é detail nem compute, ou o filtro salvo já não compila para SQL contra o catálogo atual.
401Falta ou é inválido o Authorization: Bearer.
403O usuário não tem a permissão event-criteria:view, ou o x-tenant-id não está autorizado.
404Não existe um filtro de eventos com esse id no tenant (Event criteria not found).
422O filtro é válido mas não tem projeção para esta representação (ver os doze códigos).