简体   繁体   中英

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

Got a weird bug using FaunaDB with a Node.js running on a Netlify Function.

I am building out a quick proof-of-concept and initially everything worked fine. I had a Create query that looked like this:

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

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

As I said, everything here works as expected. The trouble began when I tried to start normalizing the data FaunaDB sends back. Specifically, I want to merge the Fauna-generated ID into the data object, and send just that back with none of the other metadata.

I am already doing that with other resources, so I wrote a helper query and incorporated it:

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 },
    ),
  )
);

This Normalize function works as expected everywhere else. It builds the correct merged object with an ID with no weird side effects. However, when used with CreateFarm as above, I end up with two identical farms in the DB!!

I've spent a long time looking at the rest of the app. There is definitely only one POST request coming in, and CreateFarm is definitely only being called once. My best theory was that since Merge copies the first resource passed to it, Create is somehow getting called twice on the DB. But reordering the Merge call does not change anything. I have even tried passing in an empty object first, but I always end up with two identical objects created in the end.

Your helper creates an FQL query with two separate Create expressions. Each is evaluated and creates a new Document. This is not related to the Merge function.

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

Use Let to create the document, then Update it with the id. Note that this increases the number of Write Ops required for you application. It will basically double the cost of creating Documents. But for what you are trying to do, this is how to do it.

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") }
      )
    }
  )
)

Aside: why store id in the document data?

It's not clear why you might need to do this. Indexes can be created on the ref value themselves. If your client receives a Ref, then that can be passed into subsequent queries directly. In my experience, if you need the plain id value directly in an application, transform the Document as close to that point in the application as possible (like using ids as keys for an array of web components).

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.

Working with GraphQL, the _id field is abstracted out for you because working with Document types in GraphQL would be pretty awful. However, the best practice for FQL queries would be to use the Ref's directly as much as possible.

Don't let me talk in absolute terms, though. I believe generally that there's a reason for anything, If you believe you really need to duplicate the ID in the Documents data. then I would be interested in a comment why.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM