Modeling Votes on GAE
I'm trying to determine the most efficient way to create a votable entity on GAE's datastore. I would like to show the user a control to vote for th开发者_C百科is entity or an icon indicating that they have already voted for it; ie, I'm asking "has a user voted on this entity?" Lets say that we have a Question entity that a user may up-vote. Here is what I'm thinking of doing:
- Query for my Question entities. These questions already have a precalculated ranking on which I will sort.
- Use a relation index entity that is a child of the Question entity. Query for all Questions using the same filters as #1 where my user is a member of this relation index entity.
- Merge the results of #2 into #1 by setting a hasVoted property to true for each found set member.
This is the cleanest way I could think of doing it but it still requires two queries. I didn't to create duplicate Question entities for each user to own because it would cause too much data duplication. Is this solution a good way to handle what is effectively a join between a m2m relationship between Votes and Questions or am I thinking too relationally?
Instead of using a relation index, just have a child entity for each user that's voted on the question. Make the key_name of the child entity the ID of the user. Then, to determine if a user y has voted on a question with ID x, simply fetch the key (Question:x/Vote:y). You can batch this to fetch multiple entities for multiple questions or users, too.
I would take a look to Overheard Google App Engine sample application.
Our basic model will be to have Quotes, which contain a string for the quotation, and Votes, which contain the user name and vote for a particular user.
There's a Google article about it and here you can find the sources.
To avoid the second query, you could store all of the questions a user has voted on in a single entity. This entity could be part of the User model, or exist in a one-to-one relationship with User entities.
You could then load this information as needed (and store it to memcache to avoid datastore loads) so that you can quickly check if a user has already voted on a question (without doing a second query most of the time).
If a user may vote on a really large number of questions, then you may have to extend this idea. Here is an outline (not functionally complete) of how you might go about the simple scheme:
class UserVotes(db.Model):
# key = key_name or ID of the corresponding user entity
# if all of your question entities have IDs, then voted_on can be a list of
# integers; otherwise it can be a list of strings (key_name values)
voted_on = db.ListProperty(int, indexed=False)
# in your request handler ...
questions = ...
voted_on = memcache.get('voted-on:%s' % user_id)
if voted_on is None:
voted_on = UserVotes.get_by_id(user_id) # should do get_or_insert() instead
memcache.set(...)
for q in questions:
q.has_voted = q.key().id() in voted_on
精彩评论