En las aplicaciones empresariales, los datos suelen modelarse para un gran número de consultas concurrentes de baja latencia. Sin embargo, si se desea obtener información sobre las tendencias, se acaba deseando tener un modelo de datos completamente distinto. Los medios tradicionales para cuadrar este círculo han sido mover, transformar y cargar los datos en otro lugar, pero esto introduce su propia serie de problemas, como una latencia inaceptable, múltiples fuentes de verdad y muchos gastos.
Los clientes de Couchbase saben que el servicio Analytics proporciona una manera fácil de manejar informes analíticos y de tendencias en tiempo real sobre los datos que tienen en producción en este momento. Un ejemplo de esto surgió recientemente cuando trabajamos con un cliente que buscaba identificar la actividad de clientes de alto nivel asociada a un programa de fidelización de socios corporativos. El modelo de documento subyacente se diseñó claramente pensando en la aplicación interactiva, más que en la elaboración de informes. (Esto no es en absoluto infrecuente, como sabrá por experiencia dolorosa.) Echemos un vistazo rápido al problema y a cómo lo resolvimos.
Ejemplo de documento
El modelo de documento en nuestro caso (soporte de una aplicación de reservas online) consta de cuatro secciones. La primera incluye los identificadores básicos del documento y la aplicación. La segunda describe la información de reserva de una excursión. La tercera contiene detalles sobre uno o varios itinerarios asociados a la reserva, junto con los requisitos de uno o varios pasajeros. La última sección describe los programas de fidelización corporativos a los que podría pertenecer cada uno de los pasajeros.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
{ "_tipo": "booking", "_encabezado": { "creado": 1562888960, "fuente": "app", "version": "v1.1" }, "booking": { "status": "RESERVADO", "bookingType": "agencia", "detalles": { "agente": "FBL33", "contacto": "Arlene", "asientos": 2, "excursión": { "embarcando": 1562958000, "equipo": "123X", "línea": "SRF", "fromStation": { "código": "LAX", "facilityType": 1 }, "toStation": { "código": "SOL", "facilityType": 2 }, "bookingAgency": "PC", "agencyType": "3" } } }, "itinerario": [ { "daysOnboard": 1, "pasajeros": [ { "passengerNumber": 1, "acomodaciones especiales": falso }, { "passengerNumber": 2, "acomodaciones especiales": falso } ], "itineraryType": "negocios" } ], "passengerDetails": [ { "loyaltyId": "aaaabbbbccccdddd", "passId": 1, "tipo de programa": { "socioempresarial": verdadero, "partnerId": 1 } }, { "loyaltyId": "eeeeffffgggghhhh", "passId": 2, "tipo de programa": { "socioempresarial": falso } } ] } |
Elementos de consulta
Para completar el análisis, mi cliente debía extraer o filtrar los siguientes campos:
- estado, equipo, embarque (convertido a formato legible por humanos), línea, _tipo, daysOnboard, passengerNumber, loyaltyId, partnerId
El problema, por supuesto, es que estos campos existen en niveles jerárquicos completamente diferentes dentro del modelo de documento. Algunos son valores escalares, fácilmente accesibles desde una simple consulta:
- estado, equipo, embarque, línea, _tipo
Otro es un elemento dentro de un array (compuesto por itinerarios de viaje), que no debe estar anidado:
- díasA bordo
Dentro de esta misma matriz hay una segunda matriz (compuesta por los datos de los pasajeros), un elemento de la cual debe utilizarse como clave de unión:
- passengerNumber
Esta clave de unión se utiliza para acceder a los elementos de una tercera matriz, que por razones de aplicación empresarial no está anidada dentro de la segunda:
- loyaltyId, partnerId
Estos diferentes niveles equivalen a diferentes rutas de acceso, lo que añade cierta complejidad al análisis. Afortunadamente, N1QL for Analytics proporciona las herramientas sintácticas que necesitamos. A continuación encontrará una descripción paso a paso del proceso que podría utilizar para construir su consulta.
Paso 1 - selección simple de un elemento escalar
Este paso debería estar bastante claro para las personas con experiencia en SQL. Utilizamos una sentencia select para recuperar un valor escalar del bucket de líneas. Calificamos el campo de estado como parte de la sección de reserva y limitamos el número de registros a devolver.
1 2 3 |
seleccione reserva.estado de líneas límite 1; |
Resultados de la consulta:
1 2 3 4 5 |
[ { "status": "RESERVADO" } ] |
Paso 2 - Deshacer y añadir el elemento de la primera matriz
A continuación añadimos los datos de la sección itinerario del documento. Sin embargo, como estos elementos están incrustados en una matriz, primero debemos anular su anidación.
1 2 3 4 5 |
seleccione l.reserva.estado, i.díasA bordo de líneas l sin anestesia l.itinerario i límite 1; |
Resultados de la consulta:
1 2 3 4 5 6 |
[ { "status": "RESERVADO", "daysOnboard": 1 } ] |
Paso 3 - Desnuestre y añada el elemento de la segunda matriz (dentro de la primera)
Ahora añadimos elementos del array de pasajeros incrustado. (Observa que aumentamos nuestro límite para asegurarnos de que realmente estamos accediendo a más de un elemento del array).
1 2 3 4 5 6 7 |
seleccione l.reserva.estado, i.díasA bordo, p.passengerNumber de líneas l sin anestesia l.reserva.itinerario i sin anestesia i.pasajeros p límite 2; |
Resultados de la consulta:
1 2 3 4 5 6 7 8 9 10 11 12 |
[ { "status": "RESERVADO", "daysOnboard": 1, "passengerNumber": 1 }, { "status": "RESERVADO", "daysOnboard": 1, "passengerNumber": 2 } ] |
Paso 4 - Unnest y añadir el elemento de la tercera matriz, accesible a través de join
Los elementos de la tercera matriz (passengerDetails) no deben estar anidados y deben estar vinculados a los elementos de la matriz passengers anterior. Esto se hace mediante la cláusula where.
1 2 3 4 5 6 7 8 9 10 |
seleccione l.reserva.estado, i.díasA bordo, p.passengerNumber, pd.loyaltyId de líneas l sin anestesia l.itinerario i sin anestesia i.pasajeros p sin anestesia l.passengerDetails pd donde p.passengerNumber = pd.passId límite 2; |
Resultados de la consulta:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[ { "status": "RESERVADO", "daysOnboard": 1, "passengerNumber": 1, "loyaltyId": "aaaabbbbccccdddd" }, { "status": "RESERVADO", "daysOnboard": 1, "passengerNumber": 2, "loyaltyId": "eeeeffffgggghhhh" } ] |
Paso 5 - Añadir los demás elementos de la consulta
Se requieren otros campos para completar la consulta. Observe especialmente el campo _type añadido a la cláusula where. Con toda probabilidad, en un sistema de producción, un bucket contendrá documentos de varios tipos. Los resultados de la consulta pueden filtrarse en la propia consulta (como en el ejemplo siguiente) o como parte de la creación del conjunto de datos Analytics.
1 2 3 4 5 6 7 8 9 10 11 12 |
seleccione l.reserva.estado, l.reserva.detalles.excursión.equipo, l.reserva.detalles.excursión.línea, i.díasA bordo, p.passengerNumber, pd.loyaltyId, pd.programType.partnerId, milis_to_str(l.reserva.detalles.excursión.embarcando*1000) embarque de líneas l sin anestesia l.itinerario i sin anestesia i.pasajeros p sin anestesia l.passengerDetails pd donde p.passengerNumber = pd.passId y l.Tipo = "booking" y str_to_millis("2019-07-12T19:00:00Z") = l.reserva.detalles.excursión.embarcando*1000; |
Resultados de la consulta:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[ { "embarcando": "2019-07-12T19:00:00Z", "status": "RESERVADO", "equipo": "123X", "línea": "SRF", "daysOnboard": 1, "passengerNumber": 1, "loyaltyId": "aaaabbbbccccdddd", "partnerId": 1 }, { "embarcando": "2019-07-12T19:00:00Z", "status": "RESERVADO", "equipo": "123X", "línea": "SRF", "daysOnboard": 1, "passengerNumber": 2, "loyaltyId": "eeeeffffgggghhhh" } ] |
Para saber más
Unnest es una potente característica de N1QL for Analytics, que le proporciona los medios para dominar múltiples elementos incrustados dentro de sus datos. Puede encontrar más información sobre su sintaxis aquí: https://docs.couchbase.com/server/6.0/analytics/3_query.html#Unnest_clauses
Una guía completa de N1QL for Analytics -me enorgullece poseer una copia firmada- puede encontrarse aquí: https://www.amazon.com/SQL-Users-Tutorial-Don-Chamberlin/dp/0692184503/
Pruébelo usted mismo
Vaya directamente a https://docs.couchbase.com/server/6.0/analytics/quick-start.html#Using_docker y empieza de inmediato con un tutorial basado en Docker. O si lo prefieres, descarga Couchbase Server 6 Enterprise desde esta página: https://www.couchbase.com/downloads