Graphql custom query error: Can't convert '9' to double

Schema:

type Goal @collection(name: "goals") {
  name: String!
  targetAmount: Float!
  installmentTerms: InstallmentTerms!
  installmentAmount: Float!
  deletedAt: String
  user: User @relation
  sortOrder: Float! # needs to be float
}

custom mutation:

getUserGoals: [Goal!] @resolver(name: "getUserGoals", paginated: true)

FQL resolver:

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

userGoalsBySortOrder index

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

When I run this query:

{
  getUserGoals {
    data {
      name
    }
  }
}

it works:

{
  "data": {
    "getUserGoals": {
      "data": [
        {
          "name": "Savings goal"
        },
        {
          "name": "Savings goal"
        }
      ]
    }
  }
}

Now when I try to get the sortOrder:

{
  getUserGoals {
    data {
      name
      sortOrder
    }
  }
}

It throws an error

{
  "data": {
    "getUserGoals": {
      "data": [
        null,
        null
      ]
    }
  },
  "errors": [
    {
      "message": "Can't convert '10' to double",
      "path": [
        "getUserGoals",
        "data",
        0,
        "sortOrder"
      ],
      "locations": [
        {
          "line": 5,
          "column": 7
        }
      ]
    },
    {
      "message": "Can't convert '9' to double",
      "path": [
        "getUserGoals",
        "data",
        1,
        "sortOrder"
      ],
      "locations": [
        {
          "line": 5,
          "column": 7
        }
      ]
    }
  ]
}

It only happens when I include sortOrder in the fields.

Can you share the output of running the function in the shell?

Map(
  Paginate(
    Match(
      Index("userGoalsBySortOrder"),
      [Ref(Collection("users"), "322014450218434627"), null]
    )
  ),
  Lambda(["sortOrder", "ref"], Get(Var("ref")))
)

output:

{
  data: [
    {
      ref: Ref(Collection("goals"), "322666662996213826"),
      ts: 1643977915310000,
      data: {
        installmentAmount: 8000,
        name: "Something",
        targetAmount: 500000,
        sortOrder: 12,
        installmentTerms: "Monthly",
        user: Ref(Collection("users"), "322014450218434627")
      }
    },
    {
      ref: Ref(Collection("goals"), "322656818692096064"),
      ts: 1643971644010000,
      data: {
        installmentAmount: 8000,
        name: "Laptop",
        targetAmount: 1000000,
        sortOrder: 11,
        installmentTerms: "Monthly",
        user: Ref(Collection("users"), "322014450218434627")
      }
    },
    {
      ref: Ref(Collection("goals"), "322658389536538688"),
      ts: 1643969869080000,
      data: {
        installmentAmount: 8000,
        name: "Savings goal",
        targetAmount: 1000000,
        sortOrder: 10,
        installmentTerms: "Monthly",
        user: Ref(Collection("users"), "322014450218434627")
      }
    }
  ]
}

I copied that directly from Dashboard > Functions > getUserGoals

Wierd part that I noticed is that it seems to have turned Match(index, ...args) to Match(Index, args[]) but it still worked anyways

Thank you! This is indeed strange. If you change the sortOrder to be not whole numbers, then do you still get the error in GraphQL? For example change 9 to 9.00001 etc.

As an aside:

The latter, array version is the actual FQL specification. The javascript driver lets you spread out the args for convenience, though.

Yes it worked!

{
  getUserGoals {
    data {
      name
      sortOrder
    }
  }
}
{
  "data": {
    "getUserGoals": {
      "data": [
        {
          "name": "Something",
          "sortOrder": 12.1
        },
        {
          "name": "Laptop",
          "sortOrder": 11.1
        },
        {
          "name": "Savings goal",
          "sortOrder": 10.1
        }
      ]
    }
  }
}

That means this is a bug right?

After some digging, this is expected behavior. Note that Fauna stores numbers as either an integer or a floating point number, and that those are distinct types, even though Javascript makes a mess of things.

I can replicate the issue when I store an integer value, which can happen in a couple of different ways. Again, I think that some of that is Javascript’s fault.

Use ToDouble to set the value with FQL

If I create a Document directly, with the JS driver or with the Dashboard, and type in a whole number (or a value with a zero decimal like 9.0) it gets stored in the driver then serialized into JSON without the decimal. When it gets to Fauna it is treated as an integer and stored that way. Fauna does not enforce any schema on your documents (yet, we are working on optional schema enforcement), so it is working correctly when it accepts an integer value and stores an integer value.

When GraphQL fetches the data it expects a Float but receives an Int – the GraphQL server is working correctly, and you receive an error.

Now, if I create a Document directly with JS or the Dashboard and wrap the value in ToDouble then we explicitly tell Fauna to store the value as a double-precision floating point number. When GraphQL fetches the data it expects a Float and receives a float.

Setting the values in Graphql should be fine

If I create a document through GraphQL and type in a value like 9 into the query, it gets serialized correctly as a double. The GraphQL server knows more about the intended type during serialization, because you specified it in the schema.

This means that if your application only works through the GraphQL API, you should be fine. If there are any custom resolvers that set these float values with FQL, then I suggest using ToDouble

Migrating existing integer values

You should be able to migrate your data by updating all documents and setting the Float values to themselves wrapped in ToDouble

Update(Ref(Collection("goals"), "101"), {
  data: {
    sortOrder: ToDouble(Var("previous_sortOrder"))
  }
})
1 Like

All that said, while this is technically working correctly, it is unintuitive. We should consider if we can resolve Fauna integers as GraphQL floats to avoid this kind of confusion and issue.

I’ve created an internal ticket to consider that.

Doing ToDouble doesn’t solve the problem because ToDouble(1) is still 1 and if I supply 1.0 as a value it gets stored as 1 in Fauna and the error still occurs.

I think Fauna has to handle this to allow Float to have both Int and Float, otherwise, it is not guaranteed that the endpoint will work.

EDIT:

IT WORKED! Weird, probably needed a few seconds for the graphql to refresh

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