Google app engine ndb一对多建模:重复键属性与外键的优点

Google app engine ndb一对多建模:重复键属性与外键的优点,google-app-engine,app-engine-ndb,Google App Engine,App Engine Ndb,我的问题是关于ndb中一对多关系的建模。我知道这可以通过(至少)两种不同的方式实现:使用重复属性或“外键”。我在下面创建了一个小示例。基本上,我们有一篇文章可以有任意数量的标签。让我们假设一个标记可以被删除,但在添加后不能更改。我们还假设不担心事务安全性 我的问题是:建模这些关系的首选方法是什么? 我的考虑: 方法(A)要求对添加到 条款(一个用于条款,一个用于标签) (B) 只需要一次写入(仅需标记) 方法(A)利用 获取文章的所有标记时ndb的缓存机制 在方法(B)的情况下,需要一个查询(

我的问题是关于ndb中一对多关系的建模。我知道这可以通过(至少)两种不同的方式实现:使用重复属性或“外键”。我在下面创建了一个小示例。基本上,我们有一篇文章可以有任意数量的标签。让我们假设一个标记可以被删除,但在添加后不能更改。我们还假设不担心事务安全性

我的问题是:建模这些关系的首选方法是什么?

我的考虑:

  • 方法(A)要求对添加到 条款(一个用于条款,一个用于标签) (B) 只需要一次写入(仅需标记)
  • 方法(A)利用 获取文章的所有标记时ndb的缓存机制 在方法(B)的情况下,需要一个查询(另外还有一些查询) 自定义缓存)
这里有我遗漏的东西吗?还有其他需要考虑的因素吗

非常感谢你的帮助

示例(A):

类文章(ndb.Model):
title=ndb.StringProperty()
#更多属性
tags=ndb.KeyProperty(kind=“Tag”,repeated=True)
def创建标签(自我):
#需要两次写入
tag=tag(name=“我的标签”)
tag.put()
self.tags.append(标记)
self.put()
def get_标签(自我):
返回ndb.get_multi(self.tags)
类别标签(ndb.Model):
name=ndb.StringProperty()
user=ndb.KeyProperty(Kind=“user”)#创建标记的用户
#更多属性
示例(B):

类文章(ndb.Model):
title=ndb.StringProperty()
#更多属性
def创建标签(自我):
#需要一次书写
tag=tag(name=“my_tag”,article=self.key)
tag.put()
def get_标签(自我):
#显然,我们可以将这个查询缓存在memcache中
return Tag.gql(“WHERE article:1”,self.key)
类别标签(ndb.Model):
name=ndb.StringProperty()
article=ndb.KeyProperty(种类=“article”)
user=ndb.KeyProperty(Kind=“user”)#创建标记的用户
#更多属性

关于使用
结构化属性
,您看过以下内容了吗。关于
联系人
地址
的简短讨论可能会简化您的问题。再看看。讨论非常简短


另外,展望不允许连接的事实,选项
A
看起来更好。

如前所述,数据存储中没有连接,因此所有“外键”概念都不适用。可以做的是使用Query类查询数据存储中的正确标记

例如,如果您正在使用端点,则:

class Tag(ndb.model):
    user = ndb.UserProperty()
在请求过程中,执行以下操作:

query.filter(Tag.user == endpoints.get_current_user())
在大多数情况下,应首选方法(A)。虽然添加标记需要两次写入,但这可能比读取标记的频率要低得多。只要您没有大量的标记,它们都应该适合重复键属性

正如您所提到的,通过标签的键获取标签要比执行查询快得多。此外,如果您只需要标记的名称和用户,则可以创建标记,将
用户
作为父键,将
名称
作为标记的id:

User -> Name -> Tag
要创建此标记,请使用:

tag = Tag(id=name, parent=user, ...)
article.tags.push(tag)
ndb.put_multi([tag, article])
然后,当您检索标签时

for tag in article.tags:
    user = tag.parent()
    name = tag.id()

然后,存储在
Article.tags
中的每个键都将包含用户键和
标记
名称!这将使您无需阅读
标签
来获取这些值。

考虑使用appstats检查性能,因为这里的特定问题可能有一个特定的答案,但它可能更与您的实际使用情况相关,因此appstats可以告诉您在现实生活中,上述哪些选项更有效。你会为每一篇文章创建新的标签吗,即使它是相同的标签?我会选择选项
A
,因为您可以对每篇文章使用相同的
标记
,并且可以按标记查询
文章。@PaulC谢谢。事实上,我使用appstats进行了检查,在我的情况下,选项B更有效(1写vs 2写)。但是,由于优化规模很小,我不确定是否值得放弃使用文档化的方法(即选项A)来解决一对多关系。@aschmid00是的,我将为每一篇
文章创建一个新的
标记。这在问题中是不清楚的,我将这样修改它。这会改变你的答案吗?谢谢。我还是会去的,但在这一点上,这取决于每篇文章将有多少标签。为什么要为每个文章创建单独的标记,即使它们具有相同的名称?再看看@kasavbere的答案……谢谢你的回答,结构化属性可以完成这项工作,但在我的具体案例中,我认为它们不是最好的解决方案。你所说的“展望不允许加入的事实”是什么意思?这是GAE政策吗?是的,这是数据存储的限制。看见“特别是,数据存储查询引擎不支持联接和聚合查询。”数据存储还有其他一些限制,您可能应该熟悉: