在 AWS Amplify GraphQL DynamoDB 中通过另一个 Table 的字段(a.k.a 交叉 Table 或嵌套过滤)过滤列表查询
Filtering List Query By Another Table's Field (a.k.a Cross-Table or Nested Filtering) in AWS Amplify GraphQL DynamoDB
您的问题与哪个类别相关?
DynamoDB、AppSync(GraphQL)
放大 CLI 版本
4.50.2
提供更多详细信息,例如代码片段
背景:
我是 AWS 无服务器应用程序系统的新手,作为前端开发人员,由于自动生成的 API、tables、连接、解析器等,我非常喜欢它。我正在使用 Angular/Ionic前端和后端的 S3、DynamoDB、AppSync、Cognito、Amplify-cli。
我有什么:
这是我的架构的一部分。我可以轻松地使用自动生成的 API 来 List/Get 带有额外过滤器的反馈(即 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 只会创建顶级过滤。为了使用交叉 table 过滤,我相信我需要修改解析器、lambda 函数、查询和输入。其中,对于初学者来说,它看起来相当复杂。
我TRIED/CONSIDERED:
- 我尝试单独列出所有用户和反馈并在前端过滤它们。但是随后客户端下载了所有这些不必要的数据。同样由于分页限制,用户体验受到影响,因为他们看到一个空列表并且需要反复单击“加载更多”按钮。
- 感谢一些建议,我还考虑过在反馈 table 中复制用户详细信息以便能够 search/filter 他们。那么问题是,如果用户更新 his/her 信息,重复的值将过时。重复的数据也会太多,因为我也需要其他 table 的这个功能。
- 我也听说过使用 ElasticSearch 来解决这个问题,但是有人提到一个简单的过滤他每月要花费 30 美元,所以我退缩了。
- 我尝试了解析器解决方案以在其中添加自定义过滤。但我发现这对于初学者来说相当复杂。此外,我还需要在许多其他 table 中使用这种交叉 table 过滤,因此我认为这将很难管理。如果这是最佳实践,如果有人可以指导我完成它,我将不胜感激。
问题:
- 要实现这种交叉table 过滤,easiest/beginner-friendly 解决方案是什么?我对替代解决方案持开放态度。
- 这种交叉 table 过滤对于无 SQL 设置来说是一种糟糕的方法吗?因为我需要两个 table 之间的某种关系。 (我认为@connection 就足够了)。我应该在为时已晚之前切换到 SQL 设置吗?
- Amplify 将来是否可以为此自动生成解决方案?我觉得很多人都遇到了同样的问题。
提前致谢。
Amplify 和真正的 DynamoDB 通常需要您提前考虑您的访问模式。有很多非常好的信息可以帮助指导您完成这个思考过程。特别是,我喜欢 Nader Dabit 的 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 将此示例扩展到其他属性。
好的,感谢 @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 设置会简单得多,但目前可以使用。我希望它能帮助其他人在我的情况下。如果有任何想法可以使它变得更好,我会洗耳恭听。
我以前的回答在特定情况下对其他人仍然有用,但当我意识到您可以在自定义查询中过滤嵌套项时,我找到了实现嵌套过滤的更好方法。
架构:
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 } }]
}
这样可以同时过滤用户和反馈。
您的问题与哪个类别相关? DynamoDB、AppSync(GraphQL)
放大 CLI 版本 4.50.2
提供更多详细信息,例如代码片段
背景: 我是 AWS 无服务器应用程序系统的新手,作为前端开发人员,由于自动生成的 API、tables、连接、解析器等,我非常喜欢它。我正在使用 Angular/Ionic前端和后端的 S3、DynamoDB、AppSync、Cognito、Amplify-cli。
我有什么: 这是我的架构的一部分。我可以轻松地使用自动生成的 API 来 List/Get 带有额外过滤器的反馈(即 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 只会创建顶级过滤。为了使用交叉 table 过滤,我相信我需要修改解析器、lambda 函数、查询和输入。其中,对于初学者来说,它看起来相当复杂。
我TRIED/CONSIDERED:
- 我尝试单独列出所有用户和反馈并在前端过滤它们。但是随后客户端下载了所有这些不必要的数据。同样由于分页限制,用户体验受到影响,因为他们看到一个空列表并且需要反复单击“加载更多”按钮。
- 感谢一些建议,我还考虑过在反馈 table 中复制用户详细信息以便能够 search/filter 他们。那么问题是,如果用户更新 his/her 信息,重复的值将过时。重复的数据也会太多,因为我也需要其他 table 的这个功能。
- 我也听说过使用 ElasticSearch 来解决这个问题,但是有人提到一个简单的过滤他每月要花费 30 美元,所以我退缩了。
- 我尝试了解析器解决方案以在其中添加自定义过滤。但我发现这对于初学者来说相当复杂。此外,我还需要在许多其他 table 中使用这种交叉 table 过滤,因此我认为这将很难管理。如果这是最佳实践,如果有人可以指导我完成它,我将不胜感激。
问题:
- 要实现这种交叉table 过滤,easiest/beginner-friendly 解决方案是什么?我对替代解决方案持开放态度。
- 这种交叉 table 过滤对于无 SQL 设置来说是一种糟糕的方法吗?因为我需要两个 table 之间的某种关系。 (我认为@connection 就足够了)。我应该在为时已晚之前切换到 SQL 设置吗?
- Amplify 将来是否可以为此自动生成解决方案?我觉得很多人都遇到了同样的问题。
提前致谢。
Amplify 和真正的 DynamoDB 通常需要您提前考虑您的访问模式。有很多非常好的信息可以帮助指导您完成这个思考过程。特别是,我喜欢 Nader Dabit 的 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 将此示例扩展到其他属性。
好的,感谢 @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 设置会简单得多,但目前可以使用。我希望它能帮助其他人在我的情况下。如果有任何想法可以使它变得更好,我会洗耳恭听。
我以前的回答在特定情况下对其他人仍然有用,但当我意识到您可以在自定义查询中过滤嵌套项时,我找到了实现嵌套过滤的更好方法。
架构:
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 } }]
}
这样可以同时过滤用户和反馈。