Jaki jest najlepszy sposób tworzenia struktury danych w Firebase?

111

Jestem nowy w Firebase i chcę wiedzieć, jaki jest najlepszy sposób ustrukturyzowania danych.

Mam prosty przykład:

W moim projekcie są Wnioskodawcy i Wnioski. 1 wnioskodawca może mieć kilka aplikacji. Jak mogę powiązać te 2 obiekty w Firebase? Czy działa jak relacyjna baza danych? A może podejście musi być zupełnie inne w zakresie projektowania danych?

zbiornik
źródło

Odpowiedzi:

137

AKTUALIZACJA : jest teraz dokument dotyczący struktury danych . Zobacz także ten doskonały post na temat struktur danych NoSQL .

Głównym problemem związanym z danymi hierarchicznymi, w przeciwieństwie do RDBMS, jest to, że kuszące jest zagnieżdżanie danych, ponieważ możemy. Ogólnie rzecz biorąc, chcesz do pewnego stopnia znormalizować dane (tak jak w przypadku SQL) pomimo braku instrukcji złączenia i zapytań.

Chcesz także zdenormalizować w miejscach, w których problemem jest efektywność czytania. Jest to technika używana we wszystkich aplikacjach na dużą skalę (np. Na Twitterze i Facebooku) i chociaż jest ona sprzeczna z naszymi zasadami DRY, jest na ogół niezbędną funkcją skalowalnych aplikacji.

Istota polega na tym, że chcesz ciężko pracować nad zapisami, aby ułatwić czytanie. Przechowuj komponenty logiczne, które są odczytywane osobno, osobno (np. Dla pokojów rozmów, nie umieszczaj wiadomości, metainformacji o pokojach i list członków w tym samym miejscu, jeśli chcesz mieć możliwość późniejszego iterowania grup).

Podstawowa różnica między danymi czasu rzeczywistego Firebase a środowiskiem SQL polega na wysyłaniu zapytań do danych. Nie ma prostego sposobu na powiedzenie „WYBIERZ UŻYTKOWNIKÓW GDZIE X = Y” ze względu na charakter danych w czasie rzeczywistym (są one stale zmieniane, dzielone na fragmenty, uzgadniane itp., Co wymaga prostszego modelu wewnętrznego, aby zsynchronizować klientów)

Prosty przykład prawdopodobnie wprowadzi Cię we właściwy stan umysłu, więc oto:

/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets

Ponieważ jesteśmy w strukturze hierarchicznej, jeśli chcę iterować adresy e-mail użytkowników, robię coś takiego:

// 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));

Problem z tego podejścia jest to, że ja po prostu zmuszony do klienta w celu pobrania wszystkich użytkowników messagesi widgetszbyt. Nic wielkiego, jeśli żadna z tych rzeczy nie liczy się w tysiącach. Ale wielka sprawa dla 10 tys. Użytkowników z ponad 5 tys. Wiadomości każdy.

Zatem teraz optymalna strategia dla hierarchicznej struktury w czasie rzeczywistym staje się bardziej oczywista:

/user_meta/uid/email
/messages/uid/...
/widgets/uid/...

Dodatkowym narzędziem niezwykle przydatnym w tym środowisku są indeksy. Tworząc indeks użytkowników z określonymi atrybutami, mogę szybko zasymulować zapytanie SQL, po prostu wykonując iterację indeksu:

/users_with_gmail_accounts/uid/email

Jeśli chcę, powiedzmy, otrzymywać wiadomości dla użytkowników Gmaila, mogę zrobić coś takiego:

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));

Przedstawiłem kilka szczegółów w innym poście SO na temat denormalizacji danych, więc sprawdź je również . Widzę, że Frank opublikował już artykuł Ananta, więc nie będę tego tutaj powtarzał, ale jest to również świetna lektura.

Kato
źródło
Dzięki za ten wgląd Kato!
hopper
2
Obecnie. Widoki w wersji 2 Firebase będą zawierały świetne możliwości automatyzacji tego procesu.
Kato
Zdaję sobie sprawę, że wskrzeszam tu stary wątek komentarzy, ale staram się znaleźć bardziej aktualne rozwiązanie. Czy to nadal najlepsze podejście? tzn. pobieranie wszystkich users_with_gmail_accounts, a następnie uruchamianie forEach?
owiewio
48

Firebase bardzo nie przypomina relacyjnej bazy danych. Jeśli chcesz to porównać z czymkolwiek, porównałbym to z hierarchiczną bazą danych.

Anant niedawno napisał świetny post na blogu Firebase o denormalizacji danych: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

Rzeczywiście sugerowałbym zachowanie „ID” każdego wniosku jako dziecko każdego wnioskodawcy.

Frank van Puffelen
źródło
Dzięki Frank! To jest naprawdę pomocne. Dokładnie to, czego szukałem!
hopper
4

Twój scenariusz wygląda jak jeden do wielu w świecie relacji, ponieważ na twoim przykładzie wnioskodawca ma wiele aplikacji. Jeśli przejdziemy do firebase nosql, to wygląda to jak poniżej. Powinien skalować się bez żadnych problemów z wydajnością. Dlatego potrzebujemy denormalizacji, jak wspomniano poniżej.

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

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}
Prateep Gedupudi
źródło
Dobrze, ale mam kontynuację, w jaki sposób utworzymy tę strukturę w języku Swift lub w dowolnym innym miejscu przy użyciu pakietu SDK Firebase? Jak również możemy sprawdzić, czy nowe dane dodane do węzła aplikacji rzeczywiście istnieją na liście aplikacji przy użyciu reguł walidacji Firebase?
Tommie C.
@prateep, dobry przykład. Ale tutaj problem jest, gdy usuwam wnioski o ścieżkę / aplikację1, gdzie aplikacja1 jest dzieckiem dla niektórych wnioskodawców. Jeśli spróbuję uzyskać dostęp do wnioskodawców / aplikacji1 ścieżki, których tam nie ma. więc musisz zaktualizować indeksy w obu miejscach, takich jak aplikacja1: {kandydatów: {wnioskodawca1: prawda} ...}, więc teraz, kiedy usuwam kandydat1, muszę sprawdzić jego kandydatów podrzędnych i zaktualizować węzeł podrzędny kandydatów dla aplikacji. :)
Satish Sojitra