web-dev-qa-db-fra.com

Firestore - comment structurer un système d'alimentation et de suivi

J'utilisais la base de données en temps réel Firebase pour mon application de réseau social test dans laquelle vous pouvez simplement suivre et recevoir les messages des personnes que vous suivez. Un réseau social traditionnel . J'ai structuré ma base de données comme ceci- 

Users
--USER_ID_1
----name
----email
--USER_ID_2
----name
----email

Posts
--POST_ID_1
----image
----userid
----date
--POST_ID_2
----image
----userid
----date

Timeline
--User_ID_1
----POST_ID_2
------date
----POST_ID_1
------date

J'ai aussi un autre nœud "Contenu" qui vient de contenir l'identifiant de tous les messages de l'utilisateur. Ainsi, si "A" suivait "B", tous les articles de poste de B étaient ajoutés à la chronologie de A. Et si B a posté quelque chose, il a également été ajouté à la chronologie de tous ses suiveurs.

Maintenant, c'était ma solution pour la base de données en temps réel, mais elle présente clairement des problèmes d'évolutivité 

  • si quelqu'un a 10 000 abonnés, un nouveau message a été ajouté à la chronologie des 10 000 abonnés. 
  • Si une personne a un nombre élevé de messages, chaque nouvel abonné reçoit tous ces messages dans son calendrier.

Ce sont certains des problèmes.

Maintenant, je pense à déplacer tout cela sur firestore comme on le prétend "Scalable". Alors, comment dois-je structurer ma base de données pour que les problèmes que je rencontrais dans la base de données en temps réel puissent être éliminés dans Firestore.

31
Zicsus

J'ai vu votre question un peu plus tard, mais je vais aussi essayer de vous fournir la meilleure structure de base de données à laquelle je puisse penser. J'espère donc que vous trouverez cette réponse utile.

Je pense à un schéma comportant trois collections de niveau supérieur pour users, users that a user is following et posts.

Firestore-root
   |
   --- users (collection)
   |     |
   |     --- uid (documents)
   |          |
   |          --- name: "User Name"
   |          |
   |          --- email: "[email protected]"
   |
   --- following (collection)
   |      |
   |      --- uid (document)
   |           |
   |           --- userFollowing (collection)
   |                 |
   |                 --- uid (documents)
   |                 |
   |                 --- uid (documents)
   |
   --- posts (collection)
         |
         --- uid (documents)
              |
              --- userPosts (collection)
                    |
                    --- postId (documents)
                    |     |
                    |     --- title: "Post Title"
                    |     |
                    |     --- date: September 03, 2018 at 6:16:58 PM UTC+3
                    |
                    --- postId (documents)
                          |
                          --- title: "Post Title"
                          |
                          --- date: September 03, 2018 at 6:16:58 PM UTC+3

si quelqu'un a 10 000 abonnés, un nouveau message a été ajouté à la chronologie des 10 000 abonnés.

Cela ne posera aucun problème, car c’est la raison pour laquelle les collections sont stockées dans Firestore. Selon la documentation officielle de modélisation d'une base de données Cloud Firestore :

Cloud Firestore est optimisé pour stocker de grandes collections de petits documents.

C'est la raison pour laquelle j'ai ajouté userFollowing en tant que collection et non en tant qu'objet/carte pouvant contenir d'autres objets. N'oubliez pas que la taille maximale d'un document, conformément à la documentation officielle relative aux limites et quota est 1 MiB (1,048,576 bytes). En cas de collecte, le nombre de documents situés sous une collection n'est pas limité. En fait, Firestore est optimisé pour ce type de structures.

Donc, avoir ces 10 000 adeptes de cette manière fonctionnera parfaitement. En outre, vous pouvez interroger la base de données de manière à ne rien copier nulle part.

Comme vous pouvez le constater, la base de données est pratiquement denormalized, ce qui vous permet de l'interroger de manière très simple. Prenons quelques exemples, mais avant, créons une connexion à la base de données et obtenons la uid de l'utilisateur à l'aide des lignes de code suivantes:

FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();

Si vous souhaitez interroger la base de données pour obtenir tous les utilisateurs suivis par un utilisateur, vous pouvez utiliser un appel get() sur la référence suivante:

CollectionReference userFollowingRef = rootRef.collection("following/" + uid + "/userFollowing");

Ainsi, de cette manière, vous pouvez obtenir tous les objets utilisateur qu'un utilisateur suit. Après avoir eu trois utilisateurs, vous pouvez simplement obtenir tous les trois messages.

Supposons que vous souhaitiez intégrer dans votre chronologie les trois derniers messages de chaque utilisateur. La solution pour résoudre ce problème lors de l’utilisation de très grands ensembles de données consiste à charger les données par petits morceaux. J'ai expliqué dans ma réponse à partir de ce post une manière recommandée de paginer des requêtes en combinant des curseurs de requête avec la méthode limit(). Je vous recommande également de jeter un coup d'oeil à cette vidéo pour une meilleure compréhension. Donc, pour obtenir les trois dernières publications de chaque utilisateur, vous devriez envisager d’utiliser cette solution. Donc, vous devez d’abord obtenir les 15 premiers objets utilisateur que vous suivez, puis sur la base de leur uid, pour obtenir leurs trois derniers articles. Pour obtenir les trois derniers articles d'un même utilisateur, utilisez la requête suivante:

Query query = rootRef.collection("posts/" + uid + "/userPosts").orderBy("date", Query.Direction.DESCENDING)).limit(3);

Au fur et à mesure que vous faites défiler l'écran, chargez 15 autres objets utilisateur et obtenez leurs trois derniers articles, etc. En plus de la variable date, vous pouvez également ajouter d'autres propriétés à votre objet post, telles que le nombre de likes, de commentaires, de partages, etc.

Si une personne a un nombre élevé de messages, chaque nouvel abonné reçoit tous ces messages dans son calendrier.

En aucune façon. Il n'y a pas besoin de faire quelque chose comme ça. J'ai déjà expliqué ci-dessus pourquoi.

15
Alex Mamo

Je pense changer tout ça sur firestore

Bonne décistion. Pourquoi?


Venons maintenant au sujet. Comme vous l'avez déjà souligné, votre solution présente clairement des défauts. En voici deux que je pourrais comprendre.

  • Il gaspille énormément différentes ressources. Cela inclut le quota de données, le stockage dans le cloud, la mémoire système et la puissance du processeur.
  • Problèmes liés à l’organisation chronologique des publications.

Les solutions représentent une refonte complète de votre base de données et utilisent une méthode différente pour extraire les données, respectivement.


Refonte de la base de données

Évitez complètement la duplication des données. Voici un bon exemple de structure de base de données pour un média social.

-root
    -users
        -0001
            -name:"name"
            -profile_image:"https://www.example.com/profileimages/profileimage"
            -followings:"002, 003"
            -posts
                -0001
                    -timestamp:"1535650853"
                    -title:"title"
                    -content: "This is a dummy content"
                    -media: "https://www.example.com/medias/media"
                -0002
                    -timestamp:"1535650853"
                    -title:"title"
                    -content: "This is a dummy content"
                    -media: "https://www.example.com/medias/media"
        -0002
            -name:"name"
            -profile_image:"https://www.example.com/profileimages/profileimage"
            -posts
                -0001
                    -timestamp:"1535650853"
                    -title:"title"
                    -content: "This is a dummy content"
                    -media: "https://www.example.com/medias/media"
        -0003
            -name:"name"
            -profile_image:"https://www.example.com/profileimages/profileimage"
            -followings:"001"


Comment aller chercher les messages

Étant donné que vous devez récupérer des publications de plusieurs endroits, vous devez procéder de la sorte.

step 1 : Get a list of UIDs of all following users
step 2 : Take first UID
step 3 : Get all post with the UID and add to list of posts
step 4 : If next UID exists do step 3 with it
step 5 : Sort all according to the timestamp

Au cas où vous auriez besoin de pagination, vous pouvez opter pour une solution de complication uniforme. De toute évidence, il en résulte une amélioration considérable des performances.

step 1 : Get a list of UIDs of all following users
step 2 : Take first UID
step 3 : Get the latest post with the UID (using orderByChild(), limitToLast()) and add to a priority queue in appropriate position.If no element exists, skip the step.
         (A priority queue means an array in of elements which is about to be added to the resultant array. It should be sorted in such a way that the first element can be the next element in the resultant array.)
step 4 : If next UID exists do step 3 with it. Other wise, it means One cycle completed. Go to next step in that case.
step 5 : If limit is not exceeded, get the top element from the queue and add it to resultant array. Then remove from the priority queue. Stop otherwise.
step 6 : Get the next element from the array and add to the priority queue in appropriate position. If no element exists, skip the step.
step 7 : Go to step 5
2
Anees

J'ai parcouru une partie de la documentation Firebase et je ne comprends pas pourquoi la mise en œuvre suggérée sur https://firebase.google.com/docs/database/Android/structure-data#fanout ne serait pas travailler dans votre cas. Quelque chose comme ça: 

users
--userid(somedude)
---name
---etc
---leaders: 
----someotherdude
----someotherotherdude

leaders:
--userid(someotherdude)
---datelastupdated
---followers
----somedude
----thatotherdude
---posts
----postid

posts
--postid
---date
---image
---contentid

postcontent
--contentid
---content

Le guide continue en mentionnant "Il s'agit d'une redondance nécessaire pour les relations bilatérales. Il vous permet de récupérer rapidement et efficacement les adhésions d'Ada, même lorsque la liste des utilisateurs ou des groupes est réduite à plusieurs millions". cette évolutivité est exclusivement une chose Firestore. 

À moins que je ne manque quelque chose, le problème principal semble être l'existence du nœud de la timeline lui-même. Je comprends que cela facilite la génération d’une vue de la chronologie d’un utilisateur particulier, mais cela entraîne le coût de l’entretien de toutes ces relations et retarde considérablement votre projet. Est-il trop inefficace d'utiliser des requêtes pour créer un scénario à la volée à partir d'une structure similaire à celle ci-dessus, basée sur un utilisateur soumis?

1

Il y a deux situations

  1. Les utilisateurs de votre application ont un petit nombre d'adeptes.

  2. Les utilisateurs de votre application ont un grand nombre d'adeptes. Si nous allons stocker des suiveurs entiers dans un seul tableau dans un seul document dans Firestore. Ensuite, il atteindra la limite maximale de 1 Mo par document.


  1. Dans le premier cas, chaque utilisateur doit conserver un document qui stocke la liste des abonnés dans un seul document dans un seul tableau. En utilisant arrayUnion() et arrayRemove(), il est possible de gérer efficacement la liste des abonnés. Et lorsque vous allez poster quelque chose dans votre chronologie, vous devez ajouter la liste des abonnés dans le document post.

    Et utilisez la requête donnée ci-dessous pour récupérer les messages

    postCollectionRef.whereArrayContains("followers", userUid).orderBy("date");
    
  2. Dans le second cas, il vous suffit de séparer le document suivant utilisateur en fonction de la taille ou du nombre de groupes d'adeptes. Une fois que la taille du tableau a atteint une taille fixe, l'id du prochain suiveur doit être ajouté au document suivant. Et le premier document doit conserver le champ "hasNext", qui stocke une valeur booléenne . Lors de l'ajout d'un nouveau message, vous devez dupliquer le document et chaque document est constitué d'une liste de suiveurs qui se sépare plus tôt . même requête qui est donnée ci-dessus pour récupérer des documents.

0
Niyas