Cursores de consulta

Os cursores de consulta permitem a recuperação, por parte de um aplicativo, dos resultados de uma consulta em lotes convenientes, sendo o uso recomendado em deslocamentos inteiros para paginação. Saiba mais sobre como estruturar consultas para o aplicativo em Consultas.

Cursores de consulta

Os cursores de consulta permitem a recuperação, por parte de um aplicativo, dos resultados de uma consulta em lotes convenientes, evitando a sobrecarga do deslocamento dessa consulta. Após executar uma operação de recuperação, o aplicativo recebe um cursor, que é uma string opaca codificada em base64 que marca a posição do índice do último resultado recuperado. O aplicativo pode salvar essa string, por exemplo, no Datastore, no Memcache, em um payload de tarefa do Task Queue ou incorporada em uma página da Web como parâmetro HTTP GET ou POST. Depois, ele pode usar o cursor como ponto de partida para uma operação de recuperação subsequente. Nesse caso, o lote seguinte de resultados será conseguido a partir do ponto em que a recuperação anterior terminou. É possível que uma recuperação também especifique um cursor de término para limitar a extensão do conjunto de resultados retornados.

Deslocamentos versus cursores

O Datastore é compatível com deslocamentos de números inteiros, mas é melhor evitá-los. Em vez disso, use cursores. O uso de um deslocamento evita apenas o retorno de entidades ignoradas ao aplicativo, mas é possível recuperá-las internamente. Na verdade, as entidades ignoradas afetam a latência da consulta. Além disso, o aplicativo será cobrado pelas operações de leitura necessárias para a recuperação dessas entidades. Use cursores em vez de deslocamentos para evitar todos esses custos.

Exemplo de cursor de consulta

No Go, um aplicativo recebe um cursor depois de recuperar os resultados da consulta chamando o método Cursor do valor Iterator. Para recuperar outros resultados a partir do ponto do cursor, o aplicativo prepara uma consulta semelhante com o mesmo filtro, ordem de classificação e tipo de entidade, além de transmitir o cursor para o método Start da consulta, antes de executar a recuperação:

// 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()),
	})
}

Limitações dos cursores

Os cursores estão sujeitos às seguintes limitações:

  • Um cursor é usado somente pelo mesmo projeto que executou a consulta original e para continuar a mesma consulta. Para usar o cursor em uma operação de recuperação posterior, reconstitua na íntegra a consulta original, incluindo o mesmo tipo de entidade, filtro de ancestrais e de propriedades e ordens de classificação. Não é possível recuperar resultados usando um cursor sem configurar a mesma consulta que o gerou originalmente.
  • Cursores nem sempre funcionam como esperado em uma consulta que usa um filtro de desigualdade ou uma ordem de classificação em uma propriedade com valores múltiplos. A lógica de eliminar a duplicação para essas propriedades não se mantém entre as recuperações. Assim, é possível que o mesmo resultado seja retornado mais de uma vez.
  • Novas versões do App Engine podem alterar os detalhes de implementação internos, invalidando cursores que dependem deles. Se um aplicativo tentar usar um cursor que não é mais válido, o Datastore retornará um erro.

Cursores e atualizações de dados

A posição do cursor é definida como o local na lista de resultados após o último resultado retornado. Um cursor não é uma posição relativa na lista, não é um deslocamento, e sim um marcador para o qual o Datastore pode pular ao iniciar uma varredura de índice em busca de resultados. Caso os resultados de uma consulta sofram alteração entre os usos de um cursor, ela refletirá apenas as alterações ocorridas nos resultados após o cursor. Se na consulta aparecer um novo resultado antes da posição do cursor, ele não será retornado quando forem recuperados os resultados após o cursor. Da mesma forma, caso uma entidade não seja mais o resultado de uma consulta, mesmo tendo aparecido antes do cursor, os resultados após o cursor não serão alterados. Caso o último resultado retornado seja removido do conjunto de resultados, o cursor ainda saberá como localizar o próximo.

Ao recuperar os resultados da consulta, é possível usar um cursor inicial e um cursor final para retornar um grupo contínuo de resultados do Datastore. Usar um cursor de início e de término na recuperação de resultados não garante que o tamanho dos resultados seja o mesmo de quando os cursores foram gerados. As entidades podem ser adicionadas ou excluídas do Datastore entre o momento em que os cursores são gerados e o momento em que são usados em uma consulta.

A seguir