Support for composite primary keys

We have a need to support composite keys in our GraphQL schema to enable having multiple versions of the same object in the db. I.E.

type node {
  id: String! @id
  version: String! @id  
}

Where the determination of uniqueness of a node is the id AND the version. Is this currently a plan on the Dgraph roadmap or possibly another way to support this?

Thanks for all the hard work!

2 Likes

i dont think this has huge priority. because your node is ALWAYS unique. it always has a UID. even if you don’t specify that in the schema, it still has under the hood its respective UID (you can even query for it in DQL, even if you dont have it in your schema). it uses UID for the whole graph database thing

the only solution i can suggest you is doing this:

type node {
  youruniqueidwithversion: String! @id
}

example value:
youruniqueidwithversion = 43536456_v12.38

BTW: if you have trouble querying in graphql for NESTED values (it is NOT supported in graphql) you gotta use DQL:

{
  lots_of_friends(func: ge(count(friend), 2)) @filter(ge(age, 20) AND lt(age, 30)) {
    name@.
    age
    friend {
        name@.
    }
  }
}

@Juri thank you for your response! I dont think using uuids will work for us. As we are using Dgraph in a way to store data that is identifiable outside the system and want to allow users to then query against the Dgraph dababase using values they might already know. i.e. think of a system where we are pulling friendship data from facebook for a schema below

type friend {
  name: String! @id
  age: String!
}

Dgraph will generate a GraphQL query for getting a friend like so: getFriend(name: '...') {}

As the user making the query will already know the name of the friend they are looking for, this is simple. However, the user will not know of the uuid value generated by Dgraph so if we remove the @id directive from the name field, we will lose the getFriend(name: '...') {} query and it will become getFriend(uuid: '...') {} which is not ideal as the user will not know the relation between a friend and their (dgraph specific) uuid.

I would put forth the argument that support for composite primary keys would be extremely beneficial for use cases where end users are querying the Dgraph DB directly and it is already something supported in SQL and other DB systems.

1 Like

why not

type node {
  id: String! @id
  version: String! @id  
  youruniqueidwithversion: String! @id
}

So the issue here is that the id value will be the same in both version. i.e.

queryNode returns

[{ id: 1, version: 1, uniqueIdWithVersion: 1-1}, {id: 1, version: 2, uniqueIdWithVersion: 1-2}]

This will fail on Dgraph insert because it checks each @id field for uniqueness INDIVIDUALLY. This is the main reason for needing composite keys so Dgraph will check uniqueness using multiple @id fields.

what about

type node {
id: String! @id
version: String! @id
youruniqueidwithversionAnduuidv4AndTimestamp: String! @id
}

This will have the same issue. id: String! @id This field needs to be completely unique. It would also have an issue on this field version: String! @id as imagine you have an array of nodes:

[{ id: '1', version: '1',}, {id: '2', version: '1'}]

This would fail due to version not being unique.

thats hard… maybe @amaster507 @MichelDiz @noisykeyboard buddies have a clue how to solve that use case

This is one of the several standard features that SQL supports that DGraph doesn’t. For now, one work around is to do something like this:

type Node {
  id: String!
  version: String!
  composite: String! @id
}

Add the composite string as id__version so that it is forced to be unique. You could use a lambda to enforce this, although if you add it correctly, it will be be unique without a lambda.

J

2 Likes

It is not uuid, it is UID. That’s important, cuz they are two very different things.

I see. But how do you guarantee the uniqueness of the data?

You can simulate it using the lambda function as mentioned by Jonathan.

And I don’t think we gonna have this composite p.keys any time soon.

1 Like

The manual composite key makes sense from a logic standpoint but we lose the easy to understand get queries as instead of getNode(name: '..') {} it would be getNode(name_version: '${name}_${version}) {} which the user may not know the version. Thanks for replying!

The uniqueness of the data would be guaranteed by Dgraph would it not? Similar to how the @id field guarantees uniqueness on one field currently. And i see, ok. Would love to see this feature so I hope it can climb the roadmap at some point! thanks for the hard work!

1 Like

I think this is tracked/requested in Composite @id fields as well. App side concatenating data is possible (usually - you don’t always control the client) but annoying and doesn’t guarantee the component fields stay in sync w/ the id field after mutations/updates.

From what I see in your data example

[
   {
      "id": "1",
      "version": "1",
      "uniqueIdWithVersion": "1-1"
   },
   {
      "id": "1",
      "version": "2",
      "uniqueIdWithVersion": "1-2"
   }
]

Feels like the ID wouldn’t be unique. What part of the data would characterize it as unique?

Feels like both nodes are abstractly the same information but with different versions. So, it is the same “node” and the concatenation of the ID with the version makes it unique. Right?

In practice, they aren’t unique nodes but it represents an evolution of that data. My question was based on that premise. The uniqueness in the DB itself. This modeling can be done in DQL tho.

Cheers.

So this data example is a response to @jdgamble555 in regards to creating a unique field by concatenating the two fields I want to use as a pair to form uniqueness (a composite key). The field uniqueIdWithVersion would give uniqueness using Dgraph’s current capabilities but would lose some ease of use in regards to the GraphQL generated get* queries Dgraph generates (which is based on the @id fields). You are correct in that the id itself would not be unique. But the composite of {id, version} would be unique which is where this request originated from. Thanks for the further explanation!

You can’t use a get query on an @id type regardless, only an ID type. You would need to filter it.

query {
  queryNode(filter: { composite: { eq: $ID_$VERSION } }) {
    id
    version
    composite
  }
}

J

Ummm… this is incorrect, FYI.

https://dgraph.io/docs/graphql/schema/ids/

@amaster507 Good call. I was thinking this was one of those things they never added for some reason.

@Tyler_D - In that case, it is even easier:

query {
  getNode(id: 1, version: 2) {
    id
    version
    composite
  }
}

J

@jdgamble555 This is exactly what I am hoping to do! However, I need composite keys because the id and version fields are not unique on their own but only as a pair.

1 Like

Here is another reason why composite keys like this is close to impossible in Dgraph. This correlates to a very similar question

In Dgraph there you cannot have an index over two predicates because where would the index live if the predicates were not part of the same group. There is a control to manually move predicates to the same group, but if that is not manually done, then it is possible for the two predicates to be distributed across multiple groups (aka servers).

So logically how would you even implement this feature of a unique index across multiple predicates that may be distributed? This would be like saying, it would be like asking SQL to add a unique key constraint across two tables or even two different databases.

1 Like