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 messages
i widgets
zbyt. 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.