Saltar a contenido

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 de results/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

  1. Abrir la tienda: se crea un visitante anónimo (profileId en el panel). En Inoyu → Profiles aparece arriba (orden por última actualización).
  2. Ver tres productos de electronica → regla eval-r2b-interes-electronica fija categoriaInteres y el perfil entra en eval-interes-electronica (+10 puntos).
  3. Al carrito → entra en eval-carrito-abandonado (+5 puntos).
  4. Comprar → sale de carrito abandonado; totalPurchases=1, lastOrderId, 20 puntos más (regla eval-r1-purchase + scoring).
  5. Segunda compra → entra en eval-compradores-recurrentes; con 3 compras y 100+ puntos → eval-vip.
  6. Identificarme con lucia.benitez@demo.test → evento login protegido → el visitante se fusiona con eval-demo-p-00: su historial pasa al perfil de Lucía y el id anónimo queda como alias.
  7. En Inoyu → Segments: los conteos cambian; Rules: executionCount de 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.sh de 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): regla eval-r1-purchase, acción {"type":"incrementPropertyAction","parameterValues":{"propertyName":"totalPurchases"}} → suma 1 por purchase.
  • Importe acumulado (montoCompras, Gs.): regla eval-r3-monto-compras, acción {"type":"incrementPropertyAction","parameterValues":{"propertyName":"montoCompras","propertyTarget":"total"}}. propertyTarget toma el valor de target.properties.<campo> del evento (no de properties), por eso el purchase debe 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.total la acción lanza un error en el log de Unomi y no suma; por eso la regla exige target.properties.total con comparisonOperator: 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)

  1. Property Types → Create Property Type (pestaña Basic, con el parche de la imagen): ID productosVistos, Name Productos vistos, Target profiles, Value type integer → 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.
  2. Rules → Create Rule: nombre Cuenta productos vistos; Condition JSON {"type":"eventTypeCondition","parameterValues":{"eventTypeId":"productView"}}; Add Action → {"type":"incrementPropertyAction","parameterValues":{"propertyName":"productosVistos"}} → Create Rule.
  3. 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 contadores vistas_<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).