投影查询

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

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

SELECT name, email, phone FROM CUSTOMER

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

在 Java 8 中使用投影查询

如需构建投影查询,请使用 addProjection() 方法创建 Query 对象并向其添加属性:

private void addGuestbookProjections(Query query) {
  query.addProjection(new PropertyProjection("content", String.class));
  query.addProjection(new PropertyProjection("date", Date.class));
}

您为每个属性指定的类型必须与首次使用 Entity.setProperty() 定义属性时所用的类型相匹配。以下示例展示了如何通过遍历返回的实体列表并将每个属性值转换为预期类型来处理查询的结果:

private void printGuestbookEntries(DatastoreService datastore, Query query, PrintWriter out) {
  List<Entity> guests = datastore.prepare(query).asList(FetchOptions.Builder.withLimit(5));
  for (Entity guest : guests) {
    String content = (String) guest.getProperty("content");
    Date stamp = (Date) guest.getProperty("date");
    out.printf("Message %s posted on %s.\n", content, stamp.toString());
  }
}

分组(实验)

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

Query q = new Query("TestKind");
q.addProjection(new PropertyProjection("A", String.class));
q.addProjection(new PropertyProjection("B", Long.class));
q.setDistinct(true);
q.setFilter(Query.FilterOperator.LESS_THAN.of("B", 1L));
q.addSort("B", Query.SortDirection.DESCENDING);
q.addSort("A");

投影限制

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

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

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

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

  • 相等性 (EQUAL) 或成员资格 (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 开发服务器会在索引配置文件 datastore-indexes-auto.xml 中为您自动生成所需的索引,该文件会随应用一起上传。

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

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 已包含在现有查询中。