Consultas do Datastore

Uma consulta do Datastore obtém entidades do Cloud Datastore que cumprem um conjunto especificado de condições.

Uma consulta típica inclui o seguinte:

Quando executada, uma consulta obtém todas as entidades do tipo indicado que satisfazem todos os filtros indicados, ordenados pela ordem especificada. As consultas são executadas como só de leitura.

Esta página descreve a estrutura e os tipos de consultas usadas no App Engine para obter dados do Cloud Datastore.

Filtros

Os filtros de uma consulta definem restrições nas propriedades, nas chaves e nos ancestrais das entidades a serem obtidas.

Filtros de propriedades

Um filtro de propriedade especifica

  • Um nome da propriedade
  • Um operador de comparação
  • Um valor da propriedade
Por exemplo:

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

O valor da propriedade tem de ser fornecido pela aplicação. Não pode referir-se nem ser calculado em função de outras propriedades. Uma entidade satisfaz o filtro se tiver uma propriedade do nome indicado cujo valor seja comparado com o valor especificado no filtro da forma descrita pelo operador de comparação.

O operador de comparação pode ser qualquer um dos seguintes:

Operador Significado
= Igual a
< Inferior a
<= Inferior ou igual a
> Superior a
>= Superior ou igual a

Filtros principais

Para filtrar o valor da chave de uma entidade, use a propriedade especial __key__:

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

Quando faz a comparação para verificar a desigualdade, as chaves são ordenadas pelos seguintes critérios, por ordem:

  1. Caminho do antepassado
  2. Tipo de entidade
  3. Identificador (nome da chave ou ID numérico)

Os elementos do caminho de antepassados são comparados de forma semelhante: por tipo (string) e, em seguida, pelo nome da chave ou ID numérico. Os tipos e os nomes das chaves são strings e são ordenados por valor de byte; os IDs numéricos são números inteiros e são ordenados numericamente. Se as entidades com o mesmo elemento principal e tipo usarem uma combinação de strings de nomes de chaves e IDs numéricos, as entidades com IDs numéricos precedem as entidades com nomes de chaves.

As consultas em chaves usam índices tal como as consultas em propriedades e requerem índices personalizados nos mesmos casos, com algumas exceções: os filtros de desigualdade ou uma ordem de ordenação ascendente na chave não requerem um índice personalizado, mas uma ordem de ordenação descendente na chave requer. Tal como acontece com todas as consultas, o servidor Web de desenvolvimento cria entradas adequadas no ficheiro de configuração do índice quando é testada uma consulta que precisa de um índice personalizado.

Filtros de antecessores

Pode filtrar as suas consultas do Datastore para um ancestral especificado, de modo que os resultados devolvidos incluam apenas entidades descendentes desse ancestral:

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

Tipos de consultas especiais

Alguns tipos específicos de consulta merecem uma menção especial:

Consultas sem tipo

Uma consulta sem tipo e sem filtro de antepassados obtém todas as entidades de uma aplicação do Datastore. Isto inclui entidades criadas e geridas por outras funcionalidades do App Engine, como entidades de estatísticas e entidades de metadados do Blobstore (se existirem). Essas consultas sem tipo não podem incluir filtros nem ordens de ordenação em valores de propriedades. No entanto, podem filtrar por chaves de entidades especificando __key__ como o nome da propriedade:

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

Consultas de antepassados

Uma consulta com um filtro de antepassados limita os respetivos resultados à entidade especificada e aos respetivos descendentes:

// 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

Consultas predecessoras sem tipo

Uma consulta sem tipo que inclua um filtro de antepassados vai obter o antepassado especificado e todos os respetivos descendentes, independentemente do tipo. Este tipo de consulta não requer índices personalizados. Tal como todas as consultas sem tipo, não pode incluir filtros nem ordens de ordenação nos valores das propriedades, mas pode filtrar pela chave da entidade:

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

O exemplo seguinte ilustra como obter todas as entidades descendentes de um determinado antepassado:

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)
}

Consultas apenas com chaves

Uma consulta apenas com chaves devolve apenas as chaves das entidades de resultados, em vez das próprias entidades, com uma latência e um custo inferiores aos da obtenção de entidades inteiras:

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

Geralmente, é mais económico fazer primeiro uma consulta apenas de chaves e, em seguida, obter um subconjunto de entidades dos resultados, em vez de executar uma consulta geral que pode obter mais entidades do que as que realmente precisa.

Tenha em atenção que uma consulta apenas com chaves pode devolver mais de 1000 resultados, mas GetAll só pode obter 1000 chaves de cada vez e falha com um erro se for chamada num resultado maior. Por conseguinte, recomendamos que adicione um limite de 1000 chaves à consulta.

Consultas de projeção

Por vezes, tudo o que precisa dos resultados de uma consulta são os valores de algumas propriedades específicas. Nestes casos, pode usar uma consulta de projeção para obter apenas as propriedades nas quais tem realmente interesse, com uma latência e um custo inferiores aos da obtenção da entidade completa. Consulte a página Consultas de projeção para ver detalhes.

Ordene encomendas

Uma ordenação de uma consulta especifica

  • Um nome da propriedade
  • Uma direção de ordenação (ascendente ou descendente)

Em Go, a ordem de ordenação descendente é indicada por um hífen (-) antes do nome da propriedade. Se omitir o hífen, é especificada a ordem ascendente por predefinição. Por exemplo:

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

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

Se uma consulta incluir várias ordens de ordenação, estas são aplicadas na sequência especificada. O exemplo seguinte ordena primeiro por apelido em ordem ascendente e, em seguida, por altura em ordem descendente:

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

Se não forem especificadas ordens de ordenação, os resultados são devolvidos na ordem em que são obtidos a partir do Datastore.

Nota: devido à forma como o Datastore executa as consultas, se uma consulta especificar filtros de desigualdade numa propriedade e ordens de ordenação noutras propriedades, a propriedade usada nos filtros de desigualdade tem de ser ordenada antes das outras propriedades.

Índices

Todas as consultas do Datastore calculam os respetivos resultados através de um ou mais índices,que contêm chaves de entidades numa sequência especificada pelas propriedades do índice e, opcionalmente, os antepassados da entidade. Os índices são atualizados de forma incremental para refletir quaisquer alterações que a aplicação faça às respetivas entidades, de modo que os resultados corretos de todas as consultas estejam disponíveis sem necessidade de cálculos adicionais.

O App Engine predefine um índice simples em cada propriedade de uma entidade. Uma aplicação do App Engine pode definir mais índices personalizados num ficheiro de configuração de índices denominado index.yaml. O servidor de programação adiciona automaticamente sugestões a este ficheiro à medida que encontra consultas que não podem ser executadas com os índices existentes. Pode ajustar os índices manualmente editando o ficheiro antes de carregar a aplicação.

Exemplo de interface de consulta

A API Go Datastore fornece um tipo de consulta para preparar e executar consultas.

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.
	}
	// ...
}

O que se segue?