Concurrent Conditional Upsert On Edges Does Not Work

I Want to Do

I have a type like so:

type Membership {

with the following schema:

user: uid @reverse .
club: uid @reverse .
token: string .

The user and club types are omitted for brevity.

I’d like to make sure that I cannot duplicate memberships.

The problem is I cannot add an @upsert to uid types because it does not allow me to create indexes on them.

Since I can’t create an upsert directive, I am unable to guard against concurrent membership creations and I end up with duplicate memberships if the user and the club are exactly the same.

What I Did

Concurrently ran 5 of the following upserts with userEmail and clubName being the same but token is different every time:

upsert {
	query addMembership($userEmail: string, $clubName: string) {
		user as users(func: eq(email, $userEmail)) {
			membership as memberships: ~user @cascade {
				club @filter(eq(clubName, $clubName)) {
		club as clubs(func: eq(clubName, $clubName))
	mutation @if(eq(len(membership), 0)) {
		_:m <dgraph.type> "Membership" .
		_:m <user> uid(user) .
		_:m <proxy> uid(proxy) .
		_:m <token> "<some random token>" .

Note that if I have an @upsert on the token and the token happens to be the same concurrently, then I end up with concurrent Abort errors and all is good. However, I cannot guarantee that the token is going to be the same so I need to make the upsert aborting on the edge itself which does not work.

I can potentially add directly on the Membership type and ensure that it has an @upsert directive (which it does)…But that feels like defeating the whole purpose of a graph based database by duplicating fields across different nodes.

Hi @marwan-at-work

Would it be right to say that a Membership is unique per combination of user and club? If this is true, then you may want to consider using a composite key. For example, you can concatenate user and club names (or respective uids) into token. The @upsert directive you have set on token will ensure only one Membership per unique combination of user and club.

That sounds like a plausible solution. Thanks!