问题描述
我是 faunadb 的新手。我在从两个集合中查询数据时遇到问题。 我有一个包含以下数据的用户集合。
{
"ref": Ref(Collection("users"),"286520131377445052"),"ts": 1609505740440000,"data": {
"userId": "f35fdc8d-6197-4d19-bf27-256bd41b3751","name": "PRANTA Dutta","email": "pranta@email.com","role": "borrower","password": "somepassword"
}
}
{
"ref": Ref(Collection("loans"),"287038065061397005"),"ts": 1609999680495000,"data": {
"monthlyInstallment": 473.33,"loanDuration": 6,"interestRate": 7,"amount": 2000,"modifiedMonthlyInstallment": 513.33,"mode": "processing","userId": "f35fdc8d-6197-4d19-bf27-256bd41b3751"
}
}
重要提示:贷款和用户集合是一对多的关系,一个用户可以申请多笔贷款。
现在我想编写一个查询来立即获取具有匹配 userId 的贷款数据,以便我可以完全显示所有数据。
我想要的数据如下:
"data": {
"monthlyInstallment": 473.33,"userId": "f35fdc8d-6197-4d19-bf27-256bd41b3751",{
"data": {
"userId": "f35fdc8d-6197-4d19-bf27-256bd41b3751","password": "somepassword"
}
}
}
我如何使用 FQL 做到这一点,提前致谢。
解决方法
与直觉相反,Join 不是您所需要的:
我们有一个 Join FQL 功能,但它可能不是您所期望的。联接是从一组引用(例如一组用户引用)通过索引到另一组引用(例如贷款)的遍历。我将其称为“遍历”,因为它本质上将用贷款参考替换用户参考,因此您没有根据需要将数据放在一起。只是想在您浪费时间试图弄清楚如何使用 Join 来做到这一点之前澄清这一点。
相反,结合使用 Map 和 Get 方法
在动物群中,您的想法必须略有不同。想一想您将如何使用常规的程序编程语言执行此操作:“获取所有贷款,映射贷款,针对每笔贷款,获取用户”。
在 FQL 术语中,该描述为:
- 使用 Paginate(Match(....)) 获取文档的初始页面(例如贷款)
- 使用 Map() 和 Lambda() 循环这些(在 FQL 中,而不是在宿主语言中,它仍然是 ACID)
- 在 lambda 中:
- 如果该文档包含对您需要的其他文档的引用 (例如用户),只需使用 Get() 来获取该引用。
- 如果链接的文档包含引用或者它包含在另一个集合中(例如多对多的对象化关系),则使用索引(例如 Match(Index(... some value ... ))来检索链接的文档. 因为在这种情况下你会收到多个结果。如果它是你期望的一个值,则在该索引上使用 Get 或者如果你期望多个值(你可以在多个级别上进行分页)在索引上分页
可以在此处找到获取嵌套文档和代码的完整说明:
这里已经写出了一个关于如何获取嵌套文档的例子:
How to get nested documents in FaunaDB?
应用于您的示例:
想象一下,你会直接存储用户引用,它会是这样的:
// Disclaimer,didn't test code,sorry if I missed a bracket.
Map(
// get loan references
Paginate(Documents(Collection('loans'))),// map over pages of loan references
Lambda('loanRef',Let({
loan: Get(Var('loanRef')),user: Get(Select(['data','userRef'],Var('loan'))),},// And now we can then return whtatever we want (or could have
// omitted the let and directly return an object)
{
loan: Var('loan'),user: Var('user')
}
)
)
)
由于您已选择使用用户定义的 ID(这很好),因此需要一个额外的步骤。
Map(
// get loan references
Paginate(Documents(Collection('loans'))),userId: Select(['data','userId'],Var('loan')),// get on a match assumes the userId exists
// and that there is only one result (in your case,many-to-one..
// that's fine). It also assumes you have defined that index.
user: Get(Match(Index("users_by_userid"),Var('userId')))
},user: Var('user')
}
)
)
)
Map/Get的优势
有点推理为什么它会这样工作。在 Fauna 中,您有对 Sets 进行操作的操作,这些操作是对数据外观的描述,然后您对 Set 进行分页以实际获取数据页。
-
Join: 想象一下,我们将有一个类似 SQL 的连接,然后对该结果集调用 Paginate。第一个页面可能只包含一个用户和 10000 笔贷款(好吧.. 在这个特定场景中不太可能,但想象一下您与用户和推文打交道的例子)。这可能不是您想要的,而且(我假设)还有性能/复杂性的原因没有提供这样的功能(类 SQL 的连接特别难以扩展)。
-
Map/Get 另一方面,强制分页的 map/get 方式更具可扩展性,从用户的角度来看也非常漂亮。它使您可以控制多个级别的分页。在这种情况下,您将获得一个包含 100 个用户的页面(假设我们的 pagesize = 100),然后(假设我们的第二个 pagesize = 10,我们可以单独决定)每个用户 10 笔贷款。如果有超过 100 个用户,您会得到一个后光标以继续(或者您可以将页面大小增加到 100000)。如果某个用户有更多贷款,您将为每个用户获得一个后游标,并且可以单独控制从哪个用户获取更多数据。这可确保您作为用户拥有灵活性,同时确保性能并避免必须获取大量数据,以防您的联接具有导致结果集爆炸的高基数关系。