Structurer votre base de données

Ce guide aborde certains des concepts clés de l'architecture de données et pour structurer les données JSON dans votre Firebase Realtime Database.

Créer une base de données correctement structurée demande une certaine réflexion préalable. Plus important encore, vous devez planifier la façon dont les données seront enregistrées et récupérés par la suite afin de rendre ce processus aussi simple que possible.

Structure des données: il s'agit d'une arborescence JSON

Toutes les données Firebase Realtime Database sont stockées en tant qu'objets JSON. Vous pouvez considérer la base de données comme une arborescence JSON hébergée dans le cloud. Contrairement à une base de données SQL, il n'y a ni tables, ni enregistrements. Lorsque vous ajoutez des données à l'arborescence JSON, elles prennent la forme d'un nouveau nœud et d'une clé associée dans la structure JSON existante. Vous pouvez fournir vos propres clés, telles que des ID utilisateur ou des noms sémantiques, ou elles peuvent vous être fournies à l'aide de la méthode push().

Si vous créez vos propres clés, elles doivent être encodées au format UTF-8 et peuvent être de 768 octets, et ne peut pas contenir ., $, #, [, ], / ni la commande ASCII caractères 0-31 ou 127. Vous ne pouvez pas utiliser de caractères de contrôle ASCII dans les valeurs eux-mêmes.

Prenons l'exemple d'une application de chat qui permet aux utilisateurs de stocker profil et liste de contacts. Un profil utilisateur type se trouve dans un chemin d'accès, par exemple /users/$uid L'utilisateur alovelace peut avoir une entrée de base de données qui se présente comme suit :

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

Bien que la base de données utilise une arborescence JSON, les données stockées dans la base de données peuvent être représentés par certains types natifs correspondant aux types JSON disponibles pour vous aider à écrire un code plus facile à gérer.

Bonnes pratiques pour la structure des données

Éviter d'imbriquer des données

Comme Firebase Realtime Database permet d'imbriquer des données jusqu'à 32 niveaux de profondeur, vous pourriez être tenté de penser qu'il devrait s'agir de la structure par défaut. Toutefois, lorsque vous extrayez des données à un emplacement de votre base de données, vous récupérez également tous ses nœuds enfants. De plus, lorsque vous accordez à un utilisateur un accès en lecture ou en écriture sur un nœud de votre base de données, vous lui accordez aussi l'accès à toutes les données d'un nœud. En pratique, il est donc préférable de conserver une structure de données plate que possible.

Pour comprendre pourquoi les données imbriquées sont incorrectes, considérez ce qui suit multi-imbrication:

{
  // 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": { ... }
  }
}

Avec cette conception imbriquée, l'itération des données devient problématique. Pour Par exemple, pour répertorier les titres des conversations par chat, l'intégralité de chats est requise. comprenant tous les membres et les messages, à télécharger sur le client.

Aplatir les structures de données

Si les données sont divisées en chemins séparés, aussi appelés dénormalisation, il peut être efficacement téléchargé dans des appels distincts, si nécessaire. Envisagez d'utiliser cette structure aplatie:

{
  // 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": { ... }
  }
}

Il est désormais possible d'itérer dans la liste des salons en ne téléchargeant que quelques octets par conversation, en extrayant rapidement les métadonnées pour lister ou afficher les salons dans une UI. Les messages peuvent être récupérés séparément et affichés au fur et à mesure de leur arrivée. permettant à l'UI de rester réactive et rapide.

Créer des données évolutives

Lorsque vous créez des applications, il est souvent préférable de télécharger une partie d'une liste. Cela est particulièrement courant si la liste contient des milliers d'enregistrements. Lorsque cette relation est statique et unidirectionnelle, il vous suffit d'imbriquer les objets enfants sous le parent.

Parfois, cette relation est plus dynamique, ou il peut être nécessaire de et dénormaliser ces données. Souvent, vous pouvez dénormaliser les données en utilisant une requête pour récupérer un sous-ensemble des données, Récupérer des données

Mais cela peut même s'avérer insuffisant. Prenons l'exemple d'une relation bidirectionnelle entre les utilisateurs et les groupes. Les utilisateurs peuvent appartenir à un groupe, qui comprend liste d'utilisateurs. Au moment de décider à quels groupes appartient un utilisateur, les choses se compliquent.

Il est nécessaire de trouver un moyen élégant de lister les groupes auxquels un utilisateur appartient et de ne récupérer que les données de ces groupes. L'index des groupes C'est très intéressant ici:

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

Vous remarquerez peut-être que cela duplique certaines données en stockant la relation sous le dossier d'Ada et dans le groupe. alovelace est désormais indexé dans un groupe, et techpioneers est listé dans le profil d'Ada. Pour supprimer Ada du groupe, elle doit être mise à jour à deux endroits.

Il s'agit d'une redondance nécessaire pour les relations bidirectionnelles. Il vous permet de récupérer rapidement et efficacement les adhésions d'Ada, même lorsque la liste d'utilisateurs ou peut s'étendre à plusieurs millions ou lorsque Realtime Database règles de sécurité empêcher l'accès à certains enregistrements.

Cette approche, qui inverse les données en listant les ID en tant que clés et en définissant la valeur sur "true", permet de vérifier une clé aussi simplement que de lire /users/$uid/groups/$group_id et de vérifier s'il s'agit de null. L'index est plus rapide et bien plus efficace que d'interroger ou d'analyser les données.

Étapes suivantes