Opracowywanie podgrafów, część 2: Obsługa tablic i identyfikowanie jednostek

(ProtoFire.io)

(Część 1 ) | Część 2

Ten wpis na blogu zawiera praktyczne zalecenia dotyczące efektywnego korzystania z tablic i generowania identyfikatory jednostek, które są unikalne i możliwe do odniesienia.

W części 1 tej serii przedstawiliśmy przegląd podgrafów, aby pomóc programistom zrozumieć ich podstawową strukturę. Ponadto udostępniliśmy również nasze spostrzeżenia dotyczące mapowania i agregowania danych.

Tym razem omówimy kolejne dwa tematy: obsługę tablic i generowanie identyfikatorów dla jednostek, które są zarówno unikalne, jak i łatwe do przywołania. W ramach dyskusji przedstawimy nasze zalecenia dotyczące efektywnego zarządzania tablicami i prawidłowego nazywania jednostek.

Efektywna obsługa tablic

Dodawanie tablic do encji jest przydatne w niektórych scenariuszach. Na przykład istnieją przypadki, w których musimy modelować listę adresów dla źródła danych lub śledzić historyczne zmiany dla określonego pola w czasie.

Bez wcześniejszej wiedzy o tym, jak tablice działają w podgrafach, moglibyśmy rozważyć tworzenie pola typu tablicowego w definicji typu jednostki (w pliku schema.graphql) i inicjowanie pustej tablicy za każdym razem, gdy tworzona jest nowa jednostka tego samego typu. Kiedy nowe dane są dodawane do tablicy, możemy wypchnąć dane i zapisać jednostkę. Chociaż brzmi to intuicyjnie, niestety nie działa.

Ręczna obsługa tablic na podgrafach, szczególnie w powyższym scenariuszu, ma kilka zastrzeżeń. Za każdym razem, gdy uzyskujesz dostęp do tablicy encji, otrzymujesz kopię tablicy. Tak więc, jeśli dodasz nowe dane i zapiszesz encję, nie będzie to działać tak, jak byś oczekiwał, ponieważ po prostu modyfikujesz kopię tablicy, podczas gdy oryginał pozostaje niezmieniony.

Aby zaktualizować rzeczywistą tablicę, możemy umieścić kopię tablicy w zmiennej, a następnie zmodyfikować dane. Następnie możemy ustawić zmienną jako nową tablicę w encji. W ten sposób stara tablica zostaje zastąpiona kopią. Ten proces aktualizacji tablicy jest przedstawiony w poniższym kodzie.

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

Chociaż możesz zaktualizować tablicę w sposób przedstawiony powyżej, nie jest to idealne rozwiązanie . Oprócz tego, że jest to niewygodne, istnieje inny powód, dla którego nie należy ręcznie obsługiwać tablic – zapytania dotyczące podróży w czasie. (Przeczytaj część 1 serii, aby dowiedzieć się więcej o zapytaniach dotyczących podróży w czasie.)

Zapytania dotyczące podróży w czasie można wykonywać tylko, ponieważ podgrafy śledzą wszystkie zmiany we wszystkich bytach prezentujących wszystkie czas. Jeśli istnieje wiele jednostek z polami tablicowymi, które są duże i często aktualizowane, kopie wszystkich tablic również będą musiały być przechowywane. Będzie to miało wpływ na wydajność i miejsce na dysku każdego indeksatora, który indeksuje Twój podgraf.

Obecnie hostowana usługa The Graph jest jedynym dostępnym aktywnym indeksatorem. W przyszłości więcej osób indeksujących może dołączyć do zdecentralizowanej sieci The Graph. Te nowe indeksatory będą mogły wybrać podgrafy do indeksowania. Jeśli Twój podgraf jest słabo zoptymalizowany z powodu tablic, prawdopodobnie nie zostanie odebrany przez żaden indeksator.

Aby zoptymalizować nasze tablice, możemy użyć @derivedFrom adnotacja. Ta metoda pozwala na automatyczne wypełnienie dowolnego pola tablicowego zdefiniowanego w typie jednostki przez wszystkie jednostki określonego typu połączone z jednostką, którą definiujemy. Poniższy przykład przedstawia użycie adnotacji @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
}

W powyższym przykładzie mamy użytkownika z automatycznie wygenerowaną listą Position jednostek. Za każdym razem, gdy nasz podgraf otrzyma zapytanie z pytaniem o pole pozycji encji User, podgraf przeprowadza wyszukiwanie wsteczne dla wszystkich jednostek typu Position powiązany z określonym elementem User w ich polu user. W ten sposób połączone encje to te, które w jednym ze swoich pól mają identyfikator ciągu innych obiektów.

Korzystając z adnotacji @derivedFrom, możemy zdefiniować jednostkę wpisz, który chcemy dla naszych danych tablicowych, zdefiniuj pole używane podczas wyprowadzania tablicy i połącz je z oryginalną jednostką za pomocą ich identyfikatora. Istnieje również korzyść z możliwości dodawania większej ilości danych (np. Tworzenie lub aktualizowanie metadanych) do jednostek reprezentujących dane tablicowe. Ponieważ są to pełnoprawne encje, możemy je łatwo zaktualizować, ładując ich identyfikatory zamiast szukać ich w tablicy.

Podczas obsługi tablic z adnotacją @derivedFrom jest łatwiejsze, należy jednak pamiętać o kilku kwestiach.Po pierwsze, będzie działać tylko w przypadku relacji jeden do wielu. W relacjach wiele do wielu nadal potrzebujemy jednej strony równania, aby ręcznie obsłużyć tablicę. Po drugie, nie będziesz mieć dostępu do danych tablicy, gdy podgraf jest indeksowany, ponieważ tablica jest zapełniana podczas odpytywania.

Tworzenie nazewnictwa konwencja dotycząca identyfikatorów jednostek

Wszystkie jednostki zdefiniowane w pliku schema.graphql są identyfikowane przez pole identyfikatora, które jest zadeklarowany jako typ ID! reprezentowany jako ciąg. Pole ID jest ważne, ponieważ służy do ładowania, tworzenia i zapisywania jednostek.

Ponieważ pole ID jest głównym sposobem identyfikacji jednostki, powinno być zawsze unikalne. Mimo to zagwarantowanie niepowtarzalności dowodu osobistego nie jest trudne. Dane obecne w czasie indeksowania można łączyć w celu generowania unikalnych identyfikatorów. Poniższy kod jest tego przykładem.

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

Poprzez pobranie skrótu transakcji zdarzenia (unikalnego dla różnych transakcji) i dołączenie go do indeksu dziennika dla konkretne zdarzenie (które identyfikuje zdarzenie w ramach transakcji), możemy wygenerować unikalny identyfikator złożony. W ten sposób możemy zidentyfikować konkretną jednostkę wśród innych jednostek tego samego typu, pod warunkiem, że dla jednego zdarzenia zostanie utworzona tylko jedna jednostka. W razie potrzeby możemy również dołączyć więcej danych, aby jednoznacznie zidentyfikować dowolną liczbę podmiotów utworzonych w tym samym zdarzeniu. Na przykład moglibyśmy ustawić licznik dla każdego momentu utworzenia encji i dołączyć wartość do nowo utworzonej encji.

Posiadanie łatwej metody generowania unikalnych identyfikatorów dla naszych encji jest wygodne, ale powinniśmy również staraj się generować identyfikatory, które są przewidywalne i do których można się odwołać. Jeśli mamy jednostki powiązane z częścią naszej domeny, o którą mogą zapytać użytkownicy końcowi za pośrednictwem ich identyfikatora, możemy wygenerować identyfikator odwołujący się do domeny, nad którą pracujemy.

Jako przykład rozważmy scenariusz, w którym tworzymy jednostkę Account na podgrafie DEX. Ta Account jednostka będzie przechowywać saldo użytkownika, a także inne informacje. Jeśli utworzymy identyfikator jednostki na podstawie skrótu transakcji, użytkownik mógłby w pierwszej kolejności wyszukać transakcję, która go utworzyła, i odtworzyć ją, ale nie będzie to intuicyjne. Lepszą alternatywą byłoby utworzenie identyfikatora na podstawie adresu Ethereum użytkownika i, w razie potrzeby, połączenie go z czymś innym, związanym z domeną. W ten sposób możemy jednoznacznie zidentyfikować określone konto użytkownika na podstawie innych kont tego samego użytkownika.

Podsumowując, ogólne unikalne identyfikatory bez danych specyficznych dla domeny mogą być przydatne dla podmiotów, które nie będą stale aktualizowane. Jest to idealne rozwiązanie dla jednostek utworzonych w celu zapisywania metadanych dla zdarzeń specyficznych dla domeny, które będą używane z tablicy pochodnej w jednostce głównej. Na przykład ogólne unikalne identyfikatory lepiej nadają się do transferów, mennic, wypaleń i zamian.

Z drugiej strony, identyfikatory specyficzne dla domeny są idealne dla głównych podmiotów i wszelkich innych podmiotów, które będą często aktualizowane. Prawdopodobnie używasz kombinacji adresu Ethereum i kilku innych identyfikatorów specyficznych dla domeny. W większości przypadków inteligentny kontrakt generuje unikalne identyfikatory i rejestruje je w zdarzeniach. Jeśli tak nie jest, musisz przestudiować inteligentny kontrakt i określić, co sprawia, że ​​Twoja jednostka jest unikalna, i użyć tych danych do wygenerowania identyfikatora.

Na marginesie, i toHexString() – powszechnie używane do generowania identyfikatorów na podstawie adresów lub skrótów – zwracają ciąg z małych liter. Oznacza to, że kiedy odpytujesz podgraf pod kątem encji, podany ciąg ID powinien być zapisany małymi literami, ponieważ zapytanie rozróżnia wielkość liter.

Aby uzyskać więcej informacji na temat tworzenia podgrafów, sprawdź oficjalna dokumentacja . Dodatkowe szczegóły można również znaleźć w repozytorium GitHub projektu. The Graph ma również aktywną i rosnącą społeczność gotową do pomocy i odpowiedzi na pojawiające się pytania. Zachęcamy wszystkich zainteresowanych tworzeniem własnych podgrafów do przyłączenia się do serwera Graph na Discordzie .

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *