投影查询

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

大多数 Datastore 查询将返回完整的实体作为其结果,但实际上应用经常仅关注实体的几个属性。借助投影查询,您可以仅在 Datastore 中查询针对某个实体而言您确实需要的特定属性;与检索整个实体相比,这既可以缩短延迟时间,又可以降低费用。

投影查询类似于如下形式的 SQL 查询:

SELECT name, email, phone FROM CUSTOMER

您可以使用所有适用于标准实体查询的过滤和排序功能,但需遵守下述限制。查询会返回删节的结果,其中只有指定的属性(nameemailphone)填充了值;其他所有属性均不含值。

在 Python 2 中使用投影查询

您可以通过以下方式指定投影:QueryGqlQuery 对象均支持投影查询。这两个类都需要以下导入:

from google.appengine.ext import db

您可以通过以下方式指定投影:

proj = db.Query(entity_name, projection=('property_1', 'property_2','property_n'))

proj = db.GqlQuery("SELECT property_1, property_2, property_n FROM entity_name")

您可以像处理标准实体查询一样处理这些查询的结果,例如通过迭代结果。

以下示例查询所有 EventLog 条目的 titleread_pathdate_written 属性(按 date_written 升序排序),并将每个属性的值写入应用日志中:

for proj in db.GqlQuery("SELECT title, read_path, date_written" +
                        "FROM EventLog" +
                        "ORDER BY date_written ASC"):
  logging.info(proj.title)
  logging.info(proj.read_path)
  logging.info(proj.date_written)

分组(实验)

投影查询可以使用 distinct 关键字确保仅在结果集中返回完全唯一的结果。这将仅返回与所投影的属性具有相同值的实体的第一条结果。

query = db.Query(projection=['A', 'B'], distinct=True).filter('B >', 1).order('-B, A')

投影限制

投影查询受限于以下限制:

  • 仅可投影编入索引的属性。

    未编入索引(无论是显式还是隐式)的属性都不支持投影。长文本字符串 (Text) 和长字节字符串 (Blob) 均不编入索引。

  • 同一个属性不能投影多次

  • 相等性 (=) 或成员资格 (IN) 过滤条件内引用的属性无法投影

    例如,

    SELECT A FROM kind WHERE B = 1
    

    是有效的(投影的属性未在相等性过滤条件中使用),

    SELECT A FROM kind WHERE A > 1
    

    (不是相等性过滤条件)也是有效的,但

    SELECT A FROM kind WHERE A = 1
    

    (在相等性过滤条件中使用了投影的属性)则无效。

  • 投影查询返回的结果无法保存回 Datastore。

    由于查询返回的结果仅部分填充,因此无法将其写回到 Datastore。

投影和多值属性

投影具有多个值的属性不会填充该属性的所有值。相反,对于匹配查询的投影值的每个唯一组合,都会返回一个单独的实体。例如,假设您有一个种类为 Foo 的实体,它具有两个多值属性 AB

entity = Foo(A=[1, 1, 2, 3], B=['x', 'y', 'x'])

则以下投影查询

SELECT A, B FROM Foo WHERE A < 3

将返回具有以下值的组合的四个实体:

A = 1B = 'x'
A = 1B = 'y'
A = 2B = 'x'
A = 2B = 'y'

请注意,如果实体具有不带值的多值属性,则相关索引中不会包含任何条目,并且包含该属性的投影查询不会返回该实体对应的结果。

投影索引

投影查询要求投影中指定的所有属性都包含在 Datastore 索引中。App Engine 开发服务器会在索引配置文件 index.yaml 中为您自动生成所需的索引,该文件会随应用一起上传。

如需最大程度减少所需的索引数量,一种方式是始终投影相同的属性,即便并非总是需要所有这些属性。例如,以下查询需要两个单独的索引:

SELECT A, B FROM Kind
SELECT A, B, C FROM Kind

但是,如果您始终投影属性 ABC(即使不需要 C),则只需要一个索引。

如果投影中的属性尚未包含在查询的另一部分中,则将现有查询转换为投影查询可能需要构建新的索引。例如,假设您有如下现有查询

SELECT * FROM Kind WHERE A > 1 ORDER BY A, B

该查询需要以下索引:

Index(Kind, A, B)

将其转换为以下任一投影查询

SELECT C FROM Kind WHERE A > 1 ORDER BY A, B
SELECT A, B, C FROM Kind WHERE A > 1 ORDER BY A, B

会引入一个新属性 (C),因此需要构建一个新索引 Index(Kind, A, B, C)。请注意,投影查询

SELECT A, B FROM Kind WHERE A > 1 ORDER BY A, B

不会更改所需索引,因为投影属性 AB 已包含在现有查询中。