Query NDB

Un'applicazione può utilizzare query per cercare nel Datastore entità corrispondono a criteri di ricerca specifici chiamati filtri.

Panoramica

Un'applicazione può utilizzare query per cercare nel Datastore entità corrispondono a criteri di ricerca specifici chiamati filtri. Ad esempio: un'applicazione che tiene traccia di diversi guestbook potrebbe usa una query per recuperare i messaggi da un guestbook, ordinati per data:

from google.appengine.ext import ndb
...
class Greeting(ndb.Model):
    """Models an individual Guestbook entry with content and date."""
    content = ndb.StringProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)

    @classmethod
    def query_book(cls, ancestor_key):
        return cls.query(ancestor=ancestor_key).order(-cls.date)
...
class MainPage(webapp2.RequestHandler):
    GREETINGS_PER_PAGE = 20

    def get(self):
        guestbook_name = self.request.get('guestbook_name')
        ancestor_key = ndb.Key('Book', guestbook_name or '*notitle*')
        greetings = Greeting.query_book(ancestor_key).fetch(
            self.GREETINGS_PER_PAGE)

        self.response.out.write('<html><body>')

        for greeting in greetings:
            self.response.out.write(
                '<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

        self.response.out.write('</body></html>')

Alcune query sono più complesse di altre; il datastore ha bisogno che si riferiscono a questi indici. Questi indici predefiniti vengono specificati in un file di configurazione, index.yaml. Sul server di sviluppo, se esegui una query che richiede un indice che non hai specificato, il server di sviluppo lo aggiunge automaticamente è index.yaml. Tuttavia, nel tuo sito web, una query che richiede un indice non ancora specificato ha esito negativo. Di conseguenza, il tipico ciclo di sviluppo consiste nel provare una nuova query server web e aggiorna il sito web in modo che utilizzi il file index.yaml. Puoi aggiornare index.yaml separatamente dal caricamento eseguendo gcloud app deploy index.yaml. Se il datastore ha molte entità, ci vuole molto tempo è ora di creare un nuovo indice. in questo caso è opportuno aggiornare le definizioni dell'indice prima di caricare il codice che utilizza il nuovo indice. Puoi utilizzare la console di amministrazione per sapere quando gli indici hanno finito di costruire.

Il datastore di App Engine supporta in modo nativo i filtri per corrispondenze esatte (l'operatore ==) e confronti (gli operatori <, <=, > e >=). Supporta la combinazione di più filtri utilizzando Operazione AND, con alcune limitazioni (vedi di seguito).

Oltre agli operatori nativi, l'API supporta l'operatore !=, combinando gruppi di filtri usando l'operazione booleana OR, e l'operazione IN, che verificano l'uguaglianza con uno di un elenco di valori possibili (come l'operatore 'in' di Python). Queste operazioni non mappano 1:1 al operazioni native; quindi sono un po' stravagante e lento, relativamente. Vengono implementate mediante l'unione in memoria i flussi di risultati. Tieni presente che p != v è implementato come "p < v OR p > v". (Questo è importante per proprietà ripetute).

Limitazioni: Datastore applica alcune limitazioni su query. La violazione di tali disposizioni comporterà l'istituzione di un'eccezione. Ad esempio, combinare troppi filtri, utilizzare disuguaglianze per più proprietà o la combinazione di una disuguaglianza con un ordinamento in una proprietà diverse al momento non sono consentite. Filtri che fanno riferimento anche a volte più proprietà richiedono la configurazione di indici secondari.

Non supportato: Datastore non supporta direttamente corrispondenze di sottostringhe, corrispondenze senza distinzione tra maiuscole e minuscole o la cosiddetta ricerca a testo intero. Esistono vari modi per implementare corrispondenze senza distinzione tra maiuscole e minuscole e persino utilizzando le proprietà calcolate.

Filtro per valori proprietà

Ricorda il corso Account di Proprietà NDB:

class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

In genere non è consigliabile recuperare tutte le entità di un determinato tipo. vuoi solo quelli con un valore o un intervallo di valori specifico proprietà.

Gli oggetti proprietà sovraccaricano alcuni operatori per restituire espressioni di filtro che possono essere usate per controllare una query, ad esempio per trovare tutte le entità la cui proprietà userid ha il valore esatto 42, puoi utilizzare l'espressione

query = Account.query(Account.userid == 42)

Se sei sicuro che ci sia stato un solo Account con quel userid, potresti preferire usare userid come chiave. Account.get_by_id(...) è più veloce di Account.query(...).get().

NDB supporta le seguenti operazioni:

property == value
property < value
property <= value
property > value
property >= value
property != value
property.IN([value1, value2])

Per filtrare la disuguaglianza, puoi utilizzare una sintassi simile alla seguente:

query = Account.query(Account.userid >= 40)

Vengono trovate tutte le entità Account la cui proprietà userid è maggiore o uguale a 40.

Due di queste operazioni, != e IN, sono implementate come combinazioni delle altre e sono stravagante come descritto in != e IN.

Puoi specificare più filtri:

query = Account.query(Account.userid >= 40, Account.userid < 50)

Questa operazione combina gli argomenti del filtro specificati, restituendo tutti gli account entità il cui valore ID utente è maggiore o uguale a 40 e inferiore a 50.

Nota: Come accennato in precedenza, Datastore rifiuta le query che usano la disuguaglianza filtrando in base a più di una proprietà.

Invece di specificare un intero filtro di query in una singola espressione, potrebbe essere più pratico svilupparlo in passaggi, ad esempio:

query1 = Account.query()  # Retrieve all Account entitites
query2 = query1.filter(Account.userid >= 40)  # Filter on userid >= 40
query3 = query2.filter(Account.userid < 50)  # Filter on userid < 50 too

query3 equivale alla variabile query del dell'esempio precedente. Nota che gli oggetti query sono immutabili, quindi creazione di query2 non influisce su query1 e la costruzione di query3 non influisce query1 oppure query2.

Le operazioni != e IN

Ricorda la classe Articolo di Proprietà NDB:

class Article(ndb.Model):
    title = ndb.StringProperty()
    stars = ndb.IntegerProperty()
    tags = ndb.StringProperty(repeated=True)

!= (non uguale) e IN (abbonamento) vengono implementate combinando altri filtri che utilizzano Operazione OR. Il primo è

property != value

viene implementato

(property < value) OR (property > value)

Ad esempio,

query = Article.query(Article.tags != 'perl')

equivale a

query = Article.query(ndb.OR(Article.tags < 'perl',
                             Article.tags > 'perl'))

Nota: Forse questa query non cerca Article entità che non includono "perl" come tag. Piuttosto, trova tutte le entità con almeno un tag diverso da "perl". Ad esempio, nei risultati verrebbe inclusa la seguente entità: anche se ha "perl" come uno dei suoi tag:

Article(title='Perl + Python = Parrot',
        stars=5,
        tags=['python', 'perl'])

Tuttavia, questo non verrebbe incluso:

Article(title='Introduction to Perl',
        stars=3,
        tags=['perl'])

Non è possibile eseguire query per entità che non includi un tag uguale a "perl".

Analogamente, l'operazione IN

property IN [value1, value2, ...]

che verifica l'appartenenza a un elenco di valori possibili, viene implementata come

(property == value1) OR (property == value2) OR ...

Ad esempio,

query = Article.query(Article.tags.IN(['python', 'ruby', 'php']))

equivale a

query = Article.query(ndb.OR(Article.tags == 'python',
                             Article.tags == 'ruby',
                             Article.tags == 'php'))

Nota: Query che utilizzano OR deduplicare i risultati: il flusso dei risultati includi l'entità più di una volta, anche se un'entità corrisponde a due o più sottoquery.

Esecuzione di query su proprietà ripetute

Anche la classe Article definita nella sezione precedente è un esempio di query per proprietà ripetute. In particolare, un filtro Mi piace

Article.tags == 'python'

utilizza un singolo valore, anche se Article.tags è un proprietà ripetuta. Non puoi confrontare le proprietà ripetute con l'elenco oggetti (non saranno compresi da Datastore) e un filtro come

Article.tags.IN(['python', 'ruby', 'php'])

fa qualcosa di completamente diverso dalla ricerca Article entità il cui valore dei tag è l'elenco ['python', 'ruby', 'php']: cerca le entità il cui valore tags (considerato come un elenco) contiene almeno uno di questi valori.

La query per un valore None su una proprietà ripetuta ha comportamento indefinito; non farlo.

Combinazione delle operazioni AND e OR

Puoi nidificare le operazioni AND e OR in modo arbitrario. Ad esempio:

query = Article.query(ndb.AND(Article.tags == 'python',
                              ndb.OR(Article.tags.IN(['ruby', 'jruby']),
                                     ndb.AND(Article.tags == 'php',
                                             Article.tags != 'perl'))))

Tuttavia, a causa dell'implementazione di OR, una query di questo modulo è troppo complesso potrebbe non riuscire con un'eccezione. Sei più sicuro se normalizzi questi filtri in modo che sia presente (al massimo) una singola operazione OR all'inizio l'albero delle espressioni e un singolo livello di operazioni AND sotto il pod.

Per eseguire questa normalizzazione, devi ricordare entrambe le regole della logica booleana, e come vengono effettivamente implementati i filtri != e IN:

  1. Espandere gli operatori != e IN alla loro forma primitiva, dove != diventa un controllo per la proprietà < o > del valore e IN diventa un controllo per verificare che la proprietà sia == rispetto al primo valore o al secondo oppure...fino all'ultimo valore dell'elenco.
  2. Un AND con OR all'interno equivale a un OR di più AND applicati agli operandi originali AND, con un il singolo operando OR ha sostituito l'operando OR originale. Ad esempio: AND(a, b, OR(c, d)) equivale a OR(AND(a, b, c), AND(a, b, d))
  3. Un AND con un operando che è a sua volta un'operazione AND può incorporare gli operandi dell'elemento AND nidificato nell'oggetto AND. Ad esempio, AND(a, b, AND(c, d)) equivale a AND(a, b, c, d)
  4. Un OR con un operando che è a sua volta un'operazione OR può incorpora gli operandi dell'elemento OR nidificato nell'oggetto OR che lo contiene. Ad esempio, OR(a, b, OR(c, d)) equivale a OR(a, b, c, d)

Se applichiamo queste trasformazioni in fasi al filtro di esempio, usando una notazione più semplice rispetto a Python, ottieni:

  1. Utilizzo della regola n. 1 sugli operatori IN e !=:
    AND(tags == 'python',
      OR(tags == 'ruby',
         tags == 'jruby',
         AND(tags == 'php',
             OR(tags < 'perl', tags > 'perl'))))
  2. Utilizzo della regola n. 2 sul componente OR più interno nidificato in un AND:
    AND(tags == 'python',
      OR(tags == 'ruby',
         tags == 'jruby',
         OR(AND(tags == 'php', tags < 'perl'),
            AND(tags == 'php', tags > 'perl'))))
  3. Utilizzo della regola n. 4 su OR nidificata all'interno di un altro OR:
    AND(tags == 'python',
      OR(tags == 'ruby',
         tags == 'jruby',
         AND(tags == 'php', tags < 'perl'),
         AND(tags == 'php', tags > 'perl')))
  4. Utilizzo della regola 2 sui restanti OR nidificati all'interno di un AND:
    OR(AND(tags == 'python', tags == 'ruby'),
       AND(tags == 'python', tags == 'jruby'),
       AND(tags == 'python', AND(tags == 'php', tags < 'perl')),
       AND(tags == 'python', AND(tags == 'php', tags > 'perl')))
  5. Utilizzo della regola 3 per comprimere i AND nidificati rimanenti:
    OR(AND(tags == 'python', tags == 'ruby'),
       AND(tags == 'python', tags == 'jruby'),
       AND(tags == 'python', tags == 'php', tags < 'perl'),
       AND(tags == 'python', tags == 'php', tags > 'perl'))

Attenzione: Per alcuni filtri, questa normalizzazione può causare una funzione un'esplosione. Valuta AND di 3 OR con 2 clausole di base ciascuna. Se normalizzato, diventa un OR di 8 AND clausole con 3 clausole base ciascuna: 6 termini diventano 24.

Specificare gli ordini

Puoi utilizzare il metodo order() per specificare l'ordine in in cui una query restituisce i risultati. Questo metodo prende un elenco argomenti, ognuno dei quali è un oggetto di proprietà (da ordinare in ordine crescente) o la sua negazione (che indica l'ordine decrescente). Ad esempio:

query = Greeting.query().order(Greeting.content, -Greeting.date)

Vengono recuperate tutte le entità Greeting, ordinate per valore crescente della loro proprietà content. Le esecuzioni di entità consecutive con la stessa proprietà di contenuto saranno ordinate in base al valore decrescente della proprietà date. Puoi utilizzare più chiamate order() per ottenere lo stesso effetto:

query = Greeting.query().order(Greeting.content).order(-Greeting.date)

Nota:quando combini i filtri con order(), Datastore rifiuta determinate combinazioni. In particolare, quando utilizzi un filtro di disuguaglianza, il primo ordinamento (se presente) devono specificare la stessa proprietà del filtro. Inoltre, a volte è necessario configurare un indice secondario.

Query predecessore

Le query predecessore consentono di eseguire query a elevata coerenza nel datastore, tuttavia le entità con lo stesso predecessore sono limitate a 1 scrittura al secondo. Ecco un semplice confronto tra i compromessi struttura tra una query predecessore e non predecessore utilizzando i clienti e i relativi acquisti nel datastore.

Nel seguente esempio non predecessore, esiste un'entità nel datastore per ogni Customer, e un'entità nel datastore per ogni Purchase, con un valore KeyProperty al cliente.

class Customer(ndb.Model):
    name = ndb.StringProperty()

class Purchase(ndb.Model):
    customer = ndb.KeyProperty(kind=Customer)
    price = ndb.IntegerProperty()

Per trovare tutti gli acquisti appartenenti al cliente, puoi utilizzare la seguente query:

purchases = Purchase.query(
    Purchase.customer == customer_entity.key).fetch()

In questo caso, il datastore offre una velocità effettiva di scrittura elevata, ma solo coerenza finale. Se è stato aggiunto un nuovo acquisto, potresti ricevere dati inattivi. Puoi eliminare questo comportamento utilizzando le query dei predecessori.

Per i clienti e gli acquisti con query predecessore, hai ancora la stessa struttura con due entità separate. La parte dedicata al cliente è la stessa. Tuttavia, quando crei acquisti, non dovrai più specificare il KeyProperty() per gli acquisti. Questo è perché quando utilizzi le query dei predecessori, chiami la chiave dell'entità cliente quando un'entità di acquisto.

class Customer(ndb.Model):
    name = ndb.StringProperty()

class Purchase(ndb.Model):
    price = ndb.IntegerProperty()

Ogni acquisto ha una chiave e anche il cliente ha la sua chiave. Tuttavia, ogni la chiave di acquisto includerà la chiave di customer_entity. Ricorda: questo sarà limitato a una scrittura per predecessore al secondo. Di seguito viene creata un'entità con un predecessore:

purchase = Purchase(parent=customer_entity.key)

Per eseguire una query relativa agli acquisti di un determinato cliente, utilizza la seguente query.

purchases = Purchase.query(ancestor=customer_entity.key).fetch()

Attributi query

Gli oggetti query hanno i seguenti attributi dei dati di sola lettura:

Attributo Tipo Predefinito Descrizione
kind str None Nome del tipo (di solito il nome della classe)
predecessore Key None Il predecessore specificato per la query
filters FilterNode None Espressione di filtro
ordini Order None Ordina

La stampa di un oggetto query (o la chiamata a str() o repr()) produce una rappresentazione stringa ben formattata:

print(Employee.query())
# -> Query(kind='Employee')
print(Employee.query(ancestor=ndb.Key(Manager, 1)))
# -> Query(kind='Employee', ancestor=Key('Manager', 1))

Filtrare per i valori delle proprietà strutturate

Una query può filtrare direttamente in base ai valori dei campi delle proprietà strutturate. Ad esempio, una query per tutti i contatti con un indirizzo la cui città è 'Amsterdam' potrebbe essere

query = Contact.query(Contact.addresses.city == 'Amsterdam')

Se combini più filtri di questo tipo, i filtri potrebbero corrispondere diverse entità secondarie Address all'interno di della stessa entità Contact. Ad esempio:

query = Contact.query(Contact.addresses.city == 'Amsterdam',  # Beware!
                      Contact.addresses.street == 'Spear St')

potrebbe trovare contatti con un indirizzo la cui città è 'Amsterdam' e un altro indirizzo (diverso) la cui via è 'Spear St'. Tuttavia, almeno per i filtri di uguaglianza, crea una query che restituisce solo risultati con più valori in un sottoentità singola:

query = Contact.query(Contact.addresses == Address(city='San Francisco',
                                                   street='Spear St'))

Se utilizzi questa tecnica, le proprietà dell'entità secondaria uguale a None vengono ignorate nella query. Se una proprietà ha un valore predefinito, devi impostarlo esplicitamente su None per ignorarlo nella query, altrimenti la query include un filtro che richiede che il valore della proprietà sia uguale a quello predefinito. Ad esempio, se il modello Address aveva una proprietà country con default='us', quanto sopra esempio restituisce solo i contatti con un paese uguale a 'us'; considerare i contatti con valori di altri paesi, da filtrare per Address(city='San Francisco', street='Spear St', country=None).

Se un'entità secondaria ha valori di proprietà uguali a None, vengono ignorati. Pertanto, non ha senso applicare un filtro in base un valore di proprietà di un'entità secondaria pari a None.

Utilizzo di proprietà con nome stringa

A volte potresti voler filtrare o ordinare una query in base a una proprietà il cui nome è specificato dalla stringa. Ad esempio, se consenti all'utente di inserire query di ricerca ad esempio tags:python, sarebbe conveniente attivare in qualche modo questo in una query come

Article.query(Article."tags" == "python") # does NOT work

Se il modello è Expando, poi le tue può utilizzare GenericProperty, la classe Expando utilizza per le proprietà dinamiche:

property_to_query = 'location'
query = FlexEmployee.query(ndb.GenericProperty(property_to_query) == 'SF')

L'uso di GenericProperty funziona anche se il modello non è Expando, ma se vuoi assicurarti utilizzano solo nomi di proprietà definiti, puoi utilizzare anche Attributo classe _properties

query = Article.query(Article._properties[keyword] == value)

oppure usa getattr() per riceverlo dal corso:

query = Article.query(getattr(Article, keyword) == value)

La differenza è che getattr() utilizza il "nome Python" della proprietà, mentre _properties è indicizzato dalla "nome datastore" della proprietà. Si differenziano solo quando la proprietà è stato dichiarato

class ArticleWithDifferentDatastoreName(ndb.Model):
    title = ndb.StringProperty('t')

Qui il nome Python è title, ma il nome del datastore è t.

Questi approcci funzionano anche per ordinare i risultati delle query:

expando_query = FlexEmployee.query().order(ndb.GenericProperty('location'))

property_query = Article.query().order(Article._properties[keyword])

Iteratori query

Mentre è in corso una query, il suo stato si trova in una non è un oggetto iteratore. (la maggior parte delle applicazioni non le utilizzerà direttamente, di solito è più semplice chiamare fetch(20) rispetto per manipolare l'oggetto Iterator). Esistono due modi di base per ottenere un oggetto di questo tipo:

  • utilizzando la funzione iter() integrata di Python su Oggetto Query
  • chiamata Query metodo iter() dell'oggetto

La prima supporta l'utilizzo di un loop for Python (che chiama implicitamente la funzione iter()) per eseguire il loop su una query.

for greeting in greetings:
    self.response.out.write(
        '<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

Il secondo modo, utilizzando il parametro Query iter(), ti consente di trasmettere opzioni al per influenzarne il comportamento. Ad esempio, per utilizzare query basata solo su chiavi in un loop for, puoi scrivere questo:

for key in query.iter(keys_only=True):
    print(key)

Gli iteratori delle query hanno altri metodi utili:

Metodo Descrizione
__iter__() Parte del protocollo Iterator di Python.
next() Restituisce il risultato successivo oppure genera l'eccezione StopIteration se non è presente.

has_next() Restituisce True se next() successivo la chiamata restituirà un risultato, False se aumenterà StopIteration.

Si blocca finché la risposta a questa domanda non è nota e esegue il buffering del risultato (se presenti) finché non lo recuperi con next().
probably_has_next() Come has_next(), ma utilizza un più veloce (e a volte imprecisa).

Può restituire un falso positivo (True quando next() aumenterà effettivamente StopIteration), ma mai un falso negativo (False quando next() restituirà effettivamente un risultato.
cursor_before() Restituisce un cursore di query che rappresenta un punto subito prima di è stato restituito l'ultimo risultato.

Genera un'eccezione se non è disponibile alcun cursore (in particolare, se l'opzione di query produce_cursors non è stata superata).
cursor_after() Restituisce un cursore di query che rappresenta un punto subito dopo l'ultimo restituito un risultato.

Genera un'eccezione se non è disponibile alcun cursore (in particolare, se l'opzione di query produce_cursors non è stata superata).
index_list() Restituisce un elenco di indici utilizzati da una query eseguita, inclusi indici primari, composti, di tipo e a proprietà singola.

Cursori di query

Un curvo di query è una piccola struttura di dati opaca che rappresenta un punto di ripresa in una query. È utile Per mostrare a un utente una pagina di risultati alla volta; ma anche utile per gestire job lunghi che potrebbero dover essere arrestati e ripresi. Un modo tipico per utilizzarli è con lo fetch_page() di una query . Funziona in un certo modo come fetch(), ma restituisce un risultato triplo (results, cursor, more). Il flag more restituito indica che probabilmente ce ne sono di più i risultati; una UI può usarlo, ad esempio, per eliminare un "Pagina successiva" pulsante o link. Per richiedere pagine successive, passa il cursore restituito da uno fetch_page() chiamata alla successiva. BadArgumentError viene elevato se inserisci un cursore non valido. Tieni presente che la convalida controlla solo se con codifica Base64. Dovrai eseguire eventuali altre verifiche necessarie.

Quindi, per consentire all'utente di visualizzare tutte le entità corrispondenti a una query, recuperandole una pagina alla volta, il codice potrebbe avere il seguente aspetto:

from google.appengine.datastore.datastore_query import Cursor
...
class List(webapp2.RequestHandler):
    GREETINGS_PER_PAGE = 10

    def get(self):
        """Handles requests like /list?cursor=1234567."""
        cursor = Cursor(urlsafe=self.request.get('cursor'))
        greets, next_cursor, more = Greeting.query().fetch_page(
            self.GREETINGS_PER_PAGE, start_cursor=cursor)

        self.response.out.write('<html><body>')

        for greeting in greets:
            self.response.out.write(
                '<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

        if more and next_cursor:
            self.response.out.write('<a href="/list?cursor=%s">More...</a>' %
                                    next_cursor.urlsafe())

        self.response.out.write('</body></html>')

Nota l'utilizzo di urlsafe() e Cursor(urlsafe=s) per serializzare e deserializzare il cursore. Ciò ti consente di passare un cursore a un client sul web nella a una richiesta per riceverla dal client in un secondo momento richiesta.

Nota: Il metodo fetch_page() in genere restituisce un cursore anche se non vengono visualizzati altri risultati, ma ciò non è garantito: il cursore restituito può essere None. Tieni presente inoltre che, poiché Il flag more viene implementato utilizzando il metodo probably_has_next(), in rare circostanze potrebbe restituisce True anche se la pagina successiva è vuota.

Alcune query NDB non supportano i cursori di query, ma puoi correggerli. Se una query utilizza IN, OR o !=, i risultati della query non funzionano con i cursori a meno che non vengano ordinati per tasto. Se un'applicazione non ordina i risultati per chiave e chiama fetch_page(), riceve BadArgumentError. Se User.query(User.name.IN(['Joe', 'Jane'])).order(User.name).fetch_page(N) riceve un errore, modificalo in User.query(User.name.IN(['Joe', 'Jane'])).order(User.name, User.key).fetch_page(N)

Invece di "impaginare" tramite i risultati, puoi utilizzare iter() per ottenere un cursore in un punto preciso. Per farlo, passa produce_cursors=True a iter(); quando l'iteratore si trova nel posto giusto, chiama il suo cursor_after() per ottenere un cursore posizionato subito dopo. (O, analogamente, richiama cursor_before() per un cursore subito prima.) Tieni presente che la chiamata al numero cursor_after() o cursor_before() potrebbe effettuare una chiamata Datastore di blocco, eseguendo nuovamente parte della query per estrarre un cursore che punta al centro un batch.

Per utilizzare il cursore per scorrere i risultati della query a ritroso, crea una query inversa:

# Set up.
q = Bar.query()
q_forward = q.order(Bar.key)
q_reverse = q.order(-Bar.key)

# Fetch a page going forward.
bars, cursor, more = q_forward.fetch_page(10)

# Fetch the same page going backward.
r_bars, r_cursor, r_more = q_reverse.fetch_page(10, start_cursor=cursor)

Chiamata a una funzione per ogni entità ("Mappatura")

Supponiamo che tu debba ottenere Account entità corrispondenti a Message le entità restituite da una query. Potresti scrivere qualcosa del genere:

message_account_pairs = []
for message in message_query:
    key = ndb.Key('Account', message.userid)
    account = key.get()
    message_account_pairs.append((message, account))

ma è piuttosto inefficiente: attende il recupero di un'entità, utilizza l'entità; attende l'entità successiva, utilizza l'entità. C'è molto tempo di attesa. Un altro modo è scrivere una funzione di callback mappata sul risultati della query:

def callback(message):
    key = ndb.Key('Account', message.userid)
    account = key.get()
    return message, account

message_account_pairs = message_query.map(callback)
# Now message_account_pairs is a list of (message, account) tuples.

Questa versione verrà eseguita un po' più velocemente rispetto alla semplice for precedente perché è possibile una certa contemporaneità. Tuttavia, poiché la chiamata di get() callback() è ancora sincrono, il guadagno non è eccezionale. È un buon posto per usarlo asincrona.

GQL

GQL è un linguaggio simile a SQL per il recupero di entità o chiavi App Engine Datastore. Sebbene le caratteristiche di GQL siano diverse quelli di un linguaggio di query per un database relazionale tradizionale, è simile a quella di SQL. La sintassi di GQL è descritta nel GQL Riferimento.

Puoi utilizzare GQL per creare query. È un processo simile alla creazione di una query con Model.query(), ma utilizza la sintassi GQL per definire i filtri e l'ordine delle query. Per utilizzarla:

  • ndb.gql(querystring) restituisce un oggetto Query (lo stesso tipo restituito da Model.query()). Tutti i metodi usuali sono disponibili su questi oggetti Query: fetch(), map_async(), filter() ecc.
  • Model.gql(querystring) è una forma breve per ndb.gql("SELECT * FROM Model " + querystring). In genere, querystring è simile a "WHERE prop1 > 0 AND prop2 = TRUE".
  • Per eseguire query su modelli contenenti proprietà strutturate, puoi utilizzare foo.bar nella sintassi GQL per fare riferimento alle proprietà secondarie.
  • GQL supporta le associazioni di parametri di tipo SQL. Un'applicazione può definire una query e quindi associarvi valori:
    query = ndb.gql("SELECT * FROM Article WHERE stars > :1")
    query2 = query.bind(3)
    
    o
    query = ndb.gql("SELECT * FROM Article WHERE stars > :1", 3)

    La chiamata alla funzione bind() di una query restituisce una nuova query; questo elemento non modifica l'originale.

  • Se la classe del modello sostituisce il metodo della classe _get_kind(), la query GQL dovrebbe utilizzare il tipo restituito da quella funzione, non nome della classe.
  • Se una proprietà nel modello sostituisce il suo nome (ad es. foo = StringProperty('bar')) il tuo La query GQL deve utilizzare il nome della proprietà sottoposta a override (nell'esempio bar).

Utilizza sempre la funzionalità di associazione di parametri se alcuni valori nella tua query sono variabili fornite dall'utente. In questo modo si evitano gli attacchi basati su compromissioni sintattiche.

È un errore eseguire una query per un modello che non è stato importato (o generalmente definito).

Utilizzare il nome di una proprietà non definito dal modello è un errore a meno che non si tratti di un modello di tipo Espandio.

Specifica di un limite o di un offset per gli override fetch() della query il limite o l'offset impostato da OFFSET e LIMIT. Non combinare i valori OFFSET di GQL e LIMIT con fetch_page() Tieni presente che i 1000 risultati il valore massimo imposto da App Engine per le query si applica sia all'offset che al limite.

Se sei abituato a SQL, fai attenzione alle false ipotesi quando utilizzi GQL. GQL viene tradotto nell'API di query nativa di NDB. Questo è diverso da un tipico mapping di oggetti (come SQLAlchemy o il supporto dei database di Django), in cui le chiamate API vengono tradotte in SQL prima di essere trasmesse al server del database. GQL non supporta le modifiche di Datastore (inserimenti, eliminazioni o aggiornamenti); supporta solo le query.