投影查询

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

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

SELECT name, email, phone FROM CUSTOMER

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

在 Go 1.11 中使用投影查询

准备 Query 时,请使用 Project 方法指定投影:

q := datastore.NewQuery("People").Project("FirstName", "LastName")

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

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

q := datastore.NewQuery("EventLog").
	Project("Title", "ReadPath", "DateWritten").
	Order("DateWritten")
t := q.Run(ctx)
for {
	var l EventLog
	_, err := t.Next(&l)
	if err == datastore.Done {
		break
	}
	if err != nil {
		log.Errorf(ctx, "Running query: %v", err)
		break
	}
	log.Infof(ctx, "Log record: %v, %v, %v", l.Title, l.ReadPath, l.DateWritten)
}

分组(实验)

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

q := datastore.NewQuery("Person").
	Project("LastName", "Height").Distinct().
	Filter("Height >", 20).
	Order("-Height").Order("LastName")

投影限制

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

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

    未编入索引(无论是显式还是隐式)的属性都不支持投影。无法将长度超过 1500 个字节的字符串和拥有超过 1500 个元素的字节数组编入索引。

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

  • 在相等性 (=) 过滤条件内引用的属性无法投影。

    例如,

    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: []int{1, 1, 2, 3}, B: []string{"x", "y", "x"}}

则以下投影查询

q := datastore.NewQuery("Foo").Project("A", "B").Filter("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 已包含在现有查询中。