注意:强烈建议构建新应用的开发者使用 NDB 客户端库,它与 DB 客户端库相比具有多项优势,例如可通过 Memcache API 进行自动实体缓存。如果您当前使用的是较早的 DB 客户端库,请参阅 DB 到 NDB 的迁移指南
大多数 Datastore 查询将返回完整的实体作为其结果,但实际上应用经常仅关注实体的几个属性。借助投影查询,您可以仅在 Datastore 中查询针对某个实体而言您确实需要的特定属性;与检索整个实体相比,这既可以缩短延迟时间,又可以降低费用。
投影查询类似于如下形式的 SQL 查询:
SELECT name, email, phone FROM CUSTOMER
您可以使用所有适用于标准实体查询的过滤和排序功能,但需遵守下述限制。查询会返回删节的结果,其中只有指定的属性(name
、email
和 phone
)填充了值;其他所有属性均不含值。
在 Python 2 中使用投影查询
您可以通过以下方式指定投影:Query 和 GqlQuery 对象均支持投影查询。这两个类都需要以下导入:
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
条目的 title
、read_path
和 date_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
的实体,它具有两个多值属性 A
和 B
:
以下投影查询
SELECT A, B FROM Foo WHERE A < 3
将返回具有以下值的组合的四个实体:
A
= 1
,B
= 'x'
A
= 1
,B
= 'y'
A
= 2
,B
= 'x'
A
= 2
,B
= 'y'
请注意,如果实体具有不带值的多值属性,则相关索引中不会包含任何条目,并且包含该属性的投影查询不会返回该实体对应的结果。
投影索引
投影查询要求投影中指定的所有属性都包含在 Datastore 索引中。App Engine 开发服务器会在索引配置文件 index.yaml
中为您自动生成所需的索引,该文件会随应用一起上传。
如需最大程度减少所需的索引数量,一种方式是始终投影相同的属性,即便并非总是需要所有这些属性。例如,以下查询需要两个单独的索引:
SELECT A, B FROM Kind
SELECT A, B, C FROM Kind
但是,如果您始终投影属性 A
、B
和 C
(即使不需要 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
不会更改所需索引,因为投影属性 A
和 B
已包含在现有查询中。