Consultas do Datastore

Nota: os programadores que criam novas aplicações são fortemente aconselhados a usar a biblioteca de cliente NDB, que tem várias vantagens em comparação com esta biblioteca de cliente, como o armazenamento em cache automático de entidades através da API Memcache. Se estiver a usar atualmente a biblioteca cliente DB mais antiga, leia o guia de migração de DB para NDB

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 = Person.all()
q.filter("height <=", max_height)

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
!= Diferente de
IN Membro de (igual a qualquer um dos valores numa lista especificada)

O operador not-equal (!=) executa, na verdade, duas consultas: uma em que todos os outros filtros permanecem inalterados e o filtro not-equal é substituído por um filtro less-than (<) e outra em que é substituído por um filtro greater-than (>) . Em seguida, os resultados são unidos por ordem. Uma consulta não pode ter mais do que um filtro diferente, e uma consulta que tenha um não pode ter outros filtros de desigualdade.

O operador IN também executa várias consultas: uma para cada item na lista especificada, com todos os outros filtros inalterados e o filtro IN substituído por um filtro de igualdade (=). Os resultados são unidos pela ordem dos itens na lista. Se uma consulta tiver mais do que um filtro IN, é realizada como várias consultas, uma para cada combinação possível de valores nas listas IN.

Uma única consulta que contenha os operadores not-equal (!=) ou IN está limitada a um máximo de 30 subconsultas.

Filtros principais

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

q = Person.all()
q.filter('__key__ >', last_seen_key)

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, para que os resultados devolvidos incluam apenas entidades descendentes desse ancestral:

q = Person.all()
q.ancestor(ancestor_key)

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 = db.Query()
q.filter('__key__ >', last_seen_key)

Em Python, cada entidade devolvida pela consulta tem de ter uma classe de modelo correspondente definida para o tipo da entidade. Para definir as classes de modelos para os tipos de entidades de estatísticas, tem de importar o pacote stats:

from google.appengine.ext.db import stats

Se a sua aplicação tiver um valor Blobstore, tem de adicionar o seguinte código para que a API de consultas reconheça o tipo de entidade __BlobInfo__. (A importação da API Blobstore não define esta classe.)

from google.appengine.ext import db

class BlobInfo(db.Expando):
  @classmethod
  def kind(cls):
    return '__BlobInfo__'

Consultas de antepassados

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

tom = Person(key_name='Tom')

wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://domain.com/some/path/to/wedding_photo.jpg'
wedding_photo.put()

baby_photo = Photo(parent=tom)
baby_photo.image_url='http://domain.com/some/path/to/baby_photo.jpg'
baby_photo.put()

dance_photo = Photo(parent=tom)
dance_photo.image_url='http://domain.com/some/path/to/dance_photo.jpg'
dance_photo.put()

camping_photo = Photo()
camping_photo.image_url='http://domain.com/some/path/to/camping_photo.jpg'
camping_photo.put()


photo_query = Photo.all()
photo_query.ancestor(tom)


# This returns wedding_photo, baby_photo, and dance_photo,
# but not camping_photo, because tom is not an ancestor
for photo in photo_query.run(limit=5):
  # Do something with photo

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 = db.Query()
q.ancestor(ancestor_key)
q.filter('__key__ >', last_seen_key)

Para executar uma consulta de antepassados sem tipo usando GQL (na consola de administração do App Engine ou usando a classe GqlQuery), omita a cláusula FROM:

q = db.GqlQuery('SELECT * WHERE ANCESTOR IS :1 AND __key__ > :2',
                ancestor_key,
                last_seen_key)

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

tom = Person(key_name='Tom')

wedding_photo = Photo(parent=tom)
wedding_photo.image_url='http://domain.com/some/path/to/wedding_photo.jpg'
wedding_photo.put()

wedding_video = Video(parent=tom)
wedding_video.video_url='http://domain.com/some/path/to/wedding_video.avi'
wedding_video.put()

# The following query returns both weddingPhoto and weddingVideo,
# even though they are of different entity kinds
media_query = db.query_descendants(tom)
for media in media_query.run(limit=5):
  # Do something with media

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 = Person.all(keys_only=True)

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.

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 Python, a ordem de ordenação descendente é indicada por um hífen (-) antes do nome da propriedade. Se omitir o hífen, a ordem é ascendente por predefinição. Por exemplo:

# Order alphabetically by last name:
q = Person.all()
q.order('last_name')

# Order by height, tallest to shortest:
q = Person.all()
q.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 = Person.all()
q.order('lastName')
q.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 Python Datastore oferece duas classes para preparar e executar consultas:

  • Query usa chamadas de método para preparar a consulta.
  • GqlQuery usa uma linguagem de consulta semelhante a SQL denominada GQL para preparar a consulta a partir de uma string de consulta.
class Person(db.Model):
  first_name = db.StringProperty()
  last_name = db.StringProperty()
  city = db.StringProperty()
  birth_year = db.IntegerProperty()
  height = db.IntegerProperty()


# Query interface constructs a query using instance methods
q = Person.all()
q.filter("last_name =", "Smith")
q.filter("height <=", max_height)
q.order("-height")


# GqlQuery interface constructs a query using a GQL query string
q = db.GqlQuery("SELECT * FROM Person " +
                "WHERE last_name = :1 AND height <= :2 " +
                "ORDER BY height DESC",
                "Smith", max_height)


# Query is not executed until results are accessed
for p in q.run(limit=5):
  print "%s %s, %d inches tall" % (p.first_name, p.last_name, p.height)

O que se segue?