Best way to avoid "duplicate XID found"

Given the schema and query below, what is the best way to achieve a non-error result without having to perform two queries?

Schema:

type Page {
    Id: String! @id
    Title: String
    LinksTo: [Page]
}

Query:

mutation {
  addPage(input: [
    { Id: "root", Title: "root",  LinksTo: []},
    { Id: "level1", Title: "level1", LinksTo: [ { Id: "root"} ]}
  ]) {
    numUids
    page {
      Id
    }
  }
}

Current error:

"errors": [
    {
      "message": "couldn't rewrite mutation addPage because failed to rewrite mutation payload because duplicate XID found: root"
    }
  ]

I believe we can achieve this via

mutation {
  addPage(input: [
    { Id: "root", Title: "root",  LinksTo: [
      {
        Id: "level1", Title: "level1"
      }
    ]}
  ]) {
    numUids
    page {
      Id
    }
  }
}

Do let us know if this matches the expectation :slight_smile: .

1 Like

Kinda, but it brings me to another related question.

If I start expanding that query for a more real-life situation, and now lets say I am adding two nodes that point to the same “root” node like

Query #1

mutation {
  addPage(input: [
    { Id: "level1", Title: "level1", LinksTo: [
      {
        Id: "root", Title: "root"
      }
    ]},
    { Id: "level2", Title: "level2", LinksTo: [
      {
        Id: "root", Title: "root"
      }
    ]}
  ]) {
    numUids
    page {
      Id
    }
  }
}

The query above works. But if I do a slightly different query (note that my root page is now referenced only by the Id - without the rest of the properties), I still get the duplicated XID error.

Query #2

mutation {
  addPage(input: [
    { Id: "level1", Title: "level1", LinksTo: [
      {
        Id: "root", Title: "root"
      }
    ]},
    { Id: "level2", Title: "level2", LinksTo: [
      {
        Id: "root"
      }
    ]}
  ]) {
    numUids
    page {
      Id
    }
  }
}

So, you’ve shown me that if I use the exact same object, it does not generate a conflict, which is good, but I need to know if there is a way of making this second query work (using just a reference) without repeating the whole object - above we have an example only but you can imagine that in real life cases the objects are gonna be much bigger and avoiding to repeat them every time would be a good thing.

Also, If I have to put the whole object I might have to put some nested objects that could reference to other “pages” in this example and then I could get into some circular dependencies that could be really complicated - if not impossible - to solve.

So… is it possible to make this query #2 work without replicating the whole obj?

For reference, the way Hasura deals with this problem is by providing an on_conflict clause on insert mutations which instructs the engine on what to do when it encounters a duplicate ID, effectively converting the insert into an upsert

https://hasura.io/docs/1.0/graphql/core/mutations/upsert.html#convert-insert-mutation-to-upsert
https://hasura.io/docs/1.0/graphql/core/api-reference/graphql-api/mutation.html#conflictclause

1 Like

It is not possible at the moment. But, I am accepting the Query #2 case as an enhancement. So, either the same object needs to be given everytime, or you can give original object once, and later give just the reference to that object. Both these scenarios should be accepted as valid inputs.

1 Like

Thank you for your reply. Do you know if we can consider some estimated delivery date for that feature or we should continue with our development without that improvement in mind?

I know this is xid specific, but I run into this problem with ID (uid) as well.

If you implement a fix allowing the xid to be used as a reference only in the latter uses, then the fix to use blank nodes as ID pointer is not that far off either, I wouldn’t think. I don’t want to have to use xids everywhere to enable deep nested add/update mutations

It would be part of the next major release in November, i.e., v20.11

1 Like

For this I would not confirm just yet. UIDs are a bit different. There can be cases where one may want to carry-over the blank node uid to another mutation in a series of mutations. That would cause issues with transactions. So, this would require a bit more thought about how to implement. but, surely it would be a good feature if people could provide a blank node uid in GraphQL.

1 Like