SubQuery开发者指南丨GraphQL 架构(GraphQL Schema)
定义实体
Defining Entities
schema.graphql 文件定义了各种 GraphQL 架构。 由于 GraphQL 查询语言的工作方式,模式文件本质上决定了来自 SubQuery 的数据的形状。 要了解有关如何使用 GraphQL 架构语言编写的更多信息,我们建议查看架构和类型
(https://graphql.org/learn/schema/#type-language)
重要提示:当您对架构文件(schema file)进行任何更改时,请确保使用 yarn codegen 代码来生成重新生成类型目录。
实体
Entities
每个实体(entity)都必须定义它的必填字段id与ID的类型!它用作主键,并且在所有相同类型的实体中是唯一的。
实体(entity)中不可为空的字段由“ ! ”来表示。 请看下面的例子:
type Example @entity {
id: ID! # id field is always required and must look like this
name: String! # This is a required field
address: String # This is an optional field
}
支持的标量和类型
Supported scalars and types
我们目前支持流动标量类型:
• ID
• Int
• String
• BigInt
• Date
• Boolean
• <EntityName> 用于嵌套关系实体,您可以使用定义的实体名称作为字段之一。 请参阅实体关系
(https://doc.subquery.network/create/graphql.html#entity-relationships)。
• JSON 也可以存储结构化数据,请参阅 JSON 类型
(https://doc.subquery.network/create/graphql.html#json-type)。
按非主键字段进行索引Indexing by
non-primary-key field
为了提高查询性能,只需在非主键字段上执行 @index 注释即可索引实体字段。
但是,我们不允许用户在任何 JSON 对象上添加 @index 注释。 默认情况下,索引会自动添加到外键和数据库中的 JSON 字段,但只是为了增强查询服务性能。
这有一个例子。
type User @entity {
id: ID!
name: String! @index(unique: true) # unique can be set to true or false
title: Title! # Indexes are automatically added to foreign key field
}
type Title @entity {
id: ID!
name: String! @index(unique:true)
}
假设我们知道这个用户的名字,但我们不知道确切的 id 值,我们可以在名称字段后面添加@index,而不是提取所有用户然后按名称过滤。 这使得查询速度更快,我们还可以添加 unique: true 以确保唯一性。
如果字段不唯一,则最大结果集为 100
当代码生成运行时,这将自动在 User 模型下创建一个 getByName,然后外键字段 title 将创建一个 getByTitleId 方法,这两者都可以在映射函数中直接访问。
/* Prepare a record for title entity */
INSERT INTO titles (id, name) VALUES ('id_1', 'Captain')
// Handler in mapping function
import {User} from "../types/models/User"
import {Title} from "../types/models/Title"
const jack = await User.getByName('Jack Sparrow');
const captainTitle = await Title.getByName('Captain');
const pirateLords = await User.getByTitleId(captainTitle.id); // List of all Captains
实体关系
Entity
Relationships
一个实体(entity)通常与其他实体(entity)有嵌套关系。 默认情况下,将字段值设置为另一个实体(entity)名称将定义这两个实体(entity)之间的一对一关系。
可以使用以下示例配置不同的实体(entity)关系(一对一、一对多和多对多)。
一对一关系(One-to-One Relationships)
当只有一个实体映射到另一个实体时,一对一关系是默认的。
例子:一本护照只属于一个人,一个人只有一本护照(在这个例子中):
type Person @entity {
id: ID!
}
type Passport @entity {
id: ID!
owner: Person!
}
或者
type Person @entity {
id: ID!
passport: Passport!
}
type Passport @entity {
id: ID!
owner: Person!
}
一对多关系(One-to-Many relationships)
您可以使用方括号表示一个字段类型包括多个实体。
示例:一个人可以拥有多个帐户。
type Person @entity {
id: ID!
accounts: [Account]
}
type Account @entity {
id: ID!
publicAddress: String!
}
多对多关系(Many-to-Many relationships)
多对多关系可以通过实现一个映射实体(mapping entity)来连接其他两个实体(entity)来实现。
示例:每个人都是多个组 (PersonGroup) 的一部分,并且组有多个不同的人 (PersonGroup)。
type Person @entity {
id: ID!
name: String!
groups: [PersonGroup]
}
type PersonGroup @entity {
id: ID!
person: Person!
Group: Group!
}
type Group @entity {
id: ID!
name: String!
persons: [PersonGroup]
}
此外,可以在中间实体(entity)的多个字段中创建同一实体(entity)的连接。
例如,一个账户可以实现多次转账,每次转账都有一个源账户和目的地账户。
这将通过 Transfer 层在两个 Accounts(from 和 to)之间建立双向关系。
type Account @entity {
id: ID!
publicAddress: String!
}
type Transfer @entity {
id: ID!
amount: BigInt
from: Account!
to: Account!
}
反向查找
Reverse Lookups
为了使一个实体(entity)能够反向查询到一个关系,请将 @derivedFrom 附加到该字段并指向另一个实体(entity)的反向查找字段。
这会在可以查询的实体(entity)上创建一个虚拟字段。
通过将 sentTransfer 或 receivedTransfer 设置为从相应的 from 或 to 字段得出的值,可以从帐户实体中访问“来自” 账户的转移。
type Account @entity {
id: ID!
publicAddress: String!
sentTransfers: [Transfer] @derivedFrom(field: "from")
receivedTransfers: [Transfer] @derivedFrom(field: "to")
}
type Transfer @entity {
id: ID!
amount: BigInt
from: Account!
to: Account!
}
JSON 类型
JSON type
我们支持将数据保存为 JSON 类型(JSON type),这是一种存储结构化数据的快速方式。 我们将自动生成相应的 JSON 接口来查询这些数据,并节省您定义和管理实体(entities)的时间。
我们建议用户在以下场景中使用 JSON 类型:
• 在单个字段中存储结构化数据比创建多个单独的实体(entities)更易于管理。
• 保存任意键/值用户首选项(其中值可以是布尔值、文本或数字,并且不用为不同的数据类型设置单独的列)
• 架构是不稳定的并且经常变化
定义 JSON 指令Define JSON directive
通过在实体中添加 jsonField 注释,将该属性定义为 JSON 类型。 这将自动为您项目中 types/interfaces.ts 下的所有 JSON 对象生成接口,您也可以在映射函数中访问它们。
与实体不同,jsonField 指令对象不需要任何 id 字段。JSON 对象还可以与其他 JSON 对象嵌套。
type AddressDetail @jsonField {
street: String!
district: String!
}
type ContactCard @jsonField {
phone: String!
address: AddressDetail # Nested JSON
}
type User @entity {
id: ID!
contact: [ContactCard] # Store a list of JSON objects
}
查询 JSON 字段Querying JSON fields
使用 JSON 类型的缺点是过滤时对查询效率的影响很小,因为每次执行文本搜索时,都是针对整个实体来进行的。
但是,在我们的查询服务中,影响仍然可以接受。 下面是一个示例,说明如何在 GraphQL 查询中对 JSON 字段使用 contains 运算符来查找拥有包含“0064”的电话号码的前 5 个用户。
#To find the the first 5 users own phone numbers contains '0064'.
query{
user(
first: 5,
filter: {
contactCard: {
contains: [{ phone: "0064" }]
}
}){
nodes{
id
contactCard
}
}
}
Website:
https://www.subquery.network/
Telegram:
https://t.me/subquerynetwork
Twitter:
https://twitter.com/subquerynetwork
Medium:
https://subquery.medium.com/
Github:
https://github.com/subquery/subql
Matrix:
https://matrix.to/#/#subquery:matrix.org
Linkedin:
https://www.linkedin.com/company/subquery
Telegram:
https://t.me/joinchat/3JMF_0LAB2gxN2U1
Mixin:
https://subquery.mixinbots.com/join
往期精彩
《SubQuery开发者指南丨Hello World Explained》
《SubQuery开发者指南丨Hello World (由SubQuery 托管)》
《SubQuery开发者指南丨创建一个SubQuery项目》
《SubQuery丨2021年8月回顾》
《SubQuery开发者指南丨清单文件(Manifest File)》