サブグラフの作成
Reading time: 68 min
サブグラフは、ブロックチェーンからデータを抽出し、加工して保存し、GraphQLで簡単にクエリできるようにします。
サブグラフの定義は、いくつかのファイルで構成されています。
-
subgraph.yaml
:サブグラフのマニフェストを含む YAML ファイル -
schema.graphql
: サブグラフにどのようなデータが保存されているか、また GraphQL を使ってどのようにクエリを行うかを定義する GraphQL スキーマ -
AssemblyScript Mappings
: イベントデータをスキーマで定義されたエンティティに変換するコード (例: このチュートリアルではmapping.ts
)
In order to use your subgraph on The Graph's decentralized network, you will need to . It is recommended that you to your subgraph with at least .
Before you go into detail about the contents of the manifest file, you need to install the which you will need to build and deploy a subgraph.
Graph CLI は JavaScript で書かれており、使用するにはyarn
または npm
のいずれかをインストールする必要があります。
yarn
をインストールしたら、次のコマンドを実行して Graph CLI をインストールする。
Install with yarn:
yarn global add @graphprotocol/graph-cli
Install with npm:
npm install -g @graphprotocol/graph-cli
Once installed, the graph init
command can be used to set up a new subgraph project, either from an existing contract or from an example subgraph. This command can be used to create a subgraph in Subgraph Studio by passing in graph init --product subgraph-studio
. If you already have a smart contract deployed to your preferred network, bootstrapping a new subgraph from that contract can be a good way to get started.
次のコマンドは、既存のコントラクトのすべてのイベントにインデックスを付けるサブグラフを作成します。Etherscan からコントラクト ABI をフェッチしようとしますが、ローカルファイルパスの要求にフォールバックします。オプションの引数のいずれかが欠けている場合は、対話形式で行われます。
graph init \--product subgraph-studio--from-contract <CONTRACT_ADDRESS> \[--network <ETHEREUM_NETWORK>] \[--abi <FILE>] \<SUBGRAPH_SLUG> [<DIRECTORY>]
<SUBGRAPH_SLUG>
は、Subgraph Studio でのサブグラフの ID で、サブグラフの詳細ページに記載されています。
graph init
がサポートする 2 つ目のモードは、例となるサブグラフから新しいプロジェクトを作成することです。以下のコマンドがこれを行います:
graph init --studio <SUBGRAPH_SLUG>
The is based on the Gravity contract by Dani Grant that manages user avatars and emits NewGravatar
or UpdateGravatar
events whenever avatars are created or updated. The subgraph handles these events by writing Gravatar
entities to the Graph Node store and ensuring these are updated according to the events. The following sections will go over the files that make up the subgraph manifest for this example.
v0.31.0
以降、graph-cli
は、graph add
コマンドにより既存のサブグラフに新しいデータソースを追加することをサポートしました。
graph add <address> [<subgraph-manifest default: "./subgraph.yaml">]Options:--abi <path> Path to the contract ABI (default: download from Etherscan)--contract-name Name of the contract (default: Contract)--merge-entities Whether to merge entities with the same name (default: false)--network-file <path> Networks config file path (default: "./networks.json")
add
コマンドは Etherscan から ABI を取得し (--abi
オプションで ABI パスが指定されていない限り)、 graph init
コマンドが dataSource
--from-contract
を作成したのと同じ方法で新しい dataSource
を作成してスキーマとマッピングをそれに従って更新します。
--merge-entities
オプションは、開発者が entity
と event
の名前の衝突をどのように処理したいかを指定します。
- If
true
: the newdataSource
should use existingeventHandlers
&entities
. - If
false
: a new entity & event handler should be created with${dataSourceName}{EventName}
.
契約書のaddress
は、該当するネットワークのnetworks.json
に書き込まれることになります
Note: 対話型CLIを使用している場合、graph init
を正常に実行した後、新しいdataSource
を追加するよう促されます。
サブグラフ・マニフェストsubgraph.yaml
は、サブグラフがインデックスするスマート・コントラクト、これらのコントラクトからのどのイベントに注目するか、そしてイベント・データをグラフ・ノードが保存するエンティティにどのようにマッピングするかを定義し、クエリを可能にします。サブグラフ・マニフェストの完全な仕様は、をご覧ください。
例のサブグラフの場合、subgraph.yaml
は次のようになっています:
specVersion: 0.0.4description: Gravatar for Ethereumrepository: https://github.com/graphprotocol/graph-toolingschema:file: ./schema.graphqlindexerHints:prune: autodataSources:- kind: ethereum/contractname: Gravitynetwork: mainnetsource:address: '0x2E645469f354BB4F5c8a05B3b30A929361cf77eC'abi: GravitystartBlock: 6175244endBlock: 7175245context:foo:type: Booldata: truebar:type: Stringdata: 'bar'mapping:kind: ethereum/eventsapiVersion: 0.0.6language: wasm/assemblyscriptentities:- Gravatarabis:- name: Gravityfile: ./abis/Gravity.jsoneventHandlers:- event: NewGravatar(uint256,address,string,string)handler: handleNewGravatar- event: UpdatedGravatar(uint256,address,string,string)handler: handleUpdatedGravatarcallHandlers:- function: createGravatar(string,string)handler: handleCreateGravatarblockHandlers:- handler: handleBlock- handler: handleBlockWithCallfilter:kind: callfile: ./src/mapping.ts
マニフェストを更新する重要な項目は以下の通りです:
-
specVersion
: a semver version that identifies the supported manifest structure and functionality for the subgraph. The latest version is1.2.0
. See section to see more details on features & releases. -
description
: a human-readable description of what the subgraph is. This description is displayed in Graph Explorer when the subgraph is deployed to Subgraph Studio. -
repository
: the URL of the repository where the subgraph manifest can be found. This is also displayed in Graph Explorer. -
indexerHints.prune
: Defines the retention of historical block data for a subgraph. See in section. -
dataSources.source
: サブグラフのソースとなるスマートコントラクトのアドレスと、使用するスマートコントラクトの abi です。アドレスはオプションで、省略すると、すべてのコントラクトからのマッチングイベントにインデックスを付けることができます。 -
dataSources.source.startBlock
: データソースがインデックス作成を開始するブロックの番号(オプション)です。ほとんどの場合、コントラクトが作成されたブロックの使用をお勧めします。 -
dataSources.source.endBlock
:データ ソースがインデックス作成を停止するブロックのオプション番号 (そのブロックを含む)。必要な最小仕様バージョン:0.0.9
-
dataSources.context
:サブグラフマッピング内で使用できるキーと値のペア。Bool
、String
、Int
、Int8
、BigDecimal
、Bytes
、List
、BigInt
のような様々なデータ型をサポートしています。各変数はtype
とdata
を指定する必要がある。これらのコンテキスト変数は、マッピング・ファイルからアクセスすることができ、サブグラフ開発のためのより多くの設定可能なオプションを提供します。 -
dataSources.mapping.entities
: データソースがストアに書き込むエンティティです。各エンティティのスキーマは、schema.graphql ファイルで定義されます。 -
dataSources.mapping.abis
: ソースコントラクトおよびマッピング内から対話する他のスマートコントラクトのための 1 つまたは複数の名前付き ABI ファイルです。 -
dataSources.mapping.eventHandlers
: このサブグラフが反応するスマートコントラクトイベントと、これらのイベントをストア内のエンティティに変換するマッピング内のハンドラ(例では./src/mapping.ts)をリストアップします。 -
dataSources.mapping.callHandlers
: このサブグラフが反応するスマートコントラクト関数と、関数呼び出しの入力と出力をストア内のエンティティに変換するマッピング内のハンドラをリストアップします。 -
dataSources.mapping.blockHandlers
: このサブグラフが反応するブロックと、ブロックがチェーンに追加されたときに実行されるマッピング内のハンドラーをリストします。フィルターを使用しない場合、ブロック ハンドラーはすべてのブロックで実行されます。オプションの call-filter は、filter
フィールドとkind: call
をハンドラーに追加することで提供できます。これは、ブロックにデータ ソース コントラクトへの呼び出しが少なくとも 1 つ含まれている場合にのみ、ハンドラーを実行します。
単一のサブグラフは複数のスマートコントラクトからデータを索引化できます。dataSources
配列に、索引化するデータが必要な各コントラクトのエントリを追加してください
ブロック内のデータソースのトリガーは、以下のプロセスを使用して順序付けられます:
- イベントとコールのトリガーは、ブロック内のトランザクションインデックスで最初に並べられます。
- 同じトランザクション内のイベントトリガーとコールトリガーは、マニフェストで定義されている順序にしたがって、イベントトリガーが先、コールトリガーが後という規則で並べられます。
- ブロックトリガーは、イベントトリガーとコールトリガーの後に、マニフェストで定義されている順番で実行されます。
これらの順序規則は変更されることがあります。
Note: When new are created, the handlers defined for dynamic data sources will only start processing after all existing data source handlers are processed, and will repeat in the same sequence whenever triggered.
Topic filters, also known as indexed argument filters, are a powerful feature in subgraphs that allow users to precisely filter blockchain events based on the values of their indexed arguments.
-
These filters help isolate specific events of interest from the vast stream of events on the blockchain, allowing subgraphs to operate more efficiently by focusing only on relevant data.
-
This is useful for creating personal subgraphs that track specific addresses and their interactions with various smart contracts on the blockchain.
When a smart contract emits an event, any arguments that are marked as indexed can be used as filters in a subgraph's manifest. This allows the subgraph to listen selectively for events that match these indexed arguments.
- The event's first indexed argument corresponds to
topic1
, the second totopic2
, and so on, up totopic3
, since the Ethereum Virtual Machine (EVM) allows up to three indexed arguments per event.
// SPDX-License-Identifier: MITpragma solidity ^0.8.0;contract Token {// Event declaration with indexed parameters for addressesevent Transfer(address indexed from, address indexed to, uint256 value);// Function to simulate transferring tokensfunction transfer(address to, uint256 value) public {// Emitting the Transfer event with from, to, and valueemit Transfer(msg.sender, to, value);}}
In this example:
- The
Transfer
event is used to log transactions of tokens between addresses. - The
from
andto
parameters are indexed, allowing event listeners to filter and monitor transfers involving specific addresses. - The
transfer
function is a simple representation of a token transfer action, emitting the Transfer event whenever it is called.
Topic filters are defined directly within the event handler configuration in the subgraph manifest. Here is how they are configured:
eventHandlers:- event: SomeEvent(indexed uint256, indexed address, indexed uint256)handler: handleSomeEventtopic1: ['0xValue1', '0xValue2']topic2: ['0xAddress1', '0xAddress2']topic3: ['0xValue3']
In this setup:
topic1
corresponds to the first indexed argument of the event,topic2
to the second, andtopic3
to the third.- Each topic can have one or more values, and an event is only processed if it matches one of the values in each specified topic.
- Within a Single Topic: The logic functions as an OR condition. The event will be processed if it matches any one of the listed values in a given topic.
- Between Different Topics: The logic functions as an AND condition. An event must satisfy all specified conditions across different topics to trigger the associated handler.
eventHandlers:- event: Transfer(indexed address,indexed address,uint256)handler: handleDirectedTransfertopic1: ['0xAddressA'] # Sender Addresstopic2: ['0xAddressB'] # Receiver Address
In this configuration:
topic1
is configured to filterTransfer
events where0xAddressA
is the sender.topic2
is configured to filterTransfer
events where0xAddressB
is the receiver.- The subgraph will only index transactions that occur directly from
0xAddressA
to0xAddressB
.
eventHandlers:- event: Transfer(indexed address,indexed address,uint256)handler: handleTransferToOrFromtopic1: ['0xAddressA', '0xAddressB', '0xAddressC'] # Sender Addresstopic2: ['0xAddressB', '0xAddressC'] # Receiver Address
In this configuration:
topic1
is configured to filterTransfer
events where0xAddressA
,0xAddressB
,0xAddressC
is the sender.topic2
is configured to filterTransfer
events where0xAddressB
and0xAddressC
is the receiver.- The subgraph will index transactions that occur in either direction between multiple addresses allowing for comprehensive monitoring of interactions involving all addresses.
Declarative eth_calls
are a valuable subgraph feature that allows eth_calls
to be executed ahead of time, enabling graph-node
to execute them in parallel.
This feature does the following:
- Significantly improves the performance of fetching data from the Ethereum blockchain by reducing the total time for multiple calls and optimizing the subgraph's overall efficiency.
- Allows faster data fetching, resulting in quicker query responses and a better user experience.
- Reduces wait times for applications that need to aggregate data from multiple Ethereum calls, making the data retrieval process more efficient.
- Declarative
eth_calls
: Ethereum calls that are defined to be executed in parallel rather than sequentially. - Parallel Execution: Instead of waiting for one call to finish before starting the next, multiple calls can be initiated simultaneously.
- Time Efficiency: The total time taken for all the calls changes from the sum of the individual call times (sequential) to the time taken by the longest call (parallel).
Imagine you have a subgraph that needs to make three Ethereum calls to fetch data about a user's transactions, balance, and token holdings.
Traditionally, these calls might be made sequentially:
- Call 1 (Transactions): Takes 3 seconds
- Call 2 (Balance): Takes 2 seconds
- Call 3 (Token Holdings): Takes 4 seconds
Total time taken = 3 + 2 + 4 = 9 seconds
With this feature, you can declare these calls to be executed in parallel:
- Call 1 (Transactions): Takes 3 seconds
- Call 2 (Balance): Takes 2 seconds
- Call 3 (Token Holdings): Takes 4 seconds
Since these calls are executed in parallel, the total time taken is equal to the time taken by the longest call.
Total time taken = max (3, 2, 4) = 4 seconds
- Declarative Definition: In the subgraph manifest, you declare the Ethereum calls in a way that indicates they can be executed in parallel.
- Parallel Execution Engine: The Graph Node's execution engine recognizes these declarations and runs the calls simultaneously.
- Result Aggregation: Once all calls are complete, the results are aggregated and used by the subgraph for further processing.
Declared eth_calls
can access the event.address
of the underlying event as well as all the event.params
.
Subgraph.yaml
using event.address
:
eventHandlers:event: Swap(indexed address,indexed address,int256,int256,uint160,uint128,int24)handler: handleSwapcalls:global0X128: Pool[event.address].feeGrowthGlobal0X128()global1X128: Pool[event.address].feeGrowthGlobal1X128()
Details for the example above:
global0X128
is the declaredeth_call
.- The text before colon(
global0X128
) is the label for thiseth_call
which is used when logging errors. - The text (
Pool[event.address].feeGrowthGlobal0X128()
) is the actualeth_call
that will be executed, which is in the form ofContract[address].function(arguments)
- The
address
andarguments
can be replaced with variables that will be available when the handler is executed.
Subgraph.yaml
using event.params
calls:- ERC20DecimalsToken0: ERC20[event.params.token0].decimals()
ABI ファイルは、契約内容と一致している必要があります。ABI ファイルを入手するにはいくつかの方法があります:
- 自分のプロジェクトを構築している場合は、最新の ABI にアクセスできる可能性があります。
- 公開プロジェクトのサブグラフを作成している場合は、そのプロジェクトをコンピュータにダウンロードし、 または solc to compile を使用して ABI を取得することができます。
- ABI はにもありますが、アップロードされた ABI が古いかもしれないので、必ずしも信頼できるものではありません。正しい ABI でないと、サブグラフの実行に失敗します。
サブグラフのスキーマは、schema.graphql
というファイルにあります。GraphQL スキーマは、GraphQL インターフェース定義言語を用いて定義される。GraphQL スキーマを書いたことがない場合は、GraphQL の型システムについての入門書をご覧になることをお勧めします。GraphQL スキーマのリファレンスドキュメントは、のセクションにあります。
エンティティを定義する前に、一歩下がって、データがどのように構造化され、リンクされているかを考えることが重要です。すべてのクエリは、サブグラフのスキーマで定義されたデータモデルと、サブグラフでインデックス化されたエンティティに対して行われます。このため、Dap のニーズに合わせてサブグラフ・スキーマを定義すると良いでしょう。エンティティは、イベントや関数ではなく、「データを含むオブジェクト」と考えるとよいでしょう。
The Graphでは、schema.graphql
にエンティティタイプを定義するだけで、Graph Nodeがそのエンティティタイプのシングルインスタンスやコレクションを問い合わせるためのトップレベルのフィールドを生成してくれます。エンティティになるべき各タイプは、@entity
ディレクティブでアノテーションされることが要求されます。デフォルトでは、エンティティはミュータブルです。つまり、マッピングは既存のエンティティをロードし、それを変更し、そのエンティティの新しいバージョンを保存することができます。Mutable には代償があり、例えば、チェーンからそのまま抽出されたデータを含むなど、決して変更されないことが分かっているエンティティタイプには、@entity(immutable: true)
で immutable としてマークすることが推奨されます。マッピングは、エンティティが作成されたのと同じブロック内で変更が行われる限り、Immutableエンティティに変更を加えることができます。Immutableなエンティティは、書き込みや問い合わせが非常に高速になるため、可能な限り使用すべきです。
以下のGravatar
エンティティは、Gravatar オブジェクトを中心に構成されており、エンティティを定義する上での良い例です。
type Gravatar @entity(immutable: true) {id: Bytes!owner: BytesdisplayName: StringimageUrl: Stringaccepted: Boolean}
GravatarAccepted
エンティティとGravatarDeclined
エンティティの例は、イベントに基づいています。イベントや関数の呼び出しとエンティティを 1:1 で対応させることはお勧めできません。
type GravatarAccepted @entity {id: Bytes!owner: BytesdisplayName: StringimageUrl: String}type GravatarDeclined @entity {id: Bytes!owner: BytesdisplayName: StringimageUrl: String}
エンティティのフィールドは、必須またはオプションとして定義できます。必須フィールドは、スキーマの中で !
で示されます。マッピングで必須フィールドが設定されていない場合、フィールドを照会すると次のようなエラーが表示されます:
Null 以外のフィールド 'name' の null 値が解決されました
各エンティティには id
フィールドが必要です。このフィールドは Bytes!
または String!
型である必要があります。 Bytes!
の ID を持つエンティティは書き込みが高速になるため、ID
に人間が読み取れるテキストが含まれていない限り、通常は Bytes!
を使用することをお勧めします。 String!
id
を持つものとしてクエリします。 id
フィールドは主キーとして機能し、同じタイプのすべてのエンティティ間で一意である必要があります。歴史的な理由から、タイプ ID!
も受け入れられ、String!
と同義です。
例えば、let id = left.id.concat(right.id)
は left
と right
のidからidを生成するために使用されます。同様に、既存のエンティティのidとカウンタcount
からidを構成するには、let id = left.id.concatI32(count)
を使うことができます。この連結は、left
の長さが、例えば、left.id
がAddress
であるように、全てのそうした実体に対して同じである限り、ユニークなidを作り出すことが保証されています。
GraphQL API では、以下の Scalar をサポートしています:
タイプ | 説明書き |
---|---|
Bytes | Byte 配列で、16 進数の文字列で表されます。Ethereum のハッシュやアドレスによく使われます。 |
String | string 値の Scalar であり、Null 文字はサポートされておらず、自動的に削除されます。 |
Boolean | boolean 値を表す Scalar。 |
Int | The GraphQL spec defines Int to be a signed 32-bit integer. |
Int8 | An 8-byte signed integer, also known as a 64-bit signed integer, can store values in the range from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. Prefer using this to represent i64 from ethereum. |
BigInt | 大きな整数。Ethereum のuint32 , int64 , uint64 , ..., uint256 タイプに使用されます。注: int32 , uint24 int8 などuint32 以下のものはi32 として表現されます。 |
BigDecimal | BigDecimal は、高精度の 10 進数を記号と指数で表します。指数の範囲は -6143 ~ +6144 です。有効数字 34 桁にまとめられます。 |
Timestamp | It is an i64 value in microseconds. Commonly used for timestamp fields for timeseries and aggregations. |
スキーマ内に enums を作成することもできます。enums は次のような構文になっています:
enum TokenStatus {OriginalOwnerSecondOwnerThirdOwner}
スキーマで enum が定義されると、enum 値の文字列表現を使用してエンティティに enum フィールドを設定することができます。例えば、tokenStatus
をSecondOwner
に設定するには、まずエンティティを定義し、続いてentity.tokenStatus = "SecondOwner
でフィールドを設定します。以下の例は、Token エンティティが enum フィールドを持つように見えることを示しています:
エンティティは、スキーマ内の 1 つ以上の他のエンティティとリレーションシップを持つことができます。これらの関係は、クエリの中で走査されることがあります。The Graph のリレーションシップは単方向です。リレーションシップのどちらかの "端 "に単方向のリレーションシップを定義することで、双方向のリレーションシップをシミュレートすることができます。
リレーションシップは、指定されたタイプが他のエンティティのものであることを除けば、他のフィールドと同様にエンティティに定義されます。
TransactionReceipt
エンティティタイプとの 1 対 1 の関係を持つTransaction
エンティティタイプを定義します:
type Transaction @entity(immutable: true) {id: Bytes!transactionReceipt: TransactionReceipt}type TransactionReceipt @entity(immutable: true) {id: Bytes!transaction: Transaction}
TokenBalance
エンティティタイプに、Token エンティティタイプとの 1 対多の関係が必要なものを定義します:
type Token @entity(immutable: true) {id: Bytes!}type TokenBalance @entity {id: Bytes!amount: Int!token: Token!}
逆引きは、@derivedFrom
フィールドを使ってエンティティに定義できます。これにより、エンティティ上に仮想的なフィールドが作成されます。このフィールドにはクエリをかけることができますが、マッピング API を通じて手動で設定することはできません。むしろ、他のエンティティで定義された関係から派生します。このような関係では、関係の両側を保存することに意味があることはほとんどありません。一方の側だけを保存し、もう一方の側を派生させた方が、インデックス作成とクエリのパフォーマンスの両方が向上します。
1 対多の関係では、関係は常に「1」側に格納され、「多」側は常に派生されるべきです。「多」側にエンティティの配列を格納するのではなく、このように関係を格納することで、サブグラフのインデックス作成と問い合わせの両方で劇的にパフォーマンスが向上します。一般的に、エンティティの配列を保存することは、現実的に可能な限り避けるべきです。
tokenBalances
フィールドを派生させることで、トークンの残高にアクセスできるようになります:
type Token @entity(immutable: true) {id: Bytes!tokenBalances: [TokenBalance!]! @derivedFrom(field: "token")}type TokenBalance @entity {id: Bytes!amount: Int!token: Token!}
ユーザーがそれぞれ任意の数の組織に所属しているような多対多の関係の場合、関係をモデル化する最も簡単な方法は、関係する 2 つのエンティティのそれぞれに配列として格納することですが、一般的には最もパフォーマンスの高い方法ではありません。対称的な関係であれば、関係の片側のみを保存する必要があり、もう片側は派生させることができます。
User
エンティティタイプからOrganization
エンティティタイプへの逆引きを定義します。以下の例では、Organization
エンティティの中からmembers
属性を検索することで実現しています。クエリでは、User
のorganizations
フィールドは、ユーザの ID を含むすべてのOrganization
エンティティを見つけることで解決されます。
type Organization @entity {id: Bytes!name: String!members: [User!]!}type User @entity {id: Bytes!name: String!organizations: [Organization!]! @derivedFrom(field: "members")}
この関係を保存するためのより効率的な方法は、次のようなスキーマを持つ User
/ Organization
のペアごとに 1 つのエントリを持つマッピングテーブルを使用することです。
type Organization @entity {id: Bytes!name: String!members: [UserOrganization!]! @derivedFrom(field: "organization")}type User @entity {id: Bytes!name: String!organizations: [UserOrganization!] @derivedFrom(field: "user")}type UserOrganization @entity {id: Bytes! # Set to `user.id.concat(organization.id)`user: User!organization: Organization!}
このアプローチでは、例えばユーザーの組織を取得するために、クエリをさらに 1 つのレベルに下げる必要があります:
query usersWithOrganizations {users {organizations {# this is a UserOrganization entityorganization {name}}}}
このように多対多の関係をより精巧に保存する方法では、サブグラフに保存されるデータが少なくなるため、サブグラフのインデックス作成や問い合わせが劇的に速くなります。
As per GraphQL spec, comments can be added above schema entity attributes using the hash symble #
. This is illustrated in the example below:
type MyFirstEntity @entity {# unique identifier and primary key of the entityid: Bytes!address: Bytes!}
フルテキスト検索クエリは、テキスト検索入力に基づいてエンティティをフィルタリングし、ランク付けします。フルテキストクエリは、インデックス化されたテキストデータと比較する前に、クエリテキストの入力をステム処理することで、類似した単語のマッチを返すことができます。
フルテキストクエリの定義には、クエリ名、テキストフィールドの処理に使用される言語辞書、結果の順序付けに使用されるランキングアルゴリズム、および検索に含まれるフィールドが含まれます。各フルテキスト・クエリは複数のフィールドにまたがることができますが、含まれるフィールドはすべて単一のエンティティ・タイプのものでなければなりません。
フルテキストクエリを追加するには、GraphQL スキーマにフルテキスト指示文を含む_Schema_
タイプを記述します。
type _Schema_@fulltext(name: "bandSearch"language: enalgorithm: rankinclude: [{ entity: "Band", fields: [{ name: "name" }, { name: "description" }, { name: "bio" }] }])type Band @entity {id: Bytes!name: String!description: String!bio: Stringwallet: Addresslabels: [Label!]!discography: [Album!]!members: [Musician!]!}
例のbandSearch
フィールドは、name
、description
、bio
フィールドのテキスト文書に基づいてBand
エンティティをフィルタリングするクエリで使用できます。全文検索 API の説明や詳しい使用例については、を参照してください。
query {bandSearch(text: "breaks & electro & detroit") {idnamedescriptionwallet}}
異なる言語を選択すると、フルテキスト検索 API に決定的な影響を与えますが、場合によっては微妙な影響もあります。フルテキストクエリフィールドでカバーされるフィールドは、選択された言語のコンテキストで検査されるため、分析や検索クエリで生成される語彙は言語ごとに異なります。たとえば、サポートされているトルコ語辞書を使用した場合、"token "は "toke "にステム処理されますが、もちろん英語辞書では "token "にステム処理されます。
サポートされている言語の辞書:
コード | 辞書 |
---|---|
simple | General |
da | Danish |
nl | Dutch |
en | English |
fi | Finnish |
fr | French |
de | German |
hu | Hungarian |
it | Italian |
no | Norwegian |
pt | ポルトガル語 |
ro | Romanian |
ru | Russian |
es | Spanish |
sv | Swedish |
tr | Turkish |
サポートされている結果の順序付けのアルゴリズム:
アルゴリズム | 説明書き |
---|---|
rank | フルテキストクエリのマッチ品質 (0-1) を使用して結果を並べ替えます。 |
proximityRank | ProximityRank rank に似ていますが、マッチの近接性も含みます。 |
マッピングは、特定のソースからデータを取得し、スキーマ内で定義されているエンティティに変換します。マッピングは、WASM () にコンパイルできる と呼ばれる のサブセットで記述されます。 AssemblyScript は通常の TypeScript よりも厳密ですが、使い慣れた構文を提供します
subgraph.yaml
のmapping.eventHandlers
で定義されている各イベントハンドラに対して、同じ名前のエクスポートされた関数を作成します。各ハンドラーは、処理されるevent
の名前に対応するタイプの event という 1 つのパラメータを受け入れる必要があります。
例題のサブグラフでは、src/mapping.ts
にNewGravatar
とUpdatedGravatar
イベントのハンドラが含まれています:
import { NewGravatar, UpdatedGravatar } from '../generated/Gravity/Gravity'import { Gravatar } from '../generated/schema'export function handleNewGravatar(event: NewGravatar): void {let gravatar = new Gravatar(event.params.id)gravatar.owner = event.params.ownergravatar.displayName = event.params.displayNamegravatar.imageUrl = event.params.imageUrlgravatar.save()}export function handleUpdatedGravatar(event: UpdatedGravatar): void {let id = event.params.idlet gravatar = Gravatar.load(id)if (gravatar == null) {gravatar = new Gravatar(id)}gravatar.owner = event.params.ownergravatar.displayName = event.params.displayNamegravatar.imageUrl = event.params.imageUrlgravatar.save()}
最初のハンドラは、NewGravatar
イベントを受け取り、new Gravatar(event.params.id.toHex())
で新しいGravatar
エンティティを作成し、対応するイベント・パラメータを使ってエンティティ・フィールドを入力します。このエンティティのインスタンスは、event.params.id.toHex()
の id 値を持つ変数gravatar
で表されます。
2 番目のハンドラは、既存のGravatar
をグラフノードストアから読み込もうとします。もしまだ存在していなければ、オンデマンドで作成されます。エンティティは新しいイベント・パラメータに合わせて更新され、gravatar.save()
を使ってストアに保存されます。
It is highly recommended to use Bytes
as the type for id
fields, and only use String
for attributes that truly contain human-readable text, like the name of a token. Below are some recommended id
values to consider when creating new entities.
-
transfer.id = event.transaction.hash
-
let id = event.transaction.hash.concatI32(event.logIndex.toI32())
-
For entities that store aggregated data, for e.g, daily trade volumes, the
id
usually contains the day number. Here, using aBytes
as theid
is beneficial. Determining theid
would look like
let dayID = event.block.timestamp.toI32() / 86400let id = Bytes.fromI32(dayID)
- Convert constant addresses to
Bytes
.
const id = Bytes.fromHexString('0xdead...beef')
There is a which contains utilities for interacting with the Graph Node store and conveniences for handling smart contract data and entities. It can be imported into mapping.ts
from @graphprotocol/graph-ts
.
When creating and saving a new entity, if an entity with the same ID already exists, the properties of the new entity are always preferred during the merge process. This means that the existing entity will be updated with the values from the new entity.
If a null value is intentionally set for a field in the new entity with the same ID, the existing entity will be updated with the null value.
If no value is set for a field in the new entity with the same ID, the field will result in null as well.
スマートコントラクト、イベント、エンティティを簡単かつタイプセーフに扱うために、Graph CLIはサブグラフのGraphQLスキーマとデータソースに含まれるコントラクトABIからAssemblyScriptタイプを生成することができます。
これを行うためには
graph codegen [--output-dir <OUTPUT_DIR>] [<MANIFEST>]で行うことができます。
しかし、ほとんどの場合、package.json
によってサブグラフがあらかじめ設定されているので、以下のいずれかを実行するだけで同じことが実現できます:
# Yarnyarn codegen# NPMnpm run codegen
これにより、subgraph.yaml
で言及されている ABI ファイル内のすべてのスマート コントラクトに対して AssemblyScript クラスが生成され、これらのコントラクトをマッピング内の特定のアドレスにバインドし、ブロックに対して読み取り専用のコントラクト メソッドを呼び出すことができます。処理されます。また、すべてのコントラクト イベントのクラスを生成して、イベント パラメータ、およびイベントの発生元のブロックとトランザクションに簡単にアクセスできるようにします。これらの型はすべて <OUTPUT_DIR>/<DATA_SOURCE_NAME>/<ABI_NAME>.ts
に書き込まれます。サブグラフの例では、これは generated/Gravity/Gravity.ts
になり、マッピングでこれらの型を次のようにインポートできます。
import {// The contract class:Gravity,// The events classes:NewGravatar,UpdatedGravatar,} from '../generated/Gravity/Gravity'
これに加えて、サブグラフの GraphQL スキーマのエンティティタイプごとに 1 つのクラスが生成される。これらのクラスは、タイプセーフなエンティティのロード、エンティティ・フィールドへのリード・ライト・アクセスのほか、エンティティをストアに書き込むためのsave()
メソッドを提供する。すべてのエンティティ・クラスは<OUTPUT_DIR>/schema.ts
に書き込まれ、マッパーは以下のようにしてインポートすることができます。
import { Gravatar } from '../generated/schema'
注: GraphQL スキーマやマニフェストに含まれる ABI を変更するたびに、コード生成を再実行する必要があります。また、サブグラフをビルドまたはディプロイする前に、少なくとも一度は実行する必要があります。
Code generation does not check your mapping code in src/mapping.ts
. If you want to check that before trying to deploy your subgraph to Graph Explorer, you can run yarn build
and fix any syntax errors that the TypeScript compiler might find.
EVM 互換のスマート コントラクトの一般的なパターンは、レジストリ コントラクトまたはファクトリ コントラクトの使用です。1 つのコントラクトが、それぞれ独自の状態とイベントを持つ任意の数の他のコントラクトを作成、管理、または参照します。
これらのサブコントラクトのアドレスは、事前にわかっている場合とわかっていない場合があり、これらのコントラクトの多くは、時間の経過とともに作成および/または追加される可能性があります。このような場合、単一のデータ ソースまたは固定数のデータ ソースを定義することは不可能であり、より動的なアプローチ、つまり データ ソース テンプレートが必要とされるのはこのためです。
まず、メインコントラクトの通常のデータソースを定義します。下のスニペットは exchange factory contract のデータソースの例を簡略化して示しています。NewExchange(address,address)
イベントハンドラに注目してください。これはファクトリーコントラクトによってチェーン上に新しいエクスチェンジコントラクトが作成された際に発行されます。
dataSources:- kind: ethereum/contractname: Factorynetwork: mainnetsource:address: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95'abi: Factorymapping:kind: ethereum/eventsapiVersion: 0.0.6language: wasm/assemblyscriptfile: ./src/mappings/factory.tsentities:- Directoryabis:- name: Factoryfile: ./abis/factory.jsoneventHandlers:- event: NewExchange(address,address)handler: handleNewExchange
次に、data source templatesをマニフェストに追加します。データソース テンプレートは、source
の下に定義済みのコントラクト アドレスがないことを除けば、通常のデータソースと同じです。一般的には、親コントラクトが管理または参照するサブコントラクトのタイプごとに 1 つのテンプレートを定義することになります。
dataSources:- kind: ethereum/contractname: Factory# ... other source fields for the main contract ...templates:- name: Exchangekind: ethereum/contractnetwork: mainnetsource:abi: Exchangemapping:kind: ethereum/eventsapiVersion: 0.0.6language: wasm/assemblyscriptfile: ./src/mappings/exchange.tsentities:- Exchangeabis:- name: Exchangefile: ./abis/exchange.jsoneventHandlers:- event: TokenPurchase(address,uint256,uint256)handler: handleTokenPurchase- event: EthPurchase(address,uint256,uint256)handler: handleEthPurchase- event: AddLiquidity(address,uint256,uint256)handler: handleAddLiquidity- event: RemoveLiquidity(address,uint256,uint256)handler: handleRemoveLiquidity
最後のステップでは、メインのコントラクト マッピングを更新して、テンプレートの 1 つからダイナミック データ ソース インスタンスを作成します。この例では、メインのコントラクトマッピングを変更してExchange
テンプレートをインポートし、Exchange.create(address)
メソッドを呼び出して新しい Exchange コントラクトのインデックス作成を開始します。
import { Exchange } from '../generated/templates'export function handleNewExchange(event: NewExchange): void {// Start indexing the exchange; `event.params.exchange` is the// address of the new exchange contractExchange.create(event.params.exchange)}
注: 新しいデータ ソースは、それが作成されたブロックとそれに続くすべてのブロックの呼び出しとイベントのみを処理しますが、履歴データ (データなど) は処理しません。それは前のブロックに含まれています。
以前のブロックに新しいデータソースに関連するデータが含まれている場合は、コントラクトの現在の状態を読み取り、新しいデータソースが作成された時点でその状態を表すエンティティを作成することで、そのデータにインデックスを付けることが最善です。
データソースコンテキストは、テンプレートをインスタンス化する際に追加の設定を渡すことができます。この例では、取引所が特定の取引ペアに関連付けられており、それがNewExchange
イベントに含まれているとします。この情報は、インスタンス化されたデータソースに次のように渡すことができます。
import { Exchange } from '../generated/templates'export function handleNewExchange(event: NewExchange): void {let context = new DataSourceContext()context.setString('tradingPair', event.params.tradingPair)Exchange.createWithContext(event.params.exchange, context)}
Exchange
テンプレートのマッピングの中で、コンテキストにアクセスすることができます:
import { dataSource } from '@graphprotocol/graph-ts'let context = dataSource.context()let tradingPair = context.getString('tradingPair')
すべての値の型に対して、setString
やgetString
のようなセッターやゲッターがあります。
startBlock
はオプションの設定で、データソースがチェーンのどのブロックからインデックス作成を開始するかを定義できます。開始ブロックを設定することで、データソースは無関係な何百万ものブロックをスキップすることができます。通常、サブグラフの開発者はstartBlock
をデータソースのスマートコントラクトが作成されたブロックに設定します。
dataSources:- kind: ethereum/contractname: ExampleSourcenetwork: mainnetsource:address: '0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95'abi: ExampleContractstartBlock: 6627917mapping:kind: ethereum/eventsapiVersion: 0.0.6language: wasm/assemblyscriptfile: ./src/mappings/factory.tsentities:- Userabis:- name: ExampleContractfile: ./abis/ExampleContract.jsoneventHandlers:- event: NewEvent(address,address)handler: handleNewEvent
注: コントラクト作成ブロックは、Etherscan ですばやく検索できます。
- 検索バーにアドレスを入力してコントラクトを検索します。
Contract Creator
セクションの作成トランザクションハッシュをクリックします。- トランザクションの詳細ページを読み込んで、そのコントラクトの開始ブロックを見つけます。
The indexerHints
setting in a subgraph's manifest provides directives for indexers on processing and managing a subgraph. It influences operational decisions across data handling, indexing strategies, and optimizations. Presently, it features the prune
option for managing historical data retention or pruning.
This feature is available from specVersion: 1.0.0
indexerHints.prune
: Defines the retention of historical block data for a subgraph. Options include:
"never"
: No pruning of historical data; retains the entire history."auto"
: Retains the minimum necessary history as set by the indexer, optimizing query performance.- A specific number: Sets a custom limit on the number of historical blocks to retain.
indexerHints:prune: auto
The term "history" in this context of subgraphs is about storing data that reflects the old states of mutable entities.
History as of a given block is required for:
- , which enable querying the past states of these entities at specific blocks throughout the subgraph's history
- Using the subgraph as a in another subgraph, at that block
- Rewinding the subgraph back to that block
If historical data as of the block has been pruned, the above capabilities will not be available.
Using "auto"
is generally recommended as it maximizes query performance and is sufficient for most users who do not require access to extensive historical data.
For subgraphs leveraging , it's advisable to either set a specific number of blocks for historical data retention or use prune: never
to keep all historical entity states. Below are examples of how to configure both options in your subgraph's settings:
To retain a specific amount of historical data:
indexerHints:prune: 1000 # Replace 1000 with the desired number of blocks to retain
To preserve the complete history of entity states:
indexerHints:prune: never
You can check the earliest block (with historical state) for a given subgraph by querying the :
{indexingStatuses(subgraphs: ["Qm..."]) {subgraphsyncedhealthchains {earliestBlock {number}latestBlock {number}chainHeadBlock { number }}}}
Note that the earliestBlock
is the earliest block with historical data, which will be more recent than the startBlock
specified in the manifest, if the subgraph has been pruned.
Event handlers in a subgraph react to specific events emitted by smart contracts on the blockchain and trigger handlers defined in the subgraph's manifest. This enables subgraphs to process and store event data according to defined logic.
An event handler is declared within a data source in the subgraph's YAML configuration. It specifies which events to listen for and the corresponding function to execute when those events are detected.
dataSources:- kind: ethereum/contractname: Gravitynetwork: devsource:address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'abi: Gravitymapping:kind: ethereum/eventsapiVersion: 0.0.6language: wasm/assemblyscriptentities:- Gravatar- Transactionabis:- name: Gravityfile: ./abis/Gravity.jsoneventHandlers:- event: Approval(address,address,uint256)handler: handleApproval- event: Transfer(address,address,uint256)handler: handleTransfertopic1: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', '0xc8dA6BF26964aF9D7eEd9e03E53415D37aA96325'] # Optional topic filter which filters only events with the specified topic.
イベントはコントラクトの状態に対する関連する変更を収集するための効果的な方法を提供しますが、多くのコントラクトはガスコストを最適化するためにログの生成を避けます。このような場合、サブグラフはデータソース・コントラクトに行われたコールを購読することができます。これは、関数シグネチャを参照するコールハンドラと、この関数へのコールを処理するマッピングハンドラを定義することで実現します。これらのコールを処理するために、マッピングハンドラは、コールへの入力とコールからの出力を型付けしたethereum.Call
を引数として受け取ります。トランザクションのコールチェーンのどの深さで行われたコールでもマッピングがトリガーされ、プロキシコントラクトを介したデータソースコントラクトとのアクティビティをキャプチャすることができます。
コールハンドラーは、次の 2 つのケースのいずれかでのみトリガされます:指定された関数がコントラクト自身以外のアカウントから呼び出された場合、または Solidity で外部としてマークされ、同じコントラクト内の別の関数の一部として呼び出された場合。
Note: コールハンドラは現在、ParityトレースAPIに依存しています。BNB chainやArbitrumのような特定のネットワークは、このAPIをサポートしていません。これらのネットワークのインデックスを持つサブグラフが1つ以上のコールハンドラを含む場合、同期を開始しません。サブグラフの開発者は、代わりにイベントハンドラを使用する必要があります。イベント・ハンドラはコール・ハンドラよりもはるかに高性能であり、すべてのevmネットワークでサポートされています。
マニフェストにコール ハンドラを定義するには、購読したいデータ ソースの下に callHandlers
配列を追加します。
dataSources:- kind: ethereum/contractname: Gravitynetwork: mainnetsource:address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'abi: Gravitymapping:kind: ethereum/eventsapiVersion: 0.0.6language: wasm/assemblyscriptentities:- Gravatar- Transactionabis:- name: Gravityfile: ./abis/Gravity.jsoncallHandlers:- function: createGravatar(string,string)handler: handleCreateGravatar
function
は、コールをフィルタリングするための正規化された関数シグネチャです。handler
プロパティは、ターゲット関数がデータソースコントラクトで呼び出されたときに実行したい、マッピング内の関数の名前です。
各コールハンドラは、呼び出された関数の名前に対応するタイプを持つ 1 つのパラメータを取ります。上のサブグラフの例では、マッピングはcreateGravatar
関数が呼び出されたときのハンドラを含み、引数としてCreateGravatarCall
パラメータを受け取ります:
import { CreateGravatarCall } from '../generated/Gravity/Gravity'import { Transaction } from '../generated/schema'export function handleCreateGravatar(call: CreateGravatarCall): void {let id = call.transaction.hashlet transaction = new Transaction(id)transaction.displayName = call.inputs._displayNametransaction.imageUrl = call.inputs._imageUrltransaction.save()}
handleCreateGravatar
関数は、@graphprotocol/graph-ts
が提供するethereum.Call
のサブクラスであるCreateGravatarCall
を新たに受け取り、コールの型付けされた入出力を含みます。CreateGravatarCall
のタイプは、graph codegen
を実行したときに生成されます。
コントラクトイベントやファンクションコールの購読に加えて、サブグラフは、新しいブロックがチェーンに追加されると、そのデータを更新したい場合があります。これを実現するために、サブグラフは各ブロックの後、あるいは事前に定義されたフィルタにマッチしたブロックの後に、関数を実行することができます。
filter:kind: call
定義されたハンドラーは、ハンドラーが定義されているコントラクト(データソース)への呼び出しを含むすべてのブロックに対して一度だけ呼ばれます。
Note: コールハンドラは現在、ParityトレースAPIに依存しています。BNB chainやArbitrumのような特定のネットワークは、このAPIをサポートしていません。これらのネットワークのインデックスを持つサブグラフが1つ以上のコールハンドラを含む場合、同期を開始しません。サブグラフの開発者は、代わりにイベントハンドラを使用する必要があります。イベント・ハンドラはコール・ハンドラよりもはるかに高性能であり、すべてのevmネットワークでサポートされています。
ブロックハンドラーにフィルターがない場合、ハンドラーはブロックごとに呼び出されます。1 つのデータソースには、各フィルタータイプに対して 1 つのブロックハンドラーしか含めることができません。
dataSources:- kind: ethereum/contractname: Gravitynetwork: devsource:address: '0x731a10897d267e19b34503ad902d0a29173ba4b1'abi: Gravitymapping:kind: ethereum/eventsapiVersion: 0.0.6language: wasm/assemblyscriptentities:- Gravatar- Transactionabis:- name: Gravityfile: ./abis/Gravity.jsonblockHandlers:- handler: handleBlock- handler: handleBlockWithCallToContractfilter:kind: call
specVersion
>= 0.0.8 が必要です。
注: ポーリング フィルタは、kind: ethereum
の dataSource でのみ使用できます。
blockHandlers:- handler: handleBlockfilter:kind: pollingevery: 10
定義されたハンドラーは、n
ブロックごとに 1 回呼び出されます。n
は、every
フィールドで指定された値です。 この構成により、サブグラフが定期的なブロック間隔で特定の操作を実行できるようになります。
specVersion
>= 0.0.8 が必要です。
注: Once フィルタは、kind: ethereum
の dataSource でのみ使用できます。
blockHandlers:- handler: handleOncefilter:kind: once
Once フィルターを使用して定義されたハンドラーは、他のすべてのハンドラーが実行される前に 1 回だけ呼び出されます。 この構成により、サブグラフはハンドラーを初期化ハンドラーとして使用し、インデックス作成の開始時に特定のタスクを実行できるようになります。
export function handleOnce(block: ethereum.Block): void {let data = new InitialData(Bytes.fromUTF8('initial'))data.data = 'Setup data here'data.save()}
マッピング関数は、唯一の引数としてethereum.Block
を受け取ります。イベント用のマッピング関数と同様に、この関数はストア内の既存のサブグラフエンティティにアクセスしたり、スマートコントラクトを呼び出したり、エンティティを作成または更新したりすることができます。
import { ethereum } from '@graphprotocol/graph-ts'export function handleBlock(block: ethereum.Block): void {let id = block.hashlet entity = new Block(id)entity.save()}
Solidity で匿名イベントを処理する必要がある場合は、例のようにイベントのトピック 0 を提供することで実現できます:
eventHandlers:- event: LogNote(bytes4,address,bytes32,bytes32,uint256,bytes)topic0: '0x644843f351d3fba4abcd60109eaff9f54bac8fb8ccf0bab941009c21df21cf31'handler: handleGive
シグネチャと topic0 の両方が一致した場合にのみ、イベントが発生します。デフォルトでは、topic0
はイベントシグネチャのハッシュと同じです。
specVersion
0.0.5
および apiVersion
0.0.7
以降、イベント ハンドラーは、それらを発行したトランザクション
これを行うには、イベント ハンドラをサブグラフ マニフェストで新しい receipt: true
キー (オプション、デフォルトは false) を使用して宣言する必要があります。
eventHandlers:- event: NewGravatar(uint256,address,string,string)handler: handleNewGravatarreceipt: true
ハンドラ関数の内部では、レシートは Event.receipt
フィールドでアクセスすることができます。receipt
キーが false
に設定されているか、マニフェストで省略されている場合、代わりに null
値が返されることになります。
specVersion
0.0.4
以降、サブグラフ機能はマニフェストファイルのトップレベルにあるfeatures
セクションで、以下の表のようにcamelCase
の名前を使って明示的に宣言する必要があります:
例えば、サブグラフがFull-Text Search とNon-fatal Errorsの機能を使用する場合、マニフェストのfeatures
フィールドは次のようになります:
specVersion: 0.0.4description: Gravatar for Ethereumfeatures:- fullTextSearch- nonFatalErrorsdataSources: ...
宣言せずに機能を使用すると、サブグラフの展開時にvalidation errorが発生しますが、機能を宣言しても使用しなければエラーは発生しないことに注意してください。
Timeseries and aggregations enable your subgraph to track statistics like daily average price, hourly total transfers, etc.
This feature introduces two new types of subgraph entity. Timeseries entities record data points with timestamps. Aggregation entities perform pre-declared calculations on the Timeseries data points on an hourly or daily basis, then store the results for easy access via GraphQL.
type Data @entity(timeseries: true) {id: Int8!timestamp: Timestamp!price: BigDecimal!}type Stats @aggregation(intervals: ["hour", "day"], source: "Data") {id: Int8!timestamp: Timestamp!sum: BigDecimal! @aggregate(fn: "sum", arg: "price")}
Timeseries entities are defined with @entity(timeseries: true)
in schema.graphql. Every timeseries entity must have a unique ID of the int8 type, a timestamp of the Timestamp type, and include data that will be used for calculation by aggregation entities. These Timeseries entities can be saved in regular trigger handlers, and act as the “raw data” for the Aggregation entities.
Aggregation entities are defined with @aggregation
in schema.graphql. Every aggregation entity defines the source from which it will gather data (which must be a Timeseries entity), sets the intervals (e.g., hour, day), and specifies the aggregation function it will use (e.g., sum, count, min, max, first, last). Aggregation entities are automatically calculated on the basis of the specified source at the end of the required interval.
hour
: sets the timeseries period every hour, on the hour.day
: sets the timeseries period every day, starting and ending at 00:00.
sum
: Total of all values.count
: Number of values.min
: Minimum value.max
: Maximum value.first
: First value in the period.last
: Last value in the period.
{stats(interval: "hour", where: { timestamp_gt: 1704085200 }) {idtimestampsum}}
Note:
To use Timeseries and Aggregations, a subgraph must have a spec version ≥1.1.0. Note that this feature might undergo significant changes that could affect backward compatibility.
about Timeseries and Aggregations.
すでに同期しているサブグラフのインデックスエラーは、デフォルトではサブグラフを失敗させ、同期を停止させます。サブグラフは、エラーが発生したハンドラーによる変更を無視することで、エラーが発生しても同期を継続するように設定することができます。これにより、サブグラフの作成者はサブグラフを修正する時間を得ることができ、一方でクエリは最新のブロックに対して提供され続けますが、エラーの原因となったバグのために結果が一貫していない可能性があります。なお、エラーの中には常に致命的なものもあり、致命的でないものにするためには、そのエラーが決定論的であることがわかっていなければなりません。
注: グラフ ネットワークはまだ致命的ではないエラーをサポートしていないため、開発者はその機能を使用するサブグラフを Studio 経由でネットワークにデプロイしないでください。
非致命的エラーを有効にするには、サブグラフのマニフェストに以下の機能フラグを設定する必要があります:
specVersion: 0.0.4description: Gravatar for Ethereumfeatures:- fullTextSearch...
クエリは、subgraphError
引数を通じて、潜在的な不整合を持つデータのクエリをオプトインする必要があります。また、例のように、サブグラフがエラーをスキップしたかどうかを確認するために、_meta
をクエリすることも推奨されます:
foos(first: 100, subgraphError: allow) {id}_meta {hasIndexingErrors}
サブグラフにエラーが発生した場合、そのクエリはデータと、"indexing_error"
というメッセージを持つ graphql のエラーの両方を返します(以下のレスポンス例):
"data": {"foos": [{"id": "0xdead"}],"_meta": {"hasIndexingErrors": true}},"errors": [{"message": "indexing_error"}]
サブグラフが最初にデプロイされると、対応するチェーンのジェネシス ブロック (または各データ ソースで定義された startBlock
) でイベントのインデックス作成が開始されます。既存のサブグラフのデータを再利用し、かなり後のブロックからインデックス作成を開始することは有益です。このインデックス作成モードは グラフティング と呼ばれます。失敗した既存のサブグラフを迅速に、または一時的に再び機能させることができます。
subgraph.yaml
のサブグラフマニフェストのトップレベルにgraft
ブロックがある場合、サブグラフはベースサブグラフにグラフトされます:
description: ...graft:base: Qm... # Subgraph ID of base subgraphblock: 7345624 # Block number
マニフェストにgraft
ブロックが含まれるサブグラフがデプロイされると、グラフノードは base
サブグラフのデータを、指定されたブロックまでコピーし、そのブロック以降の新しいサブグラフのインデックスを作成し続ける。ベースサブグラフは、対象となるグラフノードのインスタンス上に存在し、少なくとも与えられたblock
までのインデックスを持っている必要があります。このような制限があるため、グラフト化は開発時や緊急時に、グラフト化されていない同等のサブグラフの生成を早めるためにのみ使用するべきです。
グラフトはベースデータのインデックスではなくコピーを行うため、スクラッチからインデックスを作成するよりもサブグラフを目的のブロックに早く到達させることができますが、非常に大きなサブグラフの場合は最初のデータコピーに数時間かかることもあります。グラフトされたサブグラフが初期化されている間、グラフノードは既にコピーされたエンティティタイプに関する情報を記録します。
グラフト化されたサブグラフは、ベースとなるサブグラフのスキーマと同一ではなく、単に互換性のある GraphQL スキーマを使用することができます。また、それ自体は有効なサブグラフのスキーマでなければなりませんが、以下の方法でベースサブグラフのスキーマから逸脱することができます。
- エンティティタイプを追加または削除する
- エンティティタイプから属性を削除する
- エンティティタイプに nullable 属性を追加する
- null 化できない属性を null 化できる属性に変更する
- enums に値を追加する
- インターフェースの追加または削除
- インターフェースがどのエンティティタイプに実装されるかを変更する
ファイルデータソースは、堅牢で拡張可能な方法でインデックス作成中にオフチェーンデータにアクセスするための新しいサブグラフ機能です。ファイルデータソースは、IPFS および Arweave からのファイルのフェッチをサポートしています。
また、オフチェーンデータの決定論的なインデックス作成、および任意のHTTPソースデータの導入の可能性についても基礎ができました。
Rather than fetching files "in line" during handler execution, this introduces templates which can be spawned as new data sources for a given file identifier. These new data sources fetch the files, retrying if they are unsuccessful, running a dedicated handler when the file is found.
This is similar to the , which are used to dynamically create new chain-based data sources.
既存のipfs.cat
APIを置き換えるものです。
ファイルデータソースは、graph-ts >=0.29.0 および graph-cli >=0.33.1 が必要です。
ファファイルが見つかったときに更新される新しいエンティティタイプを追加します。イルデータソースは、チェーンベースのエンティティにアクセスしたり更新することはできませんが、ファイル固有のエンティティを更新する必要があります。
これは、既存のエンティティからフィールドを分離し、別のエンティティにリンクさせることを意味します。
Original combined entity:
type Token @entity {id: ID!tokenID: BigInt!tokenURI: String!externalURL: String!ipfsURI: String!image: String!name: String!description: String!type: String!updatedAtTimestamp: BigIntowner: User!}
New, split entity:
type Token @entity {id: ID!tokenID: BigInt!tokenURI: String!ipfsURI: TokenMetadataupdatedAtTimestamp: BigIntowner: String!}type TokenMetadata @entity {id: ID!image: String!externalURL: String!name: String!description: String!}
親エンティティと結果のファイルデータソースエンティティの間の関係が1:1である場合、最も単純なパターンは、IPFS CIDをルックアップとして使用して、親エンティティを結果のファイルエンティティにリンクすることです。新しいファイルベースのエンティティのモデリングに問題がある場合は、Discordに連絡してください。
目的のファイルが特定されたときに生成されるデータソースです。
templates:- name: TokenMetadatakind: file/ipfsmapping:apiVersion: 0.0.7language: wasm/assemblyscriptfile: ./src/mapping.tshandler: handleMetadataentities:- TokenMetadataabis:- name: Tokenfile: ./abis/Token.json
現在、abis
が必要ですが、ファイル・データ・ソース内からコントラクトを呼び出すことはできません。
The file data source must specifically mention all the entity types which it will interact with under entities
. See for more details.
This handler should accept one Bytes
parameter, which will be the contents of the file, when it is found, which can then be processed. This will often be a JSON file, which can be processed with graph-ts
helpers ().
読みやすい文字列としてのファイルのCIDは、dataSource
を介して次のようにアクセスできます:
const cid = dataSource.stringParam()
ハンドラーの例:
import { json, Bytes, dataSource } from '@graphprotocol/graph-ts'import { TokenMetadata } from '../generated/schema'export function handleMetadata(content: Bytes): void {let tokenMetadata = new TokenMetadata(dataSource.stringParam())const value = json.fromBytes(content).toObject()if (value) {const image = value.get('image')const name = value.get('name')const description = value.get('description')const externalURL = value.get('external_url')if (name && image && description && externalURL) {tokenMetadata.name = name.toString()tokenMetadata.image = image.toString()tokenMetadata.externalURL = externalURL.toString()tokenMetadata.description = description.toString()}tokenMetadata.save()}}
チェーンベースハンドラーの実行中に、ファイルデータソースを作成できるようになりました:
- 自動生成された
templates
からテンプレートをインポートする。 - マッピング内から
TemplateName.create(cid: string)
を呼び出します。この場合、cid は IPFS または Arweave の有効なコンテンツ識別子です
IPFS の場合、グラフノードは ディレクトリを持つコンテンツ識別子 (例: bafyreighykzv2we26wfrbzkcdw37sbrby4upq7ae3aqobbq7i4er3tnxci/metadata.json
) をサポートします。
For Arweave, as of version 0.33.0 Graph Node can fetch files stored on Arweave based on their from an Arweave gateway (). Arweave supports transactions uploaded via Irys (previously Bundlr), and Graph Node can also fetch files based on .
例:
import { TokenMetadata as TokenMetadataTemplate } from '../generated/templates'const ipfshash = 'QmaXzZhcYnsisuue5WRdQDH6FDvqkLQX1NckLqBYeYYEfm'//This example code is for a Crypto coven subgraph. The above ipfs hash is a directory with token metadata for all crypto coven NFTs.export function handleTransfer(event: TransferEvent): void {let token = Token.load(event.params.tokenId.toString())if (!token) {token = new Token(event.params.tokenId.toString())token.tokenID = event.params.tokenIdtoken.tokenURI = '/' + event.params.tokenId.toString() + '.json'const tokenIpfsHash = ipfshash + token.tokenURI//This creates a path to the metadata for a single Crypto coven NFT. It concats the directory with "/" + filename + ".json"token.ipfsURI = tokenIpfsHashTokenMetadataTemplate.create(tokenIpfsHash)}token.updatedAtTimestamp = event.block.timestamptoken.owner = event.params.to.toHexString()token.save()}
これにより、新しいファイル データ ソースが作成され、グラフ ノードの構成済み IPFS または Arweave エンドポイントがポーリングされ、見つからない場合は再試行されます。ファイルが見つかると、ファイルデータソースハンドラが実行されます。
この例では、親 Token
エンティティと結果の TokenMetadata
エンティティの間のルックアップとして CID を使用しています。
以前は、サブグラフ開発者がipfs.cat(CID)
を呼び出してファイルを取得するポイントでした。
おめでとうございます!ファイルデータソースが使用できます。
任意のグラフノードにbuild
およびdeploy
できるようになりました >=v0.30.0-rc.0.
ファイルデータソースハンドラおよびエンティティは、他のサブグラフエンティティから分離され、実行時に決定論的であることを保証し、チェーンベースのデータソースを汚染しないことを保証します。具体的には、以下の通りです。
- ファイルデータソースで作成されたエンティティは不変であり、更新することはできません。
- ファイルデータソースハンドラは、他のファイルデータソースのエンティティにアクセスすることはできません。
- ファイルデータソースに関連するエンティティは、チェーンベースハンドラーからアクセスできません。
この制約は、ほとんどのユースケースで問題になることはありませんが、一部のユースケースでは複雑さをもたらすかもしれません。ファイルベースのデータをサブグラフでモデル化する際に問題がある場合は、Discordを通じてご連絡ください。
また、オンチェーンデータソースや他のファイルデータソースからデータソースを作成することはできません。この制限は、将来的に解除される可能性があります。
NFT メタデータを対応するトークンにリンクする場合、メタデータの IPFS ハッシュを使用して、トークン エンティティから Metadata エンティティを参照します。IPFSハッシュをIDとして使用してMetadataエンティティを保存します。
You can use when creating File Data Sources to pass extra information which will be available to the File Data Source handler.
複数回リフレッシュされるエンティティがある場合は、IPFSハッシュ&スタンプとエンティティIDを使用して一意のファイルベースのエンティティを作成し、チェーンベースのエンティティ内の派生フィールドを使用してそれらを参照します。
クエリが「最新版」のみを返すように、上記の推奨事項を改善するよう取り組んでいます。
ファイル データ ソースは現在、ABI が使用されていないにもかかわらず、ABI を必要とします ()。回避策は、任意のABIを追加することです。
Handlers for File Data Sources cannot be in files which import eth_call
contract bindings, failing with "unknown import: ethereum::ethereum.call
has not been defined" (). Workaround is to create file data source handlers in a dedicated file.