Eine Anwendung kann mit Abfragen den Datenspeicher nach Entitäten durchsuchen, die mit bestimmten Suchkriterien übereinstimmen. Diese werden als Filter bezeichnet.
Übersicht
Eine Anwendung kann mit Abfragen den Datenspeicher nach Entitäten durchsuchen, die mit bestimmten Suchkriterien übereinstimmen. Diese werden als Filter bezeichnet. Beispielsweise könnte eine Anwendung, die mehrere Gästebücher verfolgt, mit einer Abfrage Nachrichten aus einem Gästebuch abrufen, die nach Datum sortiert sind:
...
...
Einige Abfragen sind komplexer als andere. Hierfür benötigt der Datenspeicher vordefinierte Indexe.
Diese vordefinierten Indexe werden in der Konfigurationsdatei index.yaml
angegeben.
Wenn Sie auf dem Entwicklungsserver eine Abfrage ausführen, die einen von Ihnen angegebenen Index benötigt, fügt der Entwicklungsserver diesen automatisch zu seiner index.yaml
-Datei hinzu.
Auf Ihrer Website schlägt eine Abfrage allerdings fehl, für die ein noch nicht angegebener Index erforderlich ist.
Daher wird es bei einem typischen Entwicklungszyklus mit einer neuen Abfrage auf dem Entwicklungsserver versucht. Anschließend wird die Website aktualisiert, damit die automatisch geänderte index.yaml
-Datei verwendet wird.
Sie können index.yaml
getrennt vom Upload der Anwendung aktualisieren, indem Sie
gcloud app deploy index.yaml
ausführen.
Wenn Ihr Datenspeicher viele Entitäten hat, dauert es lange, einen neuen Index für sie zu erstellen. In diesem Fall sollten Sie die Indexdefinitionen aktualisieren, bevor Sie Code hochladen, der den neuen Index verwendet.
Sie können mithilfe der Verwaltungskonsole feststellen, wann die Erstellung der Indexe abgeschlossen wurde.
Der App Engine-Datenspeicher unterstützt nativ Filter für genaue Übereinstimmungen (der Operator "==") und Vergleiche (die Operatoren "<", "<=", ">" und ">=").
Außerdem wird die Kombination mehrerer Filter mit einem booleschen AND
-Vorgang unterstützt. Es gelten einige Einschränkungen (siehe unten).
Neben den nativen Operatoren unterstützt die API auch den !=
-Operator, wobei Gruppen von Filtern mit dem booleschen OR
-Vorgang und dem IN
-Vorgang kombiniert werden. Damit wird die Übereinstimmung mit einem der Liste möglicher Werte überprüft (wie beim "in"-Operator von Python).
Diese Vorgänge decken sich nicht genau mit den nativen Vorgängen des Datenspeichers, daher sind sie eher ungewöhnlich und relativ langsam.
Bei ihrer Implementierung werden Ergebnisstreams speicherintern zusammengeführt. Beachten Sie, dass p != v
als "p < v ODER p > v" implementiert ist.
Dies ist für wiederkehrende Attribute relevant.
Einschränkungen: Der Datenspeicher erzwingt bestimmte Einschränkungen für Abfragen. Bei Verstößen werden Ausnahmen ausgelöst. Wenn beispielsweise zu viele Filter mithilfe von Ungleichungen für mehrere Attribute kombiniert werden oder wenn eine Ungleichung mit einer Sortierfolge für ein anderes Attribut kombiniert wird, sind momentan alle Abfragen unzulässig. Bei Filtern, die auf mehrere Attribute verweisen, sind in einigen Fällen sekundäre Indexe für die Konfiguration erforderlich.
Nicht unterstützt: Der Datenspeicher unterstützt keine Teilstring-Übereinstimmungen, Übereinstimmungen, bei denen die Groß-/Kleinschreibung nicht berücksichtigt wird, und keine sogenannten Volltextsuchvorgänge. Es gibt Möglichkeiten, Übereinstimmungen, bei denen die Groß-/Kleinschreibung nicht berücksichtigt wird, und sogar Volltextsuchvorgänge mithilfe berechneter Attribute zu implementieren.
Nach Attribut-Werten filtern
Rufen Sie noch einmal die Klasse Account aus den NDB-Attributen auf:
Normalerweise empfiehlt es sich nicht, alle Entitäten einer bestimmten Art abzurufen. Sie möchten nur diejenigen mit einem bestimmten Wert oder Wertebereich für ein Attribut.
Einige Operatoren werden von Attribut-Objekten überladen, um Filterausdrücke zurückzugeben, die zur Steuerung einer Abfrage verwendet werden können: Wenn Sie beispielsweise alle "Account"-Entitäten finden möchten, deren "userid"-Attribut genau den Wert 42 hat, können Sie den folgenden Ausdruck verwenden:
(Wenn Sie sicher sind, dass nur ein Account
mit diesem userid
vorhanden war, können Sie userid
als Schlüssel verwenden.
Account.get_by_id(...)
ist schneller als Account.query(...).get()
.)
NDB unterstützt diese Operationen:
property == value
property < value
property <= value
property > value
property >= value
property != value
property.IN([value1, value2])
Sie können Syntax wie die folgende verwenden, um nach Ungleichungen zu filtern:
Dadurch werden alle "Account"-Entitäten gefunden, deren userid
-Attribut größer oder gleich 40 ist.
Zwei dieser Operationen, != und IN, werden als Kombinationen der anderen implementiert und sind etwas ungewöhnlich, wie unter != und IN beschrieben.
Sie können mehrere Filter angeben:
So werden die angegebenen Filterargumente und alle "Account"-Entitäten zurückgegeben, deren "userid"-Wert größer oder gleich 40 und kleiner als 50 ist.
Hinweis: Wie bereits erwähnt, lehnt der Datenspeicher Abfragen ab, bei denen die Ungleichheitsfilter für mehr als ein Attribut verwendet werden.
Statt einen vollständigen Abfragefilter in einem einzelnen Ausdruck anzugeben, empfiehlt es sich unter Umständen, ihn in mehreren Schritten zu erstellen. Beispiel:
query3
entspricht der Variable query
aus dem vorherigen Beispiel. Beachten Sie, dass Abfrageobjekte unveränderlich sind. Die Erstellung von query2
wirkt sich also nicht auf query1
und die Erstellung von query3
nicht auf query1
oder query2
aus.
Die Vorgänge != und IN
Rufen Sie noch einmal die Klasse Article aus den NDB-Attributen auf:
Die Operationen !=
(ungleich) und IN
(Mitgliedschaft) werden mithilfe anderer Filter und der OR
-Operation kombiniert. Die erste Operation
property != value
wird so implementiert:
(property < value) OR (property > value)
Beispiel:
entspricht
Hinweis: Bei dieser Abfrage wird nicht nach Article
-Entitäten gesucht, die nicht das Tag "perl" haben.
Stattdessen werden alle Entitäten mit mindestens einem Tag gefunden, das nicht mit "perl" identisch ist.
Beispielsweise würde die folgende Entität nicht in den Ergebnissen auftauchen, obwohl es sich bei einem seiner Tags um "perl" handelt:
Diese Entität wäre allerdings nicht enthalten:
Es können keine Entitäten abgefragt werden, die kein Tag enthalten, das mit "perl" identisch ist.
Bei der Operation "IN"
property IN [value1, value2, ...]
wird in einer Liste möglicher Werte nach Mitgliedschaft gesucht. Er wird so implementiert:
(property == value1) OR (property == value2) OR ...
Beispiel:
entspricht
Hinweis: Abfragen, die OR
verwenden, deduplizieren ihre Ergebnisse. Der Ergebnisstream enthält keine Entität mehr als einmal, selbst wenn eine Entität zwei oder mehr Unterabfragen entspricht.
Wiederkehrende Attribute abfragen
Die im vorherigen Abschnitt definierte Klasse Article
eignet sich auch als Beispiel für das Abfragen wiederkehrender Attribute. Ein Filter wie
verwendet einen einzelnen Wert, obwohl es sich bei Article.tags
um ein wiederkehrendes Attribut handelt. Sie können wiederkehrende Attribute nicht vergleichen, um Objekte aufzulisten. Dies könnte vom Datenspeicher nicht verarbeitet werden. Ein Filter wie
sucht nicht nach Article
-Entitäten, deren Tag-Wert die Liste ['python', 'ruby', 'php']
ist. Er sucht nach Entitäten, deren tags
-Wert (als Liste betrachtet) mindestens einen dieser Werte enthält.
Die Abfrage eines Werts von None
für ein wiederholtes Attribut hat ein nicht definiertes Verhalten und sollte nicht ausgeführt werden.
"AND"- und "OR"-Operationen kombinieren
AND
- und OR
-Vorgänge können beliebig verschachtelt werden.
Beispiel:
Aufgrund der Implementierung von OR
kann eine Abfrage in dieser Form mit einer Ausnahme fehlschlagen, weil sie zu komplex ist. Es empfiehlt sich, diese Filter zu normalisieren, sodass es an der Spitze der Ausdrucksstruktur höchstens eine einzelne OR
-Operation gibt sowie eine einzelne Ebene von AND
-Operationen darunter.
Um diese Normalisierung durchzuführen, müssen Sie sich sowohl Ihre Regeln der booleschen Logik als auch die Implementierung der Filter !=
und IN
merken:
- Erweitern Sie die Operatoren
!=
undIN
auf ihre einfache Form, wobei!=
prüft, ob das Attribut kleiner ("<") oder größer (">") als der Wert ist. MitIN
wird geprüft, ob das Attribut mit dem ersten Wert oder dem zweiten Wert usw. bis zum letzten Wert der Liste identisch ("==") ist. - Ein
AND
mit einemOR
darin entspricht einemOR
mit mehrerenAND
, die auf die ursprünglichenAND
-Operanden angewendet werden, wobei ein einzelnerOR
-Operand durch das OriginalOR
ersetzt wird.AND(a, b, OR(c, d))
entspricht beispielsweiseOR(AND(a, b, c), AND(a, b, d))
. - Ein
AND
mit einem Operanden, der selbst eineAND
-Operation ist, kann die Operanden des verschachteltenAND
in den einschließendenAND
einbeziehen.AND(a, b, AND(c, d))
entspricht beispielsweiseAND(a, b, c, d)
. - Ein
OR
mit einem Operanden, der selbst eineOR
-Operation ist, kann die Operanden des verschachteltenOR
in den einschließendenOR
einbeziehen.OR(a, b, OR(c, d))
entspricht beispielsweiseOR(a, b, c, d)
.
Wenn diese Transformationen mit einer einfacheren Schreibweise als Python stufenweise auf den Beispielfilter angewendet werden, erhalten Sie Folgendes:
- Anwendung von Regel 1 auf die Operationen
IN
und!=
:AND(tags == 'python', OR(tags == 'ruby', tags == 'jruby', AND(tags == 'php', OR(tags < 'perl', tags > 'perl'))))
- Anwendung von Regel 2 auf das innerste
OR
, das mit einemAND
verschachtelt ist:AND(tags == 'python', OR(tags == 'ruby', tags == 'jruby', OR(AND(tags == 'php', tags < 'perl'), AND(tags == 'php', tags > 'perl'))))
- Anwendung von Regel 4 auf das
OR
, das innerhalb eines anderenOR
verschachtelt ist:AND(tags == 'python', OR(tags == 'ruby', tags == 'jruby', AND(tags == 'php', tags < 'perl'), AND(tags == 'php', tags > 'perl')))
- Anwendung von Regel 2 auf das verbleibende
OR
, das in einemAND
verschachtelt ist: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')))
- Anwendung von Regel 3, um die verbleibenden verschachtelten
AND
s zu minimieren: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'))
Vorsicht:
Bei einigen Filtern kann diese Normalisierung zu einer kombinatorischen Explosion führen. Ziehen Sie das AND
von drei OR
-Klauseln mit jeweils zwei grundlegenden Klauseln in Erwägung.
Bei der Normalisierung wird dies ein OR
mit acht AND
-Klauseln sowie jeweils drei Basisklauseln. Sechs Bedingungen werden also zu 24.
Sortierfolgen angeben
Mit der Methode order()
können Sie die Reihenfolge angeben, in der eine Abfrage die Ergebnisse zurückgibt. Diese Methode verwendet eine Liste von Argumenten, von denen jedes entweder ein Attribut-Objekt (in aufsteigender Reihenfolge) oder seine Negation (in absteigender Reihenfolge) ist. Beispiel:
Dadurch werden alle Greeting
-Entitäten abgerufen, sortiert nach aufsteigendem Wert ihres content
-Attributs.
Werden aufeinanderfolgende Entitäten mit demselben "Content"-Attribut ausgeführt, sind sie nach absteigendem Wert ihrer date
-Attribute sortiert.
Sie können mehrere order()
-Aufrufe für denselben Effekt verwenden:
Hinweis: Wenn Sie Filter mit order()
kombinieren, lehnt der Datenspeicher bestimmte Kombinationen ab.
Das gilt insbesondere, wenn Sie einen Ungleichheitsfilter verwenden. Dann muss die erste Sortierfolge (falls vorhanden) dasselbe Attribut wie der Filter angeben.
Manchmal müssen Sie auch einen sekundären Index konfigurieren.
Ancestor-Abfragen
Mit Ancestor-Abfragen können Sie strikt konsistente Abfragen für den Datenspeicher erstellen. Entitäten mit demselben Ancestor sind jedoch auf einen Schreibvorgang pro Sekunde beschränkt. Im Folgenden finden Sie einen einfachen Vergleich der Vor- und Nachteile sowie der Struktur von Ancestor- und Nicht-Ancestor-Abfragen. Dabei werden Kunden und die mit ihnen verknüpften Käufe im Datenspeicher verwendet.
Im folgenden Beispiel für eine Nicht-Ancestor-Abfrage gibt es für jeden Customer
eine Entität im Datenspeicher und für jeden Purchase
eine Entität im Datenspeicher. Der KeyProperty
-Wert verweist auf das Element Kunde.
Mit dieser Abfrage finden Sie alle Käufe, die mit dem Kunden verknüpft sind:
In diesem Fall bietet der Datenspeicher einen hohen Schreibdurchsatz, jedoch nur Eventual Consistency. Wenn ein neuer Kauf hinzugefügt wurde, erhalten Sie möglicherweise veraltete Daten. Dieses Verhalten können Sie mit Ancestor-Abfragen umgehen.
Für Kunden und Käufe mit Ancestor-Abfragen haben Sie immer noch dieselbe Struktur mit zwei separaten Entitäten. Der Kundenteil ist identisch. Wenn Sie jedoch Käufe erstellen, müssen Sie die KeyProperty()
für Käufe nicht mehr angeben. Dies liegt daran, dass Sie beim Verwenden von Ancestor-Abfragen den Schlüssel der Kundenentität aufrufen, wenn Sie eine Kaufentität erstellen.
Jeder Kauf hat einen Schlüssel und auch der Kunde hat seinen eigenen Schlüssel. In jedem Kaufschlüssel ist jedoch der Schlüssel von "customer_entity" enthalten. Dies wird auf einen Schreibvorgang pro Vorfahr und Sekunde beschränkt. So wird eine Entität mit einem Ancestor erstellt:
Verwenden Sie die folgende Abfrage, um die Käufe eines bestimmten Kunden abzufragen.
Abfrageattribute
Abfrageobjekte haben die folgenden schreibgeschützten Datenattribute:
Attribut | Typ | Standardeinstellung | Beschreibung |
---|---|---|---|
kind | str | None | Name der Art (in der Regel der Klassenname) |
ancestor | Key | None | Zum Abfragen angegebener Ancestor |
filters | FilterNode | None | Filterausdruck |
orders | Order | None | Sortierfolgen |
Durch das Drucken eines Abfrageobjekts (oder das Aufrufen von str()
oder repr()
darauf) wird eine formatierte Stringdarstellung gedruckt:
Nach Werten für strukturierte Attribute filtern
Eine Abfrage kann direkt nach den Feldwerten strukturierter Attribute filtern.
Beispielsweise würde eine Abfrage für alle Kontakte mit einer Adresse, deren Stadt 'Amsterdam'
ist, so aussehen:
Wenn Sie mehrere Filter dieser Art kombinieren, stimmen die Filter möglicherweise mit verschiedenen Address
-Unterentitäten innerhalb derselben "Contact"-Entität überein.
Beispiel:
kann Kontakte mit einer Adresse finden, die die Stadt 'Amsterdam'
enthält, und eine andere Adresse mit der Straße 'Spear St'
. Sie haben zumindest für Gleichheitsfilter die Möglichkeit, eine Abfrage zu erstellen, die nur Ergebnisse mit mehreren Werten in einer einzigen Unterentität zurückgibt:
Wenn Sie diese Technik verwenden, werden Eigenschaften der Untereinheit gleich None
in der Abfrage ignoriert.
Wenn ein Attribut einen Standardwert hat, müssen Sie es ausdrücklich auf None
setzen, damit es in der Abfrage ignoriert wird. Andernfalls enthält die Abfrage einen Filter, bei dem der Attributwert mit der Standardeinstellung identisch sein muss.
Wenn das Address
-Modell z. B. ein Land mit der Eigenschaft country
mit default='us'
enthielte, würde das obige Beispiel nur Kontakte mit einem Land gleich 'us'
zurückgeben. Um Kontakte mit anderen Länderwerten zu berücksichtigen, müssten Sie nach Address(city='San Francisco', street='Spear St',
country=None)
filtern.
Wenn eine Unterentität Attributwerte hat, die mit None
identisch sind, werden diese ignoriert. Daher macht es keinen Sinn, nach einem Unter-Eigenschaftswert von None
zu filtern.
Vom String benannte Attribute verwenden
Sie können eine Abfrage basierend auf einem Attribut filtern oder sortieren, dessen Name durch einen String angegeben wird. Wenn Sie beispielsweise den Nutzer Suchanfragen wie tags:python
eingeben lassen, wäre es praktisch, wenn dies in eine Abfrage wie diese umgewandelt werden würde:
Article.query(Article."tags" == "python") # does NOT work
Wenn Ihr Modell ein Expando
ist, kann Ihr Filter GenericProperty
verwenden; die Klasse, die Expando
für dynamische Attribute verwendet:
Die Verwendung von GenericProperty
funktioniert auch, wenn Ihr Modell kein Expando
ist. Aber wenn Sie sicherstellen möchten, dass Sie nur definierte Attributnamen verwenden, können Sie auch das _properties
-Klassenattribut verwenden.
Sie rufen es mit getattr()
aus der Klasse ab:
Der Unterschied besteht darin, dass getattr()
den "Python-Namen" des Attributs verwendet, während _properties
durch den "Datenspeichernamen" des Attributs indexiert wird. Diese unterscheiden sich nur, wenn das Attribut in etwa so deklariert wurde:
Hier lautet der Python-Name title
, aber der Name des Datenspeichers ist t
.
Diese Ansätze sind auch für das Sortieren von Abfrageergebnissen geeignet:
Abfrage-Iteratoren
Während eine Abfrage ausgeführt wird, wird ihr Status in einem Iterator-Objekt gespeichert. Die meisten Anwendungen verwenden sie nicht direkt. In der Regel ist es einfacher, fetch(20)
aufzurufen, als das Iteratorobjekt zu bearbeiten.
Es gibt zwei grundlegende Möglichkeiten, ein solches Objekt abzurufen:
- Mit der integrierten Python-Funktion
iter()
für einQuery
-Objekt - Durch Aufrufen der Methode
iter()
desQuery
-Objekts
Bei der ersten Möglichkeit wird die Verwendung einer for
-Schleife von Python unterstützt. Hier wird die iter()
-Funktion implizit aufgerufen, um eine Abfrage zu umgehen.
Bei der zweiten Möglichkeit mit der Methode iter()
des Query
-Objekts können Sie Optionen an den Iterator übergeben, um dessen Verhalten zu beeinflussen. Um beispielsweise eine schlüsselbasierte Abfrage in einer for
-Schleife zu verwenden, können Sie Folgendes schreiben:
Abfrage-Iteratoren bieten weitere nützliche Methoden:
Method | Beschreibung |
---|---|
__iter__()
| Teil des Iterator-Protokolls von Python: |
next()
| Gibt das nächste Ergebnis zurück oder löst die Ausnahme StopIteration StopIteration aus, wenn keines vorhanden ist. |
has_next()
| Gibt True zurück, wenn ein folgender next() -Aufruf ein Ergebnis zurückgibt, und False , wenn StopIteration ausgelöst wird.Blockiert so lang, bis die Antwort auf diese Frage bekannt ist, und speichert das Ergebnis (falls vorhanden) zwischen, bis Sie es mit next() abrufen.
|
probably_has_next()
| Wie has_next() , doch es wird eine schnellere (und manchmal ungenaue) Verknüpfung verwendet.Gibt möglicherweise eine falsch positive Meldung zurück ( True , wenn next() eine StopIteration auslösen würde), aber niemals eine falsch negative (False , wenn next() ein Ergebnis zurückgeben würde).
|
cursor_before()
| Gibt einen Abfragecursor zurück, der einen Punkt unmittelbar vor dem letzten zurückgegebenen Ergebnis darstellt. Löst eine Ausnahme aus, wenn kein Cursor verfügbar ist, insbesondere wenn die Abfrageoption produce_cursors nicht übergeben wurde.
|
cursor_after()
| Gibt einen Abfragecursor zurück, der einen Punkt unmittelbar nach dem letzten zurückgegebenen Ergebnis darstellt. Löst eine Ausnahme aus, wenn kein Cursor verfügbar ist, insbesondere wenn die Abfrageoption produce_cursors nicht übergeben wurde.
|
index_list()
| Gibt eine Liste der Indexe zurück, die von einer ausgeführten Abfrage verwendet werden, unter anderem Hauptindexe, zusammengesetzte Indexe, Artindexe sowie Indexe mit einzelnen Attributen. |
Abfrage-Cursor
Ein Abfrage-Cursor ist eine kleine intransparente Datenstruktur, die einen Wiederaufnahmepunkt in einer Abfrage darstellt. Sie ist nützlich, wenn einem Nutzer Ergebnisseiten nacheinander gezeigt werden, und eignet sich außerdem, um lange Jobs zu bearbeiten, die unter Umständen angehalten und fortgesetzt werden müssen.
Eine typische Methode ist die Verwendung der fetch_page()
-Methode einer Abfrage.
Sie funktioniert ähnlich wie fetch()
, gibt jedoch ein dreifaches (results, cursor, more)
zurück.
Das zurückgegebene Flag more
gibt an, dass wahrscheinlich mehr Ergebnisse vorliegen. Eine Benutzeroberfläche kann dies beispielsweise verwenden, um eine Schaltfläche oder einen Link "Nächste Seite" zu unterdrücken.
Um nachfolgende Seiten anzufordern, übergeben Sie den von einem fetch_page()
-Aufruf zurückgegebenen Cursor an den nächsten. Wenn Sie einen ungültigen Cursor übergeben, wird eine BadArgumentError
ausgelöst. Bei der Validierung wird nur geprüft, ob der Wert base64-codiert ist. Weitere erforderliche Validierungen müssen von Ihnen durchgeführt werden.
Damit der Nutzer alle Entitäten Seite für Seite aufrufen kann, die mit einer Abfrage übereinstimmen, muss Ihr Code ungefähr so aussehen:
...
Beachten Sie, dass der Cursor mithilfe von urlsafe()
und Cursor(urlsafe=s)
serialisiert und deserialisiert wird.
Dadurch können Sie im Web einen Cursor als Antwort auf eine Anfrage an einen Client übergeben und vom Client in einer späteren Anfrage zurückerhalten.
Hinweis: Die Methode fetch_page()
gibt in der Regel auch dann einen Cursor zurück, wenn keine weiteren Ergebnisse vorhanden sind. Doch das ist nicht garantiert. Der zurückgegebene Cursorwert kann None
sein. Beachten Sie auch dies: Da das Flag more
mit der Methode probably_has_next()
des Iterators implementiert wird, kann in seltenen Fällen True
zurückgegeben werden, obwohl die nächste Seite leer ist.
Abfragecursors werden von einigen NDB-Abfragen nicht unterstützt. Sie können sie jedoch korrigieren.
Wenn eine Abfrage IN
, OR
oder !=
verwendet, funktionieren die Abfrageergebnisse nicht mit Cursors, es sei denn, sie sind nach Schlüsseln geordnet.
Wenn eine Anwendung die Ergebnisse nicht nach Schlüsseln sortiert und fetch_page()
aufruft, erhält sie einen BadArgumentError
.
Wenn
User.query(User.name.IN(['Joe', 'Jane'])).order(User.name).fetch_page(N)
einen Fehler erhält, ändern Sie die Abfrage in
User.query(User.name.IN(['Joe', 'Jane'])).order(User.name, User.key).fetch_page(N)
.
Anstatt durch Abfrageergebnisse zu "blättern", können Sie die iter()
-Methode einer Abfrage verwenden, um einen Cursor an einem bestimmten Punkt zu erhalten.
Übergeben Sie dazu produce_cursors=True
an iter()
: Wenn sich der Iterator an der richtigen Stelle befindet, rufen Sie cursor_after()
auf, um einen direkt dahinter liegenden Cursor zu erhalten. Oder rufen Sie cursor_before()
auf, um einen Cursor direkt davor zu erhalten.
Beachten Sie, dass beim Aufrufen von cursor_after()
oder cursor_before()
ein blockierender Datastore-Aufruf erfolgen kann, bei dem ein Teil der Abfrage noch einmal ausgeführt wird, um einen Cursor zu extrahieren, der auf die Mitte eines Batches verweist.
Erstellen Sie eine umgekehrte Abfrage, um mit einem Cursor rückwärts durch die Abfrageergebnisse zu blättern:
Funktion für jede Entity aufrufen ("Zuordnung")
Angenommen, Sie müssen die Account
-Entitäten abrufen, die den von einer Abfrage zurückgegebenen Message
-Entitäten entsprechen.
Sie könnten etwa dies schreiben:
Dies ist jedoch ziemlich ineffizient. Es wird zuerst darauf gewartet, eine Entität abzurufen. Anschließend wird diese Entität verwendet, bevor dann auf die nächste Entität gewartet wird, um auch diese zu verwenden. Das sind lange Wartezeiten. Eine andere Möglichkeit besteht darin, eine Callback-Funktion zu schreiben, die über die Abfrageergebnisse zugeordnet wird:
Diese Version läuft etwas schneller als die einfache for
-Schleife oben, da es zu Gleichzeitigkeiten kommen kann.
Da der Aufruf von get()
in callback()
jedoch immer noch synchron ist, ist die Verbesserung nicht besonders groß.
Dies ist eine gute Gelegenheit, um asynchrone "Gets" zu verwenden.
GQL
GQL ist eine SQL-ähnliche Sprache zum Abrufen von Entitäten oder Schlüsseln aus dem App Engine-Datenspeicher. Die Funktionen von GQL weichen zwar von jenen einer Anfragesprache für herkömmliche relationale Datenbanken ab, die Syntax von GQL ist der von SQL jedoch ähnlich. Die GQL-Syntax wird in der GQL-Referenz beschrieben.
Sie können mit GQL Abfragen erstellen. Das ist mit dem Erstellen einer Abfrage mit Model.query()
vergleichbar. Allerdings wird die GQL-Syntax zum Definieren des Abfragefilters und der Reihenfolge verwendet. Dabei gilt:
ndb.gql(querystring)
gibt einQuery
-Objekt zurück (denselben Typ wie vonModel.query()
zurückgegeben). FürQuery
-Objekte wiefetch()
,map_async()
,filter()
stehen alle üblichen Methoden zur Verfügung.Model.gql(querystring)
ist eine Abkürzung fürndb.gql("SELECT * FROM Model " + querystring)
. In der Regel ist querystring etwas wie"WHERE prop1 > 0 AND prop2 = TRUE"
.- Wenn Sie Modelle abfragen möchten, die strukturierte Attribute enthalten, können Sie in Ihrer GQL-Syntax mit
foo.bar
auf Unterattribute verweisen. - GQL unterstützt SQL-ähnliche Parameterbindungen. Eine Anwendung kann Abfragen definieren und dann Werte einbinden:
Durch das Aufrufen der Funktion
bind()
wird eine neue Abfrage zurückgegeben. Das Original wird nicht geändert. - Wenn Ihre Modellklasse die Klassenmethode
_get_kind()
überschreibt, sollte Ihre GQL-Abfrage die von dieser Funktion zurückgegebene Art und nicht den Klassennamen verwenden. - Wenn ein Attribut in Ihrem Modell seinen Namen überschreibt, z. B.
foo = StringProperty('bar')
) sollte Ihre GQL-Abfrage den überschriebenen Attributenamen verwenden (im Beispielbar
).
Verwenden Sie immer die Funktion zum Binden von Parametern, wenn es sich bei einigen der Werte in Ihrer Abfrage um Variablen handelt, die vom Nutzer bereitgestellt wurden. So werden Angriffe auf der Grundlage von syntaktischen Hacks verhindert.
Es ist ein Fehler, eine Abfrage für ein Modell auszuführen, das nicht importiert (oder definiert) wurde.
Es ist ein Fehler, einen Attributnamen zu verwenden, der nicht von der Modellklasse definiert wurde, es sei denn, dieses Modell ist ein Expando.
Durch das Angeben eines Limits oder Offsets für die fetch()
-Methode der Abfrage wird das Limit oder Offset überschrieben, das von den GQL-Klauseln OFFSET
und LIMIT
festgelegt wurde. Kombinieren Sie OFFSET
und LIMIT
von GQL nicht mit fetch_page()
. Beachten Sie, dass die von App Engine auf Abfragen festgelegte Höchstzahl von 1.000 Ergebnissen sowohl für Offset als auch für Limit gilt.
Wenn Sie mit SQL vertraut sind, sollten Sie bei der Verwendung von GQL auf falsche Annahmen achten. GQL wird in die native Abfrage-API von NDB übersetzt. Dies unterscheidet sich von einem typischen Object-Relational-Mapper wie SQLAlchemy oder der Datenbankunterstützung von Django, bei dem die API-Aufrufe in SQL übersetzt werden, bevor sie an den Datenbankserver übertragen werden. GQL unterstützt keine Datenspeicheränderungen ("inserts", "deletes" oder "updates"), sondern nur Abfragen.