Qual è il modo migliore per strutturare i dati su Firebase?


111

Sono nuovo in Firebase e voglio sapere qual è il modo migliore per strutturare i dati su di esso.

Ho un semplice esempio:

Ci sono candidati e domande sul mio progetto. 1 richiedente può avere più domande. Come posso mettere in relazione questi 2 oggetti su Firebase? Funziona come un database relazionale? O l'approccio deve essere completamente diverso in termini di progettazione dei dati?

Risposte:


137

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 messagese widgetstroppo. 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.


Grazie per questa intuizione Kato!
tramoggia

2
Per ora. Le visualizzazioni nella versione v2 di Firebase conterranno alcune ottime funzionalità per automatizzare tale processo.
Kato

Consapevole che sto resuscitando un vecchio thread di commenti qui, ma sto lottando per trovare una soluzione più aggiornata. È ancora l'approccio migliore? cioè ottenere tutti gli utenti_con_gmail_account e quindi eseguire un forEach?
owiewio

48

Firebase è molto non come un database relazionale. Se vuoi confrontarlo con qualsiasi cosa, lo paragonerei a un database gerarchico.

Anant ha recentemente scritto un ottimo post sul blog Firebase sulla denormalizzazione dei dati: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

Suggerirei infatti di conservare l '"ID" di ciascuna domanda come figlio di ciascun richiedente.


Grazie Frank! Questo è davvero utile. Esattamente quello che stavo cercando!
tramoggia

4

Il tuo scenario sembra uno a molti nel mondo relazionale, come nel tuo esempio un candidato ha molte applicazioni. Se veniamo al modo in cui firebase nosql appare di seguito. Dovrebbe scalare senza problemi di prestazioni. Ecco perché abbiamo bisogno della denormalizzazione come indicato di seguito.

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

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}

Bene, ma ho un seguito, come creiamo questa struttura da Swift o ovunque utilizzando l'SDK di Firebase? Inoltre, come possiamo verificare che i nuovi dati aggiunti al nodo delle applicazioni siano effettivamente presenti nell'elenco delle applicazioni utilizzando le regole di convalida di Firebase?
Tommie C.

@prateep, buon esempio. Ma qui il problema è quando elimino il percorso applications / application1 dove application1 è figlio per alcuni candidati. Se provo ad accedere al percorso candidati / application1 che non è presente. quindi è necessario aggiornare gli indici in entrambi i posti come application1: {richiedants: {candidate1: true} ...} così ora quando elimino richiedenteion1 devo controllare i suoi candidati secondari e aggiornare il nodo figlio dei candidati per l'applicazione. :)
Satish Sojitra
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.