AGGIORNAMENTO : ora è disponibile un documento sulla strutturazione dei dati . Inoltre, guarda questo eccellente post sulle strutture dati NoSQL .
Il problema principale con i dati gerarchici, al contrario di RDBMS, è che si è tentati di nidificare i dati perché possiamo. In genere, si desidera normalizzare i dati in una certa misura (proprio come si farebbe con SQL) nonostante la mancanza di istruzioni e query di join.
Vuoi anche denormalizzare in luoghi in cui l'efficienza di lettura è un problema. Questa è una tecnica utilizzata da tutte le app su larga scala (ad esempio Twitter e Facebook) e sebbene vada contro i nostri principi DRY, è generalmente una caratteristica necessaria delle app scalabili.
Il succo qui è che vuoi lavorare sodo sulle scritture per rendere facili le letture. Tieni separati i componenti logici letti separatamente (ad esempio per le chat room, non mettere i messaggi, le meta informazioni sulle stanze e gli elenchi di membri tutti nello stesso posto, se vuoi essere in grado di iterare i gruppi in seguito).
La differenza principale tra i dati in tempo reale di Firebase e un ambiente SQL è l'interrogazione dei dati. Non esiste un modo semplice per dire "SELEZIONA UTENTI DOVE X = Y", a causa della natura in tempo reale dei dati (è in continua evoluzione, partizionamento orizzontale, riconciliazione e così via, che richiede un modello interno più semplice per tenere sotto controllo i client sincronizzati)
Un semplice esempio ti metterà probabilmente nel giusto stato d'animo, quindi ecco qui:
/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets
Ora, poiché siamo in una struttura gerarchica, se voglio iterare gli indirizzi email degli utenti, faccio qualcosa del genere:
// 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));
Il problema di questo approccio è che ho appena costretto il client per scaricare tutti degli utenti messages
e widgets
troppo. Nessun problema se nessuna di queste cose conta migliaia. Ma un grosso problema per 10.000 utenti con oltre 5.000 messaggi ciascuno.
Quindi ora la strategia ottimale per una struttura gerarchica in tempo reale diventa più ovvia:
/user_meta/uid/email
/messages/uid/...
/widgets/uid/...
Uno strumento aggiuntivo estremamente utile in questo ambiente sono gli indici. Creando un indice di utenti con determinati attributi, posso simulare rapidamente una query SQL semplicemente iterando l'indice:
/users_with_gmail_accounts/uid/email
Ora, se voglio, ad esempio, ricevere messaggi per gli utenti di Gmail, posso fare qualcosa del genere:
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));
Ho offerto alcuni dettagli in un altro post SO sulla denormalizzazione dei dati, quindi controlla anche quelli . Vedo che Frank ha già pubblicato l'articolo di Anant, quindi non lo ripeterò qui, ma è anche un'ottima lettura.