Quelle est la meilleure façon de structurer les données sur Firebase?

111

Je suis nouveau sur Firebase et je veux savoir quelle est la meilleure façon de structurer les données dessus.

J'ai un exemple simple:

Il y a des candidats et des applications sur mon projet. 1 candidat peut avoir plusieurs candidatures. Comment relier ces 2 objets sur Firebase? Fonctionne-t-il comme une base de données relationnelle? Ou l'approche doit être complètement différente en termes de conception de données?

trémie
la source

Réponses:

137

MISE À JOUR : Il existe maintenant un document sur la structuration des données . Consultez également cet excellent article sur les structures de données NoSQL .

Le principal problème avec les données hiérarchiques, par opposition au SGBDR, est qu'il est tentant d'imbriquer des données parce que nous le pouvons. En règle générale, vous souhaitez normaliser les données dans une certaine mesure (comme vous le feriez avec SQL) malgré le manque d'instructions et de requêtes de jointure.

Vous souhaitez également dénormaliser dans les endroits où l'efficacité de lecture est un problème. Il s'agit d'une technique utilisée par toutes les applications à grande échelle (par exemple Twitter et Facebook) et bien qu'elle soit contraire à nos principes DRY, c'est généralement une caractéristique nécessaire des applications évolutives.

L'essentiel ici est que vous voulez travailler dur sur les écritures pour faciliter les lectures. Gardez les composants logiques qui sont lus séparément séparément (par exemple pour les salles de discussion, ne mettez pas les messages, les méta-informations sur les salles et les listes de membres tous au même endroit, si vous voulez pouvoir itérer les groupes plus tard).

La principale différence entre les données en temps réel de Firebase et un environnement SQL réside dans l'interrogation des données. Il n'y a pas de moyen simple de dire "SELECT USERS WHERE X = Y", en raison de la nature en temps réel des données (elles changent constamment, partitionnent, réconcilient, etc., ce qui nécessite un modèle interne plus simple pour garder les clients synchronisés sous contrôle)

Un exemple simple vous mettra probablement dans le bon état d'esprit, alors voici:

/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets

Maintenant, puisque nous sommes dans une structure hiérarchique, si je veux itérer les adresses e-mail des utilisateurs, je fais quelque chose comme ceci:

// 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));

Le problème avec cette approche est que je viens de forcer le client à télécharger tous les utilisateurs messageset widgetsaussi. Pas de problème si aucune de ces choses ne se chiffre en milliers. Mais un gros problème pour 10 000 utilisateurs avec plus de 5 000 messages chacun.

Alors maintenant, la stratégie optimale pour une structure hiérarchique en temps réel devient plus évidente:

/user_meta/uid/email
/messages/uid/...
/widgets/uid/...

Les indices sont un outil supplémentaire extrêmement utile dans cet environnement. En créant un index d'utilisateurs avec certains attributs, je peux rapidement simuler une requête SQL en itérant simplement l'index:

/users_with_gmail_accounts/uid/email

Maintenant, si je veux, par exemple, recevoir des messages pour les utilisateurs de Gmail, je peux faire quelque chose comme ceci:

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));

J'ai offert quelques détails dans un autre article du SO sur la dénormalisation des données, alors vérifiez-les également . Je vois que Frank a déjà publié l'article d'Anant, donc je ne le répéterai pas ici, mais c'est aussi une excellente lecture.

Kato
la source
Merci pour cet aperçu Kato!
trémie
2
Pour le moment. Les vues de la version v2 de Firebase contiendront d'excellentes capacités pour automatiser ce processus.
Kato
Conscient que je ressuscite un ancien fil de commentaires ici, mais j'ai du mal à trouver une solution plus à jour. Est-ce toujours la meilleure approche? c'est-à-dire obtenir tous les users_with_gmail_accounts puis exécuter un forEach?
owiewio
48

Firebase n'est pas du tout comme une base de données relationnelle. Si vous voulez le comparer à quoi que ce soit, je le comparerais à une base de données hiérarchique.

Anant a récemment écrit un excellent article sur le blog Firebase sur la dénormalisation de vos données: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

Je suggérerais en effet de conserver la «pièce d'identité» de chaque demande en tant qu'enfant de chaque demandeur.

Frank van Puffelen
la source
Merci Frank! Ceci est vraiment utile. Exactement ce que je cherchais!
trémie
4

Votre scénario ressemble à un à plusieurs dans le monde relationnel, selon votre exemple, un candidat a de nombreuses applications. Si nous arrivons à la façon dont Firebase nosql ressemble à ci-dessous. Il doit évoluer sans aucun problème de performances. C'est pourquoi nous avons besoin d'une dénormalisation comme mentionné ci-dessous.

applicants:{
applicant1:{
    .
    .
    applications:{
        application1:true,
        application3:true
    }
},
applicant2:{
    .
    .
    applications:{
        application2:true,
        application4:true
    }
}}

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}
Prateep Gedupudi
la source
Bien mais j'ai une suite, comment créer cette structure à partir de Swift ou n'importe où en utilisant le SDK Firebase? Comment pouvons-nous également valider que les nouvelles données ajoutées au nœud d'applications existent réellement dans la liste des applications à l'aide des règles de validation Firebase?
Tommie C.
@prateep, bon exemple. Mais ici, le problème est lorsque je supprime le chemin applications / application1 où application1 est enfant pour certains candidats. Si j'essaye d'accéder à des candidats de chemin / application1 qui n'est pas là vous devez donc mettre à jour les index aux deux endroits comme application1: {candidats: {candidat1: true} ...} donc maintenant, lorsque je supprime le candidat, je dois vérifier ses candidats enfants et mettre à jour le nœud enfant des candidats pour l'application. :)
Satish Sojitra