批量查询分页?是否可以从数据存储中批量获取并获取游标?
Pagination with batch queries? Is it possible to batch gets from the datastore and get a cursor?
我目前正在从数据存储中请求 20 个条目,return 使用光标将这些条目发送给用户,如果用户要求更多条目,请使用光标作为新的开始并请求接下来的 20 个条目条目。
代码看起来像
q := datastore.NewQuery("Item").
Limit(limit)
if cursor, err := datastore.DecodeCursor(cursor); err == nil {
q = q.Start(cursor)
}
var is []Item
t := q.Run(c)
for {
var i Item
_, err := t.Next(&i)
if err == datastore.Done {
break
}
is = append(is, i)
}
如果重要的话,这里是完整的代码:https://github.com/koffeinsource/kaffeeshare/blob/master/data/appengine.go#L23
使用带 append
的循环看起来是一种反模式,但我看不到在使用 GetMulti
/GetAll
或 am 时获取光标的方法我错过了什么?
我确实希望在用户查询数据存储时添加数据,因此偏移量可能会产生重复的结果。在这种情况下我应该关心批处理获取吗?
您的方法非常好,事实上,这是 AppEngine 上最好的方法。
通过设置开始游标查询后续实体不会给您重复的结果,如果插入了新记录,例如第一个。
为什么?因为游标包含 编码的最后一个 returned 实体 的键,而不是先前 returned 实体的数量。
因此,如果您设置游标,数据存储将开始列出并 returning 游标中编码的键之后的实体。如果在光标之后保存了一个新实体,则该实体将在到达时 returned。
同时使用for
和append()
是最好的方法。您可以通过预先创建足够大的切片来稍微优化它:
var is = make([]Item, 0, limit)
但请注意,我故意使用 0
长度和 limit
容量:不能保证有足够的实体来填充整个切片。
另一个优化是将其分配为 limit
长度:
var is = make([]Item, limit)
当达到datastore.Done
时,如果没有填满则重新分片,例如:
for idx := 0; ; idx++ {
var i Item
_, err := t.Next(&i)
if err == datastore.Done {
if idx < len(is) {
is = is[:idx] // Reslice as it is not filled fully
}
break
}
is[idx] = i
}
批量操作
GetMulti
, PutMulti
and DeleteMulti
are batch versions of the Get
, Put
and Delete
functions. They take a []*Key
instead of a *Key
, and may return an appengine.MultiError
when encountering partial failure.
批处理操作不能替代查询。 GetMulti
例如要求您已经准备好要获取完整实体的所有密钥。因此,这些批处理操作没有游标意义。
批量操作return你所有要求的信息(或做所有要求的操作)。没有 would/could 终止并在以后继续的实体或操作序列。
查询和批处理操作是不同的。您不必担心查询和游标性能。他们做得很好,重要的是,他们(数据存储)的扩展性很好。游标不会减慢查询的执行速度,带游标的查询 运行 与不带游标的查询一样快,而且以前 returned 实体不会影响查询执行时间:它运行 一个没有游标的查询或一个在获得一百万个实体后获得的游标(这只有在多次迭代中才有可能)都没有关系。
我目前正在从数据存储中请求 20 个条目,return 使用光标将这些条目发送给用户,如果用户要求更多条目,请使用光标作为新的开始并请求接下来的 20 个条目条目。
代码看起来像
q := datastore.NewQuery("Item").
Limit(limit)
if cursor, err := datastore.DecodeCursor(cursor); err == nil {
q = q.Start(cursor)
}
var is []Item
t := q.Run(c)
for {
var i Item
_, err := t.Next(&i)
if err == datastore.Done {
break
}
is = append(is, i)
}
如果重要的话,这里是完整的代码:https://github.com/koffeinsource/kaffeeshare/blob/master/data/appengine.go#L23
使用带 append
的循环看起来是一种反模式,但我看不到在使用 GetMulti
/GetAll
或 am 时获取光标的方法我错过了什么?
我确实希望在用户查询数据存储时添加数据,因此偏移量可能会产生重复的结果。在这种情况下我应该关心批处理获取吗?
您的方法非常好,事实上,这是 AppEngine 上最好的方法。
通过设置开始游标查询后续实体不会给您重复的结果,如果插入了新记录,例如第一个。
为什么?因为游标包含 编码的最后一个 returned 实体 的键,而不是先前 returned 实体的数量。
因此,如果您设置游标,数据存储将开始列出并 returning 游标中编码的键之后的实体。如果在光标之后保存了一个新实体,则该实体将在到达时 returned。
同时使用for
和append()
是最好的方法。您可以通过预先创建足够大的切片来稍微优化它:
var is = make([]Item, 0, limit)
但请注意,我故意使用 0
长度和 limit
容量:不能保证有足够的实体来填充整个切片。
另一个优化是将其分配为 limit
长度:
var is = make([]Item, limit)
当达到datastore.Done
时,如果没有填满则重新分片,例如:
for idx := 0; ; idx++ {
var i Item
_, err := t.Next(&i)
if err == datastore.Done {
if idx < len(is) {
is = is[:idx] // Reslice as it is not filled fully
}
break
}
is[idx] = i
}
批量操作
GetMulti
,PutMulti
andDeleteMulti
are batch versions of theGet
,Put
andDelete
functions. They take a[]*Key
instead of a*Key
, and may return anappengine.MultiError
when encountering partial failure.
批处理操作不能替代查询。 GetMulti
例如要求您已经准备好要获取完整实体的所有密钥。因此,这些批处理操作没有游标意义。
批量操作return你所有要求的信息(或做所有要求的操作)。没有 would/could 终止并在以后继续的实体或操作序列。
查询和批处理操作是不同的。您不必担心查询和游标性能。他们做得很好,重要的是,他们(数据存储)的扩展性很好。游标不会减慢查询的执行速度,带游标的查询 运行 与不带游标的查询一样快,而且以前 returned 实体不会影响查询执行时间:它运行 一个没有游标的查询或一个在获得一百万个实体后获得的游标(这只有在多次迭代中才有可能)都没有关系。