Tutorial: demo e-commerce para operar Unomi desde Inoyu OSS UI¶
Recorrido práctico en el banco de pruebas local (setup); no ejecutar en
producción. Script: unomi-eval/tests/08-demo-ecommerce.sh;
tienda: unomi-eval/tests/tienda-demo.html.
Qué hay cargado en Unomi (scope eval-shop)¶
./08-demo-ecommerce.sh deja, todo con prefijo eval-:
| Objeto | Ids | Para ver en la UI |
|---|---|---|
| 24 clientes con nombre, ciudad, correo | eval-demo-p-00 … eval-demo-p-23 |
Profiles |
| Arquetipos (6 c/u) | 00-05 navegadores · 06-11 carrito abandonado · 12-17 compradores · 18-23 recurrentes/VIP | Profiles → Segments del perfil |
| Identity stitching | eval-demo-p-movil-anonimo → alias de eval-demo-p-00 (Lucía Benítez) |
Profiles → abrir el móvil resuelve al maestro |
| Reglas | eval-r1-purchase (cuenta compras), eval-r3-monto-compras (suma importes), eval-r2a-count-*, eval-r2b-interes-*, eval-login-merge |
Rules (+ estadísticas) |
| Segmentos | eval-compradores-recurrentes (6), eval-carrito-abandonado (7), eval-interes-electronica (3), eval-interes-hogar, eval-vip (3), eval-asuncion (8) |
Segments |
| Scoring | eval-engagement (20/30/50 por 1/2/3 compras, +10 interés, +5 carrito 30 días) |
Scoring |
| Goal / lista | eval-conversion (productView → purchase), eval-newsletter |
Goals / User Lists |
| Tipos de propiedad | totalPurchases, montoCompras, categoriaInteres, ciudad, vistas_<cat>, … (tag eval-demo) |
Property Types |
Comandos¶
cd unomi-eval/tests
./08-demo-ecommerce.sh # siembra (idempotente: purga y recrea los perfiles demo)
python3 -m http.server 8090 --bind 0.0.0.0 # sirve la tienda (o SERVE=1 ./08-demo-ecommerce.sh)
./99-cleanup.sh # borra TODO lo eval-* (perfiles, reglas, segmentos, scoring, goal, lista, esquemas, scope)
- UI: http://localhost:3131. Usuario y clave en
unomi-eval/.env(INOYU_ADMIN_*). - Tienda demo:
http://localhost:8090/tienda-demo.html. Cada clic manda un evento real; el panel derecho muestra profileId, segmentos y puntos, con enlace al perfil en Inoyu. La clave del login la lee deresults/08-demo.json(la escribe el script); también se puede pasar como?peer=<UNOMI_THIRDPARTY_PROVIDER1_KEY>.
Guion de 10 minutos con la tienda¶
- Abrir la tienda: se crea un visitante anónimo (profileId en el panel). En Inoyu → Profiles aparece arriba (orden por última actualización).
- Ver tres productos de electronica → regla
eval-r2b-interes-electronicafijacategoriaInteresy el perfil entra eneval-interes-electronica(+10 puntos). - Al carrito → entra en
eval-carrito-abandonado(+5 puntos). - Comprar → sale de carrito abandonado;
totalPurchases=1,lastOrderId, 20 puntos más (reglaeval-r1-purchase+ scoring). - Segunda compra → entra en
eval-compradores-recurrentes; con 3 compras y 100+ puntos →eval-vip. - Identificarme con
lucia.benitez@demo.test→ eventologinprotegido → el visitante se fusiona coneval-demo-p-00: su historial pasa al perfil de Lucía y el id anónimo queda como alias. - En Inoyu → Segments: los conteos cambian; Rules:
executionCountde las reglas crece; Scoring: el plan muestra sus elementos.
Estado verificado (Unomi 3.0.0 + Inoyu commit e78c1bd, 2026-09-07)¶
- Funciona: Profiles (lista, detalle con propiedades/segmentos/scores/eventos), Personas, Segments (lista, conteo, alta/edición en JSON), Rules (lista, estadísticas, alta en JSON), Scoring, Goals, Campaigns, User Lists, Property Types, Scopes, Condition/Action Types.
- No funciona: Groovy Actions (500 de Unomi 3.0.0), Tenants (oculto: exige 3.1). JSON Schemas y los eventos del detalle de perfil funcionan gracias al parche
patch-json-schemas.shde la imagen. - En la edición open source de Inoyu, condiciones y acciones se editan en JSON (Monaco con validación); el constructor visual es de la versión Pro.
- La búsqueda de perfiles filtra solo la página cargada (50); para buscar en todo el dataset hay que usar la condición de búsqueda o la API.
Si un clic en la tienda no cambia nada en el perfil¶
Unomi valida cada evento contra su esquema JSON y, si lo rechaza, responde igual HTTP 200 con updated: 0;
el único rastro es un WARN SchemaServiceImpl en el log:
docker logs unomi-eval-unomi --since 10m 2>&1 | grep -A1 "Schema validation"
Caso ya corregido (2026-09-07): el tracker JS añade source (página) y target (producto/pedido) a cada
evento y los esquemas de unomi-eval/tests/schemas/*.json no los declaraban (unevaluatedProperties: false), así que la tienda
generaba perfiles anónimos sin eventos ni segmentos. Los esquemas ahora declaran ambos campos.
El botón Identificarme necesita la clave X-Unomi-Peer: la toma de results/08-demo.json o de ?peer= en la
URL; sin ella la página lo avisa en rojo y en el registro de eventos ("falta ?peer=") y no envía nada.
Segmentos que "faltan" en el panel: eval-carrito-abandonado excluye a quien compró en los últimos 7 días,
eval-vip exige 100 puntos y los de interés existen para las cuatro categorías. Los tres indicadores del
encabezado del perfil en Inoyu (Engagement Score 87, Lifetime Value $3,250, "undefined undefined" sin nombre)
son valores fijos de demostración del código de la UI, no datos de Unomi; los reales están en Properties,
Segments y en scores del perfil.
Plantillas para crear desde la UI (no mezclarlas)¶
- Segments → nuevo: pegar SOLO la condición (objeto que empieza por
"type"). Ejemplo, compras mayores a 2.000.000 Gs. en el último año (9 perfiles con la demo):
{"type":"pastEventCondition","parameterValues":{"numberOfDays":365,"minimumEventCount":1,
"eventCondition":{"type":"booleanCondition","parameterValues":{"operator":"and","subConditions":[
{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"purchase"}},
{"type":"eventPropertyCondition","parameterValues":{"propertyName":"properties.total","comparisonOperator":"greaterThan","propertyValueInteger":2000000}}]}}}}
- Rules → nueva: nombre/descripción/prioridad en el formulario; en "Condition JSON" solo la condición
(
{"type":..., "parameterValues":...}); cada acción en su propio bloque con "Add Action" ({"type":"setPropertyAction","parameterValues":{...}}). El id lo genera Inoyu. - El objeto completo (
metadata+condition+actions) es el formato de la API REST (POST /cxs/rules), no del formulario: pegarlo en cualquiera de los editores da "Missing property type".
Cómo funciona un goal (ejemplo eval-conversion)¶
Un goal mide conversión por sesión: evento de inicio (productView) y evento objetivo (purchase). Al
crearlo, Unomi genera dos reglas ocultas (eval-conversionStartEvent, eval-conversionTargetEvent) que, la
primera vez que cada evento ocurre en una sesión, escriben la marca de tiempo en
session.systemProperties.goals.eval-conversionStartReached / ...TargetReached. El informe cuenta sesiones con
inicio, sesiones con objetivo y la tasa (con la demo: 28 inicios, 15 conversiones, 53,6 %), y puede partirse por
cualquier propiedad ({"aggregate":{"type":"terms","property":"profile.properties.ciudad"}}). Un goal solo
cuenta sesiones posteriores a su creación y una vez por sesión; las campañas agrupan goals con fechas y costo.
Atributos calculados por cliente (contar y sumar compras)¶
Unomi no tiene fórmulas: un atributo calculado es una regla que reacciona a cada evento y actualiza una propiedad del perfil. Dos casos ya cargados:
- Cantidad de compras (
totalPurchases): reglaeval-r1-purchase, acción{"type":"incrementPropertyAction","parameterValues":{"propertyName":"totalPurchases"}}→ suma 1 porpurchase. - Importe acumulado (
montoCompras, Gs.): reglaeval-r3-monto-compras, acción{"type":"incrementPropertyAction","parameterValues":{"propertyName":"montoCompras","propertyTarget":"total"}}.propertyTargettoma el valor detarget.properties.<campo>del evento (no deproperties), por eso elpurchasedebe llevar"target":{"itemId":"<orderId>","itemType":"order","properties":{"total":2450000}}(el sembrado y la tienda ya lo envían así). Solo suma enteros de 32 bits: por encima de 2.147.483.647 Gs. acumulados falla; para importes grandes, enviar el total en miles. - Sin
target.properties.totalla acción lanza un error en el log de Unomi y no suma; por eso la regla exigetarget.properties.totalconcomparisonOperator: exists. - Las reglas no reprocesan el historial: el atributo se calcula desde que existe la regla. Para recalcular
desde eventos pasados hay que reenviarlos o calcular fuera (por ejemplo desde Elasticsearch) y escribir la
propiedad con
POST /cxs/profiles. - Cálculos que no sean sumas de enteros (promedios, ticket medio, decimales) requieren una acción Groovy
(
/cxs/groovyActions, funciona en 3.0.0 aunque la pantalla de Inoyu no lista) o un proceso externo.
Ejemplo guiado: contar productos vistos por cliente (desde la UI)¶
- Property Types → Create Property Type (pestaña Basic, con el parche de la imagen): ID
productosVistos, NameProductos vistos, Targetprofiles, Value typeinteger→ Save. Alternativa por API:curl -u karaf:CLAVE -H 'Content-Type: application/json' -X POST http://localhost:8181/cxs/profiles/properties -d '{"itemId":"productosVistos","metadata":{"id":"productosVistos","name":"Productos vistos"},"type":"integer","target":"profiles"}'. Declararlo es opcional: Unomi crea la propiedad al primer incremento, pero así queda tipada y con nombre legible. - Rules → Create Rule: nombre
Cuenta productos vistos; Condition JSON{"type":"eventTypeCondition","parameterValues":{"eventTypeId":"productView"}}; Add Action →{"type":"incrementPropertyAction","parameterValues":{"propertyName":"productosVistos"}}→ Create Rule. - En la tienda, "Ver" tres productos → el perfil muestra
productosVistos: 3. Cuenta vistas (repetidas incluidas), no productos distintos; solo desde que existe la regla; los contadoresvistas_<categoría>de la demo usan el mismo mecanismo.
Si un contador sube de a dos¶
Inoyu genera un id nuevo (rule-<timestamp>) cada vez que se pulsa Create Rule: guardar dos veces la misma regla
(por ejemplo tras un error de validación, o un doble clic) crea dos reglas iguales y cada evento incrementa
dos veces. Se ve en Rules: dos filas con el mismo nombre y el mismo executionCount. Borrar una con el icono de
papelera y corregir el valor del perfil por API (POST /cxs/profiles con el documento completo). Ocurrió el
2026-09-07 con "eval: Cuenta productos vistos" (26 = 2 × 13 vistas).