检索 AppSync 架构中的嵌套信息数组

Retrieving arrays of nested information in AppSync schema

我已经在 GraphQL AppSync 查询上计算出一个相当复杂的 DynamoDB 解析器链。我想知道的是,我是否可以以一种需要更少 DynamoDB 查询的方式设计它。

这是我的 GraphQL 模式:

type Tag {
    PartitionKey: ID!
    SortKey: ID!
    TagName: String!
    TagType: String
}

type Model {
    PartitionKey: ID!
    Name: String
    Version: Int
    FBX: String
    # ms since epoch
    CreatedAt: AWSTimestamp
    Description: String
    Tags: [String]
}

type Query {
    GetAllModels(count: Int, nextToken: String): PaginatedModels!
}

这是我正在做的查询:

query GetAllModels{
  GetAllModels {
    Models {    
        PartitionKey        
        Name
        Version
        CreatedAt
        Description
        Tags {
          TagName
          TagType
        }
    }
  }
}

我的 DynamoDB table 是这样设置的:

PartionKey | SortKey       | TagName | TagType | ModelName | Description
Model-0    | Model-0       |                     ModelZero | Blah Blah   
Model-0    | Tag-Pine      |
Model-0    | Tag-Apple     |
Tag-Pine   | Tag-Pine      | Pine    | Tree
Tag-Apple  | Tag-Apple     | Apple   | Fruit

所以在我的解析器中我要:

  1. GetAllModels 将使用两个过滤器进行扫描。一个以 'Model-' 开头的 PartitionKey 筛选器和另一个以 'Model-' 开头的 SortKey 筛选器。这是获取所有模型。

  2. 接下来有一个解析器附加到模型对象中的 'Tags'。这将使用两个表达式进行查询。一个用于 PartitionKey = source.Parition,第二个用于 SortKey begin_with 'Tag-' 这让我获得了模型上的所有标签。

  3. 接下来在 Tag 对象上有两个解析器。一个在 TagName 上,另一个在 TagType 上。这些执行直接 GetItem 以将 PartitionKey = source.Sort 和 SortKey = source.SortKey 设置为键来获取其适当的值。

因此,每个扫描的模型最终都会向 DynamoDB 发出 3 个以上的查询。这对我来说似乎有点过分。但是我看不到任何其他方法可以做到这一点。有什么方法可以在一个查询中同时获得 TagName 和 TagType 吗?

有没有更好的方法来解决这个问题?

我看到一些我个人会改变的事情。首先是我会避免嵌套的 DynamoDB 扫描操作。至少其中之一可以用更快的查询操作代替。第二个是我会考虑重新考虑如何存储数据。目前还没有很好的列出模型对象的方法。

为什么没有列出模型对象的好方法?

假设每个模型对象都有多个标签,那么您将有一个 table 稀疏地填充模型对象。即在 100 行中,您可能有 20 - 50 个模型,具体取决于平均模型具有的标签数量。在 DynamoDB 中,table 根据分区键拆分,导致共享相同分区键的行彼此靠近存储,以加快查询操作。在您的设置中,分区键本质上是单个模型对象的唯一 ID,这意味着我们可以轻松获得单个模型对象。您还可以快速获取单个对象的标签,因为这些记录也在附近。

问题。

DynamoDB 扫描操作一次查看每个分区,读取请求 limit 允许的尽可能多的记录,如果 limit 足够大,然后,仅在从各个分区读取记录后,在返回最终结果之前应用过滤表达式。这意味着您可能会要求前 10 个模型,但由于在扫描过滤器之前应用了限制,您很可能只能取回 1 个模型(如果该模型有 9 个或更多标签,这将在 DynamoDB 读取第一个分区)。当来自许多不同的数据库系统时,这可能看起来很奇怪,并且是其设计的重要考虑因素。

这里有两个解决这个问题的方法:

1.在一个 table 中存储模型,在另一个中存储标签。

像 DynamoDB 这样的 NoSQL 数据库允许您在同一个 table 中存储多种类型的数据,但是将它们分开并没有错。传统上,在缺少连接操作或类似操作的 NoSQL 数据库中使用多个 table 可能会很痛苦,但对我们来说幸运的是,我们可以使用 GraphQL 来 "join" 数据。通过这种方法,Model table 有一个名为 "id" 的分区键,而您的 GetAllModels 解析器仍然是一个扫描但这次是模特 table。这样 table 就不稀疏了,当你要求 10 个模型时,你会得到 10 个模型。 Tag table 应该有一个分区键 modelId 和一个排序键 tagId .然后,您将在 Model.tags 字段上有一个解析器,该解析器针对 Tag table 进行查询并查找行modelId == $ctx.source.id.

这基本上就是 @model 和 @connection 在作为 amplify cli 的一部分推出的新 graphql 转换工具中的工作方式。你可以在这里看到更多,尽管文档在写作时仍在改进中。 https://aws-amplify.github.io/amplify-js/media/api_guide

2。将模型和标签存储在相同的 table 但更改密钥结构。

如果您可以可靠地说每种数据类型(例如模型和标记)的数据少于 10GB,则此方法有效。对于这种方法,您有一个 table,PartitionKey 为 Type,Sort Key 为 id。当您创建对象时,您使用 Type 例如 "Tag" 或 "Model" 等和唯一的 id (如uuid)。要列出相同类型的对象,您可以对要列出的类型的分区键执行 DynamoDB 查询操作,例如"Tag" 或 "Model"。然后,您可以使用 GSI 高效地查找相关对象。在您的情况下,您将存储一个 "modelId" 是每个 Tag 对象。然后,您将使用 "modelId" 作为分区键创建 GSI。要列出给定模型的所有标签,您可以针对该 GSI 执行 DynamoDB 查询操作。

我相信还有很多方法可以做到这一点,但希望这有助于指明正确的方向。