Développement de sous-graphes, partie 2: gestion des tableaux et identification des entités

(ProtoFire.io)

(Partie 1 ) | Partie 2

Ce billet de blog fournit des recommandations pratiques sur la façon dutiliser efficacement les tableaux et de générer ID dentité uniques et référençables.

Dans la première partie de cette série, nous avons présenté un aperçu des sous-graphiques pour aider les développeurs à comprendre leur structure de base. En outre, nous avons également partagé nos idées pour le mappage et lagrégation de données.

Cette fois, nous aborderons deux autres sujets: la gestion des tableaux et la génération dID pour les entités qui sont à la fois uniques et facilement référencées. Dans le cadre de la discussion, nous fournirons nos recommandations sur la façon de gérer efficacement les tableaux et de nommer correctement les entités.

Gestion efficace des tableaux

Lajout de tableaux à des entités est utile dans certains scénarios. Par exemple, il y a des cas où nous devons modéliser une liste dadresses pour une source de données ou suivre les changements historiques pour un champ particulier au fil du temps.

Sans connaissance préalable du fonctionnement des tableaux dans les sous-graphiques, nous pourrions envisager création dun champ de type tableau sur une définition de type dentité (dans le fichier schema.graphql) et initialisation dun tableau vide chaque fois quune nouvelle entité du même type est créée. Lorsque de nouvelles données sont ajoutées au tableau, nous pouvons pousser les données et enregistrer lentité. Bien que cela semble intuitif, cela ne fonctionne malheureusement pas.

La gestion manuelle des tableaux sur les sous-graphiques, en particulier dans le scénario ci-dessus, comporte quelques mises en garde. Chaque fois que vous accédez à un tableau dune entité, vous obtenez en fait une copie du tableau. Ainsi, si vous ajoutez de nouvelles données et enregistrez lentité, cela ne fonctionnera pas comme prévu, puisque vous modifiez simplement une copie du tableau, tandis que loriginal reste inchangé.

Pour mettre à jour le tableau réel, nous pouvons placer la copie du tableau dans une variable puis modifier les données. Ensuite, nous pouvons définir la variable comme nouveau tableau sur lentité. De cette façon, lancien tableau est remplacé par la copie. Ce processus de mise à jour du tableau est illustré dans le code suivant.

// This won"t work
entity.numbers.push(BigInt.fromI32(1))
entity.save()// This will work
let numbers = entity.numbers
numbers.push(BigInt.fromI32(1))
entity.numbers = numbers
entity.save()

Bien que vous puissiez mettre à jour un tableau de la manière illustrée ci-dessus, ce nest pas une solution idéale . En plus dêtre peu pratique, il existe une autre raison de ne pas gérer manuellement les tableaux: les requêtes de voyage dans le temps. (Lisez la partie 1 de la série pour en savoir plus sur les requêtes de voyage dans le temps.)

Il est uniquement possible deffectuer des requêtes de voyage dans le temps, car les sous-graphiques gardent une trace de tous les changements dans toutes les entités présentent toutes les temps. Sil y a beaucoup dentités avec des champs de tableau, qui sont volumineux et souvent mis à jour, des copies de tous les tableaux devront également être stockées. Cela aura un impact sur les performances et l’espace disque de tout indexeur qui indexe votre sous-graphe.

Actuellement, le service hébergé de The Graph est le seul indexeur actif disponible. À l’avenir, davantage d’indexeurs pourront se joindre à l’ajout du réseau décentralisé de The Graph. Ces nouveaux indexeurs pourront choisir les sous-graphes à indexer. Si votre sous-graphe est mal optimisé à cause des tableaux, il ne sera probablement pas repris par un indexeur.

Pour optimiser nos tableaux, nous pouvons utiliser le @derivedFrom annotation. Cette méthode permet à tout champ de tableau défini dans un type dentité dêtre automatiquement rempli par toutes les entités du type spécifié liées à lentité que nous définissons. Lexemple suivant illustre lutilisation de lannotation @derivedFrom.

type User @entity {
id: ID! positions: [Position!]! @derivedFrom(field: “user”)
}type Position @entity {
id: ID! user: User! # This is the ID String of the User
}

Dans lexemple ci-dessus, nous avons un utilisateur avec une liste générée automatiquement des entités Position. Chaque fois que notre sous-graphe reçoit une requête demandant le champ de positions de lentité User, le sous-graphe effectue une recherche inversée pour toutes les entités de type Position lié à l’entité spécifique User dans leur champ user. De cette manière, les entités liées sont celles qui ont lID de chaîne dautres entités dans lun de ses champs.

En utilisant lannotation @derivedFrom, nous pouvons définir lentité type que nous voulons pour nos données de tableau, définissez le champ utilisé lors de la dérivation du tableau et liez-le à lentité dorigine via leur ID. Il y a également lavantage de pouvoir ajouter plus de données (par exemple, création ou mise à jour de métadonnées) aux entités représentant les données du tableau. Comme ce sont des entités à part entière, nous pouvons les mettre à jour facilement en chargeant leurs identifiants au lieu de les rechercher dans le tableau.

Lors de la gestion des tableaux avec lannotation @derivedFrom est plus facile, il y a encore quelques considérations à prendre en compte.Premièrement, cela ne fonctionnera quavec des relations un-à-plusieurs. Dans les relations plusieurs à plusieurs, nous avons encore besoin dun côté de léquation pour gérer manuellement le tableau. Deuxièmement, vous ne pourrez pas accéder aux données du tableau, pendant que le sous-graphe est en cours dindexation, car le tableau est rempli lors de linterrogation.

Création dun nom convention pour les identifiants dentités

Toutes les entités définies dans le fichier schema.graphql sont identifiées par un champ didentifiant qui est déclaré comme un type ID! représenté sous forme de chaîne. Le champ ID est important car il est utilisé pour charger, créer et enregistrer des entités.

Comme le champ ID est le principal moyen didentifier une entité, il doit toujours être unique. Cela dit, garantir lunicité dune pièce didentité nest pas difficile. Les données présentes pendant le temps dindexation peuvent être combinées pour générer des ID uniques. Le code suivant en est un exemple.

event.transaction.hash.toHex() + "-" + 
event.logIndex.toString()

En prenant le hachage de transaction dun événement (unique pour différentes transactions) et en lajoutant à lindex du journal pour lévénement particulier (qui identifie un événement dans une transaction), nous pouvons générer un ID composé unique. De cette façon, nous pouvons identifier une entité particulière parmi dautres entités du même type à condition quune seule entité soit créée pour un seul événement. Si nécessaire, nous pouvons également ajouter plus de données pour identifier de manière unique un nombre quelconque dentités créées dans le même événement. Par exemple, nous pourrions définir un compteur pour chaque fois quune entité est créée et ajouter la valeur à lentité nouvellement créée.

Bien quil soit pratique davoir une méthode simple pour générer des identifiants uniques pour nos entités, nous devrions également efforcez-vous de générer des identifiants prévisibles et référençables. Si nous avons des entités liées à une partie de notre domaine qui est susceptible dêtre interrogée par les utilisateurs finaux via leur identifiant, nous pouvons générer un identifiant qui référence le domaine sur lequel nous travaillons.

À titre dexemple, considérons un scénario dans lequel nous créons une entité Account sur un sous-graphe DEX. Cette entité Account stockera le solde de lutilisateur, ainsi que dautres informations. Si nous créons lID dentité en fonction du hachage de la transaction, lutilisateur peut rechercher la transaction qui la créée en premier lieu et la recréer, mais cela ne sera pas intuitif. Une meilleure alternative serait de créer un identifiant basé sur ladresse Ethereum de lutilisateur et, si nécessaire, de le combiner avec quelque chose dautre pertinent pour le domaine. De cette façon, nous pouvons identifier de manière unique un compte utilisateur particulier à partir dautres comptes du même utilisateur.

En résumé, les identifiants uniques génériques sans données spécifiques au domaine peuvent être utiles pour les entités qui ne seront pas constamment mises à jour. Cest idéal pour les entités créées pour enregistrer des métadonnées pour des événements spécifiques au domaine qui seront consommés à partir dun tableau dérivé sur une entité principale. Par exemple, les identifiants uniques génériques sont mieux adaptés pour les transferts, les mentions, les brûlures et les échanges.

Dun autre côté, les identifiants spécifiques au domaine sont idéaux pour les entités principales et toute autre entité qui recevra des mises à jour fréquentes. Vous utiliserez probablement une combinaison dadresse Ethereum et dautres identifiants spécifiques au domaine. Dans la plupart des cas, un contrat intelligent générera des identifiants uniques et les enregistrera sur les événements. Si ce nest pas le cas, vous devrez étudier le contrat intelligent et identifier ce qui rend votre entité unique et utiliser ces données pour générer un identifiant.

En remarque, le et toHexString() – couramment utilisées pour générer des identifiants à partir dadresses ou de hachages – renvoient une chaîne en minuscules. Cela signifie que lorsque vous interrogez un sous-graphe pour des entités, la chaîne dID fournie doit être en minuscules car la requête est sensible à la casse.

Pour plus dinformations sur le développement de sous-graphe, veuillez consulter documentation officielle . Des détails supplémentaires peuvent également être trouvés dans le référentiel GitHub du projet. Le Graph a également une communauté active et croissante prête à aider et à répondre aux questions qui se posent. Nous encourageons toute personne intéressée par le développement de ses propres sous-graphes à rejoindre le serveur Discord de Graph .

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *