La deduplicación de usuarios es una actividad importante para cualquiera que gestione un corpus de datos relacionados con usuarios. En algunos casos, es posible que la base de datos contenga varias versiones de un mismo usuario. Esto puede ocurrir por varias razones: mala gestión de los datos, datos de usuario no coincidentes, error del usuario o del sistema, etc. También es frecuente la agregación de información de usuarios procedente de muchos sistemas diferentes. Es posible que disponga de su conjunto de datos de usuarios principal, pero que desee enriquecerlo con datos de terceros, como redes sociales, sistemas de venta de entradas o de registro, o incluso con información adicional de usuarios procedente de otros silos de su propia organización.

Este artículo ha sido escrito por Andy Kruth. Andy Kruth es Consultor Principal en Avalon Consulting, LLC donde se centra en la arquitectura y el desarrollo de sistemas Big Data y soluciones NoSQL. Este post fue influenciado por el trabajo realizado para The Cincinnati Reds. Aunque el siguiente ejemplo puede parecer simple, se basa en una aplicación mucho más compleja creada para los Reds en la que los datos de usuario de varias facetas del negocio de los Reds se deduplicaron en un único conjunto de datos de cuentas y hogares.
Sea cual sea el caso, la deduplicación de la información de los usuarios en un conjunto de datos conciso y preciso es una actividad habitual. Un enfoque tradicional para lograr esto es simplemente ejecutar toda la información de usuario a través de una aplicación para que coincida con los registros sobre la base de la heurística definida y escupir un único conjunto de datos. Aunque esto "funciona", a menudo se trata de una acción que hay que realizar por lotes de forma regular, o al menos de forma regular en función de los deltas que se puedan capturar. Pero el inconveniente de este enfoque es que siempre que se utilizan acciones por lotes o programadas existe un gran potencial de que los datos resultantes sean obsoletos.
Con la introducción de Eventos de Couchbaseahora tiene una nueva opción para una deduplicación eficaz, ¡y es en tiempo real! Me abstendré de la información general sobre Eventing que ya se ha tratado a fondo en otras publicaciones (véase aquíy aquíy aquí). En su lugar, este post se centrará en un ejemplo sencillo de cómo puedes desarrollar un motor de deduplicación en tiempo real usando Couchbase Eventing. Empecemos.
La configuración del cubo
Para esta demostración necesitarás un total de 4 cubos:
- metadata - este es su cubo de metadatos obligatorio que contiene toda la información para ejecutar sus Funciones de Eventos.
- staging - este bucket actuará como zona de aterrizaje para todos sus datos de usuario.
- fieldindex - este bucket va a actuar como índice de la información de campo que necesitamos para emparejar a los usuarios
- usuarios - este cubo es nuestro producto final: un conciso conjunto de datos de usuarios
Configuración de funciones
Cuando creemos nuestra función de deduplicación (yo llamé a la mía "dedupe"), apunta el bucket de metadatos a "metadata" y el bucket de origen a "staging". También tendrás que definir dos alias: "users" que puede apuntar al bucket de usuarios y "fieldindex" que puede apuntar al bucket de fieldindex.
El código de función
Para esta demostración consideraremos un escenario de deduplicación muy simple: Queremos una lista concisa de usuarios basada en su dirección de correo electrónico y, para cada usuario, una lista de todos los documentos originales que contengan ese correo electrónico con fines de auditoría posterior.
Para este ejemplo trabajaremos con documentos muy sencillos. Aquí tienes uno para que te hagas una idea:
|
1 2 3 |
"test::1" { "email": "abc@abc.com" } |
El código de función completo es el siguiente
|
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 |
función OnUpdate(doc, meta) { var correo electrónico = getEmailDesdeDoc(doc) si (correo electrónico != null) { var usuario = getUserFromEmail(correo electrónico) var userDoc = {} si (usuario != null) { userDoc = usuarios[usuario] userDoc.coincide con = userDoc.coincide con.concat([meta.id]) } si no { userDoc = doc userDoc.nombre de usuario = meta.id userDoc.creado = Fecha.ahora() userDoc.coincide con = [meta.id] } usuarios[userDoc.nombre de usuario] = userDoc addEmailToFieldIndex(correo electrónico, userDoc.nombre de usuario) } si no { // doc no tiene email y no es válido para la deduplicación } } función getEmailDesdeDoc(doc) { para (var clave en doc) { var campo = clave.toLowerCase() si (campo.incluye("email")) { devolver doc[clave] } } devolver null } función getUserFromEmail(correo electrónico) { pruebe { devolver índice de campo["email::" + correo electrónico].nombre de usuario } captura (e) { // no se ha encontrado al usuario, devolvamos null } devolver null } función addEmailToFieldIndex(correo electrónico, usuario) { índice de campo["email::" + correo electrónico] = {"userid": usuario} } |
Análisis del documento
Comenzamos en la función OnUpdate(doc, meta). Esta función es invocada cada vez que se produce una mutación en los documentos del staging bucket, es decir, cada vez que se añade o actualiza uno de nuestros documentos en bruto. El primer paso es determinar la dirección de correo electrónico del documento entrante. Para ello, tenemos una pequeña función de ayuda: getEmailFromDoc(doc). Esta función simplemente itera a través de todos los nombres de campo del documento en busca de uno que contenga la cadena "email", entonces devolvemos ese valor. En esta función también puedes optar por buscar nombres de campo específicos. Debe tener en cuenta que si tiene varias fuentes de datos aquí, cada fuente puede utilizar diferentes nombres de campo, por lo que un enfoque generalizado anterior puede funcionar para usted.
Encontrar una coincidencia
Con la dirección de correo electrónico en la mano, es hora de seguir adelante. Si no tenemos una dirección de correo electrónico simplemente descartaremos el documento en este ejemplo. Asumiendo que hemos obtenido con éxito una dirección de correo electrónico el siguiente paso es recuperar nuestro usuario canónico del bucket "users" si ese usuario ya existe. Para ello tenemos una segunda función de ayuda: getUserFromEmail(email). Esta función utiliza nuestro bucket "fieldindex" que simplemente utilizaremos como valor clave para buscar correos electrónicos. Buscamos en el bucket "fieldindex" un documento que coincida con nuestro correo electrónico. Observará que anteponemos un tipo de dato a las claves de "fieldindex", lo que le permitirá añadir más tipos de datos más adelante sin confundirse. Si encontramos el email en "fieldindex" entonces devolvemos el userid asociado, en caso contrario simplemente null.
Deduplicación
De vuelta en la función OnUpdate, si tenemos éxito en encontrar un userid existente, entonces recuperamos ese usuario existente del bucket "users" y simplemente añadimos el meta.id de nuestros documentos actuales a la lista de documentos coincidentes. Si no encontramos un userid existente, entonces tenemos que crear un nuevo usuario. Para ello, simplemente empezamos con el documento original y añadimos algunos campos, incluyendo una fecha de creación y el inicio de un campo de coincidencias que contendrá una lista de documentos que coincidan con el correo electrónico de este usuario.
Después de obtener o crear un nuevo documento de usuario almacenamos ese documento en el bucket "usuarios". Necesitamos un meta.id para almacenar el usuario. En este ejemplo estamos utilizando simplemente el meta.id del documento original que genera este usuario, pero usted puede elegir otra cosa, depende de usted. Si el usuario ya existía, simplemente se sobrescribirá.
Indexación del usuario
Por último, tenemos que actualizar nuestro cubo "fieldindex" para que los futuros documentos con esta dirección de correo electrónico puedan identificarse correctamente. Para ello utilizaremos nuestra última función de ayuda: addEmailToFieldIndex(email, user). Esta función simplemente escribe el email dado como clave y el userid dado como cuerpo del documento en el bucket "fieldindex".
¡Y ya está! En sólo 50 líneas de código tienes los inicios de un motor de deduplicación de usuarios en tiempo real. Vamos a probarlo.
Probar la función
Después de desplegar esta función, podemos probarla creando algunos documentos en nuestro bucket "staging".
Primero vamos a crear un usuario base. Crear un nuevo documento en el cubo "staging" que se parece a esto:
|
1 2 3 |
"test::1" { "email": "abc@abc.com" } |
Deberías poder ver los efectos inmediatos en los buckets "fieldindex" y "users". En "fieldindex" tenemos nuestro documento clave/valor creado:
|
1 2 3 |
correo electrónico::abc@abc.com { "userid":"test::1" } |
Fíjate que es simplemente el email apuntando a un userid que en este caso es el meta.id de nuestro primer documento original: test::1.
A continuación, mira el cubo "usuarios". Aquí tenemos el nuevo usuario que se ha creado:
|
1 2 3 4 5 6 |
prueba::1 { "email":"abc@abc.com", "userid":"test::1", "creado":1546531965825, "cerillas":["test::1"] } |
Observe que tenemos la dirección de correo electrónico original, así como algunos campos adicionales, incluida una lista completa de documentos coincidentes (que en este momento es sólo nuestro primer documento).
Vamos a añadir algunos documentos más para probar nuestros casos extremos. Añade los dos documentos siguientes al bucket "staging":
|
1 2 3 4 5 6 7 |
prueba::2 { "nombre":"Tavi" } prueba::3 { "useremail":"abc@abc.com" } |
El documento test::2 no tiene un campo de correo electrónico y sólo sirve para comprobar que no tenemos en cuenta los documentos que no tienen correo electrónico.
El documento test::3 comprueba un par de cosas. En primer lugar, el campo de correo electrónico se llama "useremail", pero esto debería detectarse sin problemas debido a nuestra forma general de encontrar campos de correo electrónico. En segundo lugar, ¡tiene un correo electrónico que coincide! Comprobemos nuestro cubo "usuarios" y veamos cómo han ido las cosas. Seguimos teniendo un solo usuario, pero el documento es ligeramente diferente:
|
1 2 3 4 5 6 |
prueba::1 { "email":"abc@abc.com", "userid":"test::1", "creado":1546531965825, "cerillas":["test::1", "test::3"] } |
Observe que la única diferencia es que ahora tenemos una referencia al documento test::3 en nuestro campo matches. ¡Hurra! Hemos deduplicado con éxito esos documentos.
Conclusión
Como se muestra arriba el nuevo sistema de Eventing de Couchbase puede ser usado para crear un motor de deduplicación de usuarios en tiempo real. Aunque este ejemplo fue extremadamente simplificado, javascript (el lenguaje elegido para las funciones de Eventing) es extremadamente versátil y puedes hacer casi cualquier cosa que quieras.
Un motor de deduplicación más funcional puede tener en cuenta muchos más campos que sólo los correos electrónicos de los usuarios. Puede utilizar una heurística mucho más compleja para emparejar documentos. Incluso puede considerar un segundo nivel de emparejamiento donde agrupa usuarios específicos en Hogares basados en su información de identificación. Todo esto depende de ti, pero con Couchbase Eventing la deduplicación en tiempo real se hace fácil.