Wie lassen sich Daten auf Firebase am besten strukturieren?

111

Ich bin neu in Firebase und möchte wissen, wie Daten darauf am besten strukturiert werden können.

Ich habe ein einfaches Beispiel:

Es gibt Bewerber und Bewerbungen für mein Projekt. 1 Bewerber kann mehrere Bewerbungen haben. Wie kann ich diese 2 Objekte auf Firebase in Beziehung setzen? Funktioniert es wie eine relationale Datenbank? Oder muss der Ansatz in Bezug auf das Daten-Design völlig anders sein?

Trichter
quelle

Antworten:

137

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 messagesund widgetsauch 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.

Kato
quelle
Danke für diesen Einblick Kato!
Trichter
2
Vorerst. Die Ansichten in der Version 2 von Firebase enthalten einige großartige Funktionen zur Automatisierung dieses Prozesses.
Kato
Ich bin mir bewusst, dass ich hier einen alten Kommentarthread wiederbelebe, habe aber Schwierigkeiten, eine aktuellere Lösung zu finden. Ist das immer noch der beste Ansatz? dh alle users_with_gmail_accounts abrufen und dann ein forEach ausführen?
Owiewio
48

Firebase ähnelt keiner relationalen Datenbank. Wenn Sie es mit irgendetwas vergleichen möchten, würde ich es mit einer hierarchischen Datenbank vergleichen.

Anant hat kürzlich im Firebase-Blog einen großartigen Beitrag über die Denormalisierung Ihrer Daten geschrieben: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

Ich würde in der Tat vorschlagen, die "ID" jedes Antrags als Kind jedes Antragstellers beizubehalten.

Frank van Puffelen
quelle
Danke Frank! Das ist wirklich hilfreich. Genau das, wonach ich gesucht habe!
Trichter
4

Ihr Szenario sieht in der relationalen Welt wie eins zu viele aus. Gemäß Ihrem Beispiel hat ein Bewerber viele Bewerbungen. Wenn wir zu Firebase Nosql kommen, sieht es wie folgt aus. Es sollte ohne Leistungsprobleme skaliert werden können. Deshalb brauchen wir eine Denormalisierung, wie unten erwähnt.

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

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}
Prateep Gedupudi
quelle
Gut, aber ich habe eine Folge, wie erstellen wir diese Struktur aus Swift oder irgendwo mit dem Firebase SDK? Wie können wir auch anhand der Firebase-Validierungsregeln überprüfen, ob die dem Anwendungsknoten hinzugefügten neuen Daten tatsächlich in der Anwendungsliste vorhanden sind?
Tommie C.
@prateep, gutes Beispiel. Aber hier ist das Problem, wenn ich Pfadanwendungen / Anwendung1 lösche, wobei Anwendung1 für einige Bewerber untergeordnet ist. Wenn ich versuche, auf Pfad Bewerber / Anwendung1 zuzugreifen, die nicht da ist. Sie müssen also die Indizes an beiden Stellen aktualisieren, z. B. an application1: {Bewerber: {Bewerber1: true} ...}. Wenn ich also Bewerber1 lösche, muss ich die untergeordneten Bewerber überprüfen und den untergeordneten Knoten des Bewerbers für die Bewerbung aktualisieren. :)
Satish Sojitra