我如何使用Google App Engine数据存储区实现这一目标?

jim*_*ssy 3 google-app-engine google-cloud-datastore

我是Datastore的初学者,我想知道如何使用它来实现我想做的事情.

例如,我的应用需要跟踪客户及其所有购买情况.

来自关系数据库,我可以通过创建[Customers]和[Purchases]表来实现这一点.在Datastore中,我可以使[Customers]和[Purchases]种类.

我正在努力的是[购买]类型的结构.

如果我将[Purchases]作为[Customers]类型的子项,那么[Customers]中的一个实体和[Purchases]中的一个实体是否共享相同的密钥?这是否意味着在[Purchases]实体内部,我会为每次购买而持续增加一个属性?

或者,我是否会为他们进行的每次购买都有一个[购买]实体,并且在每个实体中,我会有一个指向[客户]类型的实体的属性?

数据存储在这些场景中的表现如何?

dra*_*onx 5

听起来你并不完全了解祖先.让我们首先使用非祖先版本,这是一种合法的方式:

class Customer(ndb.Model):
    # customer data fields
    name = ndb.StringProperty()

class Purchase(ndb.Model):
    customer = ndb.KeyProperty(kind=Customer)
    # purchase data fields
    price = ndb.IntegerProperty
Run Code Online (Sandbox Code Playgroud)

这是最基本的方法.每个客户在数据存储区中都有一个实体.每次购买时,您将在数据存储区中拥有一个实体,其中一个键属性指向客户.

如果您有购买,并且需要找到相关客户,那就在那里.

purchase_entity.customer.get()
Run Code Online (Sandbox Code Playgroud)

如果您有客户,则可以发出查询以查找属于该客户的所有购买:

Purchase.query(customer=customer_entity.key).fetch()
Run Code Online (Sandbox Code Playgroud)

在这种情况下,无论何时编写客户或购买实体,GAE数据存储区都会将该实体写入云中运行的任何一个不忙的数据存储区计算机.通过这种方式可以获得非常高的写入吞吐量.但是,当您查询给定客户的所有购买时,您只需读回索引中的最新数据.如果添加了新的购买,但索引尚未更新,那么您可能会获得过时的数据(最终的一致性).除非你使用祖先,否则你会遇到这种行为.

现在至于祖先版本.基本概念基本相同.您仍然拥有客户实体,并且每次购买都有单独的实体.购买不是客户实体的一部分.但是,当您使用客户作为祖先创建购买时,(大致)意味着购买存储在存储客户实体的数据存储中的同一台计算机上.在这种情况下,您的写入性能仅限于该计算机的性能,并且通告为每秒一次写入.但是,作为一个好处,您可以使用祖先查询查询该计算机,并获取给定客户的所有购买的最新列表.

使用祖先的语法有点不同.客户部分是相同的.但是,当您创建购买时,您将其创建为:

purchase1 = Purchase(ancestor=customer_entity.key)
purchase2 = Purchase(ancestor=customer_entity.key)
Run Code Online (Sandbox Code Playgroud)

此示例创建两个单独的购买实体.每次购买都有不同的密钥,客户也有自己的密钥.但是,每个购买密钥都会嵌入customer_entity的密钥.因此,您可以将购买密钥视为两倍长.但是,您不再需要为客户保留单独的KeyProperty(),因为您可以在购买密钥中找到它.

class Purchase(ndb.Model):
    # you don't need a KeyProperty for the customer anymore
    # purchase data fields
    price = ndb.IntegerProperty

purchase.key.parent().get()
Run Code Online (Sandbox Code Playgroud)

并且为了查询给定客户的所有购买:

Purchase.query(ancestor=customer_entity.key).fetch()
Run Code Online (Sandbox Code Playgroud)

实体结构的实际变化不大,主要是语法.但祖先的查询是完全一致的.

不推荐您描述的第三个选项.我只是为了完整而包含它.这有点令人困惑,会像这样:

class Purchase(ndb.Model):
    # purchase data fields
    price = ndb.IntegerProperty()

class Customer(ndb.Model):
    purchases = ndb.StructuredProperty(Purchase, repeated=True)
Run Code Online (Sandbox Code Playgroud)

这是一个使用ndb.StructuredProperty的特殊情况.在这种情况下,数据存储区中只有一个Customer实体.虽然有一个购买类,但您购买的商品不会作为单独的实体存储 - 它们只会作为数据存储在Customer实体中.

这可能有几个原因.您只处理一个实体,因此您的数据提取将完全一致.当您必须更新大量购买时,您还可以降低写入成本,因为您只需编写单个实体.您仍然可以查询Purchase类的属性.但是,这仅适用于数量有限或重复的物体,而不是数百或数千.并且每个实体的总大小限制为1MB,因此您最终会达到这个目标,并且您将无法添加更多购买.