Counting facets


(Max) #1

Hello Dgraph community!

I’m working an application involving voting, and I need to limit the number of votes a user can cast (to lets say 100). User’s can freely cast and remove votes to other nodes, subject to the constraints that the total number of votes must be between 0 and 100. I’m currently using facets to record the number of votes from one user to another, so a vote between users is represented by a vote edge with a number facet.

My question is concerning the best practices for keeping track of the number of votes cast by a given user. As I see it there are two ways to do it:

  1. Recalculate the number of votes cast whenever a user tries to add or remove a vote.
  2. Keep a running sum of the number of votes cast, and use that to decide if the user can add or remove a vote.

I’m currently using the second option, but I don’t like the idea of not having a single source of truth to consult, and the greater potential for errors bother me. However the first option seems computationally expensive and not particularly scalable - though if there can be at most 100 edges to sum the facets over, maybe my worries are misguided.

Any feedback or suggestions (is there a third option I haven’t thought of?) would be appreciated!


(Michel Conrado (Support Engineer)) #2

Why not instead of using Facets just use Edges?

voteFor: uid @reverse.

X user vote for -> Y user (or other type of node between them)

it’s like a list of friends, but votes.

So every time you can check if user X has already exceeded 100 votes easily.
As the edge will have UIDs, you can query for it and check if has less then 100.
The votes will be unique cuz UIDs are unique, so the user X can’t vote twice.

To know the total votes that the user Y has received, you should query for:

Mutation example

{
	"set": [{
			"uid": "_:Bane",
			"name": "Bane"
		},
		{
			"uid": "_:Lucas",
			"name": "Lucas",
			"voteFor": [{
					"uid": "_:Bane"
				},
				{
					"uid": "_:Bane"
				},
				{
					"uid": "_:Jack"
				},
				{
					"uid": "_:Jack3"
				},
				{
					"uid": "_:Jack4"
				},
				{
					"uid": "_:Jack5"
				}
			]
		}

	]
}
{ 
 resultsForLucas(func: has(user)) @filter(eq(name, "Lucas")) @normalize  {
   voteFor {
   totalVotes : count(uid)
  }
  }
}

#Result
{
  "data": {
    "resultsForLucas": [
      {
        "uid": "0xf",
        "totalVotes": 5
      }
    ]
  }
{ 
 bane(func: has(name)) @filter(eq(name, "Bane")) @normalize {
~voteFor {
   votes : count(uid)
  }
  }
}
# Result
{
  "data": {
    "bane": [
      {
        "uid": "0xe",
        "votes": 1 #votes received
      }
    ]
  }

If you have a range value voting scheme. Like 1 between 5 stars. You could have an Edge for each and then look through all of them and determine if the user has 100 or less open votes.

star.one: uid @reverse .
star.two: uid @reverse .
star.three: uid @reverse .
star.four: uid @reverse .
star.five: uid @reverse .

So the query would be like:


{ 
 userBane(func: has(name)) @filter(eq(name, "Bane")) @normalize {
  
~star.one {
   star.one : count(uid)
  }
  
~star.two {
   star.two : count(uid)
  }
  
~star.three {
   star.three : count(uid)
  }
  
~star.four {
   star.four : count(uid)
  }
  
~star.five {
   star.five : count(uid)
  }

}
}

Also

you could have a kind of “filter” for stars.

{ 
 getUsersWihFiveStars(func: has(~star.five)) {
  uid
  name
  somethingelse
  }
}

(Max) #3

@MichelDiz Thanks for the answer!

Unfortunately, unless I’m misunderstanding your response, I’m not sure this will work for me.

X user should be able to vote for Y user as many times as they want, as long as their total vote count is less than 100. So user X could give 50 votes to user Y, and 50 votes to user Z, but if they tried to vote once more on any user, they wouldn’t be allowed to. As I understand it, you can’t have a repeated predicate between the same two nodes, which is what led me to try and use facets.

Cheers


(Michel Conrado (Support Engineer)) #4

Okay, was a suggestion, but I suspected it would not be useful.

What about this?