UPDATE: Ora c'è un doc on structuring data. Inoltre, guarda questo post eccellente su NoSQL data structures.
Il problema principale con i dati gerarchici, a differenza di RDBMS, è che si è tentati di annidare i dati perché possiamo. In generale, si desidera normalizzare i dati in una certa misura (proprio come si farebbe con SQL) nonostante la mancanza di istruzioni e query di join.
Si desidera anche denormalize 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.
L'essenza qui è che si vuole lavorare sodo su scritture per semplificare la lettura. Mantieni separati i componenti logici che vengono letti separatamente (ad es. Per le chat room, non mettere i messaggi, le meta informazioni sulle stanze e gli elenchi dei 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 è la query dei dati. Non esiste un modo semplice per dire "SELEZIONA GLI UTENTI DOVE X = Y", a causa della natura in tempo reale dei dati (è in continua evoluzione, sharding, riconciliazione, ecc., Che richiede un modello interno più semplice per tenere sotto controllo i client sincronizzati)
un semplice esempio sarà probabilmente si imposta nel giusto stato d'animo, quindi ecco qui:
/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets
Ora, dal momento che siamo in una struttura gerarchica, se voglio iterare indirizzi di posta elettronica 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 con questo approccio è che ho appena costretto il client a scaricare tutti gli utenti messages
e widgets
. Nessun problema se nessuna di queste cose è in migliaia. Ma un grosso problema per 10k utenti con un massimo di 5k messaggi ciascuno.
Così ora la strategia ottimale per un gerarchica, struttura in tempo reale diventa più evidente:
/user_meta/uid/email
/messages/uid/...
/widgets/uid/...
Un ulteriore strumento che è estremamente utile in questo contesto sono indici.Con la creazione di un indice di utenti con determinati attributi, posso simulare rapidamente una query SQL, semplicemente scorrendo l'indice:
/users_with_gmail_accounts/uid/email
Ora, se voglio, per esempio, avere messaggi per gli utenti di Gmail, posso fare qualcosa di simile :
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 SO post su dati denormalizing, so check those out as well. Vedo che Frank ha già pubblicato l'articolo di Anant, quindi non lo ripeterò qui, ma è anche una buona lettura.
Grazie per questa intuizione Kato! –
Devo gestire questi indici manualmente? – fiatjaf
Per ora. Le visualizzazioni nella versione v2 di Firebase conterranno alcune grandi capacità per l'automazione di quel processo. – Kato