Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Zorganizuj swoją bazę danych

W tym przewodniku omówiono niektóre z kluczowych pojęć dotyczących architektury danych i sprawdzonych metod tworzenia struktury danych JSON w bazie danych czasu rzeczywistego Firebase.

Zbudowanie odpowiednio ustrukturyzowanej bazy danych wymaga sporego przemyślenia. Co najważniejsze, musisz zaplanować, w jaki sposób dane będą zapisywane i później odzyskiwane, aby maksymalnie ułatwić ten proces.

Struktura danych: to drzewo JSON

Wszystkie dane Bazy danych czasu rzeczywistego Firebase są przechowywane jako obiekty JSON. Możesz myśleć o bazie danych jako o drzewie JSON hostowanym w chmurze. W przeciwieństwie do bazy danych SQL nie ma tabel ani rekordów. Po dodaniu danych do drzewa JSON staje się ono węzłem w istniejącej strukturze JSON z powiązanym kluczem. Możesz podać własne klucze, takie jak identyfikatory użytkowników lub nazwy semantyczne, lub można je dostarczyć za pomocą metody push() .

Jeśli tworzysz własne klucze, muszą one być zakodowane w formacie UTF-8, mogą mieć maksymalnie 768 bajtów i nie mogą zawierać . , $ , # , [ , ] , / lub znaki sterujące ASCII 0-31 lub 127. Nie można również używać znaków sterujących ASCII w samych wartościach.

Weźmy na przykład aplikację do czatu, która umożliwia użytkownikom przechowywanie podstawowego profilu i listy kontaktów. Typowy profil użytkownika znajduje się w ścieżce, takiej jak /users/$uid . Użytkownik alovelace może mieć wpis w bazie danych, który wygląda alovelace tak:

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "contacts": { "ghopper": true },
    },
    "ghopper": { ... },
    "eclarke": { ... }
  }
}

Chociaż baza danych używa drzewa JSON, dane przechowywane w bazie danych mogą być reprezentowane jako pewne typy natywne, które odpowiadają dostępnym typom JSON, aby pomóc Ci napisać łatwiejszy w utrzymaniu kod.

Najlepsze praktyki dotyczące struktury danych

Unikaj zagnieżdżania danych

Ponieważ Baza danych czasu rzeczywistego Firebase umożliwia zagnieżdżanie danych do 32 poziomów, możesz ulec pokusie, by pomyśleć, że powinna to być struktura domyślna. Jednak podczas pobierania danych do lokalizacji w bazie danych pobierasz również wszystkie jego węzły podrzędne. Ponadto, gdy przyznajesz komuś dostęp do odczytu lub zapisu w węźle bazy danych, dajesz jej również dostęp do wszystkich danych w tym węźle. Dlatego w praktyce najlepiej jest, aby struktura danych była jak najbardziej płaska.

Na przykład, dlaczego zagnieżdżone dane są złe, rozważ następującą wielokrotnie zagnieżdżoną strukturę:

{
  // This is a poorly nested data architecture, because iterating the children
  // of the "chats" node to get a list of conversation titles requires
  // potentially downloading hundreds of megabytes of messages
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "messages": {
        "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
        "m2": { ... },
        // a very long list of messages
      }
    },
    "two": { ... }
  }
}

W przypadku tego zagnieżdżonego projektu iterowanie po danych staje się problematyczne. Na przykład wyświetlenie tytułów rozmów na czacie wymaga pobrania całego drzewa chats , w tym wszystkich członków i wiadomości, do klienta.

Spłaszcz struktury danych

Jeśli dane są zamiast tego podzielone na oddzielne ścieżki, zwane również denormalizacją, można je wydajnie pobrać w oddzielnych wywołaniach, jeśli jest to potrzebne. Rozważ tę spłaszczoną strukturę:

{
  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    },
    "two": { ... },
    "three": { ... }
  },

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": {
    // we'll talk about indices like this below
    "one": {
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    },
    "two": { ... },
    "three": { ... }
  },

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // conversation ID
  "messages": {
    "one": {
      "m1": {
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... },
      "m3": { ... }
    },
    "two": { ... },
    "three": { ... }
  }
}

Teraz można przeglądać listę pokoi, pobierając tylko kilka bajtów na rozmowę, szybko pobierając metadane w celu wyświetlenia lub wyświetlenia pokoi w interfejsie użytkownika. Wiadomości można pobierać oddzielnie i wyświetlać w miarę ich nadejścia, dzięki czemu interfejs użytkownika pozostaje responsywny i szybki.

Twórz dane, które skalują się

Podczas tworzenia aplikacji często lepiej jest pobrać podzbiór listy. Jest to szczególnie częste, jeśli lista zawiera tysiące rekordów. Gdy ta relacja jest statyczna i jednokierunkowa, możesz po prostu zagnieździć obiekty podrzędne pod rodzicem.

Czasami ta relacja jest bardziej dynamiczna lub może być konieczna denormalizacja tych danych. Wiele razy można zdenormalizować dane, używając zapytania w celu pobrania podzbioru danych, jak omówiono w sekcji Pobieranie danych .

Ale nawet to może być niewystarczające. Weźmy na przykład pod uwagę dwukierunkową relację między użytkownikami i grupami. Użytkownicy mogą należeć do grupy, a grupy stanowią listę użytkowników. Kiedy przychodzi czas na podjęcie decyzji, do której grupy należy użytkownik, sprawa się komplikuje.

Potrzebny jest elegancki sposób na wylistowanie grup, do których należy użytkownik, i pobranie tylko danych dla tych grup. Indeks grup może tutaj bardzo pomóc:

// An index to track Ada's memberships
{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      // Index Ada's groups in her profile
      "groups": {
         // the value here doesn't matter, just that the key exists
         "techpioneers": true,
         "womentechmakers": true
      }
    },
    ...
  },
  "groups": {
    "techpioneers": {
      "name": "Historical Tech Pioneers",
      "members": {
        "alovelace": true,
        "ghopper": true,
        "eclarke": true
      }
    },
    ...
  }
}

Możesz zauważyć, że powoduje to duplikowanie niektórych danych, przechowując relację zarówno pod rekordem Ady, jak i pod grupą. Teraz alovelace jest indeksowany w ramach grupy, a techpioneers są wymienieni w profilu Ady. Aby więc usunąć Adę z grupy, należy ją zaktualizować w dwóch miejscach.

Jest to niezbędna nadmiarowość w przypadku relacji dwukierunkowych. Umożliwia szybkie i wydajne pobieranie członkostwa Ady, nawet gdy lista użytkowników lub grup rośnie do milionów lub gdy reguły bezpieczeństwa Bazy danych czasu rzeczywistego uniemożliwiają dostęp do niektórych rekordów.

Takie podejście, polegające na odwróceniu danych poprzez wypisanie identyfikatorów jako kluczy i ustawienie wartości na true, sprawia, że ​​sprawdzenie klucza jest tak proste, jak odczytanie /users/$uid/groups/$group_id i sprawdzenie, czy jest null . Indeks jest szybszy i znacznie bardziej wydajny niż przeszukiwanie lub skanowanie danych.

Następne kroki