Query Datastore

Una query Datastore recupera entità da Cloud Datastore che soddisfano un insieme specificato di condizioni.

Una query tipica include quanto segue:

  • Un tipo di entità a cui si applica la query
  • Filtri opzionali in base ai valori delle proprietà, alle chiavi e agli antenati delle entità
  • (Facoltativo) Ordina per sequenziare i risultati
Quando viene eseguita, una query recupera tutte le entità del tipo specificato che soddisfano tutti i filtri specificati, ordinate nell'ordine specificato. Le query vengono eseguite in modalità di sola lettura.

Questa pagina descrive la struttura e i tipi di query utilizzati in App Engine per recuperare i dati da Cloud Datastore.

Filtri

I filtri di una query impostano vincoli su proprietà, chiavi e antenati delle entità da recuperare.

Filtri delle proprietà

Un filtro proprietà specifica

  • Un nome di proprietà
  • Un operatore di confronto
  • Un valore della proprietà
Ad esempio:

Filter propertyFilter =
    new FilterPredicate("height", FilterOperator.GREATER_THAN_OR_EQUAL, minHeight);
Query q = new Query("Person").setFilter(propertyFilter);

Il valore della proprietà deve essere fornito dall'applicazione; non può fare riferimento ad altre proprietà o essere calcolato in termini di altre proprietà. Un'entità soddisfa il filtro se ha una proprietà con il nome specificato il cui valore viene confrontato con il valore specificato nel filtro nel modo descritto dall'operatore di confronto.

L'operatore di confronto può essere uno dei seguenti (definiti come costanti enumerate nella classe nidificata Query.FilterOperator):

Operatore Significato
EQUAL Uguale a
LESS_THAN Minore di
LESS_THAN_OR_EQUAL Minore o uguale a
GREATER_THAN Maggiore di
GREATER_THAN_OR_EQUAL Maggiore o uguale a
NOT_EQUAL Diverso da
IN Membro di (uguale a uno qualsiasi dei valori in un elenco specificato)

L'operatore NOT_EQUAL esegue in realtà due query: una in cui tutti gli altri filtri rimangono invariati e il NOT_EQUAL filtro viene sostituito con un LESS_THAN filtro e una in cui viene sostituito con un GREATER_THAN filtro. I risultati vengono poi uniti, in ordine. Una query può avere un solo filtro NOT_EQUAL e una query che ne ha uno non può avere altri filtri di disuguaglianza.

L'operatore IN esegue anche più query: una per ogni elemento dell'elenco specificato, con tutti gli altri filtri invariati e il filtro IN sostituito da un filtro EQUAL. I risultati vengono uniti in base all'ordine degli elementi nell'elenco. Se una query ha più di un filtro IN, viene eseguita come più query, una per ogni combinazione possibile di valori negli elenchi IN.

Una singola query contenente NOT_EQUAL o gli operatori IN è limitata a un massimo di 30 sottoquery.

Per ulteriori informazioni su come le query NOT_EQUAL e IN vengono convertite in più query in un framework JDO/JPA, consulta l'articolo Query con filtri != e IN.

Filtri principali

Per filtrare in base al valore della chiave di un'entità, utilizza la proprietà speciale Entity.KEY_RESERVED_PROPERTY:

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

Sono supportati anche gli ordinamenti crescenti su Entity.KEY_RESERVED_PROPERTY.

Quando si confrontano le chiavi per la disuguaglianza, queste vengono ordinate in base ai seguenti criteri, in ordine:

  1. Percorso antenato
  2. Tipo di entità
  3. Identificatore (nome della chiave o ID numerico)

Gli elementi del percorso predecessore vengono confrontati in modo simile: per tipo (stringa), poi per nome della chiave o ID numerico. I tipi e i nomi delle chiavi sono stringhe e sono ordinati in base al valore dei byte; gli ID numerici sono numeri interi e sono ordinati numericamente. Se le entità con lo stesso elemento principale e tipo utilizzano un mix di stringhe di nomi delle chiavi e ID numerici, quelle con ID numerici precedono quelle con nomi delle chiavi.

Le query sulle chiavi utilizzano gli indici proprio come le query sulle proprietà e richiedono indici personalizzati negli stessi casi, con un paio di eccezioni: i filtri di disuguaglianza o un ordinamento crescente della chiave non richiedono un indice personalizzato, ma un ordinamento decrescente della chiave sì. Come per tutte le query, il server web di sviluppo crea le voci appropriate nel file di configurazione dell'indice quando viene testata una query che richiede un indice personalizzato.

Filtri antenati

Puoi filtrare le query Datastore in base a un antenato specificato, in modo che i risultati restituiti includano solo le entità discendenti da quell'antenato:

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

Tipi di query speciali

Alcuni tipi specifici di query meritano una menzione speciale:

Query senza tipo

Una query senza tipo e senza filtro di antenati recupera tutte le entità di un'applicazione da Datastore. Sono incluse le entità create e gestite da altre funzionalità di App Engine, come le entità statistiche e le entità metadati Blobstore (se presenti). Queste query senza tipo non possono includere filtri o ordinamenti sui valori delle proprietà. Tuttavia, possono filtrare in base alle chiavi delle entità specificando Entity.KEY_RESERVED_PROPERTY come nome della proprietà:

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

Query da predecessore

Una query con un filtro predecessore limita i risultati all'entità specificata e ai relativi discendenti:

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());

Query da predecessore senza tipo

Una query senza tipo che include un filtro per antenati recupererà l'antenato specificato e tutti i suoi discendenti, indipendentemente dal tipo. Questo tipo di query non richiede indici personalizzati. Come tutte le query senza tipo, non può includere filtri o ordinamenti in base ai valori delle proprietà, ma può filtrare in base alla chiave dell'entità:

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

L'esempio seguente mostra come recuperare tutte le entità derivate da un determinato antenato:

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());

Query basate solo su chiavi

Una query basata solo su chiavi restituisce solo le chiavi delle entità risultato anziché le entità stesse, con latenza e costi inferiori rispetto al recupero di entità intere:

Query q = new Query("Person").setKeysOnly();

Spesso è più economico eseguire prima una query solo con le chiavi e poi recuperare un sottoinsieme di entità dai risultati, anziché eseguire una query generale che potrebbe recuperare più entità di quelle di cui hai effettivamente bisogno.

Query di proiezione

A volte, tutto ciò che ti serve dai risultati di una query sono i valori di alcune proprietà specifiche. In questi casi, puoi utilizzare una query di proiezione per recuperare solo le proprietà che ti interessano effettivamente, con latenza e costi inferiori rispetto al recupero dell'intera entità. Per maggiori dettagli, consulta la pagina Query di proiezione.

Ordinare gli ordini

Un ordinamento della query specifica

  • Un nome di proprietà
  • Una direzione di ordinamento (crescente o decrescente)

Ad esempio:

// Order alphabetically by last name:
Query q1 = new Query("Person").addSort("lastName", SortDirection.ASCENDING);

// Order by height, tallest to shortest:
Query q2 = new Query("Person").addSort("height", SortDirection.DESCENDING);

Se una query include più criteri di ordinamento, questi vengono applicati nella sequenza specificata. L'esempio seguente ordina prima in base al cognome in ordine crescente e poi in base all'altezza in ordine decrescente:

Query q =
    new Query("Person")
        .addSort("lastName", SortDirection.ASCENDING)
        .addSort("height", SortDirection.DESCENDING);

Se non vengono specificati ordini di ordinamento, i risultati vengono restituiti nell'ordine in cui vengono recuperati da Datastore.

Nota:a causa del modo in cui Datastore esegue le query, se una query specifica filtri di disuguaglianza su una proprietà e ordini di ordinamento su altre proprietà, la proprietà utilizzata nei filtri di disuguaglianza deve essere ordinata prima delle altre proprietà.

Indici

Ogni query Datastore calcola i risultati utilizzando uno o più indici, che contengono chiavi di entità in una sequenza specificata dalle proprietà dell'indice e, facoltativamente, dai predecessori dell'entità. Gli indici vengono aggiornati in modo incrementale per riflettere le modifiche apportate dall'applicazione alle relative entità, in modo che i risultati corretti di tutte le query siano disponibili senza ulteriori calcoli necessari.

App Engine predefinisce un indice semplice per ogni proprietà di un'entità. Un'applicazione App Engine può definire ulteriori indici personalizzati in un file di configurazione dell'indice denominato datastore-indexes.xml, che viene generato nella directory /war/WEB-INF/appengine-generated dell'applicazione . Il server di sviluppo aggiunge automaticamente suggerimenti a questo file quando rileva query che non possono essere eseguite con gli indici esistenti. Puoi ottimizzare gli indici manualmente modificando il file prima di caricare l'applicazione.

Esempio di interfaccia di query

L'API Java Datastore di basso livello fornisce la classe Query per la creazione di query e l'interfaccia PreparedQuery per il recupero delle entità da Datastore:

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

Nota l'utilizzo di FilterPredicate e CompositeFilter per creare filtri. Se imposti un solo filtro per una query, puoi utilizzare solo FilterPredicate:

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

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

Tuttavia, se vuoi impostare più di un filtro su una query, devi utilizzare CompositeFilter, che richiede almeno due filtri. L'esempio precedente utilizza l'helper per le scorciatoie CompositeFilterOperator.and; l'esempio seguente mostra un modo per creare un filtro OR composito:

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

Passaggi successivi