Ontwikkeling subgrafiek, deel 2: arrays behandelen en entiteiten identificeren

(ProtoFire.io)

(Deel 1 ) | Deel 2

Deze blogpost bevat praktische aanbevelingen voor het efficiënt gebruiken van arrays en het genereren van entiteits-IDs die uniek zijn en waarnaar kan worden verwezen.

In deel 1 van deze serie hebben we een overzicht van subgraphs gegeven om ontwikkelaars te helpen hun basisstructuur te begrijpen. Daarnaast hebben we ook onze inzichten gedeeld voor het in kaart brengen en aggregeren van gegevens.

Deze keer zullen we nog twee onderwerpen bespreken: het omgaan met arrays en het genereren van IDs voor entiteiten die zowel uniek zijn als waarnaar gemakkelijk kan worden verwezen. Als onderdeel van de discussie zullen we onze aanbevelingen doen voor het efficiënt beheren van arrays en het correct benoemen van entiteiten.

Efficiënt omgaan met arrays

Het toevoegen van arrays aan entiteiten is handig in bepaalde scenarios. Er zijn bijvoorbeeld gevallen waarin we een lijst met adressen voor een gegevensbron moeten modelleren of historische veranderingen voor een bepaald veld in de loop van de tijd moeten volgen.

Zonder voorafgaande kennis van hoe arrays werken binnen subgraphs, zouden we kunnen overwegen het creëren van een veld van een array-type op basis van een entiteitstypedefinitie (binnen het schema.graphql -bestand) en het initialiseren van een lege array wanneer een nieuwe entiteit van hetzelfde type wordt gemaakt. Wanneer nieuwe gegevens aan de array worden toegevoegd, kunnen we de gegevens pushen en de entiteit opslaan. Hoewel dit intuïtief klinkt, werkt het helaas niet.

Het handmatig verwerken van arrays op subgraphs, met name in het bovenstaande scenario, heeft een paar kanttekeningen. Telkens wanneer u een array van een entiteit opent, krijgt u feitelijk een kopie van de array. Dus als u nieuwe gegevens toevoegt en de entiteit opslaat, zal het niet werken zoals u zou verwachten, aangezien u gewoon een kopie van de array wijzigt terwijl het origineel ongewijzigd blijft.

Om te updaten de werkelijke array, kunnen we de kopie van de array in een variabele plaatsen en vervolgens de gegevens wijzigen. Vervolgens kunnen we de variabele instellen als de nieuwe array op de entiteit. Op deze manier wordt de oude array vervangen door de kopie. Dit proces van het updaten van de array wordt geïllustreerd in de volgende code.

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

Hoewel je een array kunt updaten op de manier die hierboven is aangetoond, is het geen ideale oplossing . Behalve dat het ongemakkelijk is, is er nog een reden om arrays niet handmatig te behandelen: tijdreizen. (Lees deel 1 van de serie voor meer informatie over tijdreizenquerys.)

Het is alleen mogelijk om tijdreizenquerys uit te voeren, omdat subgraphs alle wijzigingen bijhouden in alle entiteiten die alle tijd. Als er veel entiteiten zijn met matrixvelden, die groot zijn en vaak worden bijgewerkt, moeten ook kopieën van alle arrays worden opgeslagen. Dit zal een negatieve invloed hebben op de prestaties en schijfruimte van elke indexeerder die uw subgraaf indexeert.

Momenteel is de gehoste service van The Graph de enige actieve indexer die beschikbaar is. In de toekomst kunnen meer indexeerders zich aansluiten met de toevoeging van het gedecentraliseerde netwerk van The Graph. Deze nieuwe indexeerders kunnen kiezen welke subgraphs ze willen indexeren. Als uw subgraaf slecht is geoptimaliseerd vanwege arrays, wordt deze waarschijnlijk door geen enkele indexeerder opgepikt.

Om onze arrays te optimaliseren, kunnen we de @derivedFrom annotatie. Met deze methode kan elk matrixveld dat is gedefinieerd in een entiteitstype automatisch worden gevuld door alle entiteiten van het gespecificeerde type die zijn gekoppeld aan de entiteit die we definiëren. Het volgende voorbeeld toont het gebruik van de @derivedFrom annotatie.

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
}

In het bovenstaande voorbeeld hebben we een gebruiker met een automatisch gegenereerde lijst van de Position entiteiten. Telkens wanneer onze subgraaf een zoekopdracht ontvangt waarin wordt gevraagd naar het positieveld van de entiteit User, voert de subgraaf een reverse lookup uit voor alle entiteiten van het type Position gekoppeld aan de specifieke User entiteit op hun user veld. Op deze manier zijn de gekoppelde entiteiten degene met de string-ID van andere entiteiten in een van de velden.

Met de @derivedFrom annotatie kunnen we de entiteit definiëren type dat we willen voor onze array-gegevens, definieer het veld dat wordt gebruikt bij het afleiden van de array en koppel het aan de originele entiteit via hun ID. Er is ook het voordeel dat u meer gegevens kunt toevoegen (bijvoorbeeld metagegevens maken of bijwerken) aan de entiteiten die de arraygegevens vertegenwoordigen. Aangezien dit volwaardige entiteiten zijn, kunnen we ze gemakkelijk bijwerken door hun IDs te laden in plaats van ze op te zoeken in de array.

Bij het afhandelen van arrays met de @derivedFrom annotatie gemakkelijker is, zijn er nog enkele overwegingen waar u op moet letten.Ten eerste werkt het alleen met een-op-veel-relaties. In veel-op-veel-relaties hebben we nog steeds één kant van de vergelijking nodig om de array handmatig af te handelen. Ten tweede heb je geen toegang tot de array-gegevens terwijl de subgraaf wordt geïndexeerd, aangezien de array wordt gevuld wanneer er naar wordt gevraagd.

Een naamgeving aanmaken conventie voor entiteits-IDs

Alle entiteiten die zijn gedefinieerd in het schema.graphql -bestand worden geïdentificeerd door een ID-veld dat gedeclareerd als een ID! type weergegeven als een string. Het ID-veld is belangrijk omdat het wordt gebruikt om entiteiten te laden, aan te maken en op te slaan.

Aangezien het ID-veld het belangrijkste middel is om een ​​entiteit te identificeren, moet het altijd uniek zijn. Dat gezegd hebbende, is het niet moeilijk om het unieke karakter van een ID te garanderen. Gegevens die tijdens de indexering aanwezig zijn, kunnen worden gecombineerd om unieke IDs te genereren. De volgende code is hier een voorbeeld van.

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

Door de transactie-hash van een gebeurtenis te nemen (uniek voor verschillende transacties) en deze toe te voegen aan de logindex voor de specifieke gebeurtenis (die een gebeurtenis binnen een transactie identificeert), kunnen we een unieke samengestelde ID genereren. Op deze manier kunnen we een bepaalde entiteit identificeren tussen andere entiteiten van hetzelfde type, op voorwaarde dat er slechts één entiteit wordt gemaakt voor een enkele gebeurtenis. Indien nodig kunnen we ook meer gegevens toevoegen om een ​​uniek aantal entiteiten te identificeren die in dezelfde gebeurtenis zijn gemaakt. We kunnen bijvoorbeeld een teller instellen voor elke keer dat een entiteit wordt gemaakt en de waarde toevoegen aan de nieuw gemaakte entiteit.

Hoewel het handig is om een ​​eenvoudige methode te hebben voor het genereren van unieke IDs voor onze entiteiten, moeten we ook streef ernaar om IDs te genereren die voorspelbaar zijn en waarnaar kan worden verwezen. Als we entiteiten hebben die betrekking hebben op een deel van ons domein dat door eindgebruikers waarschijnlijk via hun ID zal worden opgevraagd, kunnen we een ID genereren die verwijst naar het domein waaraan we werken.

Als voorbeeld, overweeg een scenario waarin we een Account -entiteit creëren op een DEX-subgraaf. Deze Account entiteit zal het saldo van de gebruiker opslaan, evenals andere informatie. Als we de entiteits-ID maken op basis van de transactie-hash, kan de gebruiker zoeken naar de transactie waarmee deze is gemaakt en deze opnieuw maken, maar het is niet intuïtief. Een beter alternatief zou zijn om een ​​ID te maken op basis van het Ethereum-adres van de gebruiker en, indien nodig, dat te combineren met iets anders dat relevant is voor het domein. Op deze manier kunnen we een bepaald gebruikersaccount op unieke wijze identificeren met andere accounts van dezelfde gebruiker.

Samengevat kunnen generieke unieke IDs zonder domeinspecifieke gegevens handig zijn voor entiteiten die niet constant worden bijgewerkt. Dit is ideaal voor entiteiten die zijn gemaakt om metagegevens op te slaan voor domeinspecifieke gebeurtenissen die zullen worden verbruikt uit een afgeleide array op een hoofdentiteit. Generieke unieke IDs zijn bijvoorbeeld beter geschikt voor overdrachten, pepermuntjes, burns en swaps.

Aan de andere kant zijn domeinspecifieke IDs ideaal voor hoofdentiteiten en elke andere entiteit die regelmatig wordt bijgewerkt. U gebruikt waarschijnlijk een combinatie van een Ethereum-adres en enkele andere domeinspecifieke IDs. In de meeste gevallen genereert een smart contract unieke IDs en logt deze op de gebeurtenissen. Als dit niet het geval is, moet u het slimme contract bestuderen en vaststellen wat uw entiteit uniek maakt en die gegevens gebruiken om een ​​ID te genereren.

Als een kanttekening: de toHex() en toHexString() methoden – vaak gebruikt om IDs te genereren uit adressen of hashes – retourneren een tekenreeks in kleine letters. Dit betekent dat wanneer u een subgraaf voor entiteiten opvraagt, de opgegeven ID-string in kleine letters moet zijn, aangezien de query hoofdlettergevoelig is.

Voor meer informatie over de ontwikkeling van subgrafieken, kijk dan op The Graphs officiële documentatie . Aanvullende details zijn ook te vinden in de GitHub-repository van het project. The Graph heeft ook een actieve en groeiende gemeenschap die klaar staat om te helpen en de opkomende vragen te beantwoorden. We moedigen iedereen die geïnteresseerd is in het ontwikkelen van hun eigen subgraphs aan om lid te worden van de Graph’s Discord-server .

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *