具有 id 属性 的 Graphql 类型,相同的 id 可以有不同的值

Graphql type with id property that can have different values for same id

我想知道具有 ID 属性 的对象类型在给定相同 ID 的情况下是否必须具有相同的内容。目前同一个id可以有不同的内容。

以下查询:

const query = gql`
  query products(
    $priceSelector: PriceSelectorInput!
  ) {
    productProjectionSearch(
      priceSelector: $priceSelector
    ) {
      total
      results {
        masterVariant {
          # If you do the following it will work
          # anythingButId: id
          id
          scopedPrice {
            country
          }
        }
      }
    }
  }
`;

如果 PriceSelectorInput{currency: "USD", country: "US"} 那么结果是:

{
  "productProjectionSearch": {
    "total": 2702,
    "results": [
      {
        "name": "Sweater Pinko white",
        "masterVariant": {
          "id": 1,
          "scopedPrice": {
            "country": "US",
            "__typename": "ScopedPrice"
          },
          "__typename": "ProductSearchVariant"
        },
        "__typename": "ProductProjection"
      }
    ],
    "__typename": "ProductProjectionSearchResult"
  }
}

如果 PriceSelectorInput{currency: "EUR", country: "DE"} 那么结果是:

{
  "productProjectionSearch": {
    "total": 2702,
    "results": [
      {
        "name": "Sweater Pinko white",
        "masterVariant": {
          "id": 1,
          "scopedPrice": {
            "country": "DE",
            "__typename": "ScopedPrice"
          },
          "__typename": "ProductSearchVariant"
        },
        "__typename": "ProductProjection"
      }
    ],
    "__typename": "ProductProjectionSearchResult"
  }
}

我的问题是,ProductSearchVariant 类型的 masterVariant 在这两种情况下的 id 均为 1,但 scopedPrice 的值不同。这会破坏 apollo 缓存 defaultDataIdFromObject 函数,如 this repo 中所示。我的问题是;这是 apollo 中的错误还是违反了 ProductSearchVariant 类型定义中的 graphql 标准?

TLDR

不,它不会违反规范。规范在缓存方面绝对没有强制要求。

适合可能感兴趣的人的文献

From the end of the overview section

Because of these principles [... one] can quickly become productive without reading extensive documentation and with little or no formal training. To enable that experience, there must be those that build those servers and tools.

The following formal specification serves as a reference for those builders. It describes the language and its grammar, the type system and the introspection system used to query it, and the execution and validation engines with the algorithms to power them. The goal of this specification is to provide a foundation and framework for an ecosystem of GraphQL tools, client libraries, and server implementations -- spanning both organizations and platforms -- that has yet to be built. We look forward to working with the community in order to do that.

正如我们刚刚看到的那样,规范并未提及缓存或实现细节,这些都留给了社区。本文的其余部分继续详细说明应如何处理 type-system、语言、请求和响应。

另请注意,该文档未提及正在使用的底层协议(尽管通常是 HTTP)。您可以通过 USB 设备或 infra-red 灯有效 运行 GraphQL 通信。

我们在技术会议上举办了一场有趣的演讲,您可能会觉得很有趣。这是 link:

GraphQL Anywhere - Our Journey With GraphQL Mesh & Schema Stitching • Uri Goldshtein • GOTO 2021

如果我们自己“Ctrl+F”来查找“缓存”或“ID”之类的东西,我们可以find the following section我认为这有助于在这里得出结论:

ID

The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, it is not intended to be human‐readable. While it is often numeric, it should always serialize as a String.

Result Coercion

GraphQL is agnostic to ID format, and serializes to string to ensure consistency across many formats ID could represent, from small auto‐increment numbers, to large 128‐bit random numbers, to base64 encoded values, or string values of a format like GUID.

GraphQL servers should coerce as appropriate given the ID formats they expect. When coercion is not possible they must raise a field error.

Input Coercion

When expected as an input type, any string (such as "4") or integer (such as 4) input value should be coerced to ID as appropriate for the ID formats a given GraphQL server expects. Any other input value, including float input values (such as 4.0), must raise a query error indicating an incorrect type.

它提到该字段通常用作缓存键(这是 GraphQL 实现的 Apollo collection 的默认缓存键)但它没有告诉我们任何关于“一致性return编辑了数据。

Here's the link for the full specification document for GraphQL

警告!自以为是 - 我对 ID 的看法

当然我要说的与GraphQL规范无关

有时 ID 一条信息不足以决定是否缓存某些内容。让我们考虑一下用户搜索:

如果我有一个 FavouriteSearch 实体,它在我的数据库中有一个 ID 和一个名为 textSearch 的字段。我通常想在我的 GraphQL 规范中公开一个 属性 results: [Result!]!,引用此特定文本搜索产生的所有结果。

这些结果很可能与我进行搜索时或五分钟后重新访问我最喜欢的搜索时不同。 (想想text-search在TikTok等用户可能会大量上传内容的平台上。

因此,根据实体的这个定义 FavouriteSearch,缓存行为相当出乎意料是有道理的。

如果我们从不同的角度考虑问题,我们可能需要一个 SearchResults 实体,它可以有一个 ID 和一个时间戳,并且有一个 join-table 我们引用所有相关的帖子到最初的 text-search,在这种情况下,return 在我们的 GraphQL 模式上 属性 results 的一致内容是有意义的。

问题是,这取决于我们如何定义我们的实体,它最终与 GraphQL 规范无关

您的问题的解决方案

您可以指定 Apollo 如何生成密钥以供以后用作缓存中的密钥,正如@Matt 已在评论中指出的那样。您可能想利用它并覆盖那些 __type 等于您的 masterVariant 属性 类型和 return NO_KEY 的实体的行为它们(或类似的)以避免从您的 ApolloClient 缓存这些特定字段。

希望对您有所帮助!