Sviluppo di sottografi, Parte 2: Gestione di array e identificazione di entità

(ProtoFire.io)

(Parte 1 ) | Parte 2

Questo post del blog fornisce consigli pratici su come utilizzare gli array in modo efficiente e generare ID entità univoci e referenziabili.

Nella parte 1 di questa serie, abbiamo fornito una panoramica dei sottografi per aiutare gli sviluppatori a comprendere la loro struttura di base. Inoltre, abbiamo anche condiviso le nostre intuizioni per la mappatura e laggregazione dei dati.

Questa volta discuteremo di altri due argomenti: la gestione di array e la generazione di ID per entità che sono sia uniche che facilmente referenziate. Come parte della discussione, forniremo i nostri consigli su come gestire in modo efficiente gli array e denominare correttamente le entità.

Gestire gli array in modo efficiente

Laggiunta di array alle entità è utile in certi scenari. Ad esempio, ci sono casi in cui dobbiamo modellare un elenco di indirizzi per unorigine dati o tenere traccia delle modifiche storiche per un particolare campo nel tempo.

Senza una conoscenza preliminare di come funzionano gli array allinterno dei sottografi, potremmo considerare creazione di un campo di un tipo di matrice su una definizione di tipo di entità (allinterno del file schema.graphql) e inizializzazione di un array vuoto ogni volta che viene creata una nuova entità dello stesso tipo. Quando vengono aggiunti nuovi dati allarray, possiamo eseguire il push dei dati e salvare lentità. Anche se questo suona intuitivo, sfortunatamente non funziona.

La gestione manuale di array sui sottografi, in particolare nello scenario sopra, ha alcuni avvertimenti. Ogni volta che accedi a un array di unentità, ciò che ottieni effettivamente è una copia dellarray. Pertanto, se aggiungi nuovi dati e salvi lentità, non funzionerà come ti aspetteresti, poiché stai semplicemente modificando una copia dellarray, mentre loriginale rimane invariato.

Per aggiornare larray effettivo, possiamo posizionare la copia dellarray in una variabile e quindi modificare i dati. Successivamente, possiamo impostare la variabile come nuovo array sullentità. In questo modo, il vecchio array viene sostituito dalla copia. Questo processo di aggiornamento dellarray è esemplificato nel codice seguente.

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

Sebbene sia possibile aggiornare un array nel modo mostrato sopra, non è una soluzione ideale . Oltre ad essere scomodo, cè un altro motivo per non gestire manualmente gli array: le query sui viaggi nel tempo. (Leggi la parte 1 della serie per saperne di più sulle query sui viaggi nel tempo.)

È possibile eseguire solo query sui viaggi nel tempo, perché i sottografi tengono traccia di tutti i cambiamenti in tutte le entità presenti tutte le tempo. Se sono presenti molte entità con campi array, che sono grandi e aggiornati spesso, sarà necessario memorizzare anche copie di tutti gli array. Ciò richiederà un pedaggio sulle prestazioni e sullo spazio su disco di qualsiasi indicizzatore che sta indicizzando il tuo grafico secondario.

Attualmente, il servizio ospitato di The Graph è lunico indicizzatore attivo disponibile. In futuro, più indicizzatori potranno aderire con laggiunta della rete decentralizzata di The Graph. Questi nuovi indicizzatori potranno scegliere quali sottografi indicizzare. Se il tuo sottografo è scarsamente ottimizzato a causa degli array, è probabile che non verrà rilevato da alcun indicizzatore.

Per ottimizzare i nostri array, possiamo utilizzare @derivedFrom annotazione. Questo metodo consente a qualsiasi campo dellarray definito in un tipo di entità di essere riempito automaticamente da tutte le entità del tipo specificato collegate allentità che stiamo definendo. Lesempio seguente mostra lutilizzo dellannotazione @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
}

Nellesempio precedente, abbiamo un utente con un elenco generato automaticamente delle entità Position. Ogni volta che il nostro grafico secondario riceve una query che richiede il campo delle posizioni dellentità User, il grafico secondario esegue una ricerca inversa per tutte le entità di tipo Position collegati allentità User specifica nel campo user. In questo modo, le entità collegate sono quelle che hanno lID stringa di altre entità in uno dei suoi campi.

Utilizzando lannotazione @derivedFrom, possiamo definire lentità tipo che vogliamo per i nostri dati di matrice, definire il campo utilizzato quando si ricava larray e collegarlo allentità originale tramite il loro ID. Cè anche il vantaggio di poter aggiungere più dati (ad esempio, creazione o aggiornamento di metadati) alle entità che rappresentano i dati dellarray. Poiché si tratta di entità a tutti gli effetti, possiamo aggiornarle facilmente caricando i loro ID invece di cercarli nellarray.

Mentre si gestiscono gli array con lannotazione @derivedFrom è più facile, ci sono ancora alcune considerazioni di cui essere a conoscenza.Innanzitutto, funzionerà solo con relazioni uno-a-molti. Nelle relazioni molti-a-molti, abbiamo ancora bisogno di un lato dellequazione per gestire manualmente larray. In secondo luogo, non sarai in grado di accedere ai dati dellarray, mentre il sottografo viene indicizzato, poiché larray viene popolato quando viene interrogato.

Creazione di una denominazione convenzione per gli ID entità

Tutte le entità definite nel file schema.graphql sono identificate da un campo ID che è dichiarato come un tipo ID! rappresentato come una stringa. Il campo ID è importante in quanto viene utilizzato per caricare, creare e salvare entità.

Poiché il campo ID è il mezzo principale per identificare unentità, dovrebbe essere sempre univoco. Detto questo, garantire lunicità di un ID non è difficile. I dati presenti durante il tempo di indicizzazione possono essere combinati per generare ID univoci. Il codice seguente ne è un esempio.

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

Prendendo lhash della transazione di un evento (unico per transazioni diverse) e aggiungendolo allindice di log per levento particolare (che identifica un evento allinterno di una transazione), possiamo generare un ID composto univoco. In questo modo, possiamo identificare una particolare entità tra altre entità dello stesso tipo a condizione che venga creata solo una singola entità per ogni singolo evento. Se necessario, possiamo anche aggiungere più dati per identificare in modo univoco qualsiasi numero di entità create nello stesso evento. Ad esempio, potremmo impostare un contatore per ogni volta che viene creata unentità e aggiungere il valore allentità appena creata.

Sebbene sia conveniente avere un metodo semplice per generare ID univoci per le nostre entità, dovremmo anche sforzarsi di generare ID prevedibili e di cui è possibile fare riferimento. Se abbiamo entità correlate a una parte del nostro dominio che potrebbe essere interrogata dagli utenti finali tramite il loro ID, possiamo generare un ID che fa riferimento al dominio su cui stiamo lavorando.

Come esempio, considera uno scenario in cui stiamo creando unentità Account su un grafico secondario DEX. Questa entità Account memorizzerà il saldo dellutente e altre informazioni. Se creiamo lID entità in base allhash della transazione, lutente potrebbe cercare la transazione che lha creata in primo luogo e ricrearla, ma non sarà intuitivo. Unalternativa migliore sarebbe creare un ID basato sullindirizzo Ethereum dellutente e, se necessario, combinarlo con qualcosaltro rilevante per il dominio. In questo modo, possiamo identificare in modo univoco un particolare account utente da altri account dello stesso utente.

In sintesi, ID univoci generici senza dati specifici del dominio possono essere utili per le entità che non verranno aggiornate costantemente. Questo è ideale per le entità create per salvare i metadati per eventi specifici del dominio che verranno utilizzati da un array derivato su unentità principale. Ad esempio, gli ID univoci generici sono più adatti per trasferimenti, zecche, ustioni e scambi.

Daltra parte, gli ID specifici del dominio sono ideali per le entità principali e qualsiasi altra entità che riceverà aggiornamenti frequenti. È probabile che tu stia utilizzando una combinazione di un indirizzo Ethereum e alcuni altri ID specifici del dominio. Nella maggior parte dei casi, uno smart contract genererà ID univoci e li registrerà negli eventi. In caso contrario, dovrai studiare lo smart contract e identificare ciò che rende unica la tua entità e utilizzare quei dati per generare un ID.

Come nota a margine, il e toHexString(), comunemente usati per generare ID da indirizzi o hash, restituiscono una stringa minuscola. Ciò significa che, quando esegui una query su un sottografo per entità, la stringa ID fornita deve essere minuscola poiché la query fa distinzione tra maiuscole e minuscole.

Per ulteriori informazioni sullo sviluppo del sottografo, consulta il documentazione ufficiale . Ulteriori dettagli sono disponibili anche nel repository GitHub del progetto. The Graph ha anche una comunità attiva e in crescita pronta ad aiutare e rispondere alle domande che sorgono. Incoraggiamo chiunque sia interessato a sviluppare i propri sottografi a unirsi al server Discord di Graph .

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *