How to handle deletedAt in Graphql and faunadb?

CreateIndex({
  name: 'userGoalsBySortOrder',
  source: Collection('goals'),
  terms: [
    { field: ['data',  'user'] },
    { field: ['data', 'deletedAt'] }
  ],
  values: [
    { field: ['data', 'sortOrder'], reverse: true },
    { field: ['ref'] }
  ]
})

Custom query: getUserGoals

Update(Function("getUserGoals"), {
  role: "admin",
  body: Query(
    Lambda(
      ["size", "afterCursor", "beforeCursor"],
      Map(
        Paginate(
          Match(
            Index("userGoalsBySortOrder"),
            CurrentIdentity(),
            null
          )
        ),
        Lambda(
          ["sortOrder", "ref"],
          Get(Var("ref"))
        )
      )
    )
  )
});

If I manually run this function, it works as I expect it to work, but if I use it with graphql, I always get an empty data back.

The only thing “partially works” (but doesn’t give the result I want) is this:

CreateIndex({
  name: 'userGoalsBySortOrder',
  source: Collection('goals'),
  terms: [
    { field: ['data',  'user'] }
  ],
  values: [
    { field: ['data', 'sortOrder'], reverse: true },
    { field: ['ref'] }
  ]
})

This will give me “ALL” records, even those with deletedAt

Are you getting an error or just empty results?

The biggest thing that stands out to me is that you are using CurrentIdentity(). If the key you are using for GraphQL does not have an identity then you should get an error. If you are not getting an error, perhaps the caller simply has no deleted documents.

Can you be more specific about how you are running it manually? Are you entering a Ref directly or still using CurrentIdentity() and the same key? I would make sure you are running the FQL with the same key as in GraphQL.

I’m not getting an error, just an empty array.

  • If I call the graphql query (with the user logged in of course), I get an empty array.
  • If I copy-paste what the function is doing and just replace the CurrentIdentity() with the Ref of the same user, I get the right results.

Rather than replace the CurrentIdentity() with a hard-coded Ref, can you use the same key as the GraphQL query and run the UDF?

I ran this query:

Map(
  Paginate(
    Match(
      Index("userGoalsBySortOrder"),
      CurrentIdentity(),
      null
    )
  ),
  Lambda(
    ["sortOrder", "ref"],
    Get(Var("ref"))
  )
)

on the dashboard by providing a “custom secret”

I got 2 results:

{
  data: [
    {
      ref: Ref(Collection("goals"), "323005644921634883"),
      ts: 1644301037650000,
      data: {
        name: "Savings goal",
        targetAmount: 4848484884,
        installmentTerms: "Monthly",
        installmentAmount: 3290,
        sortOrder: 21,
        user: Ref(Collection("users"), "322014450218434627")
      }
    },
    {
      ref: Ref(Collection("goals"), "322888010772250690"),
      ts: 1644191649090000,
      data: {
        name: "Savings goal",
        targetAmount: 1000000,
        installmentTerms: "Monthly",
        installmentAmount: 8000,
        sortOrder: 18.99981000189998,
        user: Ref(Collection("users"), "322014450218434627")
      }
    }
  ]
}

which are correct.

Compared to the graphql query:

{
  getUserGoals {
    data {
      name
      deletedAt
    }
  }
}

which gives me the following results:

{
  "data": {
    "getUserGoals": {
      "data": []
    }
  }
}

Using the same secret.

Side note: I was trying to fetch the data using TS, but I got a TS error: Argument of type 'null' is not assignable to parameter of type 'ExprArg'.ts(2345)

const results = await client.query(
  query.Map(
    query.Paginate(
      query.Match(
        query.Index('userGoalsBySortOrder'),
        query.CurrentIdentity(),
        null
      )
    ),
    query.Lambda(['sortOrder', 'ref'], query.Get(query.Var('ref')))
  )
);

I cannot replicate your issue. Taking information from here and from this previous topic, I’ve reconstructed your schema as best I can (including copy-pasting the query to update the function) and I get the same results in GraphQL as I do while using FQL.

I created a key for a user and created a script to run Call("getUserGoals", 50 , null, null)

image

I used the same key in the GraphQL auth header and ran getUserGoals

My best guess at this point is that your GraphQL API was not running the same code as you were in the shell.

The reason I was asking you to run the UDF, and not paste the definition into the shell, was to make sure that GraphQL was, in fact, running the code you expected it to.

Note if I query for the logged in user, then all of the goals, I get the “deleted” goals, indicating that the index to filter for “deletedAt == null” in the getUserGoals UDF is working correctly.

It looks like your latest topic means that you are trying to find a workaround for the issue you have here. I think we need to figure out what’s going on here, though, because it really should work!

Yeah, since we can’t get this to work, we used a filter for now. It works but it’s not the ideal solution, ideally, we’d like to use an index for it.

I’ll try to create a repro for it and send it here.

after finally getting the time to focus on this, I think the reason is because the null filter doesn’t work.

I had to create an index with custom binding:

CreateIndex({
  name: 'getByDeletedAt',
  source: {
    collection: Collection('items'),
    fields: {
      isDeleted: Query(
        Lambda(
          ['doc'],
          ContainsPath(
            ['data', 'deletedAt'],
            Var('doc')
          )
        )
      )
    }
  },
  terms: [
    { field: ['data',  'user'] },
    { binding: 'isDeleted' }
  ],
  values: [
    { field: ['data', 'sortOrder'], reverse: true },
    { field: ['ref'] }
  ]
})

Then, you’d use it like so:

Match(
  Index('userGoalsByDeletedAt'),
  CurrentIdentity(),
  false
)

If either all of the terms resolve to null or all of the values resolve to null, then there is no index entry created. However, if some terms have values and some are missing, then the missing ones will resolve to null, and an entry will be created with the null values.

null is a valid value to search by, if there are other terms that ensure the index entry is created. If you are facing a mixture of data, the binding you shared is a good idea! Nice workaround.

I’m still not sure why it didn’t originally work for you. Did you try the original UDF body once more? Because it worked fine for me. In any case, I am glad to hear of you making progress!

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.