subgraphs > Developing > Creating > The Graph QL Schema

The Graph QL Schema

Reading time: 12 min

概要

このセクションへのリンク

The schema for your subgraph is in the file schema.graphql. GraphQL schemas are defined using the GraphQL interface definition language.

Note: If you've never written a GraphQL schema, it is recommended that you check out this primer on the GraphQL type system. Reference documentation for GraphQL schemas can be found in the GraphQL API section.

Before defining entities, it is important to take a step back and think about how your data is structured and linked.

  • All queries will be made against the data model defined in the subgraph schema. As a result, the design of the subgraph schema should be informed by the queries that your application will need to perform.
  • It may be useful to imagine entities as "objects containing data", rather than as events or functions.
  • You define entity types in schema.graphql, and Graph Node will generate top-level fields for querying single instances and collections of that entity type.
  • Each type that should be an entity is required to be annotated with an @entity directive.
  • By default, entities are mutable, meaning that mappings can load existing entities, modify them and store a new version of that entity.
    • Mutability comes at a price, so for entity types that will never be modified, such as those containing data extracted verbatim from the chain, it is recommended to mark them as immutable with @entity(immutable: true).
    • If changes happen in the same block in which the entity was created, then mappings can make changes to immutable entities. Immutable entities are much faster to write and to query so they should be used whenever possible.

The following Gravatar entity is structured around a Gravatar object and is a good example of how an entity could be defined.

type Gravatar @entity(immutable: true) {
id: Bytes!
owner: Bytes
displayName: String
imageUrl: String
accepted: Boolean
}

The following example GravatarAccepted and GravatarDeclined entities are based around events. It is not recommended to map events or function calls to entities 1:1.

type GravatarAccepted @entity {
id: Bytes!
owner: Bytes
displayName: String
imageUrl: String
}
type GravatarDeclined @entity {
id: Bytes!
owner: Bytes
displayName: String
imageUrl: String
}

任意フィールドと必須フィールド

このセクションへのリンク

Entity fields can be defined as required or optional. Required fields are indicated by the ! in the schema. If the field is a scalar field, you get an error when you try to store the entity. If the field references another entity then you get this error:

Null 以外のフィールド 'name' の null 値が解決されました

Each entity must have an id field, which must be of type Bytes! or String!. It is generally recommended to use Bytes!, unless the id contains human-readable text, since entities with Bytes! id's will be faster to write and query as those with a String! id. The id field serves as the primary key, and needs to be unique among all entities of the same type. For historical reasons, the type ID! is also accepted and is a synonym for String!.

For some entity types the id for Bytes! is constructed from the id's of two other entities; that is possible using concat, e.g., let id = left.id.concat(right.id) to form the id from the id's of left and right. Similarly, to construct an id from the id of an existing entity and a counter count, let id = left.id.concatI32(count) can be used. The concatenation is guaranteed to produce unique id's as long as the length of left is the same for all such entities, for example, because left.id is an Address.

組み込みの Scalar タイプ

このセクションへのリンク

GraphQL がサポートする Scalar

このセクションへのリンク

The following scalars are supported in the GraphQL API:

タイプ説明書き
BytesByte 配列で、16 進数の文字列で表されます。Ethereum のハッシュやアドレスによく使われます。
StringScalar for string values. Null characters are not supported and are automatically removed.
BooleanScalar for boolean values.
IntThe GraphQL spec defines Int to be a signed 32-bit integer.
Int8An 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.
BigIntLarge integers. Used for Ethereum's uint32, int64, uint64, ..., uint256 types. Note: Everything below uint32, such as int32, uint24 or int8 is represented as i32.
BigDecimalBigDecimal High precision decimals represented as a significand and an exponent. The exponent range is from −6143 to +6144. Rounded to 34 significant digits.
TimestampIt is an i64 value in microseconds. Commonly used for timestamp fields for timeseries and aggregations.

スキーマ内に enums を作成することもできます。enums は次のような構文になっています:

enum TokenStatus {
OriginalOwner
SecondOwner
ThirdOwner
}

Once the enum is defined in the schema, you can use the string representation of the enum value to set an enum field on an entity. For example, you can set the tokenStatus to SecondOwner by first defining your entity and subsequently setting the field with entity.tokenStatus = "SecondOwner". The example below demonstrates what the Token entity would look like with an enum field:

More detail on writing enums can be found in the GraphQL documentation.

エンティティのリレーションシップ

このセクションへのリンク

エンティティは、スキーマ内の 1 つ以上の他のエンティティとリレーションシップを持つことができます。これらの関係は、クエリの中で走査されることがあります。The Graph のリレーションシップは単方向です。リレーションシップのどちらかの "端 "に単方向のリレーションシップを定義することで、双方向のリレーションシップをシミュレートすることができます。

リレーションシップは、指定されたタイプが他のエンティティのものであることを除けば、他のフィールドと同様にエンティティに定義されます。

1 対 1 のリレーションシップ

このセクションへのリンク

Define a Transaction entity type with an optional one-to-one relationship with a TransactionReceipt entity type:

type Transaction @entity(immutable: true) {
id: Bytes!
transactionReceipt: TransactionReceipt
}
type TransactionReceipt @entity(immutable: true) {
id: Bytes!
transaction: Transaction
}

1 対多のリレーションシップ

このセクションへのリンク

Define a TokenBalance entity type with a required one-to-many relationship with a Token entity type:

type Token @entity(immutable: true) {
id: Bytes!
}
type TokenBalance @entity {
id: Bytes!
amount: Int!
token: Token!
}

逆引き(Reverse lookups)

このセクションへのリンク

Reverse lookups can be defined on an entity through the @derivedFrom field. This creates a virtual field on the entity that may be queried but cannot be set manually through the mappings API. Rather, it is derived from the relationship defined on the other entity. For such relationships, it rarely makes sense to store both sides of the relationship, and both indexing and query performance will be better when only one side is stored and the other is derived.

1 対多の関係では、関係は常に「1」側に格納され、「多」側は常に派生されるべきです。「多」側にエンティティの配列を格納するのではなく、このように関係を格納することで、サブグラフのインデックス作成と問い合わせの両方で劇的にパフォーマンスが向上します。一般的に、エンティティの配列を保存することは、現実的に可能な限り避けるべきです。

We can make the balances for a token accessible from the token by deriving a tokenBalances field:

type Token @entity(immutable: true) {
id: Bytes!
tokenBalances: [TokenBalance!]! @derivedFrom(field: "token")
}
type TokenBalance @entity {
id: Bytes!
amount: Int!
token: Token!
}

多対多のリレーションシップ

このセクションへのリンク

ユーザーがそれぞれ任意の数の組織に所属しているような多対多の関係の場合、関係をモデル化する最も簡単な方法は、関係する 2 つのエンティティのそれぞれに配列として格納することですが、一般的には最もパフォーマンスの高い方法ではありません。対称的な関係であれば、関係の片側のみを保存する必要があり、もう片側は派生させることができます。

Define a reverse lookup from a User entity type to an Organization entity type. In the example below, this is achieved by looking up the members attribute from within the Organization entity. In queries, the organizations field on User will be resolved by finding all Organization entities that include the user's ID.

type Organization @entity {
id: Bytes!
name: String!
members: [User!]!
}
type User @entity {
id: Bytes!
name: String!
organizations: [Organization!]! @derivedFrom(field: "members")
}

A more performant way to store this relationship is through a mapping table that has one entry for each User / Organization pair with a schema like

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 entity
organization {
name
}
}
}
}

このように多対多の関係をより精巧に保存する方法では、サブグラフに保存されるデータが少なくなるため、サブグラフのインデックス作成や問い合わせが劇的に速くなります。

スキーマへのコメントの追加

このセクションへのリンク

As per GraphQL spec, comments can be added above schema entity attributes using the hash symbol #. This is illustrated in the example below:

type MyFirstEntity @entity {
# unique identifier and primary key of the entity
id: Bytes!
address: Bytes!
}

フルテキスト検索フィールド(Full Text Search)の定義

このセクションへのリンク

フルテキスト検索クエリは、テキスト検索入力に基づいてエンティティをフィルタリングし、ランク付けします。フルテキストクエリは、インデックス化されたテキストデータと比較する前に、クエリテキストの入力をステム処理することで、類似した単語のマッチを返すことができます。

フルテキストクエリの定義には、クエリ名、テキストフィールドの処理に使用される言語辞書、結果の順序付けに使用されるランキングアルゴリズム、および検索に含まれるフィールドが含まれます。各フルテキスト・クエリは複数のフィールドにまたがることができますが、含まれるフィールドはすべて単一のエンティティ・タイプのものでなければなりません。

To add a fulltext query, include a _Schema_ type with a fulltext directive in the GraphQL schema.

type _Schema_
@fulltext(
name: "bandSearch"
language: en
algorithm: rank
include: [{ entity: "Band", fields: [{ name: "name" }, { name: "description" }, { name: "bio" }] }]
)
type Band @entity {
id: Bytes!
name: String!
description: String!
bio: String
wallet: Address
labels: [Label!]!
discography: [Album!]!
members: [Musician!]!
}

The example bandSearch field can be used in queries to filter Band entities based on the text documents in the name, description, and bio fields. Jump to GraphQL API - Queries for a description of the fulltext search API and more example usage.

query {
bandSearch(text: "breaks & electro & detroit") {
id
name
description
wallet
}
}

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

異なる言語を選択すると、フルテキスト検索 API に決定的な影響を与えますが、場合によっては微妙な影響もあります。フルテキストクエリフィールドでカバーされるフィールドは、選択された言語のコンテキストで検査されるため、分析や検索クエリで生成される語彙は言語ごとに異なります。たとえば、サポートされているトルコ語辞書を使用した場合、"token "は "toke "にステム処理されますが、もちろん英語辞書では "token "にステム処理されます。

サポートされている言語の辞書:

Code辞書
simpleGeneral
daDanish
nlDutch
enEnglish
fiFinnish
frFrench
deGerman
huHungarian
itItalian
noNorwegian
ptポルトガル語
roRomanian
ruRussian
esSpanish
svSwedish
trTurkish

ランキングアルゴリズム

このセクションへのリンク

サポートされている結果の順序付けのアルゴリズム:

AlgorithmDescription
rankフルテキストクエリのマッチ品質 (0-1) を使用して結果を並べ替えます。
proximityRankSimilar to rank but also includes the proximity of the matches.
ページを編集

Subgraph Manifest
Writing AssemblyScript Mappings
ページを編集