查询游标

查询游标可让应用以便捷的批量方式检索查询结果,因此我们建议使用查询游标,而不是使用整数偏移来实现分页功能。如需详细了解如何为应用设计查询结构,请参阅查询

查询游标

查询游标让应用以便捷的批量方式检索查询结果,而且不会因查询偏移而产生开销。执行检索操作后,应用会获得一个游标,该游标是一个不透明的 base64 编码字符串,用于标记上次检索的结果的索引位置。应用可以保存此字符串(例如,保存到 Datastore、Memcache、任务队列的任务载荷中,或者以 HTTP GETPOST 参数形式嵌入网页中),然后可使用游标作为起点来执行后续检索操作,以从上一次检索结束的位置获取下一批结果。检索还可以指定结束游标,以限制所返回的结果集的范围。

偏移与游标

尽管 Datastore 支持整数偏移,但应该尽量避免使用此类偏移。而应该使用游标。使用偏移只能避免将跳过的实体返回给应用,但这些实体仍会在内部被检索。跳过的实体会影响查询的延迟时间,同时由于检索这些实体需要执行读取操作,您的应用会因此而产生费用。使用游标取代偏移可让您省掉所有这些费用。

查询游标示例

在 Go 中,应用会在检索查询结果之后,调用 Iterator 值的 Cursor 方法来获取一个游标。为了从游标所在的位置检索其他结果,应用会使用相同的实体种类、过滤条件和排序顺序准备一个类似的查询,并在执行检索之前将游标传递给该查询的 Start 方法:

// Create a query for all Person entities.
q := datastore.NewQuery("Person")

// If the application stored a cursor during a previous request, use it.
item, err := memcache.Get(ctx, "person_cursor")
if err == nil {
	cursor, err := datastore.DecodeCursor(string(item.Value))
	if err == nil {
		q = q.Start(cursor)
	}
}

// Iterate over the results.
t := q.Run(ctx)
for {
	var p Person
	_, err := t.Next(&p)
	if err == datastore.Done {
		break
	}
	if err != nil {
		log.Errorf(ctx, "fetching next Person: %v", err)
		break
	}
	// Do something with the Person p
}

// Get updated cursor and store it for next time.
if cursor, err := t.Cursor(); err == nil {
	memcache.Set(ctx, &memcache.Item{
		Key:   "person_cursor",
		Value: []byte(cursor.String()),
	})
}

游标的限制

游标存在如下限制:

  • 游标只能由执行原始查询的同一应用使用,并且只能用于继续执行相同查询。为了在后续检索操作中使用游标,您必须准确地重构原始查询,包括相同的实体种类、祖先实体过滤条件、属性过滤条件以及排序顺序。如果没有设置最初生成游标的同一查询,则无法使用游标检索结果。
  • 对于使用不等性过滤条件或排序顺序来处理多值属性的查询,游标并非总能发挥预期的效果。这种多值属性的去重逻辑不会在多次检索之间持续存在,可能导致多次返回相同结果。
  • 新的 App Engine 版本可能会更改内部实现细节,导致依赖于这些细节的游标失效。如果应用尝试使用不再有效的游标,则 Datastore 会返回错误。

游标和数据更新

游标的位置是指结果列表中上次返回的结果之后的位置。游标不是列表中的相对位置(它不是偏移量),而是 Datastore 在开始对结果进行索引扫描时可以跳转到的标记。如果查询的结果在两次使用游标之间发生了变化,则查询只会注意位于游标之后的结果中发生的变化。如果一个新结果出现在查询的游标位置之前,则在获取游标后的结果时不会返回该结果。同样,如果某个不再是查询结果的实体出现在游标之前,则出现在游标后的结果也不会变化。如果从结果集中移除了上次返回的结果,则游标仍会知道如何定位下一个结果。

检索查询结果时,您可以同时使用开始游标和结束游标,以从 Datastore 返回一组连续的结果。使用开始游标和结束游标检索结果时,结果大小不一定会与生成游标时的大小相同。在生成游标与在查询中使用游标这两个时间点之间,可以在 Datastore 中添加实体或者从中删除实体。

后续步骤