App Engine Python中的父子关系(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')
最后一个问题……如果我最常用的场景是根据多个标签来查询帖子,比如“找到所有带有标签{'苹果', '橙子', '黄瓜', '自行车'}的帖子”,这几种方法中哪一种更适合用来查询包含某些标签的帖子呢?
谢谢,知道这问题有点多。:-)
2 个回答
我会选择最后一种方法,因为它可以直接根据标签获取帖子列表。
第一种方法基本上让我们无法保持一个标准的标签集合。换句话说,想要回答“系统中当前有哪些标签”这个问题是非常费力的。
第二种方法解决了这个问题,但正如我提到的,它并不能帮助你根据标签来获取帖子。
实体组有点神秘,但简单来说,第一种方法并不会创建实体组,实体组仅在进行事务性数据库操作时是“必要的”,有时在优化数据读取时也有用,但在小型应用中可能并不需要。
需要提到的是,无论你选择哪种方法,都需要配合一个聪明的缓存策略才能发挥好效果。GAE应用非常喜欢缓存。要好好了解memcache的API,并学习如何在memcache和数据存储上进行批量读写操作。
像第一种或第二种方法非常适合用于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()
如果你使用字符串标签(不区分大小写)作为标签实体的名称,你就可以高效地查询带有特定标签的帖子,或者列出某个帖子的标签,甚至获取标签的统计信息:
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)
第三种方法在进行大多数基本操作时需要额外的查询或获取数据,如果你想查询多个标签,这会更加困难。