@upsert for counting UIDs not possible?

Hello,

I have player and game nodes, and I’d like to be sure only 2 players subscribe to a game.
Players create games and then each game should be played by only one player.

So I set the following schema:

player: uid @count @upsert .

And then searched for a pending game with:

me(func: eq(count(player), 1)) {
  ...
}

But I got this error:

Index tokenizer is mandatory for: [player] when specifying @upsert directive

Is it impossible to use upserts for counting predicates?

edit: removed reverse predicate as it’s not possible to count them

Upserts and Count are distinct concepts. They do not mix properly. Upserts for a UID conceptually would be like wanting to find the existence of a UID - If there is no Node it would create a new one. As if it were an “allocation”. Not for what you want to do. You should try to take another approach for this.

About count: https://docs.dgraph.io/query-language#count
About upserts: https://docs.dgraph.io/howto/#upserts

Thank you for the explanation, I now have a better understanding of count and upsert.

So a solution could be to have an integer value counting the players and an upsert on it?

Or have you any suggestion for this common use case where the first/second/… to connect to a node should be the last?

Let’s say, On your main Node to which you join the two players, you could add two special predicates. By then you can filter and know that there are two players there.

e.g:

{
  Games(func: eq(game, "main game")) @filter(has(player.one) AND has(player.two)) {
    name@en
    points
    someotherpredicate
    players {
      name@en
      age
     }
   total.players : count(players)
  }
}

So when player X joins the game, your application will add a predicate “player.one” or “player.two”.

And you can combine it with the Count model you were using - To find all games that have at least 2 players. And they are signed with their due players position (one or two):

In this case the filter is optional

{
  Games(func: eq(count(players), 2) ) @filter(has(player.one) AND has(player.two)) {
    name@en
    points
    someotherpredicate
    players {
      name@en
      age
     }
   total.players : count(players)
  }
}

I hope it helped you. Cheers.

Thank you for the example.

But as I understand your suggestion, I might still have 3 players on a game instead of 2:

  • A player A create a game G, a ‘player.one’ predicate is added to the game node
  • 2 other players (B and C) ask for a pending game (a game with currently only one player ready to play)
  • Each server threads of players B and C will search for a game with the ‘player.one’ predicate and NOT the ‘player.two’ predicate
  • They might find that same game G and they both will try to add the ‘player.two’ predicate
  • Thus they will both think they got the game, and will have a game with 3 players, not 2!

I’d like to be sure only one player take the second slot.

After the query and the mutation I could also make a third request to count how many players have joined the game and if I got 3 players, I could reject the late one (based on a nanosecond timestamp). But this requires another query (count), another mutation (reject) and starting over the game search for the rejected player.

Well, Dgraph will only accept one if they are at the same time. The other will be discarded in the transaction.

It may happen that more than one user subscribes, to solve this use the UID of the user subscribed within the predicate player.one and player.two. That way the third party will be connected, but not registered. By logic.

If this occurs, your application could make a check before every game starts, remove the third user and warn that he lost a chair.

Thank you.
Do you mean dGraph throws an abort error if there is an attempt to add the same predicate between the same 2 nodes? And this without upsert index or anything?

What do you really mean?

“No two objects can occupy the same place at one time” Sir Isaac Newton.

If it is the same predicate for distinct nodes, there is no such conflict assessment. It would be a normal mutation indeed. Conflict is only for the same Node and simultaneously.


I’m not sure if it sends an error to the user, but that two concurrent transactions are dealt in a conflicting way giving win to the first mutation. And the last is discarded. But if there is a second mutation later on, it will be overwritten.

But that does not mean much to your question, it was just a comment related to conflicts in the transaction. “if they are at the same time.”

Ok thank you.
I was just wondering if I could rely on conflict management to handle that issue.
I’ll try something in the spirit of your suggestion.

Maybe I could add a nanoseconds timestamp as a facet to the player predicate so I can easily discard the third to subscribe, if any.

P(layer) -created- > G(ame) -playwith-> P(layer)

playwith may have a time facet , so that you can sort by it.
if you have a Game , you just count(playwith) it .

:grinning:

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