构建 NDB 模型以使 "antijoin" 查询成为可能
Structuring NDB models to make an "antijoin" query possible
我想为我的应用程序提供建议 API,用户可以在其中获得对他们 尚未 尚未见过的对象的建议,而我无法弄清楚我应该如何构建我的数据以使这样的查询高效。
这是一个使用书籍的例子。假设这是我的模型,最规范化的方式:
def User(ndb.Model):
name = ndb.StringProperty()
def Book(ndb.Model):
title = ndb.StringProperty()
def Review(ndb.Model):
user = ndb.KeyProperty(User)
book = ndb.KeyProperty(Book)
stars = ndb.IntegerProperty()
text = ndb.TextProperty()
现在给定一个用户,我想检索一本用户没有评论过的书,这似乎基本上不可能高效和大规模地完成(例如 50k 用户,100k 本书)。
我看了一圈,我意识到我应该以某种方式对我的数据进行非规范化,但对于我来说,我想不出一个好的方法来做到这一点。我考虑过将 Review
作为 StructuredProperty
放在 Book
中,但我认为这对我来说意义不大,这意味着我会受到限制我可以添加到一本书中的评论数量(由于条目的大小限制)。
当其他人问类似问题时,我看到的其他事情经常提到的是祖先和 ComputedProperty
,但我也没有真正看到他们在这里如何帮助我。
当然也不是不可能,只是我对最佳实践的理解很薄弱,对吧?
一个有用的反规范化可能是添加到 User
他们评论过的书籍列表:
def User(ndb.Model):
name = ndb.StringProperty()
seen = ndb.KeyProperty('Book', repeated=True)
和 'Book' 整体 "score" 您将要订购的查询:
def Book(ndb.Model):
title = ndb.StringProperty()
score = ndb.IntegerProperty()
像往常一样,当 撰写 时,反规范化的成本就会出现——除了创建新评论之外,您还需要更新 User
和 Book
实体(你可能需要一个事务,因此,实体组,如果多个用户可能同时评论一本书,但我跳过那部分:-)。
优点是,当需要向给定用户推荐一本新书时,您可以查询 Book(仅键,按分数排序),使用游标(或仅在查询上循环)以"page through" 查询的结果,并拒绝那些已经存在于给定用户 seen
属性.
内存中的键
为此目的获取用户实体后,您可以将 seen
变成 set
,因此检查速度会非常快。这假设用户不会评论超过几千本书,所以需要的一切应该很好地适合内存...
我想为我的应用程序提供建议 API,用户可以在其中获得对他们 尚未 尚未见过的对象的建议,而我无法弄清楚我应该如何构建我的数据以使这样的查询高效。
这是一个使用书籍的例子。假设这是我的模型,最规范化的方式:
def User(ndb.Model):
name = ndb.StringProperty()
def Book(ndb.Model):
title = ndb.StringProperty()
def Review(ndb.Model):
user = ndb.KeyProperty(User)
book = ndb.KeyProperty(Book)
stars = ndb.IntegerProperty()
text = ndb.TextProperty()
现在给定一个用户,我想检索一本用户没有评论过的书,这似乎基本上不可能高效和大规模地完成(例如 50k 用户,100k 本书)。
我看了一圈,我意识到我应该以某种方式对我的数据进行非规范化,但对于我来说,我想不出一个好的方法来做到这一点。我考虑过将 Review
作为 StructuredProperty
放在 Book
中,但我认为这对我来说意义不大,这意味着我会受到限制我可以添加到一本书中的评论数量(由于条目的大小限制)。
当其他人问类似问题时,我看到的其他事情经常提到的是祖先和 ComputedProperty
,但我也没有真正看到他们在这里如何帮助我。
当然也不是不可能,只是我对最佳实践的理解很薄弱,对吧?
一个有用的反规范化可能是添加到 User
他们评论过的书籍列表:
def User(ndb.Model):
name = ndb.StringProperty()
seen = ndb.KeyProperty('Book', repeated=True)
和 'Book' 整体 "score" 您将要订购的查询:
def Book(ndb.Model):
title = ndb.StringProperty()
score = ndb.IntegerProperty()
像往常一样,当 撰写 时,反规范化的成本就会出现——除了创建新评论之外,您还需要更新 User
和 Book
实体(你可能需要一个事务,因此,实体组,如果多个用户可能同时评论一本书,但我跳过那部分:-)。
优点是,当需要向给定用户推荐一本新书时,您可以查询 Book(仅键,按分数排序),使用游标(或仅在查询上循环)以"page through" 查询的结果,并拒绝那些已经存在于给定用户 seen
属性.
为此目的获取用户实体后,您可以将 seen
变成 set
,因此检查速度会非常快。这假设用户不会评论超过几千本书,所以需要的一切应该很好地适合内存...