Strukturieren Sie Ihre Datenbank

In diesem Leitfaden werden einige der wichtigsten Konzepte der Datenarchitektur und Best Practices zum Strukturieren der JSON-Daten in Ihrer Firebase Realtime Database behandelt.

Der Aufbau einer richtig strukturierten Datenbank erfordert einiges an Voraussicht. Am wichtigsten ist, dass Sie planen müssen, wie Daten gespeichert und später abgerufen werden, um diesen Vorgang so einfach wie möglich zu gestalten.

Wie Daten strukturiert sind: Es ist ein JSON-Baum

Alle Firebase Realtime Database-Daten werden als JSON-Objekte gespeichert. Sie können sich die Datenbank als in der Cloud gehosteten JSON-Baum vorstellen. Im Gegensatz zu einer SQL-Datenbank gibt es keine Tabellen oder Datensätze. Wenn Sie der JSON-Struktur Daten hinzufügen, wird sie zu einem Knoten in der vorhandenen JSON-Struktur mit einem zugeordneten Schlüssel. Sie können Ihre eigenen Schlüssel, wie Benutzer - IDs oder semantische Namen bereitstellen, oder sie können für Sie mit zur Verfügung gestellt werden childByAutoId .

Wenn Sie Ihren eigenen Schlüssel erstellen, müssen sie auch sein UTF-8 codiert, ein Maximum von 768 Bytes sein kann, und enthalten nicht . , $ , # , [ , ] , / Oder ASCII - Steuerzeichen 0-31 oder 127. Sie nicht ASCII - Steuerzeichen in den Werten selbst verwenden können, auch nicht .

Betrachten Sie beispielsweise eine Chat-Anwendung, die es Benutzern ermöglicht, ein grundlegendes Profil und eine Kontaktliste zu speichern. Ein typisches Benutzerprofil ist auf einem Weg, wie sie /users/$uid . Der Benutzer alovelace könnte einen Datenbank - Eintrag hat , die etwa wie folgt aussieht:

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

Obwohl die Datenbank einen JSON-Baum verwendet, können in der Datenbank gespeicherte Daten als bestimmte native Typen dargestellt werden, die den verfügbaren JSON-Typen entsprechen, damit Sie besser wartbaren Code schreiben können.

Best Practices für die Datenstruktur

Verschachtelung von Daten vermeiden

Da die Firebase Realtime Database die Verschachtelung von Daten mit einer Tiefe von bis zu 32 Ebenen ermöglicht, könnten Sie versucht sein zu denken, dass dies die Standardstruktur sein sollte. Wenn Sie jedoch Daten an einem Speicherort in Ihrer Datenbank abrufen, rufen Sie auch alle ihre untergeordneten Knoten ab. Wenn Sie jemandem Lese- oder Schreibzugriff auf einen Knoten in Ihrer Datenbank gewähren, gewähren Sie ihm außerdem Zugriff auf alle Daten unter diesem Knoten. Daher ist es in der Praxis am besten, die Datenstruktur so flach wie möglich zu halten.

Ein Beispiel dafür, warum verschachtelte Daten fehlerhaft sind, sehen Sie in der folgenden mehrfach verschachtelten 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": { ... }
  }
}

Bei diesem verschachtelten Design wird das Durchlaufen der Daten problematisch. Zum Beispiel erfordert die Titel der Chat - Konversationen Auflistung der gesamten chats Baum, darunter alle Mitglieder und Nachrichten, auf den Client heruntergeladen werden.

Datenstrukturen abflachen

Wenn die Daten stattdessen in separate Pfade aufgeteilt werden, auch Denormalisierung genannt, können sie bei Bedarf effizient in separaten Aufrufen heruntergeladen werden. Betrachten Sie diese abgeflachte 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": { ... }
  }
}

Es ist jetzt möglich, die Liste der Räume zu durchlaufen, indem nur wenige Byte pro Konversation heruntergeladen werden, um schnell Metadaten zum Auflisten oder Anzeigen von Räumen in einer Benutzeroberfläche abzurufen. Nachrichten können separat abgerufen und beim Eintreffen angezeigt werden, sodass die Benutzeroberfläche reaktionsschnell und schnell bleibt.

Erstellen Sie skalierbare Daten

Beim Erstellen von Apps ist es oft besser, eine Teilmenge einer Liste herunterzuladen. Dies ist besonders häufig der Fall, wenn die Liste Tausende von Datensätzen enthält. Wenn diese Beziehung statisch und unidirektional ist, können Sie die untergeordneten Objekte einfach unter dem übergeordneten Objekt verschachteln.

Manchmal ist diese Beziehung dynamischer, oder es kann erforderlich sein, diese Daten zu denormalisieren. Viele Male können Sie die Daten denormalize durch eine Abfrage mit einer Teilmenge der Daten abzurufen, wie in diskutiert Abrufen von Daten .

Aber auch das kann nicht ausreichen. Betrachten Sie beispielsweise eine wechselseitige Beziehung zwischen Benutzern und Gruppen. Benutzer können einer Gruppe angehören, und Gruppen umfassen eine Liste von Benutzern. Wenn es darum geht, zu entscheiden, zu welchen Gruppen ein Benutzer gehört, wird es kompliziert.

Was benötigt wird, ist eine elegante Möglichkeit, die Gruppen aufzulisten, zu denen ein Benutzer gehört, und nur Daten für diese Gruppen abzurufen. Ein Index von Gruppen kann sehr viel helfen hier:

// 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
      }
    },
    ...
  }
}

Sie werden möglicherweise feststellen, dass dadurch einige Daten dupliziert werden, indem die Beziehung sowohl unter Adas Datensatz als auch unter der Gruppe gespeichert wird. Jetzt alovelace unter einer Gruppe indiziert und techpioneers ist in Ada Profil aufgelistet. Um Ada aus der Gruppe zu löschen, muss sie also an zwei Stellen aktualisiert werden.

Dies ist eine notwendige Redundanz für wechselseitige Beziehungen. Es ermöglicht Ihnen, die Mitgliedschaften von Ada schnell und effizient abzurufen, selbst wenn die Liste der Benutzer oder Gruppen in die Millionen geht oder wenn Sicherheitsregeln der Echtzeitdatenbank den Zugriff auf einige der Datensätze verhindern.

Dieser Ansatz, der die Daten invertiert durch die IDs als Schlüssel Liste und den Wert auf true setzen, macht für einen Schlüssel so einfach wie das Lesen Überprüfung /users/$uid/groups/$group_id und prüfen , ob es null . Der Index ist schneller und viel effizienter als das Abfragen oder Scannen der Daten.

Nächste Schritte