SubQuery开发者指南丨GraphQL 架构(GraphQL Schema)

定义实体(Defining Entities) schema.graphql 文件定义了各种 GraphQL 架构。 由于 GraphQL 查询语言的工作方式,模式文件本质上决定了来自 SubQuery 的数据的形状。 要了解有关如何使用 GraphQL 架构语言编写的更多信息,我们建议查看架构和类型(https://graphql.org/learn/schema/#type-language)

定义实体(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 } } }

原创文章,作者:ghmb,如若转载,请注明出处:https://mbebtc.com/news/47179.html

发表评论

邮箱地址不会被公开。 必填项已用*标注

联系我们

400-800-8888

在线咨询:点击这里给我发消息

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息