Structurer votre base de données

Avant de commencer

Avant de pouvoir utiliser Realtime Database, vous devez :

  • Enregistrez votre projet Unity et configurez-le pour utiliser Firebase.

    • Si votre projet Unity utilise déjà Firebase, il est déjà enregistré et configuré pour Firebase.

    • Si vous ne disposez pas d'un projet Unity, vous pouvez télécharger un exemple d'application.

  • Ajoutez le SDK Unity Firebase (plus précisément, FirebaseDatabase.unitypackage) pour votre projet Unity.

Notez que l'ajout de Firebase à votre projet Unity implique des tâches à la fois dans la console Firebase et dans votre projet Unity ouvert (par exemple, vous téléchargez des fichiers de configuration Firebase à partir de la console, puis les déplacez dans votre projet Unity).

Structurer les données

Ce guide présente certains des concepts clés de l'architecture des données et les bonnes pratiques 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, comme les ID utilisateur ou les noms sémantiques, ou vous pouvez les fournir 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 non plus utiliser de caractères de contrôle ASCII dans les valeurs elles-mêmes.

Prenons l'exemple d'une application de chat qui permet aux utilisateurs de stocker un profil de base et une liste de contacts. Un profil utilisateur type se trouve à un chemin, par exemple /users/$uid. L'utilisateur alovelace peut avoir une entrée de base de données qui ressemble à ceci:

{
  "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ées sous la forme de certains types natifs qui correspondent aux types JSON disponibles pour vous aider à écrire du code plus facile à gérer.

Bonnes pratiques pour la structure des données

Éviter l'imbrication de 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 récupérez 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 à un nœud de votre base de données, vous lui accordez également l'accès à toutes les données sous ce nœud. Par conséquent, en pratique, il est préférable de conserver votre structure de données aussi 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 à mesure qu'ils arrivent, ce qui permet à l'interface utilisateur 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 un sous-ensemble 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 dénormaliser ces données. Vous pouvez souvent dénormaliser les données à l'aide d'une requête pour récupérer un sous-ensemble de données, comme indiqué dans la section 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. Lorsque vient le moment de déterminer à quels groupes un utilisateur appartient, les choses se compliquent.

Il faut un moyen élégant de lister les groupes auxquels un utilisateur appartient et récupérer uniquement les données de ces groupes. Un index de groupes peut vous aider à y parvenir :

// 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 à la fois dans l'enregistrement d'Ada et dans le groupe. alovelace est désormais indexé sous un et techpioneers apparaît dans le profil d'Ada. Pour supprimer Ada du groupe, vous devez la mettre à 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 atteindre 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 beaucoup plus efficace que d'interroger ou d'analyser les données.

Étapes suivantes