Datastore 查询

Datastore 查询的作用是从 Cloud Datastore 中检索符合一组指定条件的实体

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

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

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

过滤条件

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

属性过滤条件

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

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

q := datastore.NewQuery("Person").Filter("Height <=", maxHeight)

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

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

运算符 含义
= 等于
< 小于
<= 小于或等于
> 大于
>= 大于或等于

键过滤条件

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

q := datastore.NewQuery("Person").Filter("__key__ >", lastSeenKey)

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

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

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

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

祖先过滤条件

您可以将 Datastore 查询的过滤条件设置为特定的祖先实体,使返回的结果仅包含来自该祖先实体的后代实体:

q := datastore.NewQuery("Person").Ancestor(ancestorKey)

特殊查询类型

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

不分类查询

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

q := datastore.NewQuery("").Filter("__key__ >", lastSeenKey)

祖先查询

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

// Create two Photo entities in the datastore with a Person as their ancestor.
tomKey := datastore.NewKey(ctx, "Person", "Tom", 0, nil)

wPhoto := Photo{URL: "http://example.com/some/path/to/wedding_photo.jpg"}
wKey := datastore.NewKey(ctx, "Photo", "", 0, tomKey)
_, err := datastore.Put(ctx, wKey, wPhoto)
// check err

bPhoto := Photo{URL: "http://example.com/some/path/to/baby_photo.jpg"}
bKey := datastore.NewKey(ctx, "Photo", "", 0, tomKey)
_, err = datastore.Put(ctx, bKey, bPhoto)
// check err

// Now fetch all Photos that have tomKey as an ancestor.
// This will populate the photos slice with wPhoto and bPhoto.
q := datastore.NewQuery("Photo").Ancestor(tomKey)
var photos []Photo
_, err = q.GetAll(ctx, &photos)
// check err
// do something with photos

不分类祖先查询

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

q := datastore.NewQuery("").Ancestor(ancestorKey).Filter("__key__ >", lastSeenKey)

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

tomKey := datastore.NewKey(ctx, "Person", "Tom", 0, nil)

weddingPhoto := &Photo{URL: "http://example.com/some/path/to/wedding_photo.jpg"}
_, err := datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Photo", tomKey), weddingPhoto)

weddingVideo := &Video{URL: "http://example.com/some/path/to/wedding_video.avi"}
_, err = datastore.Put(ctx, datastore.NewIncompleteKey(ctx, "Video", tomKey), weddingVideo)

// The following query returns both weddingPhoto and weddingVideo,
// even though they are of different entity kinds.
q := datastore.NewQuery("").Ancestor(tomKey)
t := q.Run(ctx)
for {
	var x interface{}
	_, err := t.Next(&x)
	if err == datastore.Done {
		break
	}
	if err != nil {
		log.Errorf(ctx, "fetching next Photo/Video: %v", err)
		break
	}
	// Do something (e.g. switch on types)
	doSomething(x)
}

仅限于键的查询

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

q := datastore.NewQuery("Person").KeysOnly()

与执行获取的实体数量比所需数量多的常规查询相比,首先执行仅限于键的查询,然后从结果中获取实体的子集往往更为经济。

请注意,仅限于键的查询返回的结果数可能超过 1000,但 GetAll 一次只能检索 1000 个键,如果调用数超出 1000,将会失败并显示错误。因此,我们建议在查询中添加 1000 个键的上限。

投影查询

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

排序顺序

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

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

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

// Order alphabetically by last name:
q := datastore.NewQuery("Person").Order("LastName")

// Order by height, tallest to shortest:
q = datastore.NewQuery("Person").Order("-Height")

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

q := datastore.NewQuery("Person").Order("LastName").Order("-Height")

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

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

索引

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

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

查询接口示例

Go Datastore API 提供用于准备和执行查询的 Query 类型

type Person struct {
	FirstName string
	LastName  string
	City      string
	BirthYear int
	Height    int
}

func handle(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	// The Query type and its methods are used to construct a query.
	q := datastore.NewQuery("Person").
		Filter("LastName =", "Smith").
		Filter("Height <=", maxHeight).
		Order("-Height")

	// To retrieve the results,
	// you must execute the Query using its GetAll or Run methods.
	var people []Person
	if _, err := q.GetAll(ctx, &people); err != nil {
		// Handle error.
	}
	// ...
}

后续步骤