El controlador de llamadas, el controlador de bloques, la relación de entidad y el índice de texto completo de Graph 3 subGraph

En la sección anterior, construimos un subgrafo con funciones básicas basadas en el ejemplo oficial. En esta sección, presentamos algunas otras características.

manejador de llamadas

Si bien los eventHandlers brindan una forma eficiente de recopilar cambios relevantes en el estado del contrato, muchos contratos evitan generar eventos para optimizar los costos de gas. En estos casos, los subgrafos pueden suscribirse a llamadas a métodos de contrato especificados. Esto se logra definiendo un controlador de llamadas que hace referencia a la firma de la función y un controlador asignado que maneja las llamadas a esta función. Para manejar estas llamadas, el controlador de mapeo recibirá un ethereum.Call como parámetro, que contiene la entrada y salida de la llamada.

callHandler solo se activará en uno de dos casos: cuando la función especificada es llamada por una cuenta que no sea el contrato mismo, o cuando está marcada como externa en Solidity y llamada como otra función en el mismo contrato.

callHandler actualmente se basa en la API de seguimiento de Parity. Ciertas redes, como la cadena BNB y Arbitrum, no admiten esta API. Al encontrar estas redes, los desarrolladores de subgrafos deben usar eventHandle. Funcionan mucho mejor que llamar a callHandler y son compatibles con todas las redes de evm.

Para definir callHandler, simplemente agregue la matriz callHandlers debajo de la fuente de datos a la que desea suscribirse.

subgrafo.yaml:

specVersion: 0.0.5
schema:
  file: ./schema.graphql
dataSources:
  - kind: ethereum
    name: GravatarRegistry
    network: goerli
    source:
      address: "0x964F658FC863BAceFC719b85e8730fbc11c86ce4"
      abi: GravatarRegistry
      startBlock: 8266411
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.7
      language: wasm/assemblyscript
      entities:
        - Gravatar
        - Transaction
      abis:
        - name: GravatarRegistry
          file: ./abis/GravatarRegistry.json
      callHandlers:
        - function: createGravatar(string,string)
          handler: handleCreateGravatar
      # eventHandlers:
      #   - event: NewGravatar(uint256,address,string,string)
      #     handler: handleNewGravatar
      #   - event: UpdatedGravatar(uint256,address,string,string)
      #     handler: handleUpdatedGravatar
      file: ./src/gravatar-registry.ts

Esta vez modificamos ligeramente el ejemplo del capítulo anterior, redefinimos una entidad, usamos el valor hash de cada transacción como id y registramos la información básica de cada transacción.

esquema.graphql:

type Transaction @entity(immutable: true) {
  id: String!
  displayName: String! # string
  imageUrl: String! # string
}

gravatar-registry.ts:

import {
  CreateGravatarCall,
  NewGravatar as NewGravatarEvent,
  UpdatedGravatar as UpdatedGravatarEvent
} from "../generated/GravatarRegistry/GravatarRegistry"
import { Gravatar,Transaction} from "../generated/schema"

export function handleCreateGravatar(call: CreateGravatarCall): void {
  let id = call.transaction.hash.toHexString()
  let transaction = new Transaction(id)
  transaction.displayName = call.inputs._displayName
  transaction.imageUrl = call.inputs._imageUrl
  transaction.save()
}

Vuelva a publicar el subgrafo:

graph codegen
graph build
yarn deploy

Los resultados de la consulta son los siguientes:

manejadores de bloques

Además de suscribirse a eventos de contratos o llamadas a funciones, un subgrafo puede desear actualizar sus datos cuando se agregan nuevos bloques a la cadena. Para lograr esto, los subgráficos pueden ejecutar una función después de cada bloque o bloque que coincida con un filtro predefinido.

Los llamados filtros predefinidos son los siguientes:

filter:
  kind: call

Si el bloque contiene una llamada al contrato especificado, se activará la función de controlador definida.

Al igual que callHandler, el filtro de blockHandlers se basa en la API de seguimiento de Parity. Ciertas redes, como la cadena BNB y Arbitrum, no admiten esta API.

subgrafo.yaml:

specVersion: 0.0.5
schema:
  file: ./schema.graphql
dataSources:
  - kind: ethereum
    name: GravatarRegistry
    network: goerli
    source:
      address: "0x964F658FC863BAceFC719b85e8730fbc11c86ce4"
      abi: GravatarRegistry
      startBlock: 8266411
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.7
      language: wasm/assemblyscript
      entities:
        - Gravatar
        - Transaction
        - Block
      abis:
        - name: GravatarRegistry
          file: ./abis/GravatarRegistry.json
      eventHandlers:
        - event: NewGravatar(uint256,address,string,string)
          handler: handleNewGravatar
        - event: UpdatedGravatar(uint256,address,string,string)
          handler: handleUpdatedGravatar
      callHandlers:
        - function: createGravatar(string,string)
          handler: handleCreateGravatar
      blockHandlers:
        - handler: handleBlockWithCallToContract
          filter:
            kind: call
      file: ./src/gravatar-registry.ts

Esta vez modificamos ligeramente el ejemplo del capítulo anterior, redefinimos una entidad, usamos el valor hash de cada transacción como id y registramos la información básica de cada transacción.

esquema.graphql:

type Block @entity(immutable: true) {
  id: String!
  blockNumber: BigInt!
  blockTimestamp: BigInt!
}

gravatar-registry.ts:

import {
  CreateGravatarCall,
  NewGravatar as NewGravatarEvent,
  UpdatedGravatar as UpdatedGravatarEvent
} from "../generated/GravatarRegistry/GravatarRegistry"
import { Gravatar,Transaction,Block} from "../generated/schema"
import { ethereum } from '@graphprotocol/graph-ts'

export function handleBlockWithCallToContract(block: ethereum.Block): void {
  let id = block.hash.toHexString()
  let entity = new Block(id)
  entity.blockNumber = block.number
  entity.blockTimestamp = block.timestamp

  entity.save()
}

Vuelva a publicar el subgrafo:

graph codegen
graph build
yarn deploy

Los resultados de la consulta son los siguientes:

relación entre entidades

Una entidad puede tener una relación con una o más entidades del esquema. Estas relaciones se pueden atravesar en las consultas. Las relaciones en The Graph son unidireccionales y las relaciones bidireccionales también se pueden simular definiendo una relación unidireccional en "un extremo" de la relación. Definir una relación en una entidad es como cualquier otro campo, excepto que el tipo especificado es el de la otra entidad.

En el ejemplo anterior, involucramos tres entidades, Gravatar, Transaction y Block, y su relación es la siguiente:

En la figura anterior se puede ver que Gravatar y Transaction son relaciones de uno a uno, y Block y Transaction son relaciones de uno a muchos. Redefinamos los campos de entidad:

type Gravatar @entity(immutable: false) {
  id: String!
  owner: Bytes! # address
  displayName: String! # string
  imageUrl: String! # string
  transaction: Transaction
}


type Transaction @entity(immutable: true) {
  id: Bytes!
  block: Block
  gasPrice: BigInt!
}

type Block @entity(immutable: true) {
  id: Bytes!
  blockNumber: BigInt!
  blockTimestamp: BigInt!
  transactions: [Transaction!]
}

gravatar-registry.ts:

import {
  CreateGravatarCall,
  NewGravatar as NewGravatarEvent,
  UpdatedGravatar as UpdatedGravatarEvent
} from "../generated/GravatarRegistry/GravatarRegistry"
import { Gravatar,Transaction,Block} from "../generated/schema"
import { ethereum } from '@graphprotocol/graph-ts'

export function handleNewGravatar(event: NewGravatarEvent): void {
  let gravatar = new Gravatar(event.params.id.toString());
  gravatar.owner = event.params.owner
  gravatar.displayName = event.params.displayName
  gravatar.imageUrl = event.params.imageUrl
  gravatar.transaction = event.transaction.hash
  gravatar.save()
}

export function handleUpdatedGravatar(event: UpdatedGravatarEvent): void {
  let id = event.params.id.toString();
  let gravatar = Gravatar.load(id)
  if (gravatar == null) {
    gravatar = new Gravatar(id)
  }
  gravatar.owner = event.params.owner
  gravatar.displayName = event.params.displayName
  gravatar.imageUrl = event.params.imageUrl
  gravatar.transaction = event.transaction.hash
  gravatar.save()
}


export function handleCreateGravatar(call: CreateGravatarCall): void {
  let transaction = new Transaction(call.transaction.hash)
  transaction.gasPrice = call.transaction.gasPrice
  transaction.block = call.block.hash
  transaction.save()
}


export function handleBlockWithCallToContract(block: ethereum.Block): void {
  let entity = new Block(block.hash)
  entity.blockNumber = block.number
  entity.blockTimestamp = block.timestamp

  entity.save()
}

Después de volver a publicar, haz la siguiente consulta:

{
  gravatars(first: 5) {
    id
    owner
    displayName
    imageUrl
    transaction{
      id
      gasPrice
      block {
        id
        blockNumber
      }
    }
  }
}

El resultado es el siguiente:

{
  "data": {
    "gravatars": [
      {
        "id": "0",
        "owner": "0xba8b604410ca76af86bda9b00eb53b65ac4c41ac",
        "displayName": "Carl",
        "imageUrl": "https://thegraph.com/img/team/team_04.png",
        "transaction": {
          "id": "0x5b8f57f7b377165f046fc1bcda50846d6eba2500212266752492f0dd0476a7f9",
          "gasPrice": "22045257798",
          "block": {
            "id": "0x98f069280b3105a73f6743997bc460dcf3295860c1ec7ae783938903da412c66",
            "blockNumber": "8272525"
          }
        }
      },
      {
        "id": "1",
        "owner": "0xba8b604410ca76af86bda9b00eb53b65ac4c41ac",
        "displayName": "gambo2",
        "imageUrl": "https://thegraph.com/img/team/bw_Lucas2.jpg",
        "transaction": null
      },
      {
        "id": "2",
        "owner": "0x04f367f342a3c763f2f337572ec150b2d2b84e37",
        "displayName": "gambo017",
        "imageUrl": "https://thegraph.com/img/team/team_04.png",
        "transaction": null
      }
    ]
  }
}

Observamos que algunas transacciones son nulas, esto se debe a que nuestro callHandler no capturó la función de actualización, por lo que la transacción correspondiente no se guardó en el nodo del gráfico.

búsqueda inversa

Para una relación de uno a muchos, nuestra expectativa es que la matriz contenida en el lado "varios" se pueda ver a través del lado "uno". Sin embargo, nuestra estructura de entidad actual no ha cumplido con tales expectativas.

Aquí puede usar la anotación @derivedFrom para definir la derivación entre relaciones de entidad.

Modifique la definición de la entidad Bloque de la siguiente manera:

type Block @entity(immutable: true) {
  id: Bytes!
  blockNumber: BigInt!
  blockTimestamp: BigInt!
  transactions: [Transaction!] @derivedFrom(field: "block")
}

El efecto después de volver a publicar es el siguiente:

La explicación oficial está aquí:

For one-to-many relationships, the relationship should always be stored on the 'one' side, and the 'many' side should always be derived. Storing the relationship this way, rather than storing an array of entities on the 'many' side, will result in dramatically better performance for both indexing and querying the subgraph. In general, storing arrays of entities should be avoided as much as is practical.

对于一对多的关系,其“关联关系”应该存储在“one”端,而“many”端应该被派生出来。通过这种方式存储关联关系,其查询和索引的性能要好过直接存储“many”端的实体数组。一般来说,应该尽可能避免存储实体数组。

个人也不是太明白其表达的意思,暂且理解为,一对多的关系,通过one端查询到的“many”端的数组,并不是简单的数组保存,而是两个实体之间关系的派生。通过实验可以直接通过“many”端中的字段做过滤。读者可自行尝试:

query MyQuery {
  blocks(where: {transactions_: {id: "0x5b8f57f7b377165f046fc1bcda50846d6eba2500212266752492f0dd0476a7f9"}}) {
    id
  }
}

全文搜索字段

全文搜索查询基于文本搜索输入对实体进行筛选和排序。全文查询能够通过将查询文本输入处理为词干,然后将它们与索引文本数据进行比较,从而返回相似单词的匹配。

全文查询定义包括查询名称、用于处理文本字段的语言字典、用于对结果排序的排序算法以及搜索中包含的字段。每个全文查询可以跨越多个字段,但所有包含的字段必须来自同一个实体。

要添加一个全文查询,在schema.graphql中定义一个带有全文指令的_Schema_。这里对之前的Gravatar实体稍作修改

type _Schema_
  @fulltext(
    name: "gravatarSearch"
    language: en
    algorithm: rank
    include: [{ entity: "Gravatar", fields: [{ name: "displayName" }, { name: "description" }] }]
  )


type Gravatar @entity(immutable: false) {
  id: String!
  owner: Bytes! # address
  displayName: String! # string
  description: String! # string
  imageUrl: String! # string
  transaction: Transaction
}

支持的language

Code

Dictionary

simple

General

da

Danish

nl

Dutch

en

English

fi

Finnish

fr

French

de

German

hu

Hungarian

it

Italian

no

Norwegian

pt

Portuguese

ro

Romanian

ru

Russian

es

Spanish

sv

Swedish

tr

Turkish

支持的排序算法(algorithm)

Algorithm

Description

rank

使用全文查询的匹配质量(0-1)对结果进行排序。

proximityRank

类似于rank,但也包括近似的匹配。

From specVersion 0.0.4 and onwards, fullTextSearch must be declared under the features section in the subgraph manifest.

从specVersion 0.0.4开始,fullTextSearch必须在subgraph清单的features部分下声明。

修改subgraph.yaml如下

specVersion: 0.0.5
schema:
  file: ./schema.graphql
features:
  - fullTextSearch
dataSources:
  - kind: ethereum
    name: GravatarRegistry
    network: goerli
    source:
      address: "0x964F658FC863BAceFC719b85e8730fbc11c86ce4"
      abi: GravatarRegistry
      startBlock: 8266411
    mapping:
      kind: ethereum/events
      apiVersion: 0.0.7
      language: wasm/assemblyscript
      entities:
        - Gravatar
        - Transaction
        - Block
      abis:
        - name: GravatarRegistry
          file: ./abis/GravatarRegistry.json
      eventHandlers:
        - event: NewGravatar(uint256,address,string,string)
          handler: handleNewGravatar
        - event: UpdatedGravatar(uint256,address,string,string)
          handler: handleUpdatedGravatar
      callHandlers:
        - function: createGravatar(string,string)
          handler: handleCreateGravatar
      blockHandlers:
        - handler: handleBlockWithCallToContract
          filter:
            kind: call
      file: ./src/gravatar-registry.ts

修改gravatar-registry.ts文件,添加description字段

export function handleNewGravatar(event: NewGravatarEvent): void {
  let gravatar = new Gravatar(event.params.id.toString());
  gravatar.owner = event.params.owner
  gravatar.displayName = event.params.displayName
  gravatar.imageUrl = event.params.imageUrl
  gravatar.description = event.params.imageUrl.replaceAll("/"," ");
  gravatar.transaction = event.transaction.hash
  gravatar.save()
}

export function handleUpdatedGravatar(event: UpdatedGravatarEvent): void {
  let id = event.params.id.toString();
  let gravatar = Gravatar.load(id)
  if (gravatar == null) {
    gravatar = new Gravatar(id)
  }
  gravatar.owner = event.params.owner
  gravatar.displayName = event.params.displayName
  gravatar.description = event.params.imageUrl.replaceAll("/"," ");
  gravatar.imageUrl = event.params.imageUrl
  gravatar.transaction = event.transaction.hash
  gravatar.save()
}

重新发布后查询结果如下:

query MyQuery {
  gravatarSearch(text: " Carl | https") {
    id
    displayName
    imageUrl
    description
  }
}
{
  "data": {
    "gravatarSearch": [
      {
        "id": "1",
        "displayName": "gambo2",
        "imageUrl": "https://thegraph.com/img/team/bw_Lucas2.jpg",
        "description": "https:  thegraph.com img team bw_Lucas2.jpg"
      },
      {
        "id": "2",
        "displayName": "gambo017",
        "imageUrl": "https://thegraph.com/img/team/team_04.png",
        "description": "https:  thegraph.com img team team_04.png"
      },
      {
        "id": "0",
        "displayName": "Carl",
        "imageUrl": "https://thegraph.com/img/team/team_04.png",
        "description": "https:  thegraph.com img team team_04.png"
      }
    ]
  }
}

其排序结果似乎是根据关联度从低到高,暂时还没有找到降序的方法。

查询语法如下:

Symbol

Operator

Description

&

And

用于将多个搜索词组合到包含所有提供的词的实体的过滤器中

|

Or

使用or操作符分隔的多个搜索词的查询将返回与所提供的任何词匹配的所有实体

<->

Follow by

指定两个单词之间的距离。

:*

Prefix

使用前缀搜索词查找前缀匹配的单词(需要2个字符)。

https://github.com/ziyiyu/subgraph-example

Supongo que te gusta

Origin blog.csdn.net/gambool/article/details/128695729
Recomendado
Clasificación