Datastore 查询

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

Datastore 查询会从 Cloud Datastore 中检索满足一组指定条件的实体

典型的查询包括以下内容:

执行时,查询会检索给定种类的实体中满足所有给定过滤条件的所有实体,并按指定顺序排序。查询以只读方式执行。

此页面介绍了 App Engine 中用于从 Cloud Datastore 中检索数据的查询的结构和类型。

过滤条件

查询的过滤条件用于对待检索实体的属性祖先实体设定限制。

属性过滤条件

属性过滤条件指定以下三项:

  • 属性名称
  • 比较运算符
  • 属性值
例如:

q = Person.all()
q.filter("height <=", max_height)

属性值必须由应用提供;不能引用其他属性或者根据其他属性进行计算。如果实体具有给定名称的属性,并且其值与过滤条件中指定值的比较结果与比较运算符所述的方式一致,则该实体满足该过滤条件。

比较运算符可以是以下任何一种:

运算符 含义
= 等于
< 小于
<= 小于或等于
> 大于
>= 大于或等于
!= 不等于
IN 成员(等于指定列表中的任意值)

不等于 (!=) 运算符实际上执行两次查询:一次查询中,所有其他过滤条件都保持不变,不等于过滤条件被替换为小于 (<) 过滤条件;另一次查询中,不等于过滤条件被替换为大于 (>) 过滤条件。所得结果随后按顺序合并。一个查询只能有一个不等于过滤条件,已有一个不等于过滤条件的查询不能有任何其他不等性过滤条件。

IN 运算符也执行多次查询:指定列表中的每一项对应一次查询,所有其他过滤条件保持不变,但 IN 过滤条件被替换为相等性 (=) 过滤条件。结果按列表中内容的顺序合并。如果查询有多个 IN 过滤条件,那么可以作为多个查询执行,每个查询对应于 IN 列表中每种可能的值组合

如果单个查询包含不等于 (!=) 或 IN 运算符,则最多包含 30 个子查询。

键过滤条件

如需将实体键的值作为过滤条件,请使用特殊属性 __key__

q = Person.all()
q.filter('__key__ >', last_seen_key)

在进行不等性比较时,键会依次按照以下标准排序:

  1. 祖先实体路径
  2. 实体种类
  3. 标识符(键名称或数字 ID)

祖先实体路径的元素采用类似的方式进行比较:按种类(字符串),然后按键名称或数字 ID 比较。种类和键名称是字符串,按字节值排序;数字 ID 是整数,按数字顺序排列。如果具有相同父级和种类的实体混合使用键名称字符串和数字 ID,那么使用数字 ID 的实体排在使用键名称的实体前面。

针对键的查询就像针对属性的查询一样使用索引,并且在相同情况下需要自定义索引,但有一些例外:不等性过滤条件或键的升序排序不需要自定义索引,但键的降序排序则需要自定义索引。与所有查询一样,测试需要自定义索引的查询时,开发 Web 服务器会在索引配置文件中创建相应条目。

祖先过滤条件

您可以对 Datastore 查询设置过滤条件以便仅查询指定的祖先实体ancestor,从而使返回的结果仅包含来自该祖先实体的实体:

q = Person.all()
q.ancestor(ancestor_key)

特殊查询类型

应特别注意以下特定类型的查询:

不分类查询

没有种类、没有祖先实体的查询会从 Datastore 中检索某个应用的所有实体。这包括由其他 App Engine 功能创建和管理的实体,例如统计信息实体Blobstore 元数据实体(若有)。这种“不分类查询”不能包含针对属性值的过滤条件或排序顺序。但是,此类查询可以指定 __key__ 作为属性名称对实体键进行过滤:

q = db.Query()
q.filter('__key__ >', last_seen_key)

在 Python 中,查询返回的每个实体都必须具有为实体种类定义的相应模型类。要为统计信息实体类型定义模型类,则必须导入 stats 包:

from google.appengine.ext.db import stats

如果应用具有 Blobstore 值,则必须添加以下代码以使查询 API 识别 __BlobInfo__ 实体种类。(导入 Blobstore API 不会定义此类。)

from google.appengine.ext import db

class BlobInfo(db.Expando):
  @classmethod
  def kind(cls):
    return '__BlobInfo__'

祖先查询

设有祖先过滤条件的查询将其结果限制为指定实体及其后代:

tom = Person(key_name='Tom')

wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://domain.com/some/path/to/wedding_photo.jpg'
wedding_photo.put()

baby_photo = Photo(parent=tom)
baby_photo.image_url='http://domain.com/some/path/to/baby_photo.jpg'
baby_photo.put()

dance_photo = Photo(parent=tom)
dance_photo.image_url='http://domain.com/some/path/to/dance_photo.jpg'
dance_photo.put()

camping_photo = Photo()
camping_photo.image_url='http://domain.com/some/path/to/camping_photo.jpg'
camping_photo.put()


photo_query = Photo.all()
photo_query.ancestor(tom)


# This returns wedding_photo, baby_photo, and dance_photo,
# but not camping_photo, because tom is not an ancestor
for photo in photo_query.run(limit=5):
  # Do something with photo

不分类祖先查询

包含祖先过滤条件的不分类查询将检索指定的祖先及其所有后代,不管其属于哪种类型。此类查询无需自定义索引。跟所有不分类查询一样,其不能包含针对属性值的过滤条件或排序顺序,但可以过滤实体的键:

q = db.Query()
q.ancestor(ancestor_key)
q.filter('__key__ >', last_seen_key)

如需使用 GQL(在 App Engine 管理控制台中或使用 GqlQuery 类)执行不分类祖先查询,请省略 FROM 子句:

q = db.GqlQuery('SELECT * WHERE ANCESTOR IS :1 AND __key__ > :2',
                ancestor_key,
                last_seen_key)

以下示例说明了如何检索来自给定祖先的所有实体:

tom = Person(key_name='Tom')

wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://domain.com/some/path/to/wedding_photo.jpg'
wedding_photo.put()

wedding_video = Video(parent=tom)
wedding_video.video_url='http://domain.com/some/path/to/wedding_video.avi'
wedding_video.put()

# The following query returns both weddingPhoto and weddingVideo,
# even though they are of different entity kinds
media_query = db.query_descendants(tom)
for media in media_query.run(limit=5):
  # Do something with media

仅限于键的查询

“仅限于键的查询”仅返回结果实体的键而非实体本身,与检索整个实体相比,延迟时间更短,费用更低:

q = Person.all(keys_only=True)

执行常规查询获取的实体数量可能超过所需数量,因此首先执行仅限于键的查询,然后从结果中获取实体的子集的做法往往更为经济。

投影查询

有时,您在查询结果中真正需要的只是一些特定属性的值。在这种情况下,您可以使用投影查询仅检索您实际上关注的属性,与检索整个实体相比,这种做法的延迟时间更短、费用更低。如需了解详情,请参阅投影查询页面。

排序顺序

查询“排序顺序”需指定以下两项:

  • 属性名称
  • 排序方向(升序或降序)

在 Python 中,降序排序顺序由属性名称前面加上连字符 (--) 表示;如果省略连字符,则默认指定升序。例如:

# Order alphabetically by last name:
q = Person.all()
q.order('last_name')

# Order by height, tallest to shortest:
q = Person.all()
q.order('-height')

如果查询包含多个排序顺序,则按指定的顺序加以应用。以下示例先按姓升序排序,然后按身高降序排序:

q = Person.all()
q.order('lastName')
q.order('-height')

如果未指定排序顺序,将按照从 Datastore 检索的顺序返回结果。

注意:由于 Datastore 执行查询的方式,如果查询对某个属性指定了不等性过滤条件,而对其他属性指定了排序顺序,则不等性过滤条件中使用的属性必须先于其他属性进行排序。

索引

每个 Datastore 查询使用一个或多个索引来计算其结果,索引包含索引属性指定的序列中的实体键,还可以选择包含实体的祖先实体。索引会逐渐更新以体现应用对其实体所做的更改,从而保证无需进一步计算即可提供所有查询的正确结果。

App Engine 会为实体的每个属性预定义简单索引。App Engine 应用可以在名为 index.yaml索引配置文件中定义更多的自定义索引。开发服务器遇到无法使用现有索引执行的查询时,会自动向此文件添加建议。 在上传应用之前,可以通过编辑该文件来手动优化索引。

查询接口示例

Python Datastore API 提供了两个用于准备和执行查询的类:

  • Query 使用方法调用准备查询。
  • GqlQuery 使用名为 GQL 的查询语言(与 SQL 类似)根据查询字符串准备查询。
class Person(db.Model):
  first_name = db.StringProperty()
  last_name = db.StringProperty()
  city = db.StringProperty()
  birth_year = db.IntegerProperty()
  height = db.IntegerProperty()


# Query interface constructs a query using instance methods
q = Person.all()
q.filter("last_name =", "Smith")
q.filter("height <=", max_height)
q.order("-height")


# GqlQuery interface constructs a query using a GQL query string
q = db.GqlQuery("SELECT * FROM Person " +
                "WHERE last_name = :1 AND height <= :2 " +
                "ORDER BY height DESC",
                "Smith", max_height)


# Query is not executed until results are accessed
for p in q.run(limit=5):
  print "%s %s, %d inches tall" % (p.first_name, p.last_name, p.height)

后续步骤