Good data model for finding your favorite favorite stories

Original design

This is how I originally installed my Models :

 class UserData(db.Model): user = db.UserProperty() favorites = db.ListProperty(db.Key) # list of story keys # ... class Story(db.Model): title = db.StringProperty() # ... 

On each page on which the story was shown, I would query UserData for the current user:

 user_data = UserData.all().filter('user =' users.get_current_user()).get() story_is_favorited = (story in user_data.favorites) 

New design

After watching this talk: Google I / O 2009 - Scalable complex applications in App Engine , I wondered if I could configure it more efficiently.

 class FavoriteIndex(db.Model): favorited_by = db.StringListProperty() 

Story Model the same, but I got rid of the UserData Model . Each instance of the new FavoriteIndex Model has a Story instance as a parent. And each FavoriteIndex stores a list of user IDs in it favorited_by .

If I want to find all stories that have been approved by a specific user:

 index_keys = FavoriteIndex.all(keys_only=True).filter('favorited_by =', users.get_current_user().user_id()) story_keys = [k.parent() for k in index_keys] stories = db.get(story_keys) 

This approach avoids serialization / deserialization that are otherwise associated with ListProperty.

Efficiency vs Simplicity

I'm not sure how effective the new design is, especially after the user solves the favorite 300 stories, but here's why I like it:

  • The selected story is related to the user, not user data

  • On the page where I show Story , it's pretty easy to ask Story if he was selected (without calling a separate object filled with user data).

     fav_index = FavoriteIndex.all().ancestor(story).get() fav_of_current_user = users.get_current_user().user_id() in fav_index.favorited_by 
  • It is also easy to get a list of all the users who prefer the story (using the method in # 2)

Is there an easier way?

Please, help. How is this usually done?

+4
source share
4 answers

What you described is a good solution. However, you can optimize it: for each favorite, create a โ€œUserFavoriteโ€ object as a child of the corresponding Story record (or, equivalently, as a child of the UserInfo record), with a key name set by a unique user I AM. This way you can determine if the user prefers a story with a simple get:

 UserFavorite.get_by_name(user_id, parent=a_story) 
Operations

get is 3-5 times faster than queries, so this is a significant improvement.

+2
source

I do not want to solve your real question, but here is a very small tip: you can replace this code:

 if story in user_data.favorites: story_is_favorited = True else: story_is_favorited = False 

with this single line:

 story_is_favorited = (story in user_data.favorites) 

You donโ€™t even need to put parentheses around the story in user_data.favorites if you do not want to; I just find it more readable.

+1
source

You can make your favorite index as a combination of two models

 class FavoriteIndex(db.Model): user = db.UserProperty() story = db.ReferenceProperty() 

or

 class FavoriteIndex(db.Model): user = db.UserProperty() story = db.StringListProperty() 

Then your request by the user returns one FavoriteIndex object for each story that the user prefers

You can also request a story to find out how many users have on its list.

You do not want to scan anything if you do not know that it is limited by small size

+1
source

With your new design, you can search if the user prefers a specific story with a request.
You do not need objects of the UserFavorite class.
This key_only request is not as fast as get (key), but faster than a regular request.
All FavoriteIndex classes have the same key name = 'favs'.
You can filter based on __key__.

 a_story = ...... a_user_id = users.get_current_user().user_id() favIndexKey = db.Key.from_path('Story', a_story.key.id_or_name(), 'FavoriteIndex', 'favs') doesFavStory = FavoriteIndex.all(keys_only=True).filter('__key__ =', favIndexKey).filter('favorited_by =', a_user_id).get() 

If you use multiple favorite files as children of Story, you can use the ancestor filter

 doesFavStory = FavoriteIndex.all(keys_only=True).ancestor(a_story).filter('favorited_by =', a_user_id).get() 
+1
source

All Articles