在 FaunaDB 中将 Merge 与单个 Create 调用结合使用会创建两个文档吗?

Using Merge with a single Create call in FaunaDB is creating two documents?

在 Netlify 函数上使用带有 Node.js 运行 的 FaunaDB 时遇到一个奇怪的错误。

我正在建立一个快速的概念验证,最初一切正常。我有一个 Create 查询,看起来像这样:

const faunadb = require('faunadb');
const q = faunadb.query;

const CreateFarm = (data) => (
  q.Create(
    q.Collection('farms'),
    { data },
  )
);

正如我所说,这里的一切都按预期工作。当我试图开始规范化 FaunaDB 发回的数据时,麻烦就开始了。具体来说,我想将 Fauna 生成的 ID 合并到 data 对象中,然后将其与其他元数据的 none 一起发回。

我已经在使用其他资源这样做了,所以我编写了一个辅助查询并将其合并:

const faunadb = require('faunadb');
const q = faunadb.query;

const Normalize = (resource) => (
  q.Merge(
    q.Select(['data'], resource),
    { id: q.Select(['ref', 'id'], resource) },
  )
);

const CreateFarm = (data) => (
  Normalize(
    q.Create(
      q.Collection('farms'),
      { data },
    ),
  )
);

Normalize 函数在其他任何地方都按预期工作。它使用 ID 构建正确的合并对象,没有奇怪的副作用。但是,当与上述 CreateFarm 一起使用时,我最终在数据库中得到 两个 个相同的农场!

我花了很长时间查看应用程序的其余部分。肯定只有一个 POST 请求进来,而 CreateFarm 肯定只被调用一次。我最好的理论是,由于 Merge 复制了传递给它的第一个资源,Create 不知何故在数据库上被调用了两次。但是重新排序 Merge 调用不会改变任何东西。我什至尝试过先传入一个空对象,但最后总是创建两个相同的对象。

您的助手使用两个单独的 Create 表达式创建 FQL 查询。每个都被评估并创建一个新文档。这与 Merge 函数无关。

Merge(
    Select(['data'], Create(
      Collection('farms'),
      { data },
    )),
    { id: Select(['ref', 'id'], Create(
      Collection('farms'),
      { data },
    )) },
  )

使用 Let 创建文档,然后 Update 使用 id 创建文档。请注意,这会增加您的应用程序所需的写入操作数。它基本上会使创建文档的成本增加一倍。但是对于您要尝试做的事情,这是如何做的。

Let(
  {
    newDoc: Create(q.Collection("farms"), { data }),
    id: Select(["ref", "id"], Var("newDoc")),
    data: Select(["data"], Var("newDoc"))
  },
  Update(
    Select(["ref"], Var("newDoc")), 
    {
      data: Merge(
        Var("data"), 
        { id: Var("id") }
      )
    }
  )
)

旁白:为什么要在文档数据中存储 id?

不清楚为什么您可能需要这样做。可以在 ref 值本身上创建索引。如果您的客户收到 Ref,则可以将其直接传递到后续查询中。根据我的经验,如果您直接在应用程序中需要纯 id 值,请将文档转换为尽可能接近应用程序中的那个点(例如使用 id 作为一组 Web 组件的键)。

There's even a slight Compute advantage for using Ref values rather than re-building Ref expressions from a Collection name and ID. The expression Ref(Collection("farms"), "1234") counts as 2 FQL functions toward Compute costs, but reusing the Ref value returned by queries is free.

使用 GraphQL,_id 字段为您抽象出来,因为在 GraphQL 中使用文档类型会非常糟糕。但是,FQL 查询的最佳做法是尽可能直接使用 Ref。

不过,不要让我说绝对的话!我普遍认为任何事情都是有原因的。如果您认为您确实需要在文档数据中复制 ID,那么我会对为什么发表评论感兴趣。