Validar
Valida una definición de audience criteria y ejecuta un dry-run contra BigQuery, desde el cuerpo (aún sin persistir) o por id de un criterio guardado; devuelve si es válida y el costo estimado, sin datos ni SQL.
Validar
Valida una definición de audience criteria y, si pasa todas las validaciones, ejecuta un dry-run contra BigQuery. Un mismo motor sirve dos entradas:
POST /api/audience-criterias/validate-from-body— valida una definición enviada en el cuerpo (aún no persistida).POST /api/audience-criterias/:id/validate— valida un criterio ya guardado, referenciado por suid.
Ambos devuelven la misma forma de respuesta:
IValidateAudienceCriteriaResult, con la
validez y el costo estimado del query. La validación no devuelve filas ni el SQL
generado: solo si la definición es ejecutable y cuántos bytes procesaría.
Dentro de un proyecto, toda audiencia que no sea la audiencia raíz se valida intersectada con la audiencia raíz del proyecto — ver Intersección con la audiencia raíz.
Auth: Requerida — permiso audience-criteria:view-sql (ambos endpoints).
Distingue dos caminos de fallo que este recurso trata de forma distinta:
- La validación estructural y de negocio (DTO, catálogo, reglas de agregación,
ventana temporal, etc.) ocurre antes del dry-run y, si falla, responde un error
HTTP (
400/404) — no llega al cuerpo de respuesta. - El dry-run de BigQuery es lo único que se refleja en el cuerpo como
valid: false(con status201).
POST /api/audience-criterias/validate-from-body
Valida una definición enviada en el cuerpo, sin persistirla.
Auth: Requerida — permiso audience-criteria:view-sql
El endpoint responde 201 Created —no 200—, aunque no cree ningún
recurso.
Cuerpo de la solicitud
Nivel raíz (CriteriaDefinitionDto): la definición del criterio, sin envoltorio.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
queryBlocks | QueryBlockDto[] | Sí | Mínimo 1 elemento. Cada bloque ≈ un statement SQL completo. |
composedWith | CreateCompositionDto[] | No | Compone esta definición con otras audiencias vía operaciones de conjunto. |
criteriaId | string (UUID) | null | No | La audiencia guardada que esta definición edita: el borrador toma la identidad de esa audiencia, así que da el mismo resultado que esa audiencia dará al guardarse (ver Intersección con la audiencia raíz). Ausente o null: una audiencia nueva. Debe ser un UUID válido (si no, 400) de una audiencia del proyecto activo (si no, 404). |
A diferencia de Crear, la definición
no lleva name ni description: es solo el árbol de consulta. Tampoco existe el
campo format de Generar SQL
—validar no emite SQL—. La validación del DTO es estricta, así que enviar
name/description/format (u otra propiedad desconocida) produce un 400.
Definición del criterio
Es el mismo árbol que recibe Crear, sin los
campos name/description. Se documenta aquí de forma autocontenida.
queryBlocks[] — bloque de consulta
Cada elemento de queryBlocks (QueryBlockDto) es un statement SQL completo:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
order | number (entero) | Sí | Posición del bloque; entero ≥ 0. |
setOperator | SetOperatorEnum | No | Combina este bloque con el anterior. Ausente en el primer bloque; obligatorio en el resto (ver reglas). |
from | FromDto | Sí | Fuente de datos: tabla principal + joins. |
select | SelectDto | Sí | Proyección (columnas y/o agregaciones). |
timeWindow | TimeWindowDto | No | Ventana temporal del bloque (unión discriminada por timeOperator). |
where | WhereDto | Sí | Árbol de predicados. Obligatorio aunque sea un grupo vacío. |
groupBy | ColumnRefDto[] | No | Columnas de agrupación. |
having | HavingDto | No | Filtro sobre agregados. |
El setOperator de un query block combina bloques dentro de la misma definición;
no debe confundirse con el setOperator de una composición,
que combina criterios enteros. Son dos niveles distintos de operación de conjunto.
ColumnRefDto — referencia a columna
Se usa en todo el árbol (select, filtros, group by, joins, agregados, ventana temporal):
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
table | string | Sí | Tabla del catálogo. No vacío. |
column | string | Sí | Columna. No vacío. Puede ser un path de STRUCT (p. ej. "pricing.final_amount"). |
Toda columna se cualifica siempre con su table para evitar ambigüedad.
from — fuente de datos (FromDto)
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
table | string | Sí | Tabla principal del catálogo. No vacío. |
joins | JoinDto[] | No | Cruces con otras tablas. Default []. |
unnests | ColumnRefDto[] | No | Columnas repetidas declaradas como fuente de filas (una fila por elemento). Una audiencia no las admite: omite el campo o envía [] (el default); una lista no vacía da 400. |
Cada JoinDto:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
type | JoinTypeEnum | Sí | INNER | LEFT | RIGHT | FULL. |
table | string | Sí | Tabla a unir (del catálogo). No vacío. |
on | JoinPredicateDto[] | Sí | Condiciones de unión. |
Cada JoinPredicateDto es un par de columnas: { left: ColumnRefDto, right: ColumnRefDto }.
select — proyección (SelectDto)
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
distinct | boolean | Sí | Aplica SELECT DISTINCT. |
items | (SelectColumnItem | SelectAggregateItem)[] | Sí | No vacío. Unión discriminada por el campo kind. |
Los items se discriminan por kind:
-
Columna —
kind: "column":Campo Tipo Requerido Descripción kind"column"Sí Discriminador. columnColumnRefDtoSí Columna proyectada. aliasstring No Debe matchear ^[A-Za-z_][A-Za-z0-9_]*$. -
Agregado —
kind: "aggregate":Campo Tipo Requerido Descripción kind"aggregate"Sí Discriminador. aggregateAggregateDtoSí Expresión de agregación. aliasstring Sí Aquí el alias es obligatorio. Mismo regex de identificador.
AggregateDto:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
aggregationOperator | AggregationOperatorEnum | Sí | COUNT | SUM | AVG | MIN | MAX. |
operand | object | Sí | Lo que se agrega. Unión discriminada por kind: column, star o arithmetic (ver abajo). |
distinct | boolean | No | Aplica DISTINCT dentro del agregado. |
operand —lo que se agrega— se discrimina por kind:
kind | Campos adicionales | Significado |
|---|---|---|
column | column: ColumnRefDto | Una columna del catálogo. |
star | (ninguno) | *: cuenta filas. Solo es válido con COUNT y sin distinct (COUNT(DISTINCT *) no está permitido). |
arithmetic | arithmeticOperator: ArithmeticOperatorEnum, left, right | Expresión binaria. Cada lado es otro operando: una column o un arithmetic anidado (nunca star). |
ArithmeticOperatorEnum: ADD | SUBTRACT | MULTIPLY | SAFE_DIVIDE (una división que da
NULL cuando el denominador es cero, en vez de hacer fallar la consulta). Las columnas de una
expresión aritmética tienen que ser numéricas (INT64, NUMERIC, BIGNUMERIC, FLOAT64), y las
expresiones se anidan hasta 3 niveles de arithmetic, contando el exterior.
where — árbol de predicados (WhereDto)
El where es un árbol recursivo. El nodo raíz es siempre un grupo, y sus hojas son de
dos tipos:
kind | Qué filtra |
|---|---|
group | No es una hoja: agrupa hijos con AND / OR. Se pueden anidar grupos. |
predicate | Una columna escalar de una tabla del from. |
recordPredicate | Una columna RECORD REPEATED: existe ≥1 elemento que cumple el sub-filtro. |
Grupo — kind: "group"
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
kind | "group" | Sí | Discriminador. |
logicalOperator | LogicalOperatorEnum (AND | OR) | Sí | Conectivo entre los hijos. |
children | (Grupo | Hoja)[] | Sí | Hijos del grupo; pueden ser grupos anidados. |
logicalOperator es obligatorio siempre en un grupo, pero solo tiene efecto con 2 o
más hijos. Con menos de 2 hijos su valor es indiferente (genera el mismo SQL), pero igual
debe enviarse. Un where sin filtros se expresa como un grupo vacío:
{ "kind": "group", "logicalOperator": "AND", "children": [] }.
Hoja de columna — kind: "predicate"
Compara una columna escalar cualificada por su tabla.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
kind | "predicate" | Sí | Discriminador. |
column | ColumnRefDto | Sí | Columna del predicado. |
comparisonOperator | ComparisonOperatorEnum | Sí | Operador de comparación. |
value | PredicateValue | Depende | Su forma depende del operador. |
{
"kind": "predicate",
"column": { "table": "user_properties", "column": "country" },
"comparisonOperator": "IN",
"value": ["CL", "MX"]
}Hoja de registro — kind: "recordPredicate"
Filtra por una columna RECORD REPEATED (un array de structs, como attributes).
Significa existe al menos un elemento del array que cumple el sub-filtro.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
kind | "recordPredicate" | Sí | Discriminador. |
column | ColumnRefDto | Sí | Columna RECORD REPEATED de una tabla del from. |
subWhere | Sub-árbol relativo | Sí | Condiciones sobre los sub-campos del elemento. No puede quedar vacío. |
{
"kind": "recordPredicate",
"column": { "table": "event_transactions", "column": "attributes" },
"subWhere": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{ "kind": "predicate", "field": "key", "comparisonOperator": "EQUALS", "value": "canal" },
{ "kind": "predicate", "field": "value", "comparisonOperator": "EQUALS", "value": "web" }
]
}
}Las condiciones del subWhere se evalúan sobre un mismo elemento del array, no sobre
el array entero. El ejemplo de arriba selecciona filas que tienen un atributo cuya key
es canal y cuyo value es web — no filas que tengan un atributo con esa key y
otro, distinto, con ese value.
Qué columnas admiten un filtro de registro lo dice el
catálogo: las que vienen con
kind: "record".
Sub-árbol relativo
El subWhere es un grupo AND/OR cuyas hojas se resuelven contra los sub-campos que el
registro declara.
La diferencia con el árbol de primer nivel es una sola, y es la fuente de error más común:
Dentro de un sub-árbol relativo los operandos no llevan tabla: se escribe
"field": "key", no "column": { "table": "attributes", "column": "key" }. La hoja usa
la clave field (un string), no column.
Hojas admitidas dentro de un sub-árbol relativo:
- Hoja escalar —
{ "kind": "predicate", "field": string, "comparisonOperator": …, "value"?: … }. Elfielddebe ser un sub-campo escalar del registro. - Registro anidado —
{ "kind": "recordPredicate", "field": string, "subWhere": … }. Un registro dentro de otro registro; se anida sin límite declarado, siempre que el catálogo declare cada nivel. - Grupo — la misma forma de grupo, para combinar las hojas anteriores con
AND/OR.
Forma de value según comparisonOperator
| Operadores | Forma de value | Ejemplo |
|---|---|---|
IS_NULL, IS_NOT_NULL | Sin value (se omite) | (se omite) |
IN, NOT_IN | Lista no vacía ScalarValue[] | ["CL", "MX"] |
BETWEEN | Par [min, max] | [10, 20] |
El 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 | "activo" · 42 |
ScalarValue = string | number | boolean. Además, cada operador debe ser
compatible con el tipo de la columna (ver reglas de validación).
groupBy — columnas de agrupación
Lista opcional de ColumnRefDto[]. Cuando el bloque agrega (declara groupBy o tiene un
agregado en el select), el groupBy debe coincidir exactamente con las columnas
no agregadas del select (ver reglas de validación).
having — filtro sobre agregados (HavingDto)
Misma estructura recursiva que where (grupos AND/OR anidados y misma regla de value
por operador), pero la hoja opera sobre un agregado, no sobre una columna:
- Predicado (hoja) —
{ kind: "predicate", aggregate: AggregateDto, comparisonOperator: ComparisonOperatorEnum, value?: PredicateValue }. - Grupo — idéntico al de
where.
timeWindow — ventana temporal (TimeWindowDto)
Unión discriminada por timeOperator. Acota el bloque a un período de tiempo sobre una
columna TIMESTAMP del catálogo (reference). Es un campo hermano del where, no un
nodo del árbol de filtros.
timeOperator | Campos adicionales |
|---|---|
ALL_TIME | (ninguno) — sin acotar por tiempo. |
IN_THE_LAST | reference: ColumnRefDto, unit: TimeUnitEnum, value: entero > 0 |
IN_THE_FIRST | reference: ColumnRefDto, unit: TimeUnitEnum, value: entero > 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 negocio de cada operador:
ALL_TIME— sin ventana; considera todo el histórico.IN_THE_LAST— ventana rodante relativa a "ahora": los últimosvalueunit(p. ej. últimos30 DAYS). Se recomputa en cada corrida.IN_THE_FIRST— ventana relativa al origen del ciclo de vida del usuario (su fecha de alta): los primerosvalueunitde vida de cada usuario. Se ancla por usuario, no a una fecha fija.SINCE— desde la fecha civilfrom(inclusiva) en adelante.BETWEEN— entrefromyto, ambas fechas civiles inclusivas de su día completo.
Las fechas civiles YYYY-MM-DD (SINCE, BETWEEN) se resuelven al inicio del día en
la zona horaria del tenant. El detalle de zona horaria es interno; basta con enviar la
fecha civil.
composedWith[] — composición con otras audiencias
Cada elemento (CreateCompositionDto) combina esta definición con otra audiencia
vía una operación de conjunto:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
operandCriteriaId | string (UUID) | Sí | Id de la otra audiencia (operando). Debe existir. |
setOperator | SetOperatorEnum | Sí | UNION_DISTINCT | UNION_ALL | INTERSECT_DISTINCT. |
Ejemplo
Definición de una audiencia de usuarios con 3 o más transacciones COMPLETED en los
últimos 90 días.
curl -X POST https://api.reten.ai/api/audience-criterias/validate-from-body \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "x-project-id: <project-id>" \
-H "Content-Type: application/json" \
-d '{
"queryBlocks": [
{
"order": 0,
"from": { "table": "event_transactions" },
"select": {
"distinct": false,
"items": [
{
"kind": "column",
"column": { "table": "event_transactions", "column": "user_id" }
}
]
},
"timeWindow": {
"timeOperator": "IN_THE_LAST",
"reference": { "table": "event_transactions", "column": "event_date" },
"unit": "DAYS",
"value": 90
},
"where": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{
"kind": "predicate",
"column": { "table": "event_transactions", "column": "status" },
"comparisonOperator": "EQUALS",
"value": "COMPLETED"
}
]
},
"groupBy": [{ "table": "event_transactions", "column": "user_id" }],
"having": {
"kind": "group",
"logicalOperator": "AND",
"children": [
{
"kind": "predicate",
"aggregate": {
"aggregationOperator": "COUNT",
"operand": { "kind": "star" }
},
"comparisonOperator": "GREATER_THAN_OR_EQUAL",
"value": 3
}
]
}
}
]
}'import axios from 'axios';
const body = {
queryBlocks: [
{
order: 0,
from: { table: 'event_transactions' },
select: {
distinct: false,
items: [
{
kind: 'column',
column: { table: 'event_transactions', column: 'user_id' },
},
],
},
timeWindow: {
timeOperator: 'IN_THE_LAST',
reference: { table: 'event_transactions', column: 'event_date' },
unit: 'DAYS',
value: 90,
},
where: {
kind: 'group',
logicalOperator: 'AND',
children: [
{
kind: 'predicate',
column: { table: 'event_transactions', column: 'status' },
comparisonOperator: 'EQUALS',
value: 'COMPLETED',
},
],
},
groupBy: [{ table: 'event_transactions', column: 'user_id' }],
having: {
kind: 'group',
logicalOperator: 'AND',
children: [
{
kind: 'predicate',
aggregate: {
aggregationOperator: 'COUNT',
operand: { kind: 'star' },
},
comparisonOperator: 'GREATER_THAN_OR_EQUAL',
value: 3,
},
],
},
},
],
};
const response = await axios.post(
'https://api.reten.ai/api/audience-criterias/validate-from-body',
body,
{ headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const result = response.data; // IValidateAudienceCriteriaResultLa respuesta se documenta en
IValidateAudienceCriteriaResult, con un
ejemplo válido y uno inválido.
POST /api/audience-criterias/:id/validate
Valida un criterio ya guardado, referenciado por su id. La definición no viaja en el
cuerpo: se toma del criterio persistido en el proyecto activo.
Auth: Requerida — permiso audience-criteria:view-sql
Al igual que validate-from-body, el endpoint responde 201 Created —no 200—,
aunque no cree ningún recurso.
Parámetros de ruta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
id | UUID | Sí | Id del audience criteria. Debe ser un UUID válido (si no, 400). |
Este endpoint no recibe cuerpo ni query params.
Ejemplo
curl -X POST https://api.reten.ai/api/audience-criterias/f47ac10b-58cc-4372-a567-0e02b2c3d479/validate \
-H "Authorization: Bearer <token>" \
-H "x-tenant-id: <tenant-id>" \
-H "x-project-id: <project-id>"import axios from 'axios';
const id = 'f47ac10b-58cc-4372-a567-0e02b2c3d479';
const response = await axios.post(
`https://api.reten.ai/api/audience-criterias/${id}/validate`,
undefined, // sin cuerpo
{ headers: { Authorization: 'Bearer <token>', 'x-tenant-id': '<tenant-id>', 'x-project-id': '<project-id>' } },
);
const result = response.data; // IValidateAudienceCriteriaResultRespuesta — IValidateAudienceCriteriaResult
Status 201 Created en ambos endpoints. El cuerpo reporta el resultado del dry-run
de BigQuery:
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
valid | boolean | Sí | true si el dry-run tuvo éxito; false si BigQuery rechazó el query. |
errors | string[] | No | Presente solo cuando valid: false. Trae el/los mensaje(s) de error de BigQuery. |
bytesProcessed | number | Sí | Bytes que BigQuery estima procesar. En éxito, la estimación del dry-run; en fallo, 0. |
El dry-run no ejecuta el query ni devuelve filas: solo lo valida contra BigQuery y
estima su costo (bytesProcessed). Para obtener una muestra de filas reales use
Previsualizar; para ver el SQL
generado, Generar SQL.
errors es un arreglo por contrato, pero en la práctica el dry-run devuelve un único
mensaje (el que reporta BigQuery). Discrimine siempre por valid antes de leer
errors.
Ejemplo — válida (valid: true)
Respuesta 201 a una definición que compila y pasa el dry-run:
{
"valid": true,
"bytesProcessed": 10485760
}Cuando valid es true, errors se omite y bytesProcessed trae la estimación de
BigQuery (en este ejemplo, ~10 MB).
Ejemplo — inválida (valid: false)
La definición pasó la validación estructural y de negocio y se compiló a SQL, pero el
dry-run de BigQuery la rechazó (p. ej. una columna del catálogo que aún no existe en la
tabla real del dataset, o un query cuyo costo estimado supera el límite de bytes
facturables). Aun así el status es 201:
{
"valid": false,
"errors": [
"Unrecognized name: legacy_column at [3:8]"
],
"bytesProcessed": 0
}Con valid: false, errors trae el mensaje de BigQuery y bytesProcessed es 0.
Intersección con la audiencia raíz
Todo proyecto tiene una audiencia raíz, y toda otra
audiencia del proyecto se evalúa dentro de ella. El dry-run corre sobre el mismo SQL que devuelve
Generar SQL:
para cualquier audiencia que no sea la audiencia raíz, intersectado con la audiencia raíz del
proyecto. Por eso valid y bytesProcessed se refieren a esa consulta intersectada, y la
estimación incluye la parte de la audiencia raíz.
No hay intersección cuando la audiencia validada es la propia audiencia raíz — :id/validate con
el id de la audiencia raíz, o un borrador de validate-from-body cuyo criteriaId es la
audiencia raíz.
La intersección es posicional (SELECT * en los dos lados): la audiencia debe proyectar las
mismas columnas que la audiencia raíz, en el mismo orden y con tipos compatibles — con la
definición por defecto de la audiencia raíz, solo user_id. Si no, BigQuery rechaza la
consulta intersectada en el dry-run y la respuesta es valid: false, con el mensaje de
BigQuery en errors.
Validación estructural vs. dry-run
Este es el punto clave del recurso: no todos los fallos llegan a errors[]. Una
solicitud atraviesa etapas y cada una falla de forma distinta.
| Etapa | Cómo falla | Dónde se ve |
|---|---|---|
| 1. Validación del DTO (estructura, tipos, enums) | Error HTTP 400 | message (arreglo de strings); corta la solicitud. |
2. Resolución del criterio (solo :id, criteriaId y composedWith) | Error HTTP 404 | message (string); corta la solicitud. |
| 3. Validación de negocio (catálogo, agregación, tiempo) | Error HTTP 400 | message (string); corta la solicitud. |
| 4. Dry-run de BigQuery | valid: false (status 201) | Campo errors del cuerpo de respuesta. |
En otras palabras:
- Todo lo que ocurre antes del dry-run (etapas 1–3) que falla, responde un error
HTTP y no produce un cuerpo
IValidateAudienceCriteriaResult. Por ejemplo: una tabla fuera del catálogo, un criterio que no proyecta su columna de identidad, untimeWindowmal formado, unido uncriteriaIdinexistente, o unoperandCriteriaIdque no existe. valid: falsees exclusivo del dry-run: la definición ya era estructuralmente válida y se compiló a SQL, pero BigQuery la rechazó al estimarla. El status sigue siendo201.
No asuma que "criterio inválido" siempre llega como valid: false. La mayoría de los
errores de forma y de negocio son errores HTTP 400/404 y nunca alcanzan el
dry-run. valid: false significa específicamente que BigQuery rechazó un query que
ya había pasado toda la validación local.
Reglas de validación
Antes del dry-run, la definición pasa por las mismas dos capas de validación que
Crear, y ambas
devuelven 400 Bad Request:
- Validación del DTO (estructura, tipos, enums, campos requeridos).
messagees un arreglo de strings y se rechazan propiedades desconocidas (incluidosname/description/format). - Validación de negocio (la definición debe generar SQL válido y coherente contra el
catálogo).
messagees un string único con la regla incumplida, y se detiene en el primer invariante violado.
La validación de negocio corre en ambos endpoints: validate-from-body valida la
definición del cuerpo, y :id/validate valida el criterio guardado antes de compilarlo.
El catálogo completo de reglas y sus mensajes exactos está en
Crear → Reglas de validación.
Respuestas de error
Estos son los errores HTTP (distintos de valid: false, que viaja con status 201):
| Estado | Descripción |
|---|---|
400 | DTO inválido (campo faltante, tipo incorrecto, enum fuera de rango, propiedad desconocida como name/description/format) o una regla de negocio incumplida. En :id/validate, también si el id no es un UUID válido; en validate-from-body, si no lo es el criteriaId. |
401 | Falta o es inválido el Authorization: Bearer. |
403 | El usuario no tiene el permiso audience-criteria:view-sql, o el x-tenant-id no está autorizado. |
404 | En :id/validate, no existe un audience criteria con ese id en el proyecto activo. En validate-from-body, un operandCriteriaId de composedWith o el criteriaId no existe en el proyecto activo. |
Generar SQL
Compila una definición de audience criteria a SQL de BigQuery, desde el cuerpo (aún sin persistir) o por id de un criterio guardado, en formato parametrizado o literal.
Previsualizar
Ejecuta una audience criteria contra BigQuery y devuelve una muestra de filas reales más el total, desde el cuerpo (aún sin persistir) o por id de un criterio guardado.