如何从 faunadb 中的两个集合中获取数据

How to Get Data from two collection in faunadb

我是 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,
       "loanDuration": 6,
       "interestRate": 7,
       "amount": 2000,
       "modifiedMonthlyInstallment": 513.33,
       "mode": "processing",
       "userId": "f35fdc8d-6197-4d19-bf27-256bd41b3751",
       {
          "data": {
             "userId": "f35fdc8d-6197-4d19-bf27-256bd41b3751",
             "name": "PRANTA Dutta",
             "email": "pranta@email.com",
             "role": "borrower",
             "password": "somepassword"
          }
       }
   }

我如何使用 FQL 做到这一点,在此先感谢。

违反直觉 Join 不是您需要的:

我们有加入 FQL 功能,但它可能不是您所期望的。 Join 更像是一种遍历,从一组引用(例如一组用户引用)通过索引到达另一组引用(例如贷款)。我称它为 'traverse',因为它本质上将用贷款参考替换用户参考,因此您没有所需的数据。只是想澄清一下,在您浪费时间尝试弄清楚如何使用 Join 之前。

而是结合使用 Map and Get 方法

在动物群中,你的想法略有不同。想想你会如何用常规的程序化编程语言来做到这一点:“获取所有贷款,映射贷款,对于每笔贷款,获取用户”。

在 FQL 术语中,该描述为:

  • 使用 Paginate(Match(....))
  • 获取文档的初始页面(例如贷款)
  • 使用 Map()Lambda() 循环这些(在 FQL 中,而不是在宿主语言中,它仍然是 ACID)
  • 在 lambda 中:
    • 如果该文档包含对您需要的其他文档的引用 (例如用户),只需使用 Get() 获取该引用。
    • 如果链接文档包含引用或它包含在另一个集合中(例如,多对多的对象化关系),则使用索引(例如 Match(Index(... some value ... ))来检索链接的文档。因为在这种情况下您将收到多个结果。如果它是您期望的一个值,则在该索引上使用 Get;如果您期望许多值,则在索引上使用分页(您可以在多个级别上分页)

获取嵌套文档和代码的完整说明可在此处找到:

例子,如何获取嵌套文档,这里已经写出来了:

应用于您的示例:

假设您直接存储了用户引用,它会是这样的:

// 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'))),
  // map over pages of loan references
  Lambda('loanRef',
    Let({
        loan: Get(Var('loanRef')),
        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')))
      },
      // 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')
      }
    )
  )
)

Map/Get的优点

关于它为何如此工作的一些推理。在 Fauna 中,您有对 Sets 进行操作的操作,这些操作更像是对数据外观的描述,然后 Paginate 设置为实际获取数据页。

  • Join: 假设我们有一个类似 SQL 的连接,然后在该结果集上调用 Paginate。第一页可能只包含一个用户和 10000 笔贷款(好吧.. 在这种特定情况下,这种可能性较小,但想象一下您与用户和推文打交道的例子)。这可能不是您想要的,并且还有(我假设)performance/complexity 原因不提供这样的功能(SQL 类联接特别难以扩展)。

  • Map/Get 另一方面,map/get 强制分页的方式更具可扩展性,并且从用户视角。它使您可以控制多个级别的分页。在这种情况下,您将获得一个包含 100 个用户的页面(假设我们的页面大小 = 100),然后(假设我们的第二个页面大小 = 10,我们可以单独决定)每个用户 10 笔贷款。如果有超过 100 个用户,您会看到一个光标以继续(或者您可以将页面大小增加到 100000)。如果某个用户有更多贷款,您将为每个用户获得一个后光标,并且可以单独控制从哪个用户获取更多数据。这确保您作为用户具有灵活性,同时确保性能,并避免在您的连接具有高基数关系导致结果集爆炸的情况下必须获取大量数据。