在 AWS Amplify GraphQL DynamoDB 中按另一个表的字段(也称为交叉表或嵌套过滤)过滤列表查询

Posted

技术标签:

【中文标题】在 AWS Amplify GraphQL DynamoDB 中按另一个表的字段(也称为交叉表或嵌套过滤)过滤列表查询【英文标题】:Filtering List Query By Another Table's Field (a.k.a Cross-Table or Nested Filtering) in AWS Amplify GraphQL DynamoDB 【发布时间】:2021-08-02 17:29:23 【问题描述】:

您的问题与哪个类别有关? DynamoDB、AppSync(GraphQL)

放大 CLI 版本 4.50.2

提供其他详细信息,例如代码sn-ps

背景: 我是 AWS 无服务器应用程序系统的新手,作为前端开发人员,我非常喜欢它,这要归功于自动生成的 API、表、连接、解析器等。我在前端和 S3、DynamoDB、AppSync 中使用 Angular/Ionic , Cognito, Amplify-cli 用于后端。

我有什么: 这是我的架构的一部分。我可以轻松地使用自动生成的 API 来列出/获取带有附加过滤器的反馈(即 score: ge: 3 )。感谢@connection,我可以在列出的反馈项中看到用户的详细信息。

type User @model @auth(rules: [ allow: owner ]) 
  id: ID!
  email: String!
  name: String!
  region: String!
  sector: String!
  companyType: String!


type Feedback @model @auth(rules: [ allow: owner ]) 
  id: ID!
  user: User @connection
  score: Int!
  content: String

我想要什么: 我想根据用户类型的几个字段列出反馈,例如用户的区域(即 user.region: contains: 'United States' )。现在我搜索了一个很像 #2311 的解决方案,我了解到 amplify codegen 只创建***过滤。为了使用跨表过滤,我相信我需要修改解析器、lambda 函数、查询和输入。其中,对于初学者来说,它看起来相当复杂。

我尝试过/考虑过的事情:

    我尝试单独列出所有用户和反馈并在前端过滤它们。但随后客户端下载了所有这些不必要的数据。此外,由于分页限制,用户体验会受到影响,因为他们看到一个空列表并且需要反复点击“加载更多”按钮。 感谢一些建议,我还考虑在反馈表中复制用户详细信息以便能够搜索/过滤它们。那么问题是如果用户更新他/她的信息,重复的值将是过时的。另外会有太多重复的数据,因为我也需要这个功能来处理其他表。 我也听说过使用 ElasticSearch 来解决这个问题,但有人提到一个简单的过滤,他每月需要 30 美元的费用,所以我退缩了。 我尝试使用解析器解决方案在其中添加自定义过滤。但我发现这对于初学者来说相当复杂。此外,我还需要在许多其他表中进行这种跨表过滤,所以我认为这很难管理。如果这是最佳做法,如果有人能指导我完成它,我将不胜感激。

问题:

    对我来说,实现这种跨表过滤的最简单/对初学者友好的解决方案是什么?我愿意接受替代解决方案。 对于无 SQL 设置,这种交叉表过滤是一种不好的方法吗?因为我需要两个表之间的一些关系。 (我认为@connection 就足够了)。我应该在为时已晚之前切换到 SQL 设置吗? Amplify 将来是否可以为此自动生成解决方案?我觉得很多人都遇到了同样的问题。

提前谢谢你。

【问题讨论】:

【参考方案1】:

Amplify 和真正的 DynamoDB 通常要求您提前考虑您的访问模式。有很多非常好的信息可以帮助指导您了解这个思维过程的样子。特别喜欢纳德·达比特的https://dev.to/dabit3/data-modeling-in-depth-with-graphql-aws-amplify-17-data-access-patterns-4meh

乍一看,我想我会在 User 模型中添加一个名为 byCountry 的新 @key,它会在 DDB 中为您在该属性上创建一个新的全局二级索引,并且还会为您提供一些新的查询方法。查看https://docs.amplify.aws/cli/graphql-transformer/key#designing-data-models-using-key 了解更多示例。

一旦您设置了 User.getByCountry,您就应该能够恢复每个用户的反馈。

query USAUsersWithFeedbacks 
  listUsersByCountry(country: "USA") 
    items 
      feedbacks 
        items 
          content
        
        nextToken
      
    
    nextToken
  


最后,您可以在 nextToken 不为 null 的情况下使用 javascript 获取所有内容。您将能够针对您感兴趣的每个国家/地区重复使用此功能,并且您应该能够通过添加额外的@keys 将此示例扩展到其他属性。

【讨论】:

感谢您提供的优质资源,我会继续调查。到目前为止,我在 User 模型中收集的 @key of byCountry 将允许按国家/地区查询用户。但我想按用户(发送反馈)国家/地区列出所有反馈。这有没有可能?如果你能给我一个小例子,我将不胜感激。 我用一个例子和更多解释编辑了原始答案。 这是我尝试过的:将 byRegion 键添加到用户模型。在用户模型中添加了带有 @connection 的反馈。在调用 CreateFeedback 时添加了 userFeedbacksId 参数。添加了自定义查询:UserByRegionWithFeedback。现在我可以按地区过滤用户,并在项目对象下接收反馈。现在,每次 nextToken 不为空时,我都可以重新调用此 api 以获取该区域中的所有用户。反馈如何?我看到它返回一个嵌套的 nextToken 但是,有没有办法使用这个 api 向该用户询问更多反馈?例如,如果用户超过 10 个,我不想错过任何一个。【参考方案2】:

好的,感谢@alex 的回答,我实现了以下内容。我们的想法不是列出反馈并尝试按用户字段过滤它们,而是列出用户并从响应中收集他们的反馈:

    更新 schema.graphql 如下:

     type User
         @model
         @auth(rules: [ allow: owner ])
         @key(name: "byRegion", fields: ["region"], queryField: "userByRegion") # <-- added byRegion key 
         id: ID!
         email: String!
         name: String!
         region: String!
         sector: String!
         companyType: String!
         feedbacks: [Feedback] @connection # <-- added feedbacks connection
     
    

    在调用 CreateFeedback 时添加了 userFeedbacksId 参数。所以他们会在列出用户时出现。

    在 src/graphql/custom-queries.graphl 下添加自定义查询 UserByRegionWithFeedback 并使用 amplify codegen 构建它:

     query UserByRegionWithFeedback(
         $region: String
         $sortDirection: ModelSortDirection
         $filter: ModelUserFilterInput
         $limit: Int
         $nextToken: String # <-- nextToken for getting more Users
         $nextTokenFeedback: String # <-- nextToken for getting more Feedbacks
        ) 
         userByRegion(
             region: $region
             sortDirection: $sortDirection
             filter: $filter
             limit: $limit
             nextToken: $nextToken
         ) 
             items 
             id
             email
             name
             region
             sector
             companyType
             feedbacks(nextToken: $nextTokenFeedback)  
                 items 
                 content
                 createdAt
                 id
                 score
                 
                 nextToken
             
             createdAt
             updatedAt
             owner
             
             nextToken
             
          
    

    现在我像下面这样调用这个 API:

     nextToken = 
         user: null,
         feedback: null
     ;
     feedbacks: any;
    
     async listFeedbacks() 
         try 
             const res = await this.api.UserByRegionWithFeedback(
                 'Turkey', // <-- region: filter Users by their region, I will add UI input later
                 null, // <-- sortDirection
                 null, // <-- filter
                 null, // <-- limit
                 this.nextToken.feedback == null ? this.nextToken.user : null, // <-- User nextToken: Only send if Feedback NextToken is null
                 this.nextToken.feedback // <-- Feedback nextToken
             );
    
             // Get User NextToken
             this.nextToken.user = res.nextToken;
             // Initialize Feedback NextToken as null
             this.nextToken.feedback = null;
             // Loop Users in the response
             res.items.map((user) => 
             // Get Feedback NextToken from User if it is not null (Or else last User in the list could overrite it)
             if (user.feedbacks.nextToken) 
                 this.nextToken.feedback = user.feedbacks.nextToken;
             
             // Push the feedback items into the list to diplay in UI
             this.feedbacks.push(...user.feedbacks.items);
             );
          catch (error) 
             this.handleError.show(error);
         
     
    

    最后,我在 UI 中添加了一个加载更多按钮,该按钮调用 listFeedbacks() 函数。因此,如果有任何反馈 NextToken,我会将其发送到 API。 (请注意,多个用户反馈可以有一个 nextToken)。 如果所有反馈都正常并且有用户 NextToken,我将其发送到 API 并为新用户重复该过程。

我相信使用 SQL 设置可能会更简单,但目前还可以。我希望它可以帮助其他人在我的情况。如果有任何想法可以让它变得更好,我会全力以赴。

【讨论】:

【参考方案3】:

我之前的回答在特定情况下仍然对其他人有用,但是当我意识到您可以在自定义查询中过滤嵌套项时,我找到了实现嵌套过滤的更好方法。

架构:

    type User @model 
        id: ID!
        email: String!
        name: String!
        region: String!
        sector: String!
        companyType: String!
        feedbacks: [Feedback] @connection # <-- User has many feedbacks
    

自定义查询:

    query ListUserWithFeedback(
        $filter: ModelUserFilterInput # <-- Filter Users by Region or any other User field
        $limit: Int
        $nextToken: String
        $filterFeedback: ModelFeedbackFilterInput # <-- Filter inner Feedbacks by Feedback fields
        $nextTokenFeedback: String
        ) 
        listUsers(filter: $filter, limit: $limit, nextToken: $nextToken) 
            items 
            id
            email
            name
            region
            sector
            companyType
            feedbacks(filter: $filterFeedback, nextToken: $nextTokenFeedback) 
                items 
                content
                createdAt
                id
                score
                
                nextToken
            
            createdAt
            updatedAt
            
            nextToken
        
    

$filter 可以是这样的:

     region:  contains: 'Turkey'  

$filterFeedback 可以是:

    
        and: [ content:  contains: 'hello' , score:  ge: 4  ]
    

这样可以同时过滤用户和反馈。

【讨论】:

以上是关于在 AWS Amplify GraphQL DynamoDB 中按另一个表的字段(也称为交叉表或嵌套过滤)过滤列表查询的主要内容,如果未能解决你的问题,请参考以下文章

aws amplify appsync 中的 Graphql 突变错误

AWS Amplify GraphQL Schema 导致错误

AWS Amplify (GraphQL) - 使用“graphqlOperation”与普通查询?

带有 AWS Amplify 的 GraphQL - 如何启用对查询的排序

在 AWS Amplify 中更改我的 GraphQL 架构时如何防止丢失生产数据?

如何防止更新 AWS Amplify GraphQL API 的 ownerField?