Consultas do Datastore

Uma consulta do Datastore recupera entidades do Cloud Datastore que atendem a um conjunto específico de condições.

Uma consulta típica inclui:

Quando executada, a consulta recupera todas as entidades do tipo determinado que satisfazem aos filtros fornecidos, classificados na ordem especificada. As consultas são executadas no modo de somente leitura.

Nesta página, descrevemos a estrutura e tipos de consultas usadas no App Engine para recuperar dados do Cloud Datastore.

Filtros

O conjunto de filtros de uma consulta restringe as propriedades, chaves e ancestrais das entidades que serão recuperadas.

Filtros de propriedade

Um filtro de propriedade especifica:

  • um nome de propriedade;
  • um operador de comparação;
  • um valor de propriedade.
Por exemplo:

O aplicativo precisa fornecer o valor da propriedade. Ele não pode referir-se a outras propriedades nem ser calculado em relação a elas. Uma entidade satisfaz ao filtro se tiver uma propriedade com o nome determinado com valor igual ao especificado no filtro, da maneira descrita pelo operador de comparação.

O operador de comparação pode ser qualquer um destes (definidos como constantes enumeradas na classe aninhada Query.FilterOperator):

Operador Significado
EQUAL Igual a
LESS_THAN Menor que
LESS_THAN_OR_EQUAL Menor que ou igual a
GREATER_THAN Maior que
GREATER_THAN_OR_EQUAL Maior que ou igual a
NOT_EQUAL Diferente de
IN Membro de (igual a qualquer um dos valores em uma lista especificada)

O operador NOT_EQUAL executa duas consultas: uma em que todos os outros filtros se mantêm inalterados e o filtro NOT_EQUAL é substituído por um LESS_THAN, e uma em que ele é substituído por um filtro GREATER_THAN. Em seguida, os resultados são mesclados, em ordem. Uma consulta não pode ter mais que um filtro NOT_EQUAL, e uma consulta que tenha um filtro de desigualdade não pode ter outros filtros desse tipo.

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 EQUAL. Os resultados são mesclados na ordem dos itens na lista. Se uma consulta tiver mais de um filtro IN, ela será executada como várias consultas, uma para cada combinação possível de valores nas listas IN.

Uma única consulta contendo operadores NOT_EQUAL ou IN é limitada a no máximo 30 subconsultas.

Para saber mais sobre como as consultas NOT_EQUAL e IN são convertidas em várias consultas em um framework JDO/JPA, consulte o artigo Consultas com filtros != e IN.

Filtros de chave

Para filtrar o valor da chave de uma entidade, use a propriedade especial Entity.KEY_RESERVED_PROPERTY:

Java 8

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query("Person").setFilter(keyFilter);

Java 7

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query("Person").setFilter(keyFilter);

As classificações em ordem crescente em Entity.KEY_RESERVED_PROPERTY também são permitidas.

Ao comparar quanto à desigualdade, as chaves são ordenadas pelos seguintes critérios, por ordem:

  1. Caminho do ancestral
  2. Tipo de entidade
  3. identificador (nome da chave ou código numérico)

Os elementos do caminho ancestral são comparados de forma similar: por tipo (string) e depois pelo nome da chave ou código numérico. Tipos e nomes de chaves são strings e são ordenados por valor de byte. Já os códigos numéricos são números inteiros e são ordenados numericamente. Se as entidades com a mesma mãe e o mesmo tipo usarem uma combinação de strings de nomes de chave e códigos numéricos, aquelas com códigos numéricos precedem as com nomes de chave.

As consultas por chaves usam índices da mesma maneira que as consultas por propriedades e exigem índices personalizados nos mesmos casos. Porém, há algumas exceções: filtros de desigualdade ou uma ordem de classificação crescente na chave não exigem um índice personalizado, mas uma ordem de classificação decrescente na chave, sim. Como em todas as consultas, o servidor da Web de desenvolvimento cria as entradas adequadas no arquivo de configuração de índices quando uma consulta que precisa de um índice personalizado é testada.

Filtros ancestrais

É possível filtrar as consultas do Datastore com um ancestral específico para que os resultados retornados incluam apenas entidades descendentes desse ancestral:

Java 8

Query q = new Query("Person").setAncestor(ancestorKey);

Java 7

Query q = new Query("Person").setAncestor(ancestorKey);

Tipos de consulta especiais

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

Consultas sem tipo

Uma consulta sem tipo e sem nenhum filtro ancestral recupera todas as entidades de um aplicativo do Datastore. Isso inclui entidades criadas e gerenciadas por outros recursos do App Engine, como entidades de estatísticas e entidades de metadados do Blobstore (se houver). Essas consultas sem tipo não podem incluir filtros ou ordens de classificação para valores de propriedade. No entanto, é possível filtrar por chaves de entidade ao especificar Entity.KEY_RESERVED_PROPERTY como o nome da propriedade:

Java 8

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query().setFilter(keyFilter);

Java 7

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query().setFilter(keyFilter);

Consultas de ancestral

Uma consulta com um filtro ancestral limita os resultados à entidade especificada e aos seus descendentes:

Java 8

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);

Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/wedding_photo.jpg");

Entity babyPhoto = new Entity("Photo", tomKey);
babyPhoto.setProperty("imageURL", "http://domain.com/some/path/to/baby_photo.jpg");

Entity dancePhoto = new Entity("Photo", tomKey);
dancePhoto.setProperty("imageURL", "http://domain.com/some/path/to/dance_photo.jpg");

Entity campingPhoto = new Entity("Photo");
campingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/camping_photo.jpg");

List<Entity> photoList = Arrays.asList(weddingPhoto, babyPhoto, dancePhoto, campingPhoto);
datastore.put(photoList);

Query photoQuery = new Query("Photo").setAncestor(tomKey);

// This returns weddingPhoto, babyPhoto, and dancePhoto,
// but not campingPhoto, because tom is not an ancestor
List<Entity> results =
    datastore.prepare(photoQuery).asList(FetchOptions.Builder.withDefaults());

Java 7

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);

Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/wedding_photo.jpg");

Entity babyPhoto = new Entity("Photo", tomKey);
babyPhoto.setProperty("imageURL", "http://domain.com/some/path/to/baby_photo.jpg");

Entity dancePhoto = new Entity("Photo", tomKey);
dancePhoto.setProperty("imageURL", "http://domain.com/some/path/to/dance_photo.jpg");

Entity campingPhoto = new Entity("Photo");
campingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/camping_photo.jpg");

List<Entity> photoList = Arrays.asList(weddingPhoto, babyPhoto, dancePhoto, campingPhoto);
datastore.put(photoList);

Query photoQuery = new Query("Photo").setAncestor(tomKey);

// This returns weddingPhoto, babyPhoto, and dancePhoto,
// but not campingPhoto, because tom is not an ancestor
List<Entity> results =
    datastore.prepare(photoQuery).asList(FetchOptions.Builder.withDefaults());

Consultas de ancestral sem tipo

Uma consulta sem tipo que inclui um filtro ancestral recuperará o ancestral especificado e todos os descendentes dele, independentemente do tipo. Esse tipo de consulta não requer índices personalizados. Como todas as consultas sem tipo, não pode incluir filtros ou ordens de classificação em valores de propriedade, mas pode filtrar a chave da entidade:

Java 8

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query().setAncestor(ancestorKey).setFilter(keyFilter);

Java 7

Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, lastSeenKey);
Query q = new Query().setAncestor(ancestorKey).setFilter(keyFilter);

O exemplo a seguir mostra como recuperar todas as entidades descendentes de um determinado ancestral:

Java 8

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);

Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/wedding_photo.jpg");

Entity weddingVideo = new Entity("Video", tomKey);
weddingVideo.setProperty("videoURL", "http://domain.com/some/path/to/wedding_video.avi");

List<Entity> mediaList = Arrays.asList(weddingPhoto, weddingVideo);
datastore.put(mediaList);

// By default, ancestor queries include the specified ancestor itself.
// The following filter excludes the ancestor from the query results.
Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, tomKey);

Query mediaQuery = new Query().setAncestor(tomKey).setFilter(keyFilter);

// Returns both weddingPhoto and weddingVideo,
// even though they are of different entity kinds
List<Entity> results =
    datastore.prepare(mediaQuery).asList(FetchOptions.Builder.withDefaults());

Java 7

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Entity tom = new Entity("Person", "Tom");
Key tomKey = tom.getKey();
datastore.put(tom);

Entity weddingPhoto = new Entity("Photo", tomKey);
weddingPhoto.setProperty("imageURL", "http://domain.com/some/path/to/wedding_photo.jpg");

Entity weddingVideo = new Entity("Video", tomKey);
weddingVideo.setProperty("videoURL", "http://domain.com/some/path/to/wedding_video.avi");

List<Entity> mediaList = Arrays.asList(weddingPhoto, weddingVideo);
datastore.put(mediaList);

// By default, ancestor queries include the specified ancestor itself.
// The following filter excludes the ancestor from the query results.
Filter keyFilter =
    new FilterPredicate(Entity.KEY_RESERVED_PROPERTY, FilterOperator.GREATER_THAN, tomKey);

Query mediaQuery = new Query().setAncestor(tomKey).setFilter(keyFilter);

// Returns both weddingPhoto and weddingVideo,
// even though they are of different entity kinds
List<Entity> results =
    datastore.prepare(mediaQuery).asList(FetchOptions.Builder.withDefaults());

Consultas apenas de chaves

Em uma consulta apenas de chaves, somente as chaves das entidades resultantes são retornadas, em vez das próprias entidades, o que é mais barato e tem menos latência do que recuperar entidades inteiras:

Muitas vezes, é mais econômico fazer uma consulta apenas de chaves primeiro e, em seguida, buscar um subconjunto de entidades nos resultados em vez de executar uma consulta geral, que pode buscar mais entidades do que você realmente precisa.

Consultas de projeção

Às vezes a única coisa de que você precisa dos resultados de uma consulta são os valores de algumas propriedades específicas. Nesses casos, é possível usar uma consulta de projeção para recuperar apenas as propriedades relevantes, com latência e custos menores do que recuperar toda a entidade. Para mais detalhes, acesse a página Consultas de projeção.

Ordens de classificação

A ordem de classificação de uma consulta específica:

  • um nome de propriedade;
  • uma direção de classificação (crescente ou decrescente).

Por exemplo:

Se uma consulta incluir várias ordens de classificação, elas serão aplicadas na sequência especificada. O exemplo a seguir classifica primeiro por sobrenome em ordem crescente e depois por altura em ordem decrescente:

Se nenhuma ordem de classificação for especificada, os resultados serão retornados na ordem em que forem recuperados do Cloud Datastore.

Observação: devido à maneira como o Cloud Datastore executa consultas, se uma consulta especificar filtros de desigualdade em uma propriedade e ordens de classificação em outras propriedades, será preciso ordenar a propriedade usada nos filtros de desigualdade antes das outras propriedades.

Índices

Toda consulta do Datastore calcula os resultados usando um ou mais índices, que contêm chaves de entidade em uma sequência especificada pelas propriedades do índice e, como opção, pelos ancestrais da entidade. Os índices são atualizados de forma incremental para refletir as alterações feitas pelo aplicativo em suas entidades, de modo que os resultados corretos de todas as consultas estejam disponíveis sem necessidade de computação adicional.

O App Engine predefine um índice simples em cada propriedade de uma entidade. Um aplicativo do App Engine pode definir outros índices personalizados em um arquivo de configuração de índice chamado datastore-indexes.xml, que é gerado no diretório /war/WEB-INF/appengine-generated do aplicativo. O servidor de desenvolvimento adiciona sugestões a esse arquivo automaticamente quando encontra consultas não executáveis com os índices atuais. Você pode ajustar os índices manualmente editando o arquivo antes de fazer o upload do aplicativo.

Exemplo de interface de consulta

A API Java Datastore de nível inferior fornece a classe Query para construir consultas e a interface PreparedQuery para recuperar entidades do Datastore:

Java 8

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Filter heightMinFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);

Filter heightMaxFilter =
    new FilterPredicate("height", FilterOperator.LESS_THAN_OR_EQUAL, maxHeight);

// Use CompositeFilter to combine multiple filters
CompositeFilter heightRangeFilter =
    CompositeFilterOperator.and(heightMinFilter, heightMaxFilter);

// Use class Query to assemble a query
Query q = new Query("Person").setFilter(heightRangeFilter);

// Use PreparedQuery interface to retrieve results
PreparedQuery pq = datastore.prepare(q);

for (Entity result : pq.asIterable()) {
  String firstName = (String) result.getProperty("firstName");
  String lastName = (String) result.getProperty("lastName");
  Long height = (Long) result.getProperty("height");

  out.println(firstName + " " + lastName + ", " + height + " inches tall");
}

Java 7

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

Filter heightMinFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);

Filter heightMaxFilter =
    new FilterPredicate("height", FilterOperator.LESS_THAN_OR_EQUAL, maxHeight);

// Use CompositeFilter to combine multiple filters
CompositeFilter heightRangeFilter =
    CompositeFilterOperator.and(heightMinFilter, heightMaxFilter);

// Use class Query to assemble a query
Query q = new Query("Person").setFilter(heightRangeFilter);

// Use PreparedQuery interface to retrieve results
PreparedQuery pq = datastore.prepare(q);

for (Entity result : pq.asIterable()) {
  String firstName = (String) result.getProperty("firstName");
  String lastName = (String) result.getProperty("lastName");
  Long height = (Long) result.getProperty("height");

  out.println(firstName + " " + lastName + ", " + height + " inches tall");
}

Observe o uso de FilterPredicate e CompositeFilter para construir filtros. Se você estiver configurando apenas um filtro em uma consulta, use o FilterPredicate sozinho:

Java 8

Filter heightMinFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);

Query q = new Query("Person").setFilter(heightMinFilter);

Java 7

Filter heightMinFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);

Query q = new Query("Person").setFilter(heightMinFilter);

No entanto, se você quiser definir mais de um filtro em uma consulta, use CompositeFilter, que requer pelo menos dois filtros. O exemplo acima usa o auxiliar de atalho CompositeFilterOperator.and. O exemplo a seguir mostra uma maneira de construir um filtro OR composto:

Java 8

Filter tooShortFilter = new FilterPredicate("height", FilterOperator.LESS_THAN, minHeight);

Filter tooTallFilter = new FilterPredicate("height", FilterOperator.GREATER_THAN, maxHeight);

Filter heightOutOfRangeFilter = CompositeFilterOperator.or(tooShortFilter, tooTallFilter);

Query q = new Query("Person").setFilter(heightOutOfRangeFilter);

Java 7

Filter tooShortFilter = new FilterPredicate("height", FilterOperator.LESS_THAN, minHeight);

Filter tooTallFilter = new FilterPredicate("height", FilterOperator.GREATER_THAN, maxHeight);

Filter heightOutOfRangeFilter = CompositeFilterOperator.or(tooShortFilter, tooTallFilter);

Query q = new Query("Person").setFilter(heightOutOfRangeFilter);

Próximas etapas

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…

Ambiente padrão do App Engine para Java 8