FaunaDB - 如何批量更新单个 graphQL 突变中的条目列表?
Posted
技术标签:
【中文标题】FaunaDB - 如何批量更新单个 graphQL 突变中的条目列表?【英文标题】:FaunaDB - How to bulk update list of entries within single graphQL mutation? 【发布时间】:2020-06-29 21:07:47 【问题描述】:我想批量更新动物数据库中带有 graphQL 突变的条目列表。 输入数据是来自外部来源的冠状病毒病例列表。它会经常更新。如果集合中存在条目名称,则突变应更新现有条目,如果不存在,则创建新条目。
当前的 GRAPHQL 突变
mutation UpdateList($data: ListInput!)
updateList(id: "260351229231628818", data: $data)
title
cities
data
name
infected
图形变量
"data":
"title": "COVID-19",
"cities":
"create": [
"id": 22,
"name": "Warsaw",
"location":
"create":
"lat": 52.229832,
"lng": 21.011689
,
"deaths": 0,
"cured": 0,
"infected": 37,
"type": "ACTIVE",
"created_timestamp": 1583671445,
"last_modified_timestamp": 1584389018
]
架构
type cityEntry
id: Int!
name: String!
deaths: Int!
cured: Int!
infected: Int!
type: String!
created_timestamp: Int!
last_modified_timestamp: Int!
location: LatLng!
list: List
type LatLng
lat: Float!
lng: Float!
type List
title: String!
cities: [cityEntry] @relation
type Query
items: [cityEntry!]
allCities: [cityEntry!]
cityEntriesByDeathFlag(deaths: Int!): [cityEntry!]
cityEntriesByCuredFlag(cured: Int!): [cityEntry!]
allLists: [List!]
每次突变运行时,它都会创建新的副本。 在单个突变中更新列表的最佳方法是什么?
【问题讨论】:
嗨!您能否在答案中分享架构(或架构的一部分)?看起来你可能需要一个 upsert 突变才能这样做,但不幸的是,目前 FaunaDB 上不可用。您可以尝试预先创建城市,然后在根据您共享的突变创建该类型时使用“connect”而不是“create”。 感谢您的建议。我添加了架构细节。 upsert 突变正是我所需要的——“如果不存在插入条目,则更新它们”。如果动物 graphQL 尚不支持它,则可能是 Fauna Query Language。 它绝对可以在 FQL 中作为一个单独的函数来实现。要实现自定义解析器,您可以查看:css-tricks.com/instant-graphql-backend-using-faunadb。我会让 Leo 给你详细信息,因为他是专家。同时,您正在构建的东西似乎很有趣,如果您想与我们讨论或需要帮助以使您的进展顺利,请加入community.fauna.com并与我们交谈 @BrechtDeRooms 我在 FaunaDB 社区问了两次同样的问题,但没有得到任何答复。而且这里似乎也没有回复。此外,文档也没有显示如何使用解析器将参数从 graphql 传递给 UDF 的单个示例。 好的,我没有想到@resolver 文档中缺少架构示例。我已经让我们的文档团队知道并在下面构建了一个答案:) 【参考方案1】:我对延迟表示歉意,我不确定丢失的信息到底是什么,因此我首先发表评论:)。
架构
具有参数的架构部分示例:
type Mutation
register(email: String!, password: String!): Account! @resolver
login(email: String!, password: String!): String! @resolver
在 FaunaDB 中导入此类模式时,将提供占位符函数。
UDF 参数
如您所见,该函数所做的所有事情都是 Abort 并显示该函数仍需执行的消息。实现从一个带有参数的 Lambda 开始,这些参数必须与您在解析器中定义的内容相匹配。
Query(Lambda(['email', 'password'],
... function body ...
))
使用参数是通过 Var 完成的,在这种情况下,这意味着 Var('email') 或 Var('password')。例如,在我的特定情况下,我们将使用传入的电子邮件通过电子邮件获取帐户,并使用密码传递给 Login 函数,该函数将返回一个秘密(我这样做的原因此处的选择是 GraphQL 解析器的返回值必须是有效的 GraphQL 结果(例如纯 JSON
Query(Lambda(['email', 'password'],
Select(
['secret'],
Login(Match(Index('accountsByEmail'), Var('email')),
password: Var('password')
)
)
))
通过 GraphQL 调用 UDF 解析器
最后,调用的时候怎么传参数呢?从 GraphQL 操场上应该很清楚,因为它将为您提供文档和自动完成功能。例如,这是在我的架构导入后自动生成的 GraphQL 文档告诉我的:
也就是说我们可以这样称呼它:
mutation CallLogin
login (
email: "<some email>"
password: "<some pword>"
)
批量更新
对于批量更新,您还可以将值列表传递给用户定义函数 (UDF)。假设我们希望通过 UI 将特定团队中的多个帐户组合在一起,因此希望同时更新多个帐户。
我们的 Schema 中的突变可能如下所示(GraphQL 中的 ID 类似于字符串)
type Mutation updateAccounts(accountRefs: [ID]): [ID]! @resolver
然后我们可以通过提供我们从 FaunaDB 收到的 id 来调用突变(如果您混合使用 FQL 和 GraphQL,则不是字符串,而不是 Ref,如果您只使用 GraphQL,请不要担心)。
mutation
updateAccounts(accountRefs: ["265317328423485952", "265317336075993600"] )
和之前一样,我们必须填写由 FaunaDB 生成的用户定义函数。一个只接收数组并返回它的骨架函数如下所示:
Query(Lambda(['arr'],
Var('arr')
))
有些人可能已经看到了更简单的语法,并很想使用它:
Query(Lambda(arr => arr))
但是,这个目前在传入数组时不适用于 GraphQL,这是一个已知问题,将得到修复。 下一步是实际循环数组。 FQL 不是声明性的,它从函数式语言中汲取灵感,这意味着您只需使用“map”或“foreach”即可完成此操作
Query(Lambda(["accountArray"],
Map(Var("accountArray"),
Lambda("account", Var("account")))
))
我们现在遍历列表,但还没有对它做任何事情,因为我们只是在地图主体中返回帐户。我们现在将更新帐户并在其中设置一个值“teamName”。为此,我们需要采用 FaunaDB Reference 的 Update 函数。 GraphQL 向我们发送字符串而不是引用,因此我们需要将这些 ID 字符串转换为带有 Ref 的引用,如下所示:
Ref(Collection('Account'), Var("account"))
如果我们把它们放在一起,我们可以在账户 ID 列表中添加一个额外的属性,如下所示:
Query(Lambda(["accountArray"],
Map(Var("accountArray"),
Lambda("account",
Do(
Update(
Ref(Collection('Account'), Var("account")),
data: teamName: "Awesome live-coders"
),
Var("account")
)
)
)
))
在 Map 的末尾,我们只需使用 Var("account") 再次返回帐户的 ID,以便返回纯 JSON 的内容,否则我们将返回不仅仅是 JSON 的 FaunaDB Refs并且不会被 GraphQL 调用接受。
传递更复杂的类型。
有时您想要传递更复杂的类型。假设我们有一个简单的 todo 模式。
type Todo
title: String!
completed: Boolean!
我们希望将具有特定标题的待办事项列表的完成值设置为 true。我们可以在 FaunaDB 生成的扩展模式中看到有一个 TodoInput。
如果您看到扩展架构,您可能会想:“嘿,这正是我需要的!”但是当你写你的突变时你不能访问它,因为你在创建时没有架构的那部分,因此不能只写:
type Mutation updateTodos(todos: [TodoInput]): Boolean! @resolver
因为它会返回以下错误。
但是,我们可以自己将其添加到架构中。 Fauna 只会接受您已经编写过它而不是覆盖它(确保您保留必填字段,否则您生成的“createTodo”突变将不再起作用)。
type Todo
title: String!
completed: Boolean!
input TodoInput
title: String!
completed: Boolean!
type Mutation updateTodos(todos: [TodoInput]): Boolean! @resolver
这意味着我现在可以写了:
mutation
updateTodos(todos: [title: "test", completed: true])
并深入研究 FQL 函数以处理此输入。 或者,如果您想在数据中包含 ID,您可以定义一个新类型。
input TodoUpdateInput
id: ID!
title: String!
completed: Boolean!
type Mutation updateTodos(todos: [TodoUpdateInput]): Boolean! @resolver
一旦您掌握了窍门并想了解有关 FQL 的更多信息(这是一个完全不同的主题),我们目前正在编写一系列文章以及第一篇出现在这里的代码:https://css-tricks.com/rethinking-twitter-as-a-serverless-app/ 这可能是好温柔的介绍。
【讨论】:
非常感谢您的回答@BrechtDeRooms。您的示例中唯一缺少的难题是我们需要传入一个 ID 数组才能进行批量更新。那么如何传递所有需要更改的文件呢?例如。 updateBulkArticle(id: [ID]): [Article] @resolver(name: "bulkArticleUpdate" ) 我假设您的意思是带有 id 的 FaunaDB References (Ref) 对吗?如果是这样,我想我明白你的困惑 是的,我可以使用 graphql 在我的应用程序中发送的任何内容,并且 UDF 知道要“批量更新”什么。我相信上面的原始问题具有相同的意图。 是的,我确实忘记解决批量方面的问题。在我编辑答案之前,这是否回答了您的问题:仅传递一组项目的模式示例: type Mutation test(testInput: [String]): [String]! @resolver 调用它:mutation Test test(testInput: ["test"]) 一个示例 UDF,它在其上接收数组映射(或使用 Foreach)并仅返回原始字符串。但是这些元素可以让你用它做一些事情 Query(Lambda(["arr"], Map(Var("arr"), Lambda("x", Var("x"))))) 它必须是 ["arr"],而不是 "arr" 是的,确实可以回答。希望@user1276919 的情况相同,我们可以勾选这个答案。当然 Graphql 不理解“Refs”,但我可以传递一个 ID 数组,然后 UDF 会处理它。非常感谢您的支持。我相信这对未来的其他人也会有用。以上是关于FaunaDB - 如何批量更新单个 graphQL 突变中的条目列表?的主要内容,如果未能解决你的问题,请参考以下文章
我可以在不知道其 ID 的情况下更新 FaunaDB 文档吗?