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 messages
y widgets
tambié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.