Docs
搜索⌘ K
  • 主页
  • 关于 The Graph
  • 支持的网络
  • 协议合约
  • 子图
    • 子流
      • 代币 API
        • Hypergraph
          • AI Suite
            • 索引
              • 资源
                子图 > 开发 > 创建

                14 分钟

                GraphQL 模式

                概述

                您的子图的模式位于文件 schema.graphql。GraphQL模式是使用GraphQL 接口定义语言定义的。

                注意:如果您从未写过GraphQL模式,建议您在GraphQL类型系统上查看此初级读物。 GraphQL模式的参考文档可以在 GraphQL API部分找到。

                定义实体

                在定义实体之前,重要的是回头一步,思考你的数据是如何架构和联系的。

                • 所有查询都将根据子图模式定义的数据模型进行。 因此,子图模式的设计应通知您的应用程序需要执行的查询。
                • 将实体想象成“含有数据的对象”,而不是作为事件或功能,可能是有用的。
                • 您在 schema.graphql 中定义实体类型, Graph节点将生成顶级字段以查询该实体类型的单个实例和集合。
                • 每一类型的实体都需要使用 @entity 指令注明。
                • 默认情况下,实体是可变的,意味着映射可以加载现有实体,修改它们并存储该实体的新版本。
                  • 易燃性是以一种价格计算的,对于那些永远不会被修改的实体类型,例如那些载有从链中逐字提取的数据的类型, 建议用@entity(immutable:true)标记为不可变的。
                  • 如果在创建实体的同一区块中发生更改,则映射可以对不可变的实体进行更改。 不可变的实体可以更快地撰写和查询,以便在可能的情况下使用。

                好示例

                下面的 Gravatar 实体围绕 Gravatar 对象构建,是如何定义实体的一个很好的示例。

                1type Gravatar @entity {2  id: Id!3  owner: Bytes4  displayName: String5  imageUrl: String6  accepted: Boolean7}

                坏榜样

                下面的示例中,GravatarAccepted 和 GravatarDeclined 实体都基于事件。 不建议将事件或函数调用以 1:1 的方式映射到实体。

                1type GravatarAccepted @entity {2  id: Bytes!3  owner: Bytes4  displayName: String5  imageUrl: String6}78type GravatarDeclined @entity {9  id: Bytes!10  owner: Bytes11  displayName: String12  imageUrl: String13}

                可选和必选字段

                实体字段可以定义为必填字段或可选字段。在模式中,所需字段以 ! 标明。 如果字段是缩放字段,当您尝试存储实体时会出现错误。 如果字段引用了另一个实体,你就会得到这个错误:

                1Null value resolved for non-null field 'name'

                每个实体必须有一个 id 字段,其类型必须是 Bytes!或者String!。通常建议使用Bytes!,除非 id 包含人类可读的文本,因为有Bytes! id的实体比使用String!``id的写入和查询速度会更快。id 字段充当主钥,并且需要在同一类型的所有实体中是唯一的。由于历史原因,类型 ID!也被接受,是 String!的同义词。

                对于Bytes!的某些实体类型,id 是由另外两个实体的 id 构成的; 这可以使用 concat,例如,let id = left.id.concat(right.id) 来从left和right的 id 构成 id。类似地,要从现有实体的 id 和count构造 id,可以使用 let id = left.id.concatI32(count) 。只要left的长度对于所有这样的实体都是相同的,这种串联就一定会产生唯一的 id,例如,因为 left.id是一个 Address。

                内置标量类型

                GraphQL 支持的标量

                GraphQL API支持以下缩写:

                类型描述
                Bytes字节数组,表示为十六进制字符串。 通常用于以太坊hash和地址。
                Stringstring值的标量。不支持空字符,将被自动删除。
                Booleanboolean 值的标量。
                IntGraphQL spec定义Int为一个带符号的32位整数。
                Int8一个8字节有符号整数,也称为64位有符号整数,可以存储从-9,223,372,036,854,775,808到9,223,372,036,854,775,807的范围内的值。建议使用此类型来表示以太坊中的i64。
                BigInt大整数。 用于以太坊的 uint32, int64, uint64, …, uint256 类型。 注意:uint32以下的所有类型,例如int32, uint24或int8都表示为i32。
                BigDecimalBigDecimal表示为有效数字和指数的高精度小数。 指数范围是 -6143 到 +6144。 四舍五入到 34 位有效数字。
                Timestamp它是一个微秒的i64值。通常用于时间序列和聚合的时间戳字段。

                枚举类型

                您还可以在模式中创建枚举类型。 枚举类型具有以下语法:

                1enum TokenStatus {2  OriginalOwner3  SecondOwner4  ThirdOwner5}

                在模式中定义枚举后,您可以使用枚举值的字符串表示形式在实体上设置枚举字段。 例如,您可以将 tokenStatus 设置为 SecondOwner,方法是首先定义您的实体,然后使用 entity.tokenStatus = "SecondOwner" 设置字段。 下面的示例演示了带有枚举字段的 Token 实体:

                在GraphQL文档⁠中找到更多关于写入编码的详细信息。

                实体关系

                一个实体可能与模式中的一个或多个其他实体发生联系。 您可以在您的查询中遍历这些联系。 The Graph 中的联系是单向的。 可以通过在关系的任一“端”上定义单向关系来模拟双向关系。

                关系是在实体上定义的,就像任何其他字段一样,除了指定的类型是另一个实体类型。

                一对一关系

                定义一个 Transaction 实体类型,该类型与一个 TransactionRecipt 实体类型是可选的一对一的关系:

                1type Transaction @entity(immutable: true) {2  id: Bytes!3  transactionReceipt: TransactionReceipt4}56type TransactionReceipt @entity(immutable: true) {7  id: Bytes!8  transaction: Transaction9}

                一对多关系

                定义一个 TokenBalance 实体类型,它与代币实体类型具有必备的一对多的关系:

                1type Token @entity(immutable: true) {2  id: Bytes!3}45type TokenBalance @entity {6  id: Bytes!7  amount: Int!8  token: Token!9}

                反向查找

                反向查找可以通过 @arotovedFrom 字段定义的实体。 这将在实体上创建一个虚拟字段,可以查询,但不能通过Mappings API手动设置。 相反,它产生于与其他实体界定的关系。 对于这种关系来说,把双方都放在一起是很不合理的。 如果只存储一方而得出另一方,则索引和查询性能都会更好。

                对于一对多的关系,这种关系应该始终保存在’one’一边,而’many’一边应该总是被派生出来的。 以这种方式存储关系,而不是将一系列实体存储在’many’侧, 用于索引和查询Subgraph的性能将会大大提高。 一般而言,应尽量避免储存实体阵列。

                示例

                我们可以从 tokenBalances 中产生一个 `tokenBalances ’ 字段来获取代币:

                1type Token @entity(immutable: true) {2  id: Bytes!3  tokenBalances: [TokenBalance!]! @derivedFrom(field: "token")4}56type TokenBalance @entity {7  id: Bytes!8  amount: Int!9  token: Token!10}

                下面是如何为具有反向查找的子图撰写映射的示例:

                1let token = new Token(event.address) // Create Token2token.save() // tokenBalances is derived automatically34let tokenBalance = new TokenBalance(event.address)5tokenBalance.amount = BigInt.fromI32(0)6tokenBalance.token = token.id // Reference stored here7tokenBalance.save()

                多对多关系

                对于多对多关系,例如每个可能属于任意数量的组织的用户,对关系建模的最直接,但通常不是最高效的方法,是在所涉及的两个实体中的每一个中定义数组。 如果关系是对称的,则只需要存储关系的一侧联系,就可以导出另一侧。

                示例

                定义从User实体类型向Organization实体类型的反向查找。 在下面的例子中,实现这个目标的方法是在 Organization 实体内查找 members 属性。 在查询时,User上的 organization 字段将通过找到所有包含用户ID的 Organization 实体来解决。

                1type Organization @entity {2  id: Bytes!3  name: String!4  members: [User!]!5}67type User @entity {8  id: Bytes!9  name: String!10  organizations: [Organization!]! @derivedFrom(field: "members")11}

                存储这种关系的更加有效的方法是通过一个映射表,这个表为每一个 ’ User’ / ’ Organization’ 配对提供一个条目和一个类似的模式:

                1type Organization @entity {2  id: Bytes!3  name: String!4  members: [UserOrganization!]! @derivedFrom(field: "organization")5}67type User @entity {8  id: Bytes!9  name: String!10  organizations: [UserOrganization!] @derivedFrom(field: "user")11}1213type UserOrganization @entity {14  id: Bytes! # Set to `user.id.concat(organization.id)`15  user: User!16  organization: Organization!17}

                这种方法要求查询下降一个额外的级别来检索,例如,用户的组织:

                1query usersWithOrganizations {2  users {3    organizations {4      # this is a UserOrganization entity5      organization {6        name7      }8    }9  }10}

                这种存储多对多关系的更精细的方式,将导致为子图存储的数据更少,因此,子图的索引和查询速度通常会大大加快。

                向模式添加注释

                根据 GraphQL 规范,可以使用双引号#在模式实体属性上方添加注释。 这在下面的示例中进行了说明:

                1type MyFirstEntity @entity {2  # unique identifier and primary key of the entity3  id: Bytes!4  address: Bytes!5}

                定义全文搜索字段

                全文搜索查询根据文本搜索输入来过滤和排列实体。 通过在与索引文本数据进行比较之前,将查询文本输入处理到词干中,全文查询能够返回相似词的匹配项。

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

                要添加全文查询,请在 GraphQL 模式中包含带有全文指令的 _Schema_ 类型。

                1type _Schema_2  @fulltext(3    name: "bandSearch"4    language: en5    algorithm: rank6    include: [{ entity: "Band", fields: [{ name: "name" }, { name: "description" }, { name: "bio" }] }]7  )89type Band @entity {10  id: Bytes!11  name: String!12  description: String!13  bio: String14  wallet: Address15  labels: [Label!]!16  discography: [Album!]!17  members: [Musician!]!18}

                示例bandSearch 字段可以用来在查询中根据name、description、和bio字段中的文本文档过滤Band 实体。 跳转到GraphQL API - 查询以了解全文搜索 API 和更多示例用法。

                1query {2  bandSearch(text: "breaks & electro & detroit") {3    id4    name5    description6    wallet7  }8}

                **特征管理:**从 specVersion 0.0.4 及以后, fullTextSearch 必须在子图清单中features 部分下申报。

                支持的语言

                选择不同的语言将对全文搜索 API 产生明确的(尽管有时是微妙的)影响。 全文查询字段涵盖的字段将会在所选语言的内容中进行检查,因此分析和搜索查询产生的词位因语言而异。 例如:当使用支持的土耳其语词典时,“token”的词干为“toke”,而英语词典当然会认为其词干为“token”。

                支持的语言词典:

                代码词典
                simple通用
                da丹麦语
                nl荷兰语
                en英语
                fi芬兰语
                frFrench
                de德语
                hu匈牙利语
                it意大利语
                no挪威语
                pt葡萄牙语
                ro罗马尼亚语
                ru俄语
                es西班牙语
                sv瑞典语
                tr土耳其语

                排序算法

                支持的排序结果算法:

                算法说明
                排名使用全文查询的匹配质量 (0-1) 对结果进行排序。
                proximityRank与 rank 类似,但也包括匹配的接近程度。
                ⁠在GitHub上编辑⁠

                子图清单Writing AssemblyScript Mappings
                在此页面上
                • 概述
                • 定义实体
                • 内置标量类型
                • 枚举类型
                • 实体关系
                • 反向查找
                • 向模式添加注释
                • 定义全文搜索字段
                • 支持的语言
                • 排序算法
                The GraphStatusTestnetBrand AssetsForum安全Privacy PolicyTerms of Service