简体   繁体   English

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

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

I was wondering if an object type that has an id property has to have the same content given the same id.我想知道具有 id 属性的 object 类型在给定相同 id 的情况下是否必须具有相同的内容。 At the moment the same id can have different content.目前同一个id可以有不同的内容。

The following query:以下查询:

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
          }
        }
      }
    }
  }
`;

If the PriceSelectorInput is {currency: "USD", country: "US"} then the result is:如果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"
  }
}

If the PriceSelectorInput is {currency: "EUR", country: "DE"} then the result is:如果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"
  }
}

My question is that masterVariant of type ProductSearchVariant has id of 1 in both cases but different values for scopedPrice.我的问题是,ProductSearchVariant 类型的 masterVariant 在这两种情况下的 id 均为 1,但 scopedPrice 的值不同。 This breaks apollo cache defaultDataIdFromObject function as demonstrated in this repo .本回购协议所示,这会破坏 apollo 缓存 defaultDataIdFromObject function。 My question is;我的问题是; is this a bug in apollo or would this be a violation of a graphql standard in the type definition of ProductSearchVariant?这是 apollo 中的错误还是违反了 ProductSearchVariant 类型定义中的 graphql 标准?

TLDR TLDR

No it does not break the spec.不,它不会违反规范。 The spec forces absolutely nothing in regards caching.该规范在缓存方面绝对没有任何强制性。

Literature for people that may be interested可能感兴趣的人的文学

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.本规范的目标是为尚未构建的 GraphQL 工具、客户端库和服务器实现的生态系统提供基础和框架——跨越组织和平台。 We look forward to working with the community in order to do that.我们期待与社区合作以实现这一目标。

As we just saw the spec says nothing about caching or implementation details, that's left out to the community.正如我们刚刚看到的规范中没有提到缓存或实现细节,这些都留给了社区。 The rest of the paper proceeds to give details on how the type-system, the language, requests and responses should be handled.该论文的 rest 继续详细说明应如何处理类型系统、语言、请求和响应。

Also note that the document does not mention which underlying protocol is being used (although commonly it's HTTP).另请注意,该文档没有提及正在使用的底层协议(尽管通常是 HTTP)。 You could effectively run GraphQL communication over a USB device or over infra-red light.您可以通过 USB 设备或红外线有效地运行 GraphQL 通信。

We hosted an interesting talk at our tech conferences which you might find interesting.我们在技术会议上举办了一次有趣的演讲,您可能会觉得很有趣。 Here's a link:这是一个链接:

GraphQL Anywhere - Our Journey With GraphQL Mesh & Schema Stitching • Uri Goldshtein • GOTO 2021 GraphQL Anywhere - 我们的 GraphQL 网格和模式拼接之旅 • Uri Goldshtein • GOTO 2021

If we "Ctrl+F" ourselves to look for things as "Cache" or "ID" we can find the following section which I think would help get to a conclusion here:如果我们自己“Ctrl+F”来查找“缓存”或“ID”之类的东西,我们可以找到以下部分,我认为这有助于在这里得出结论:

ID ID

The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. ID 标量类型表示唯一标识符,通常用于重新获取 object 或用作缓存的键。 The ID type is serialized in the same way as a String; ID 类型以与 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 与 ID 格式无关,并序列化为字符串以确保 ID 可以表示的多种格式的一致性,从小的自动递增数字到大的 128 位随机数,再到 base64 编码值,或 GUID 等格式的字符串值。

GraphQL servers should coerce as appropriate given the ID formats they expect. GraphQL 服务器应该根据他们期望的 ID 格式进行适当的强制转换。 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. 当预期作为输入类型时,任何字符串(例如“4”)或 integer(例如 4)输入值都应根据给定 GraphQL 服务器期望的 ID 格式强制转换为 ID。 Any other input value, including float input values (such as 4.0), must raise a query error indicating an incorrect type. 任何其他输入值,包括浮点输入值(例如 4.0),都必须引发查询错误,指示类型不正确。

It mentions that such field it is commonly used as a cache key (and that's the default cache key for the Apollo collection of GraphQL implementations) but it doesn't tell us anything about "consistency of the returned data".它提到该字段通常用作缓存键(这是 GraphQL 实现的 Apollo 集合的默认缓存键),但它没有告诉我们任何关于“返回数据的一致性”的信息。

Here's the link for the full specification document for GraphQL这是 GraphQL 的完整规范文档的链接

Warning!警告! Opinionated - My take on ID's自以为是——我对身份证的看法

Of course all I am about to say has nothing to do with the GraphQL specification当然我要说的一切都与GraphQL规范无关

Sometimes an ID is not enough of a piece of information to decide whether to cache something.有时一个ID不足以作为一条信息来决定是否缓存某些东西。 Let's think about user searches:让我们考虑一下用户搜索:

If I have a FavouriteSearch entity that has an ID on my database and a field called textSearch .如果我有一个FavouriteSearch实体,它在我的数据库中有一个 ID 和一个名为textSearch的字段。 I'd commonly like to expose a property results: [Result!]!我通常想公开一个属性results: [Result!]! on my GraphQL specification referencing all the results that this specific text search yielded.在我的 GraphQL 规范中引用了这个特定文本搜索产生的所有结果。

These results are very likely to be different from the moment I make the search or five minutes later when I revisit my favourite search.这些结果很可能与我进行搜索时或五分钟后我重新访问我最喜欢的搜索时不同。 (Thinking about a text-search on a platform such as TikTok where users may massively upload content). (考虑在 TikTok 等用户可能会大量上传内容的平台上进行文本搜索)。

So based on this definition of the entity FavouriteSearch it makes sense that the caching behavior is rather unexpected.因此,基于实体FavouriteSearch的定义,缓存行为相当出乎意料是有道理的。

If we think of the problem from a different angle we might want a SearchResults entity which could have an ID and a timestamp and have a join-table where we reference all those posts that were related to the initial text-search and in that case it would make sense to return a consistent content for the property results on our GraphQL schema.如果我们从不同的角度考虑这个问题,我们可能需要一个SearchResults实体,它可以有一个 ID 和一个时间戳,并且有一个连接表,我们在其中引用所有与初始文本搜索相关的帖子,在这种情况下它为我们的 GraphQL 模式返回一致的属性results内容是有意义的。

Thing is that it depends on how we define our entities and it's ultimately not related to the GraphQL spec事情是,这取决于我们如何定义我们的实体,它最终与 GraphQL 规范无关

A solution for your problem您的问题的解决方案

You can specify how Apollo generates the key for later use as key on the cache as @Matt already pointed in the comments.您可以指定 Apollo 如何生成密钥以供以后用作缓存中的密钥,正如 @Matt 已经在评论中指出的那样。 You may want to tap into that and override that behavior for those entitites that have a __type equal to your masterVariant property type and return NO_KEY for all of them (or similar) in order to avoid caching from your ApolloClient on those specific fields.您可能想要利用它并覆盖那些__type等于您的masterVariant属性类型的实体的行为,并为所有这些(或类似的)返回 NO_KEY 以避免从您的 ApolloClient 在这些特定字段上进行缓存。

I hope this was helpful!我希望这可以帮到你!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM