【问题标题】:parent->child relationships in appengine python (bigtable)appengine python(bigtable)中的父->子关系
【发布时间】:2011-07-05 13:42:59
【问题描述】:

我仍在学习有关使用 bigtable/nosql 进行数据建模的课程,希望能得到一些反馈。 如果我经常需要在父母之间处理汇总的孩子,我应该避免在数据建模中使用父->子关系,这样公平吗?

举个例子,假设我正在构建一个博客,该博客将由许多作者贡献,彼此都有帖子,每个帖子都有标签。所以我可能会设置这样的东西:

class Author(db.Model): 
  owner = db.UserProperty()

class Post(db.Model): 
  owner = db.ReferenceProperty(Author, 
    collection_name='posts') 
  tags = db.StringListProperty() 

据我了解,这将基于作者父级创建一个实体组。 如果我主要需要按我希望跨越多个作者的标签查询帖子,这会导致效率低下吗?

我了解对列表属性进行查询可能效率低下。假设每个帖子平均有大约 3 个标签,但可以一直增加到 7 个。我希望我收集的可能标签在几百个以内。 将模型改成这样有什么好处吗?

class Author(db.Model): 
  owner = db.UserProperty()

class Post(db.Model): 
  owner = db.ReferenceProperty(Author, 
    collection_name='posts') 
  tags = db.ListProperty(db.Key)

class Tag(db.Model): 
  name = db.StringProperty() 

或者我做这样的事情会更好吗?

class Author(db.Model): 
  owner = db.UserProperty()

class Post(db.Model): 
  owner = db.ReferenceProperty(Author, 
    collection_name='posts')

class Tag(db.Model): 
  name = db.StringProperty() 

class PostTag(db.Model): 
  post = db.ReferenceProperty(Post, 
    collection_name='posts') 
  tag = db.ReferenceProperty(Tag, 
    collection_name='tags') 

最后一个问题...如果我最常见的用例是通过多个标签查询帖子怎么办。例如,“在 {'apples', 'oranges', 'cucumbers', 'bicycles'} 中查找所有带有标签的帖子” 这些方法之一是否更适合用于查找具有任何集合的帖子的查询标签?

谢谢,我知道那是一口。 :-)

【问题讨论】:

  • 您的示例都没有创建实体组。在第一个示例中,您使用的是 ReferenceProperty,它创建对另一个实体的引用 - 这是可变的,并不意味着所有权。通过为实体的构造函数指定“父”参数来创建父引用 - 有关详细信息,请参阅此页面:code.google.com/appengine/docs/python/datastore/…
  • 啊,谢谢尼克。我错过了那部分......认为它是创建父关系的引用并且缺少您需要将父级传递给构造函数。现在说得通了。

标签: python google-app-engine database-design nosql bigtable


【解决方案1】:

第一种或第二种方法非常适合 App Engine。考虑以下设置:

class Author(db.Model): 
  owner = db.UserProperty()

class Post(db.Model): 
  author = db.ReferenceProperty(Author, 
    collection_name='posts') 
  tags = db.StringListProperty()

class Tag(db.Model): 
  post_count = db.IntegerProperty()

如果使用字符串标签(大小写规范化)作为标签实体key_name,您可以高效地查询具有特定标签的帖子,或列出帖子的标签,或获取标签统计信息:

post = Post(author=some_author, tags=['app-engine', 'google', 'python'])
post_key = post.put()
# call some method to increment post counts...
increment_tag_post_counts(post_key)

# get posts with a given tag:
matching_posts = Post.all().filter('tags =', 'google').fetch(100)
# or, two tags:
matching_posts = Post.all().filter('tags =', 'google').filter('tags =', 'python').fetch(100)

# get tag list from a post:
tag_stats = Tag.get_by_key_name(post.tags)

第三种方法对于大多数基本操作都需要额外的查询或提取,如果要查询多个标签,则更加困难。

【讨论】:

  • 太棒了,谢谢罗伯特。这实际上是我写它的方式。但我还是新手,所以我不确定这是否真的是最好的方法,所以感谢您分享您的经验!
  • @Bob Ralian,要警惕的一件事是爆炸式索引。总体概念是好的;您可能还会发现“关系索引”模式很有用,但由于您的列表非常小并且您需要不需要单独实体的标签。 (google.com/events/io/2009/sessions/…)
【解决方案2】:

我会选择最后一种方法,因为它允许在给定标签的情况下直接检索帖子列表。

第一种方法基本上不可能保留一组规范的标签。换句话说,“系统中当前存在哪些标签”这个问题的答案非常昂贵。

第二种方法解决了这个问题,但正如我所提到的,它并不能帮助您检索给定标签的帖子。

实体组有点神秘,但只要说第一种方法不会创建实体组就足够了,它们只是必要用于事务性数据库操作,有时对优化的数据读取,但在小型应用程序中可能不需要。

应该提到的是,您采用的任何方法都只能与智能缓存策略结合使用才能很好地工作。 GAE 应用程序喜欢缓存。深入了解memcache api,了解memcache和datastore的批量读写操作。

【讨论】:

  • 感谢三联画。我实际上并不担心规范问题,因为我会在保存前的验证期间处理它。 Re:entity 组,文档说“要在组中创建一个实体,您在创建它时声明另一个实体是新实体的父级。”所以我认为这意味着父->子关系将创建一个实体组,如果它在创建时在子级上声明。我了解实体组的意义在于交易。但是它们是否会导致跨实体组的选择延迟/效率低下?可以跨组交易吗?
  • 跨组事务是不可能的,但如果您要跨实体组进行大量选择,这是一个软指示,表明您无论如何都不应该使用它们。此外,如果使用第一种方法,请了解您的验证过程将需要读取 datstore 中每个 Post 模型中的每个标签。
  • 无论如何我都会有一个单独的标签模型。我会将它们保存在内存缓存中。第一种方法实际上并没有与它们绑定,而是使用它们来指示可接受的字符串。它不一定适合我按标签选择的帖子,但更适合读者偏好之类的东西,我只需要拉标签列表。
猜你喜欢
  • 1970-01-01
  • 2016-05-06
  • 1970-01-01
  • 2017-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-03
相关资源
最近更新 更多