Desenvolvimento de subgrafo, Parte 2: Manipulando matrizes e identificando entidades

(ProtoFire.io)

(Parte 1 ) | Parte 2

Esta postagem do blog fornece recomendações práticas sobre como usar matrizes de maneira eficiente e gerar IDs de entidade que são exclusivos e referenciáveis.

Na parte 1 desta série, fornecemos uma visão geral dos subgráficos para ajudar os desenvolvedores a entender sua estrutura básica. Além disso, também compartilhamos nossas percepções para mapear e agregar dados.

Desta vez, discutiremos outros dois tópicos: manipulação de matrizes e geração de IDs para entidades que são exclusivas e facilmente referenciadas. Como parte da discussão, forneceremos nossas recomendações sobre como gerenciar matrizes de maneira eficiente e nomear entidades de maneira adequada.

Manipulação de matrizes de maneira eficiente

Adicionar matrizes a entidades é útil em certos cenários. Por exemplo, há casos em que precisamos modelar uma lista de endereços para uma fonte de dados ou rastrear mudanças históricas para um determinado campo ao longo do tempo.

Sem conhecimento prévio de como as matrizes funcionam dentro dos subgráficos, poderíamos considerar criando um campo de um tipo de array em uma definição de tipo de entidade (dentro do arquivo schema.graphql) e inicializando um array vazio sempre que uma nova entidade do mesmo tipo é criada. Quando novos dados são adicionados ao array, podemos enviar os dados e salvar a entidade. Embora pareça intuitivo, infelizmente não funciona.

O tratamento manual de matrizes em subgráficos, especificamente no cenário acima, tem algumas ressalvas. Sempre que você acessa um array de uma entidade, o que você realmente obtém é uma cópia do array. Assim, se você adicionar novos dados e salvar a entidade, não funcionará como você esperava, já que você está simplesmente modificando uma cópia da matriz, enquanto o original permanece inalterado.

Para atualizar a matriz real, podemos colocar a cópia da matriz em uma variável e, em seguida, modificar os dados. Em seguida, podemos definir a variável como a nova matriz na entidade. Dessa forma, o array antigo é substituído pela cópia. Este processo de atualização da matriz é exemplificado no código a seguir.

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

Embora você possa atualizar uma matriz da maneira demonstrada acima, não é uma solução ideal . Além de ser inconveniente, há outro motivo para não manipular matrizes manualmente – consultas de viagem no tempo. (Leia a parte 1 da série para aprender mais sobre consultas de viagem no tempo.)

Só é possível realizar consultas de viagem no tempo, porque os subgráficos rastreiam todas as mudanças em todas as entidades presentes em todos os Tempo. Se houver muitas entidades com campos de array, que são grandes e atualizados com freqüência, as cópias de todos os arrays também precisarão ser armazenadas. Isso prejudicará o desempenho e o espaço em disco de qualquer indexador que esteja indexando seu subgráfico.

Atualmente, o serviço hospedado do Graph é o único indexador ativo disponível. No futuro, mais indexadores podem se juntar com a adição da rede descentralizada do The Graph. Esses novos indexadores poderão escolher quais subgráficos indexar. Se seu subgráfico for mal otimizado por causa de matrizes, provavelmente não será captado por nenhum indexador.

Para otimizar nossas matrizes, podemos usar o @derivedFrom anotação. Este método permite que qualquer campo de array definido em um tipo de entidade seja preenchido automaticamente por todas as entidades do tipo especificado vinculadas à entidade que estamos definindo. O exemplo a seguir descreve o uso da anotação @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
}

No exemplo acima, temos um usuário com uma lista gerada automaticamente das entidades Position. Sempre que nosso subgráfico recebe uma consulta solicitando o campo de posições da entidade User, o subgráfico realiza uma pesquisa reversa para todas as entidades do tipo Position vinculado à entidade User específica em seu campo user. Desta forma, as entidades vinculadas são aquelas que possuem o ID da string de outras entidades em um de seus campos.

Usando a anotação @derivedFrom, podemos definir a entidade tipo que desejamos para nossos dados de array, defina o campo usado ao derivar o array e vincule-o à entidade original por meio de seu ID. Há também o benefício de ser capaz de adicionar mais dados (por exemplo, criação ou atualização de metadados) às entidades que representam os dados da matriz. Como essas entidades são totalmente desenvolvidas, podemos atualizá-las facilmente carregando seus IDs em vez de procurá-los na matriz.

Ao lidar com matrizes com a anotação @derivedFrom é mais fácil, ainda existem algumas considerações a ter em conta.Primeiro, só funcionará com relacionamentos um-para-muitos. Em relacionamentos muitos para muitos, ainda precisamos de um lado da equação para lidar manualmente com a matriz. Em segundo lugar, você não poderá acessar os dados da matriz, enquanto o subgráfico estiver sendo indexado, uma vez que a matriz é preenchida quando consultada.

Criando uma nomenclatura convenção para IDs de entidade

Todas as entidades definidas no arquivo schema.graphql são identificadas por um campo de ID que é declarado como um tipo ID! representado como uma string. O campo ID é importante porque é usado para carregar, criar e salvar entidades.

Visto que o campo ID é o meio principal de identificar uma entidade, ele deve ser sempre único. Dito isso, garantir a exclusividade de um ID não é difícil. Os dados presentes durante o tempo de índice podem ser combinados para gerar IDs exclusivos. O código a seguir é um exemplo disso.

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

Pegando o hash de transação de um evento (exclusivo para transações diferentes) e anexando-o ao índice de log para o evento particular (que identifica um evento dentro de uma transação), podemos gerar um ID composto único. Dessa forma, podemos identificar uma determinada entidade entre outras entidades do mesmo tipo, desde que apenas uma única entidade seja criada para cada evento. Se necessário, também podemos acrescentar mais dados para identificar com exclusividade qualquer número de entidades criadas no mesmo evento. Por exemplo, poderíamos definir um contador para cada vez que uma entidade é criada e anexar o valor à entidade recém-criada.

Embora seja conveniente ter um método fácil de gerar IDs exclusivos para nossas entidades, também devemos esforce-se para gerar IDs previsíveis e que possam ser referenciados. Se tivermos entidades relacionadas a uma parte de nosso domínio que provavelmente será consultada por usuários finais por meio de seus IDs, podemos gerar um ID que faça referência ao domínio no qual estamos trabalhando.

Como exemplo, considere um cenário onde estamos criando uma Account entidade em um subgráfico DEX. Esta Account entidade armazenará o saldo do usuário, bem como outras informações. Se criarmos o ID da entidade com base no hash da transação, o usuário poderá pesquisar a transação que a criou em primeiro lugar e recriá-la, mas não será intuitivo. Uma alternativa melhor seria criar um ID com base no endereço Ethereum do usuário e, se necessário, combiná-lo com algo mais relevante para o domínio. Dessa forma, podemos identificar com exclusividade uma conta de usuário específica de outras contas do mesmo usuário.

Em resumo, IDs únicos genéricos sem quaisquer dados específicos de domínio podem ser úteis para entidades que não serão atualizadas constantemente. Isso é ideal para entidades criadas para salvar metadados para eventos específicos de domínio que serão consumidos de uma matriz derivada em uma entidade principal. Por exemplo, IDs únicos genéricos são mais adequados para transferências, balas, queimaduras e trocas.

Por outro lado, IDs específicos de domínio são ideais para entidades principais e qualquer outra entidade que obterá atualizações frequentes. É provável que você esteja usando uma combinação de um endereço Ethereum e alguns outros IDs específicos de domínio. Na maioria dos casos, um contrato inteligente gerará IDs exclusivos e os registrará nos eventos. Se esse não for o caso, você precisará estudar o contrato inteligente e identificar o que torna sua entidade única e usar esses dados para gerar um ID.

Como observação, o e toHexString() – comumente usados ​​para gerar IDs a partir de endereços ou hashes – retornam uma string minúscula. Isso significa que, quando você consulta um subgráfico de entidades, a string de ID fornecida deve estar em minúsculas, pois a consulta diferencia maiúsculas de minúsculas.

Para obter mais informações sobre o desenvolvimento de subgráficos, consulte documentação oficial . Detalhes adicionais também podem ser encontrados no repositório GitHub do projeto. A Graph também conta com uma comunidade ativa e crescente pronta para ajudar e esclarecer as dúvidas que surgirem. Encorajamos todos os interessados ​​em desenvolver seus próprios subgráficos a se juntar ao servidor Graph’s Discord .

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *