【问题标题】:A good data model for finding a user's favorite stories用于查找用户最喜欢的故事的良好数据模型
【发布时间】:2009-10-13 18:36:46
【问题描述】:

原创设计

这是我最初设置 Models 的方式:

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

class Story(db.Model):
    title = db.StringProperty()
    # ...

在显示故事的每个页面上,我都会查询当前用户的 UserData:

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

新设计

看完这个演讲后:Google I/O 2009 - Scalable, Complex Apps on App Engine,我想知道我是否可以更有效地进行设置。

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

Story Model 是一样的,但我去掉了UserData Model。新FavoriteIndex Model 的每个实例都有一个Story 实例作为父实例。每个FavoriteIndex 都在其favorited_by 属性中存储了一个用户ID 列表。

如果我想查找某个用户收藏的所有故事:

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)

这种方法避免了与 ListProperty 关联的序列化/反序列化。

效率与简单

我不确定新设计的效率如何,尤其是在用户决定收藏 300 个故事之后,但这就是我喜欢它的原因:

  1. 收藏的故事与用户相关联,而不是与她的user data

  2. 在我显示story 的页面上,很容易询问story 是否已被收藏(无需调用填充用户数据的单独实体)。

    fav_index = FavoriteIndex.all().ancestor(story).get()
    fav_of_current_user = users.get_current_user().user_id() in fav_index.favorited_by
    
  3. 也很容易获得所有收藏过故事的用户的列表(使用#2中的方法)

有没有更简单的方法?

请帮忙。这种事情一般是怎么做的?

【问题讨论】:

  • 也许这里有一些我不明白的地方,但根据查询文档 (code.google.com/appengine/docs/python/datastore/…),列表的使用非常有限。因此,如果您想查找某个用户的收藏夹,那么您编写的按“favorited_by”过滤的查询几乎仅限于 30 个用户......我在这里遗漏了什么吗?

标签: python google-app-engine google-cloud-datastore


【解决方案1】:

您所描述的是一个很好的解决方案。但是,您可以进一步优化它:对于每个收藏夹,创建一个“UserFavorite”实体作为相关故事条目的子实体(或等效地,作为 UserInfo 条目的子实体),并将键名设置为用户的唯一ID。这样,您可以通过简单的获取来确定用户是否收藏了某个故事:

UserFavorite.get_by_name(user_id, parent=a_story)

get 操作比查询快 3 到 5 倍,因此这是一个很大的改进。

【讨论】:

  • 非常感谢,这正是我想要的。顺便说一句,它应该是 get_by_key_name
  • 2 个快速问题:(1) 我认为密钥的名称必须是唯一的,但我想您的示例有效,因为只有密钥本身必须是唯一的? (2) 是我的“新设计”(扫描 所有 最喜欢的索引中的用户列表以查找单个用户)真的比仅仅抓住一组键更有效,就像我在“原创设计?
  • 嗯,还有:UserFavorite 模型应该有任何属性吗?
  • 1) 是的,密钥必须是唯一的,而不仅仅是密钥名称,2) 我不确定我是否理解您的问题 - 我没有看到任何“扫描” ,只是查询。 3) UserFavorite 不需要任何属性,除非您想将一些数据(例如创建时间戳)与收藏项关联起来。
  • 1) 它在文档中说键名必须是唯一的,但我只是说如果它与其他相同实体的父级不同,则它不必是唯一的种类。 2)我只是想知道我描述的新方法是否可以扩展到成千上万的用户和故事。看起来数据存储必须做更多的工作才能使用新方法找到用户最喜欢的故事,而不是我设置它的原始方式。非常感谢您的所有回答:)
【解决方案2】:

我不想解决您的实际问题,但这里有一个非常小的提示:您可以替换此代码:

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

单行:

story_is_favorited = (story in user_data.favorites)

如果你不想的话,你甚至不需要在story in user_data.favorites 周围加上括号;我只是认为这更具可读性。

【讨论】:

    【解决方案3】:

    您可以将最喜欢的索引设置为两个模型的连接

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

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

    然后,您对用户的查询会为用户收藏的每个故事返回一个 FavoriteIndex 对象

    您也可以按故事查询,看看有多少用户收藏了它。

    你不想扫描任何东西,除非你知道它被限制在一个小尺寸

    【讨论】:

      【解决方案4】:

      使用您的新设计,您可以通过查询查找用户是否收藏了某个故事。
      您不需要 UserFavorite 类实体。
      这是一个 keys_only 查询,因此不如 get(key) 快,但比普通查询快。
      FavoriteIndex 类都具有相同的 key_name='favs'。
      您可以根据 __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()
      

      如果您使用多个 FavoriteIndex 作为 Story 的子项,则可以使用祖先过滤器

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-08
        • 2014-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-25
        • 1970-01-01
        相关资源
        最近更新 更多