¿Cuál es la mejor forma de estructurar datos en firebase?

111

Soy nuevo en firebase y quiero saber cuál es la mejor forma de estructurar los datos en él.

Tengo un ejemplo simple:

Hay candidatos y aplicaciones en mi proyecto. 1 solicitante puede tener varias aplicaciones. ¿Cómo puedo relacionar estos 2 objetos en firebase? ¿Funciona como una base de datos relacional? ¿O el enfoque debe ser completamente diferente en términos de diseño de datos?

tolva
fuente

Respuestas:

137

ACTUALIZACIÓN : ahora hay un documento sobre la estructuración de datos . Además, vea esta excelente publicación sobre estructuras de datos NoSQL .

El principal problema con los datos jerárquicos, a diferencia del RDBMS, es que es tentador anidar datos porque podemos. Generalmente, desea normalizar los datos hasta cierto punto (tal como lo haría con SQL) a pesar de la falta de consultas y declaraciones de combinación.

También desea desnormalizar en lugares donde la eficiencia de lectura es una preocupación. Esta es una técnica utilizada por todas las aplicaciones a gran escala (por ejemplo, Twitter y Facebook) y, aunque va en contra de nuestros principios DRY, generalmente es una característica necesaria de las aplicaciones escalables.

La esencia aquí es que desea trabajar duro en las escrituras para facilitar la lectura. Mantenga separados los componentes lógicos que se leen por separado (por ejemplo, para salas de chat, no coloque los mensajes, la metainformación sobre las salas y las listas de miembros en el mismo lugar, si desea poder iterar los grupos más adelante).

La principal diferencia entre los datos en tiempo real de Firebase y un entorno SQL es la consulta de datos. No hay una forma sencilla de decir "SELECCIONAR USUARIOS DONDE X = Y", debido a la naturaleza en tiempo real de los datos (cambia constantemente, se fragmenta, se reconcilia, etc., lo que requiere un modelo interno más simple para mantener a los clientes sincronizados bajo control)

Un ejemplo simple probablemente lo pondrá en el estado mental correcto, así que aquí va:

/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets

Ahora, dado que estamos en una estructura jerárquica, si quiero iterar las direcciones de correo electrónico de los usuarios, hago algo como esto:

// I could also use on('child_added') here to great success
// but this is simpler for an example
firebaseRef.child('users').once('value')
.then(userPathSnapshot => {
   userPathSnapshot.forEach(
      userSnap => console.log('email', userSnap.val().email)
   );
})
.catch(e => console.error(e));

El problema con este enfoque es que acabo de obligar al cliente a descargar todos los usuarios messagesy widgetstambién. No es problema si ninguna de esas cosas se cuentan por miles. Pero es un gran problema para 10.000 usuarios con más de 5.000 mensajes cada uno.

Así que ahora la estrategia óptima para una estructura jerárquica en tiempo real se vuelve más obvia:

/user_meta/uid/email
/messages/uid/...
/widgets/uid/...

Una herramienta adicional de gran utilidad en este entorno son los índices. Al crear un índice de usuarios con ciertos atributos, puedo simular rápidamente una consulta SQL simplemente iterando el índice:

/users_with_gmail_accounts/uid/email

Ahora, si quiero, digamos, recibir mensajes para los usuarios de Gmail, puedo hacer algo como esto:

var ref = firebase.database().ref('users_with_gmail_accounts');
ref.once('value').then(idx_snap => {
   idx_snap.forEach(idx_entry => {
       let msg = idx_entry.name() + ' has a new message!';
       firebase.database().ref('messages').child(idx_entry.name())
          .on(
             'child_added', 
             ss => console.log(msg, ss.key);
          );
   });
})
.catch(e => console.error(e));

Ofrecí algunos detalles en otra publicación de SO sobre la desnormalización de datos, así que échales un vistazo también . Veo que Frank ya publicó el artículo de Anant, así que no lo reiteraré aquí, pero también es una gran lectura.

Kato
fuente
¡Gracias por esta información, Kato!
Hopper
2
Siendo por el momento. Las vistas en la versión v2 de Firebase contendrán algunas capacidades excelentes para automatizar ese proceso.
Kato
Consciente de que estoy resucitando un antiguo hilo de comentarios aquí, pero estoy luchando por encontrar una solución más actualizada. ¿Sigue siendo este el mejor enfoque? es decir, obtener todos los users_with_gmail_accounts y luego ejecutar un forEach?
owiewio
48

Firebase es en gran medida no como una base de datos relacional. Si quiere compararlo con algo, lo compararía con una base de datos jerárquica.

Anant escribió recientemente una excelente publicación en el blog de Firebase sobre la desnormalización de sus datos: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

De hecho, sugiero mantener el "ID" de cada solicitud como hijo de cada solicitante.

Frank van Puffelen
fuente
¡Gracias Frank! Esto es realmente útil. Exactamente lo que estaba buscando !
Hopper
4

Su escenario parece uno a muchos en el mundo relacional, según su ejemplo, un solicitante tiene muchas aplicaciones. Si llegamos a la forma nosql de la base de fuego, se verá a continuación. Debería escalar sin problemas de rendimiento. Es por eso que necesitamos la desnormalización como se menciona a continuación.

applicants:{
applicant1:{
    .
    .
    applications:{
        application1:true,
        application3:true
    }
},
applicant2:{
    .
    .
    applications:{
        application2:true,
        application4:true
    }
}}

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}
Prateep Gedupudi
fuente
Bien, pero tengo un seguimiento, ¿cómo creamos esta estructura desde Swift o en cualquier lugar usando el SDK de Firebase? Además, ¿cómo podemos validar que los nuevos datos agregados al nodo de aplicaciones realmente existen en la lista de aplicaciones usando las reglas de validación de Firebase?
Tommie C.
@prateep, Buen ejemplo. Pero aquí el problema es cuando elimino las aplicaciones de ruta / application1 donde application1 es secundaria para algunos solicitantes. Si intento acceder a la ruta de los solicitantes / aplicación1 que no está allí. por lo que debe actualizar los índices en ambos lugares, como application1: {solicitantes: {solicitante1: verdadero} ...} así que ahora, cuando elimino el solicitante1, tengo que verificar los solicitantes secundarios y actualizar el nodo secundario de los solicitantes para la solicitud. :)
Satish Sojitra