定义高级键
定义和使用复合键、嵌套键字段等
根据您的实体字段和用法,您可能需要使用更高级的@key
。例如,您可能需要定义一个复合 @key
如果需要多个字段来唯一标识一个实体。如果不同的子图与实体的不同字段交互,您可能需要为该实体定义多个——有时不同的——@key
。
复合 @key
一个单独的@key
可以由多个字段组成,这些字段的组合可以唯一标识一个实体。这被称为复合键或合成键。In the following example, the combination of both username
and domain
fields is required to uniquely identify the User
entity:
type User @key(fields: "username domain") {username: String!domain: String!}
在复合 @key
中的嵌套字段
嵌套 字段常用于复合键。以下示例中,User
实体's @key
包含了一个用户的 id
以及该用户相关的 Organization
的 id
:
type User @key(fields: "id organization { id }") {id: ID!organization: Organization!}type Organization {id: ID!}
ⓘ 注意
虽然嵌套 字段最常用于复合键,但你也可以将其用作单个 @key
字段。
多个 @key
当不同的 子图 与实体 的不同字段交互时,你可能需要为实体定义多个 @key
。例如,一个 评论 子图可能会通过其 ID 引用产品,而一个 库存 子图可能会使用 SKU。
以下示例中,Product
实体可以通过其 id
或其 sku
唯一识别:
type Product @key(fields: "id") @key(fields: "sku") {id: ID!sku: String!name: String!price: Int}
ⓘ 注意
如果你包含了多组 @key
字段,查询规划器会使用最高效的组来解决实体。例如,假设你允许使用以下类型来识别:
type Product @key(fields: "id") @key(fields: "id sku") {# ...}
这意味着 id
或 (id
且 sku
) 就足够唯一地识别实体。由于 id
本身就足够了,查询规划器将仅使用该字段来解决实体,并且 @key(fields: "id sku")
将有效地被忽略。
使用多个键引用实体
一个子图,它是通过不提供任何字段来引用实体的,可以在这个实体的stub定义中使用任何键字段。
type Product @key(fields: "id") @key(fields: "sku") {id: ID!sku: String!name: String!price: Int}
例如,如果“产品”子图定义实体如Product
:
# Either:type Product @key(fields: "id", resolvable: false) {id: ID!}# Or:type Product @key(fields: "sku", resolvable: false) {sku: String!}
在一个具有多个键的实体引用解析时,可以根据哪些键存在来决定如何解析。例如,如果你使用,则可能如下所示:
// Products subgraphconst resolvers = {Product: {__resolveReference(productRepresentation) {if(productRepresentation.sku){return fetchProductBySku(productRepresentation.sku);} else {return fetchProductByID(productRepresentation.id);}}},// ...other resolvers...}
不同子图中的不同键
尽管实体在子图中通常使用完全相同的@key
字段,但你可以使用具有不同字段的不同的@key
。例如,你可以定义跨子图的共享实体“Product”,一个具有sku
和upc
作为其@key
字段,另一个仅具有upc
作为字段:
type Product @key(fields: "sku") @key(fields: "upc") {sku: ID!upc: String!name: String!price: Int}
type Product @key(fields: "upc") {upc: String!inStock: Boolean!}
要合并子图之间的实体,实体必须在子图之间至少共享一个字段。例如,不能合并以下子图中的“Product”实体,因为它们没有在@key
选择集中指定的任何共享字段:
❌
type Product @key(fields: "sku") {sku: ID!name: String!price: Int}
type Product @key(fields: "upc") {upc: String!inStock: Boolean!}
有不同键选择的操作
子图中的不同键会影响可以从每个子图中解析实体的哪些字段。如果存在从根查询到字段的可遍历路径,则请求可以解析字段。
以下子图模式为例:
type Product @key(fields: "sku") {sku: ID!upc: String!name: String!price: Int}type Query {product(sku: ID!): Productproducts: [Product!]!}
type Product @key(fields: "upc") {upc: String!inStock: Boolean!}
产品子图定义的查询可以始终解析所有产品字段,因为产品实体可以通过同时存在于两个模式中的 upc
字段进行联接。
另一方面,添加到库存子图的查询无法解析来自产品子图的字段:
type Product @key(fields: "sku") {sku: ID!upc: String!name: String!price: Int}
type Product @key(fields: "upc") {upc: String!inStock: Boolean!}type Query {productsInStock: [Product!]!}
由于产品子图的 Product
类型定义不包括 upc
作为键字段,而 sku
字段在库存子图中不存在,因此 productsInStock
查询无法解析来自产品子图中的字段。
如果产品子图包含 @key(fields: "upc")
,所有来自库存子图的查询都可以解析所有产品字段:
type Product @key(fields: "sku") @key(fields: "upc") {sku: ID!upc: String!name: String!price: Int}
type Product @key(fields: "upc") {upc: String!inStock: Boolean!}type Query {productsInStock: [Product!]!}
下一步
如果您还没有学习如何 将实体字段贡献给超级图 并从未贡献任何字段的子图中 引用它们。