Python 2 不再受社区支持。我们建议您将 Python 2 应用迁移到 Python 3

实体、属性 (Property) 和键

注意强烈建议构建新应用的开发者使用 NDB 客户端库,它与 DB 客户端库相比具有多项优势,例如可通过 Memcache API 进行自动实体缓存。如果您当前使用的是较早的 DB 客户端库,请参阅 DB 到 NDB 的迁移指南

Datastore 中的数据对象被称为实体。实体具有一个或多个命名属性,且每个属性可具有一个或多个值。相同种类的实体无需具有相同的属性,且实体的给定属性的值无需都是同一数据类型。(必要时,应用可在其自身的数据模型中设定和实施此类限制)。

Datastore 支持多种属性值数据类型,其中包括:

  • 整数
  • 浮点数
  • 字符串
  • 日期
  • 二进制数据

如需查看类型的完整列表,请参阅属性和值类型

Datastore 中的每个实体都有一个唯一标识它的键。键由以下部分组成:

  • 实体的命名空间,可实现多租户
  • 实体所属的种类,用于对实体进行分类以执行 Datastore 查询
  • 具体实体的标识符,可以是下面任意一种
    • 键名字符串
    • 整数数字 ID
  • (可选)祖先路径,用于在 Datastore 层次结构中确定实体的位置

应用可以使用具体实体的键从 Datastore 中提取该实体,也可以根据实体的键或属性值发出查询以检索一个或多个实体。

Python App Engine SDK 包含一个数据建模库,用于将 Datastore 实体表示为 Python 类的实例,此外还用于在 Datastore 中存储和检索这些实例。

Datastore 本身并不对实体结构施加任何限制,例如限制给定属性采用特定类型的值;该任务由应用和数据建模库执行。

种类和标识符

每个 Datastore 实体均属于特定“种类”,如此可将实体进行分类以便于查询;例如,人力资源应用可以通过种类为 Employee 的实体来表示公司的每位员工。在 Python Datastore API 中,实体的种类由其模型类决定,模型类在应用中定义为数据建模库类 db.Model 的子类。模型类的名称将成为属于其的实体的种类。以两个下划线 (__) 字符开头的所有种类名称均属于保留名称,不得使用。

以下示例创建种类为 Employee 的实体,填充其属性值,并将其保存到 Datastore:

import datetime
from google.appengine.ext import db

class Employee(db.Model):
  first_name = db.StringProperty()
  last_name = db.StringProperty()
  hire_date = db.DateProperty()
  attended_hr_training = db.BooleanProperty()

employee = Employee(first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

Employee 类声明了数据模型的四个属性:first_namelast_namehire_dateattended_hr_trainingModel 父类可确保 Employee 对象的特性 (Attribute) 符合此模型要求:例如,尝试将字符串值分配给 hire_date 特性将导致运行时错误,是因为 hire_date 的数据模型已声明为 db.DateProperty

除了种类,在创建实体时还会为每个实体分配标识符。标识符是实体键的一部分,因此与实体永久关联且不可更改。标识符可通过下述两种方式分配:

  • 应用可为实体指定自己的键名字符串
  • 您可让 Datastore 自动为实体分配一个整数数字 ID。

如需为实体分配键名,请在创建实体时将命名参数 key_name 提供给模型类构造函数:

# Create an entity with the key Employee:'asalieri'.
employee = Employee(key_name='asalieri')

如需让 Datastore 自动分配数字 ID,请忽略 key_name 参数:

# Create an entity with a key such as Employee:8261.
employee = Employee()

分配标识符

Datastore 可配置为使用两种不同的自动 ID 政策生成自动 ID:

  • default 政策会生成大致均匀分布的未使用 ID 的随机序列。每个 ID 最多可包含 16 位十进制数字。
  • legacy 政策会创建一系列不连续的较小整数 ID。

如果希望向用户显示实体 ID 和/或按照顺序显示实体 ID,最好是使用手动分配。

Datastore 生成大致均匀分布的未使用 ID 的随机序列。每个 ID 最多可包含 16 位十进制数字。

系统分配的 ID 值对实体组而言肯定是唯一的。如果您将实体从一个实体组或命名空间复制到另一实体组或命名空间,且希望保留键的 ID 部分,请务必先分配该 ID,防止 Datastore 在后续分配时选择该 ID。

祖先路径

Cloud Datastore 中的实体形成一个与文件系统目录结构类似的层级结构空间。创建实体时,您可选择指定另一实体作为其父实体;新实体是父实体的子实体(请注意,与文件系统不同,无需实际存在父实体)。没有父实体的实体是根实体。实体与其父实体之间的关联是永久的,实体创建后就无法更改。Cloud Datastore 绝不向父实体相同的两个实体分配同一数字 ID,也不分配给两个根实体(即没有父实体的实体)。

实体的父实体、父实体的父实体和以此类推得出的实体都是该实体的祖先实体;而实体的子实体和子实体的子实体等都是它的后代实体。根实体及其所有后代实体都属于同一个实体组。实体序列从根实体开始,接着从父实体到子实体,再指向给定的实体,这就构成了实体的祖先路径。识别实体的完整键由一系列种类/标识符对构成,它们指定实体的祖先路径并以实体自身的种类/标识符对终止。

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

对于根实体,祖先路径为空,且键仅由实体自身的种类和标识符组成:

[Person:GreatGrandpa]

此概念如下图所示:

显示实体组中的根实体与子实体的关系

如需指定实体的父实体,请在创建子实体时将 parent 参数用于模型类构造函数。此参数的值可以是父实体本身或其键;您可以通过调用父实体的 key() 方法来获取该键。以下示例创建了一个种类为 Address 的实体,并显示了将 Employee 实体指定为其父实体的两种方式:

# Create Employee entity
employee = Employee()
employee.put()

# Set Employee as Address entity's parent directly...
address = Address(parent=employee)

# ...or using its key
e_key = employee.key()
address = Address(parent=e_key)

# Save Address entity to datastore
address.put()

事务和实体组

每次尝试创建、更新或删除实体时,这些操作都将在事务上下文中进行。单个事务可以包括任意数量的此类操作。为保持数据的一致性,事务确保其包含的所有操作作为一个单元应用于 Datastore,只要有任何操作失败,则所有操作都不会应用。此外,在同一个事务中执行的所有强一致读取(祖先查询或 get 方法)都遵从一致的数据快照。

如上所述,实体组是一组通过祖先实体连接到共同根元素的实体。将数据整理成实体组可以限制可执行的事务:

  • 一个事务所访问的所有数据都必须包含在最多 25 个实体组中。
  • 如果想在事务内使用查询,必须将数据整理成实体组,如此用户才可以指定与正确数据匹配的祖先过滤器。
  • 单个实体组的写入吞吐量限制为每秒约一个事务。存在此限制是因为 Datastore 会在大范围的地理区域内,对每个实体组执行无主同步复制,以提供高可靠性和容错性。

在许多应用中,可在广泛查看互不相关数据时使用最终一致性(即跨多个实体组进行非祖先查询,有时可能会返回稍过时的数据),然后在查看或修改一组高度相关的数据时,使用强一致性(祖先查询,或使用 get 方法查询单个实体)。在此类应用中,通常适合为每组高度相关的数据使用独立的实体组。如需了解详情,请参阅设计结构以确保高度一致性

属性和值类型

与实体关联的数据值由一个或多个属性构成。每个属性都有一个名称和一个或多个值。一个属性可具有多个类型的值,且两个实体的同一属性可具有不同类型的值。属性可以编入索引,也可以不编入索引(对属性 P 排序或过滤的查询会忽略未将 P 编入索引的实体)。一个实体最多可以有 20000 个编入索引的属性。

支持以下值类型:

值类型 Python 类型 排序顺序 备注
整数 int
long
数字 64 位整数,有符号
浮点数 float 数字 64 位双精度,
IEEE 754
布尔值 bool False<True
短文本字符串 str
unicode
Unicode
(将 str 作为 ASCII 处理)
最多 1500 字节
长文本字符串 db.Text 最多 1 兆字节

未编入索引
短字节字符串 db.ByteString 字节顺序 最多 1500 字节
长字节字符串 db.Blob 最多 1 兆字节

未编入索引
日期和时间 datetime.date
datetime.time
datetime.datetime
时间顺序
地理位置点 db.GeoPt 先按纬度排序,
再按经度排序
邮寄地址 db.PostalAddress Unicode
电话号码 db.PhoneNumber Unicode
电子邮件地址 db.Email Unicode
Google 帐号用户 users.User 按 Unicode 顺序排序的
电子邮件地址
即时消息传输句柄 db.IM Unicode
链接 db.Link Unicode
类别 db.Category Unicode
评分 db.Rating 数字
Datastore 键 db.Key 按路径元素
(种类、标识符、
种类、标识符...)排序
Blobstore 键 blobstore.BlobKey 字节顺序
Null NoneType

重要提示:我们强烈建议您不要存储 UserProperty,因为它包含电子邮件地址和用户的唯一 ID。如果用户更改了其电子邮件地址,当您将其存储的旧 User 与新 User 值进行比较时,它们将无法匹配。

对于文本字符串和未编码的二进制数据(字节字符串),Datastore 支持两种值类型:

  • 短字符串(最多 1500 字节)会编入索引,可以在查询过滤条件和排序顺序中使用。
  • 长字符串(最多 1 兆字节)不编入索引,不能在查询过滤条件和排序顺序中使用。
注意:在 Datastore API 中,长字节字符串类型名为 Blob。此类型与 Blobstore API 中使用的 Blob 无关。

如果查询涉及的属性具有混合类型的值,Datastore 会根据内部表示法确定排序方式:

  1. Null 值
  2. 定点数
    • 整数
    • 日期和时间
    • 评分
  3. 布尔值
  4. 字节序列
    • 字节字符串
    • Unicode 字符串
    • Blobstore 键
  5. 浮点数
  6. 地理位置点
  7. Google 帐号用户
  8. Datastore 键

由于长文本字符串和长字节字符串均不编入索引,因此它们没有已定义的排序方式。

处理实体

应用可以使用 Datastore API 创建、检索、更新和删除实体。如果应用知道完整的实体键(或可从其父键、种类和标识符中提取),则可使用此键直接操作实体。应用还可以通过 Datastore 查询获取实体的键;如需了解详情,请参阅 Datastore 查询页面。

创建实体

在 Python 中,您可以通过如下方式创建一个新实体:设计模型类实例的结构,填充其属性(如有必要),然后调用其 put() 方法以将其保存到 Datastore。您可以通过将 key_name 参数传递给构造函数来指定实体的键名:

employee = Employee(key_name='asalieri',
                    first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

如果您未提供键名,则 Datastore 将自动为该实体的键生成数字 ID:

employee = Employee(first_name='Antonio',
                    last_name='Salieri')

employee.hire_date = datetime.datetime.now().date()
employee.attended_hr_training = True

employee.put()

检索实体

如需检索由给定键标识的实体,请将 Key 对象作为参数传递给 db.get() 函数。您可以使用 Key.from_path() 类方法生成 Key 对象。 完整路径是祖先路径中的一系列实体,每个实体由其种类(字符串)后跟其标识符(键名或数字 ID)来表示:

address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
address = db.get(address_k)

db.get() 会返回相应模型类的实例。确保已导入要检索的实体的模型类。

更新实体

如需更新现有实体,请修改相应对象的属性,然后调用该实体的 put() 方法。该对象数据将覆盖现有实体。每次调用 put() 时,整个对象都会发送到 Datastore。

要删除属性,请从 Python 对象中删除该属性:

del address.postal_code

然后保存该对象。

删除实体

如果您知道实体的键,可以使用 db.delete() 函数删除实体

address_k = db.Key.from_path('Employee', 'asalieri', 'Address', 1)
db.delete(address_k)

也可以调用实体自身的 delete() 方法来将其删除:

employee_k = db.Key.from_path('Employee', 'asalieri')
employee = db.get(employee_k)

# ...

employee.delete()

批量操作

db.put()db.get()db.delete() 函数(及其对应的异步函数 db.put_async()db.get_async()db.delete_async())可接受列表参数,以便在单个 Datastore 调用中对多个实体执行操作:

# A batch put.
db.put([e1, e2, e3])

# A batch get.
entities = db.get([k1, k2, k3])

# A batch delete.
db.delete([k1, k2, k3])

批量操作不会改变费用。无论批量操作中的每个键是否存在,您都要为各个键付费。操作所涉及的实体大小不会影响费用。

批量删除实体

如果您需要删除大量实体,我们建议您使用 Dataflow 批量删除实体

使用空列表

对于 NDB 接口,Datastore 之前会写入一个空列表来作为静态和动态属性的省略属性。为了保持向后兼容性,默认将沿用此处理方式。要全局或按 ListProperty 替换此处理方式,请在 Property 类中将 write_empty_list 参数设置为 true,然后空列表即会被写入 Datastore 并原样读取。

之前,对于 DB 接口,如果属性是动态属性,则根本不允许空列表写入:如果您尝试执行此操作,则会收到错误。这意味着不需要为保持 DB 动态属性的向后兼容性而保留默认处理方式,因此,您只需在动态模型中写入和读取空列表,无需执行任何更改。

不过,对于 DB 静态属性,空列表作为省略属性写入,并且为了保持向后兼容性,将默认沿用此处理方式。如果您需要为 DB 静态属性开启空列表,请在 Property 类中将 write_empty_list 参数设置为 true,然后空列表即会被写入 Datastore 中。