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ámetro | Tipo | Descripción |
|---|---|---|
id | UUID | ID del filtro de eventos. |
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
format | string | No | Enviar 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:
| Campo | Tipo | Descripción |
|---|---|---|
id | string (UUID) | Id del filtro. |
name | string | Nombre del filtro. |
conditions | IComputeCondition[] | 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
| Campo | Tipo | Descripción |
|---|---|---|
field | string | Path de la columna dentro de source. |
operator | Operador del subset | Cualquiera menos CONTAINS_RECORD. |
value | escalar | arreglo | Ausente —no null— para IS NULL e IS NOT NULL. Arreglo para IN y NOT IN. Escalar para el resto. |
source | string | Tabla de la que sale la condición. |
Condición de registro
| Campo | Tipo | Descripción |
|---|---|---|
field | string | Columna RECORD REPEATED dentro de source. |
operator | "CONTAINS_RECORD" | Solo existe en esta forma. |
source | string | Tabla de la que sale la condición. |
match_all | boolean | Cómo se combinan los sub_filters dentro de un elemento (ver abajo). |
sub_filters | arreglo | Condiciones 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:
match_allcombina lossub_filtersdentro del elemento, no sobre los elementos.match_all: truesignifica que existe un elemento del array que cumple todos lossub_filters. Un elemento que coincide solo enkeyo solo envalueno lo satisface.match_all: falsees el mismo existencial conORadentro: existe un elemento que cumple al menos uno de lossub_filters. Nunca significa "todos los elementos".- Con un solo
sub_filtersiempre viajamatch_all: true. Con menos de dos sub-filtros el conectivo no cambia el resultado, ytruees la forma canónica. CONTAINS_RECORDes existencial también con operadores negativos.itemsconcategory NOT IN ["devolucion"]ymatch_all: truesignifica "existe un ítem que no es devolución", no "ningún ítem es devolución". Esta representación no sabe expresar cuantificación universal.- Un array vacío o
NULLnunca satisface unCONTAINS_RECORD, y un sub-campo enNULLno 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 aparece | Qué contiene | Ejemplo |
|---|---|---|
| Condición escalar de primer nivel | Path de la columna dentro de source. | pricing.final_amount |
| Condición de registro | Nombre de la columna RECORD REPEATED dentro de source. | attributes |
Dentro de sub_filters | Path relativo al registro, sin su prefijo. | key — no attributes.key |
| Condición desde una tabla relacionada | Path 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ón | operator de esta respuesta |
|---|---|
EQUALS | = |
NOT_EQUALS | != |
GREATER_THAN | > |
GREATER_THAN_OR_EQUAL | >= |
LESS_THAN | < |
LESS_THAN_OR_EQUAL | <= |
IN | IN |
NOT_IN | NOT IN |
IS_NULL | IS NULL |
IS_NOT_NULL | IS 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 unIN, 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 columna | Tipo JSON del value |
|---|---|
STRING | string |
TIMESTAMP | string |
BOOLEAN | boolean |
FLOAT64 | number |
INT64 | number |
NUMERIC | string |
BIGNUMERIC | string |
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"
}| Campo | Tipo | Descripción |
|---|---|---|
code | string | Uno de los doce códigos de abajo. |
path | string | Ruta 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. |
message | string | Descripción legible del rechazo. |
Los doce códigos
code | Qué lo dispara | path |
|---|---|---|
MULTIPLE_QUERY_BLOCKS | El filtro tiene más de un bloque de consulta. Esta representación es de un solo bloque. | queryBlocks |
AGGREGATION_UNSUPPORTED | El bloque agrega: groupBy no vacío, un agregado en el select, o un having con condiciones. | queryBlocks[0] |
TIME_WINDOW_UNSUPPORTED | El bloque declara una ventana temporal distinta de ALL_TIME. | queryBlocks[0].timeWindow |
JOIN_UNSUPPORTED | El bloque declara joins. | queryBlocks[0].from.joins |
TOP_LEVEL_OR_UNSUPPORTED | El árbol de filtros de primer nivel se conjuga con OR. La lista se une con AND. | queryBlocks[0].where |
MIXED_LOGICAL_OPERATORS_UNSUPPORTED | Un 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_UNSUPPORTED | Un operador fuera del subset (CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH, BETWEEN). | La hoja que lo usa |
NESTED_RECORD_UNSUPPORTED | Un filtro de registro anidado dentro de otro. Los sub_filters son planos y escalares. | La hoja anidada |
SEMI_JOIN_OR_UNSUPPORTED | El 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_SOURCE | Dos 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_GROUP | Un sub-árbol relativo sin condiciones: no restringiría nada. | El sub-árbol vacío |
VALUE_NOT_REPRESENTABLE | Un 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
| Estado | Descripción |
|---|---|
400 | El 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. |
401 | Falta o es inválido el Authorization: Bearer. |
403 | El usuario no tiene el permiso event-criteria:view, o el x-tenant-id no está autorizado. |
404 | No existe un filtro de eventos con ese id en el tenant (Event criteria not found). |
422 | El filtro es válido pero no tiene proyección a esta representación (ver los doce códigos). |