Reten Docs
Filtros de eventos

Formato compute

Representación plana de un filtro de eventos como lista de condiciones, en snake_case, para consumidores de analítica.

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

Devuelve un filtro de eventos como una lista plana de condiciones, en lugar del árbol completo. Es la otra rama del query param format de GET /event-criterias/:id.

Auth: Requerida — permiso event-criteria:view

Esta representación es más angosta que la definición: transporta condiciones de fila sobre un solo bloque de consulta. Un filtro cuya definición no cabe en ella se rechaza con 422 y un código que dice exactamente qué no cabe — nunca se devuelve una versión degradada del filtro.

Parámetros de ruta

ParámetroTipoDescripción
idUUIDID del filtro de eventos.

Parámetros de consulta

ParámetroTipoRequeridoDescripción
formatstringNoEnviar compute para pedir esta representación. Omitirlo —o enviar detail— devuelve el detalle completo.

Ejemplo

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 de la respuesta 200 OK

Los campos de esta respuesta usan snake_case (sub_filters, match_all), a diferencia del resto del recurso, que usa camelCase.

IComputeEventCriteria:

CampoTipoDescripción
idstring (UUID)Id del filtro.
namestringNombre del filtro.
conditionsIComputeCondition[]Condiciones a aplicar. Se conjugan con AND. [] es válido.

Cada elemento de conditions es una de dos formas, distinguibles por su operator:

Condición escalar

CampoTipoDescripción
fieldstringPath de la columna dentro de source.
operatorOperador del subsetCualquiera menos CONTAINS_RECORD.
valueescalar | arregloAusente —no null— para IS NULL e IS NOT NULL. Arreglo para IN y NOT IN. Escalar para el resto.
sourcestringTabla de la que sale la condición.

Condición de registro

CampoTipoDescripción
fieldstringColumna RECORD REPEATED dentro de source.
operator"CONTAINS_RECORD"Solo existe en esta forma.
sourcestringTabla de la que sale la condición.
match_allbooleanCómo se combinan los sub_filters dentro de un elemento (ver abajo).
sub_filtersarregloCondiciones sobre los sub-campos del elemento: field, operator, value — sin source.

Los cuatro casos

1 · Condición escalar

Eventos cuyo status es Facturada.

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

2 · Registro con dos sub-filtros

Eventos con un atributo cuya key es documento_tipo y cuyo value es 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

Ventas facturadas con al menos una línea que no es una devolución.

{
  "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 + tabla relacionada

Ventas facturadas con al menos una línea cuyo producto es de categoría Food Service u HORECA. La condición sobre la tabla relacionada llega al mismo nivel que las demás, distinguida por su source: el camino hasta product_properties no 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"
    }
  ]
}

Un filtro de tabla relacionada con varios sub-filtros emite una condición por sub-filtro, todas con el mismo source. Es la única hoja cuya cardinalidad de salida no es 1.

Qué significa match_all

Es donde más fácil se equivoca una integración, así que va explícito:

  1. match_all combina los sub_filters dentro del elemento, no sobre los elementos. match_all: true significa que existe un elemento del array que cumple todos los sub_filters. Un elemento que coincide solo en key o solo en value no lo satisface.
  2. match_all: false es el mismo existencial con OR adentro: existe un elemento que cumple al menos uno de los sub_filters. Nunca significa "todos los elementos".
  3. Con un solo sub_filter siempre viaja match_all: true. Con menos de dos sub-filtros el conectivo no cambia el resultado, y true es la forma canónica.
  4. CONTAINS_RECORD es existencial también con operadores negativos. items con category NOT IN ["devolucion"] y match_all: true significa "existe un ítem que no es devolución", no "ningún ítem es devolución". Esta representación no sabe expresar cuantificación universal.
  5. Un array vacío o NULL nunca satisface un CONTAINS_RECORD, y un sub-campo en NULL no matchea, tampoco con un operador negativo.

Qué significa field

El field es el path completo relativo a su scope, y el scope nunca se repite:

Dónde apareceQué contieneEjemplo
Condición escalar de primer nivelPath de la columna dentro de source.pricing.final_amount
Condición de registroNombre de la columna RECORD REPEATED dentro de source.attributes
Dentro de sub_filtersPath relativo al registro, sin su prefijo.key — no attributes.key
Condición desde una tabla relacionadaPath completo dentro de esa tabla, con source = la tabla relacionada.categorization.category_name

Subset de operadores

Los operadores viajan como literales SQL, no como los miembros del enum de la definición:

comparisonOperator de la definiciónoperator de esta respuesta
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 no traduce ningún operador de comparación: lo emite la hoja de registro.

Estos operadores no están en el subset, y un filtro que los use se rechaza con 422 y código OPERATOR_UNSUPPORTED:

  • CONTAINS · NOT_CONTAINS · STARTS_WITH · ENDS_WITH — los operadores de texto.
  • BETWEEN — emitir el par como lista lo convertiría en un IN, que es otro filtro.

Tipo JSON del value

Con qué tipo JSON viaja el valor lo fija el tipo de la columna en el catálogo, no la forma en que la definición quedó guardada:

Tipo de la columnaTipo JSON del value
STRINGstring
TIMESTAMPstring
BOOLEANboolean
FLOAT64number
INT64number
NUMERICstring
BIGNUMERICstring

La definición acepta las dos formas para varios tipos —100 y "100" valen igual para un FLOAT64—, así que emitir lo guardado haría que la misma columna viajara ya como número, ya como texto, según quién la escribió, y que un IN mezclara las dos formas en una sola lista. NUMERIC y BIGNUMERIC viajan como string porque como número perderían los dígitos exactos que su tipo garantiza.

Respuesta 422 Unprocessable Entity

El filtro es válido y compila a SQL; lo que no existe es su proyección a esta representación. El cuerpo trae el diagnóstico:

Este cuerpo de error viaja sin envoltura: es code, path y message a secas, sin el statusCode ni el error que traen los demás errores de la API.

{
  "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"
}
CampoTipoDescripción
codestringUno de los doce códigos de abajo.
pathstringRuta al nodo del árbol que no tiene proyección, en notación de acceso (queryBlocks[0].where.children[1].subWhere). Es lo que hay que cambiar.
messagestringDescripción legible del rechazo.

Los doce códigos

codeQué lo disparapath
MULTIPLE_QUERY_BLOCKSEl filtro tiene más de un bloque de consulta. Esta representación es de un solo bloque.queryBlocks
AGGREGATION_UNSUPPORTEDEl bloque agrega: groupBy no vacío, un agregado en el select, o un having con condiciones.queryBlocks[0]
TIME_WINDOW_UNSUPPORTEDEl bloque declara una ventana temporal distinta de ALL_TIME.queryBlocks[0].timeWindow
JOIN_UNSUPPORTEDEl bloque declara joins.queryBlocks[0].from.joins
TOP_LEVEL_OR_UNSUPPORTEDEl árbol de filtros de primer nivel se conjuga con OR. La lista se une con AND.queryBlocks[0].where
MIXED_LOGICAL_OPERATORS_UNSUPPORTEDUn mismo scope mezcla AND y OR en grupos anidados. Solo se admite un conectivo por scope.El grupo más superficial donde se detecta el conflicto
OPERATOR_UNSUPPORTEDUn operador fuera del subset (CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH, BETWEEN).La hoja que lo usa
NESTED_RECORD_UNSUPPORTEDUn filtro de registro anidado dentro de otro. Los sub_filters son planos y escalares.La hoja anidada
SEMI_JOIN_OR_UNSUPPORTEDEl sub-árbol de un filtro de tabla relacionada se conjuga con OR. Aplanado se volvería AND: un filtro más estricto.El subWhere de la hoja
MULTIPLE_SEMI_JOINS_SAME_SOURCEDos scopes distintos emitirían el mismo source. Son dos existenciales independientes que aplanados se aplicarían a la misma fila.La segunda hoja que reclama ese source
EMPTY_PREDICATE_GROUPUn sub-árbol relativo sin condiciones: no restringiría nada.El sub-árbol vacío
VALUE_NOT_REPRESENTABLEUn operador que necesita valor y no lo trae, o un INT64 fuera del rango exacto de un número JSON.La hoja

Estos códigos dicen qué esta representación no puede expresar, no qué la API no soporta: todas esas formas son definiciones válidas que compilan a SQL y funcionan en Generar SQL, Validar y Previsualizar.

Relación con conditionCount

El conditionCount del listado cuenta las hojas del árbol de la definición y no coincide necesariamente con el largo de conditions. Un filtro con una hoja de tabla relacionada de tres sub-filtros tiene conditionCount: 1 y devuelve tres condiciones. Los dos números son correctos y miden cosas distintas.

Respuestas de error

EstadoDescripción
400El id no es un UUID válido, format no es detail ni compute, o el filtro guardado ya no compila a SQL contra el catálogo actual.
401Falta o es inválido el Authorization: Bearer.
403El usuario no tiene el permiso event-criteria:view, o el x-tenant-id no está autorizado.
404No existe un filtro de eventos con ese id en el tenant (Event criteria not found).
422El filtro es válido pero no tiene proyección a esta representación (ver los doce códigos).