如何通过书名和作者姓名筛选AWS Amplify DynamoDB GraphQL书籍
结合你提供的AWS Amplify GraphQL Schema(能看到@model、@connection这些Amplify专属指令),目前Book类型只支持通过title和authorId过滤,要实现书名+作者姓名的搜索需求,我整理了几个实用方案,按推荐优先级排序:
方案1:自定义Resolver实现后端联合查询(首推)
既然Author和Book已经通过authorId关联上了,我们可以在后端做一次联合查询:先通过作者姓名匹配到对应的Author ID,再用这些ID去过滤书名匹配的Book。这种方式只需要客户端发起一次请求,体验最好,也是我在项目里常用的方式。
具体步骤:
给Schema加自定义查询字段
在你的Schema根Query类型里新增一个专门的搜索字段:type Query { # 新增:通过书名和作者姓名搜书 searchBooksByTitleAndAuthorName(title: String!, authorName: String!): [Book] }编写Resolver逻辑(AppSync VTL示例)
如果你用的是AWS AppSync,可以用VTL模板来实现这个联合查询。这里需要先给Author表建一个基于name的GSI(全局二级索引),不然按姓名查作者会很慢;同理给Book表建基于title的GSI。请求模板示例(分两步:先查作者,再查书):
# 第一次请求:查询匹配姓名的作者(排除已删除的) #if($ctx.result == null) $util.toJson({ "version": "2018-05-29", "operation": "Query", "index": "authorByName", # 提前创建的Author表GSI,包含name和isDeleted字段 "query": { "expression": "name = :name AND isDeleted = :isDeleted", "expressionValues": { ":name": $util.dynamodb.toDynamoDB($ctx.args.authorName), ":isDeleted": $util.dynamodb.toDynamoDB(0) } } }) #else # 第二次请求:用查到的作者ID匹配书名 #set($authorIds = $util.toJson($ctx.result.items[*].id)) $util.toJson({ "version": "2018-05-29", "operation": "Query", "index": "bookByTitle", # 提前创建的Book表GSI,包含title、authorId和isDeleted字段 "query": { "expression": "title = :title AND authorId IN (:authorIds) AND isDeleted = :isDeleted", "expressionValues": { ":title": $util.dynamodb.toDynamoDB($ctx.args.title), ":authorIds": $util.dynamodb.toDynamoDBJson($authorIds), ":isDeleted": $util.dynamodb.toDynamoDB(0) } } }) #end关联Resolver并部署
在Amplify控制台或者本地配置里,把这个VTL模板关联到新增的searchBooksByTitleAndAuthorName查询字段,然后部署项目就可以用了。
方案2:客户端分两次查询(快速落地)
如果不想折腾后端Resolver,这种方式最省事:先查作者拿到ID,再用ID搜书。虽然是两次请求,但实现起来没门槛,适合快速验证需求。
示例查询代码:
# 第一步:搜匹配姓名的作者,只拿ID query FindAuthors($name: String!) { listAuthors(filter: {name: {contains: $name}, isDeleted: {eq: 0}}) { items { id } } } # 第二步:用书名+作者ID列表搜书 query FindBooks($title: String!, $authorIds: [ID!]!) { listBooks(filter: {title: {contains: $title}, authorId: {in: $authorIds}, isDeleted: {eq: 0}}) { items { id title authors { name description } } } }
客户端逻辑就是先调用第一个查询,拿到ID数组后再调用第二个查询,拼一下结果就行。
方案3:给Book表加作者姓名字段(不推荐)
你也可以给Book类型新增一个authorName字段,然后建一个包含title和authorName的GSI,这样就能直接用这两个字段搜索。但这个方案有个大坑:如果作者姓名修改了,你得同步更新所有关联Book的authorName字段,维护成本极高,除非你的作者姓名是绝对不会变的,否则不建议用。
修改后的Book类型示例:
type Book @model(subscriptions: null) @auth(...) @key(name: "bookByCreatedAt", fields: ["isDeleted", "createdAt"], queryField: "bookByCreatedAt") @key(name: "byAuthor", fields: ["authorId"]) # 新增GSI:通过书名+作者姓名搜索 @key(name: "bookByTitleAndAuthorName", fields: ["title", "authorName", "isDeleted"], queryField: "booksByTitleAndAuthorName") { # 原有字段... authorId: ID! authorName: String! # 新增字段,存储作者姓名 authors: Author @connection(fields: ["authorId"]) }
总结
- 追求体验和性能:选方案1,后端Resolver一次搞定
- 快速验证需求:选方案2,客户端两次查询零后端改动
- 作者姓名永不修改:可以考虑方案3,但一定要做好数据同步
内容的提问来源于stack exchange,提问作者sadam

