UPDATE : Es gibt jetzt ein Dokument zur Strukturierung von Daten . Lesen Sie auch diesen hervorragenden Beitrag zu NoSQL-Datenstrukturen .
Das Hauptproblem bei hierarchischen Daten im Gegensatz zu RDBMS ist, dass es verlockend ist, Daten zu verschachteln, weil wir dies können. Im Allgemeinen möchten Sie Daten trotz fehlender Join-Anweisungen und Abfragen bis zu einem gewissen Grad normalisieren (genau wie bei SQL).
Sie möchten auch an Stellen denormalisieren, an denen die Leseeffizienz ein Problem darstellt. Dies ist eine Technik, die von allen großen Apps (z. B. Twitter und Facebook) verwendet wird. Obwohl sie gegen unsere DRY-Prinzipien verstößt, ist sie im Allgemeinen eine notwendige Funktion skalierbarer Apps.
Das Wesentliche hier ist, dass Sie hart an Schreibvorgängen arbeiten möchten, um das Lesen zu vereinfachen. Bewahren Sie logische Komponenten, die separat gelesen werden, getrennt auf (z. B. platzieren Sie für Chatrooms keine Nachrichten, Metainformationen zu den Räumen und Mitgliederlisten an derselben Stelle, wenn Sie die Gruppen später wiederholen möchten).
Der Hauptunterschied zwischen den Echtzeitdaten von Firebase und einer SQL-Umgebung besteht in der Abfrage von Daten. Es gibt keine einfache Möglichkeit, "BENUTZER AUSWÄHLEN, WO X = Y" zu sagen, da die Daten in Echtzeit vorliegen (sie ändern sich ständig, werden gesplittert, abgeglichen usw., was ein einfacheres internes Modell erfordert, um die synchronisierten Clients in Schach zu halten).
Ein einfaches Beispiel wird Sie wahrscheinlich in den richtigen Geisteszustand versetzen.
/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets
Da wir uns in einer hierarchischen Struktur befinden, mache ich Folgendes, wenn ich die E-Mail-Adressen der Benutzer iterieren möchte:
// 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));
Das Problem bei diesem Ansatz ist, dass ich den Client gerade gezwungen habe, alle Benutzer messages
und widgets
auch herunterzuladen . Kein Problem, wenn keines dieser Dinge zu Tausenden zählt. Aber eine große Sache für 10.000 Benutzer mit jeweils mehr als 5.000 Nachrichten.
Jetzt wird die optimale Strategie für eine hierarchische Echtzeitstruktur offensichtlicher:
/user_meta/uid/email
/messages/uid/...
/widgets/uid/...
Ein zusätzliches Werkzeug, das in dieser Umgebung äußerst nützlich ist, sind Indizes. Durch Erstellen eines Benutzerindex mit bestimmten Attributen kann ich eine SQL-Abfrage schnell simulieren, indem ich einfach den Index iteriere:
/users_with_gmail_accounts/uid/email
Wenn ich beispielsweise Nachrichten für Google Mail-Benutzer abrufen möchte, kann ich Folgendes tun:
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));
Ich habe in einem anderen SO-Beitrag einige Details zum Denormalisieren von Daten angeboten. Schauen Sie sich diese also auch an . Ich sehe, dass Frank bereits Anants Artikel gepostet hat, daher werde ich das hier nicht wiederholen, aber es ist auch eine großartige Lektüre.