Query Datastore

Nota: gli sviluppatori che creano nuove applicazioni sono vivamente incoraggiati a utilizzare la libreria client NDB, che offre diversi vantaggi rispetto a questa libreria client, come la memorizzazione nella cache automatica delle entità tramite l'API Memcache. Se attualmente utilizzi la libreria client DB precedente, leggi la Guida alla migrazione dal database a NDB

Una query Datastore recupera le 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 facoltativi basati su valori, chiavi e predecessori delle proprietà delle entità
  • (Facoltativo) Ordinare gli ordini per mettere in sequenza 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 sola lettura.

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

Filtri

I filtri di una query impostano vincoli per le proprietà, le chiavi e i predecessori delle entità da recuperare.

Filtri proprietà

Un filtro di proprietà specifica

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

q = Person.all()
q.filter("height <=", max_height)

Il valore della proprietà deve essere fornito dall'applicazione e non può fare riferimento ad altre proprietà né essere calcolato. Un'entità soddisfa il filtro se ha una proprietà del nome specificato il cui valore si raffronta a quello specificato nel filtro nel modo descritto dall'operatore di confronto.

L'operatore di confronto può essere uno dei seguenti:

Operatore Significato
= Uguale a
< Minore di
<= Minore o uguale a
> Maggiore di
>= Maggiore o uguale a
!= Diverso da
IN Membro di (uguale a uno qualsiasi dei valori in un elenco specificato)

L'operatore non uguale (!=) in realtà esegue due query: una in cui tutti gli altri filtri sono invariati e il filtro non uguale viene sostituito con un filtro minore di (<) e una in cui viene sostituito con un filtro maggiore di (>) . I risultati vengono quindi uniti, in ordine. Una query non può avere più di un filtro non uguale e una query che ne ha uno non può avere altri filtri di disuguaglianza.

L'operatore IN esegue inoltre più query: una per ogni elemento nell'elenco specificato, con tutti gli altri filtri invariati e il filtro IN sostituito con un filtro di uguaglianza (=) . I risultati vengono uniti in ordine di elementi nell'elenco. Se una query ha più di un filtro IN, viene eseguita come più query, una per ogni possibile combinazione di valori negli elenchi IN.

Una singola query contenente operatori operatori non uguali (!=) o IN è limitata a un massimo di 30 sottoquery.

Filtri principali

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

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

Quando si confrontano le disuguaglianze, le chiavi vengono ordinate in base ai seguenti criteri:

  1. Percorso predecessore
  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) e poi per nome chiave o ID numerico. I tipi e i nomi delle chiavi sono stringhe ordinati per valore in byte; gli ID numerici sono numeri interi in ordine numerico. Se le entità con lo stesso tipo e padre utilizzano una combinazione di stringhe di nomi chiave e ID numerici, quelle con ID numerici precedono quelle con i 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 sulla chiave non richiedono un indice personalizzato, a differenza di un ordinamento decrescente della chiave. 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 predecessore

Puoi filtrare le query Datastore in base a un ancestor specificato, in modo che i risultati restituiti includano solo le entità discendenti da tale predecessore:

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

Tipi di query speciali

Alcuni tipi di query specifici meritano una menzione speciale:

Query disponibili

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

q = db.Query()
q.filter('__key__ >', last_seen_key)

In Python, ogni entità restituita dalla query deve avere una classe modello corrispondente definita per il tipo di entità. Per definire le classi dei modelli per i tipi di entità statistiche, devi importare il pacchetto stats:

from google.appengine.ext.db import stats

Se la tua applicazione ha un valore Blobstore, devi aggiungere il codice seguente per fare in modo che l'API Query riconosca il tipo di entità __BlobInfo__. L'importazione dell'API Blobstore non definisce questa classe.

from google.appengine.ext import db

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

Query predecessori

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

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

Query sui predecessori Kindless

Una query senza tipo che include un filtro dei predecessori 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 ordinare ordini in base ai valori delle proprietà, ma può filtrare in base alla chiave dell'entità:

q = db.Query()
q.ancestor(ancestor_key)
q.filter('__key__ >', last_seen_key)

Per eseguire una query sui predecessori kindless utilizzando GQL (nella console di amministrazione di App Engine o tramite la classe GqlQuery), ometti la clausola FROM:

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

L'esempio seguente illustra come recuperare tutte le entità discendenti da un determinato predecessore:

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

Query basate solo su chiavi

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

q = Person.all(keys_only=True)

Spesso è più economico eseguire prima una query basata solo su chiavi e poi recuperare un sottoinsieme di entità dai risultati, anziché eseguire una query generale che potrebbe recuperare più entità di quelle effettivamente necessarie.

Query di proiezione

A volte, l'unica cosa di cui hai bisogno 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 i dettagli, consulta la pagina Query di proiezione.

Ordina gli ordini

Un ordinamento di query specifica

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

In Python, l'ordinamento decrescente è indicato da un trattino (-) che precede il nome della proprietà; l'omissione del trattino specifica l'ordine crescente per impostazione predefinita. Ad esempio:

# 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 una query include più ordinamenti, questi vengono applicati nella sequenza specificata. Nell'esempio seguente, l'ordinamento viene ordinato prima in base al cognome crescente e poi in base all'altezza decrescente:

q = Person.all()
q.order('lastName')
q.order('-height')

Se non vengono specificati ordini di ordinamento, i risultati vengono restituiti nell'ordine in cui sono stati 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 in altre proprietà, la proprietà utilizzata nei filtri di disuguaglianza deve essere ordinata prima delle altre.

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 eventuali modifiche apportate dall'applicazione alle sue entità, in modo che siano disponibili i risultati corretti di tutte le query senza bisogno di ulteriori calcoli.

App Engine predefinisce un indice semplice su ogni proprietà di un'entità. Un'applicazione App Engine può definire ulteriori indici personalizzati in un file di configurazione degli indici denominato index.yaml. 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 Python Datastore fornisce due classi per la preparazione e l'esecuzione delle query:

  • Query utilizza le chiamate al metodo per preparare la query.
  • GqlQuery utilizza un linguaggio di query simile a SQL chiamato GQL per preparare la query a partire da una stringa di query.
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)

Che cosa succede dopo?