Mit Sammlungen den Überblick behalten
Sie können Inhalte basierend auf Ihren Einstellungen speichern und kategorisieren.
Mit Abfrage-Cursors kann eine Anwendung die Ergebnisse einer Abfrage in praktischen Batches abrufen. Sie werden statt ganzzahliger Offsets für die Paginierung empfohlen.
Weitere Informationen zur Strukturierung von Abfragen für Ihre Anwendung finden Sie unter Abfragen.
Abfrage-Cursors
Mit Abfrage-Cursors kann eine Anwendung die Ergebnisse einer Abfrage in praktischen Batches ohne den Overhead eines Abfrage-Offsets abrufen. Nach Ausführung eines Abrufvorgangs kann die Anwendung einen Cursor abrufen, bei dem es sich um einen intransparenten base64-codierten String handelt, der die Indexposition des letzten abgerufenen Ergebnisses markiert. Die Anwendung kann diesen String speichern (beispielsweise in Datastore, in Memcache, in der Aufgabennutzlast einer Aufgabenwarteschlange oder eingebettet in einer Webseite als HTTP-GET- oder -POST-Parameter). Dann kann sie den Cursor als Ausgangspunkt für einen nachfolgenden Abrufvorgang verwenden, um den nächsten Batch Ergebnisse ab dem Punkt abzurufen, an dem der vorherige Abrufvorgang beendet wurde. Für den Abruf kann auch ein Endcursor angegeben werden, um den Umfang der zurückgegebenen Ergebnisse zu begrenzen.
Offsets und Cursors im Vergleich
Datastore unterstützt zwar ganzzahlige Offsets, aber Sie sollten diese nach Möglichkeit nicht verwenden. Verwenden Sie stattdessen Cursors. Die Verwendung eines Offsets verhindert nur die Rückgabe der übersprungenen Entitäten an Ihre Anwendung, die Entitäten werden jedoch dennoch intern abgerufen. Die übersprungenen Entitäten wirken sich auf die Latenz der Abfrage aus. Außerdem werden Ihrer Anwendung die Lesevorgänge in Rechnung gestellt, die für deren Abruf erforderlich sind. Wenn Cursors anstelle von Offsets verwendet werden, können Sie diese Kosten vermeiden.
Beispiel für einen Abfrage-Cursor
In Python erhält eine Anwendung nach dem Abrufen von Abfrageergebnissen durch das Aufrufen der Methode cursor() des Query-Objekts einen Cursor. Zum Abrufen weiterer Ergebnisse ab dem Punkt des Cursors erstellt die Anwendung eine ähnliche Abfrage mit identischem Entitätstyp, identischen Filtern sowie identischen Sortierfolgen und leitet den Cursor an die Methode with_cursor() der Abfrage weiter. Erst dann erfolgt der Abruf:
fromgoogle.appengine.apiimportmemcachefromgoogle.appengine.extimportdb# class Person(db.Model): ...# Start a query for all Person entitiespeople=Person.all()# If the application stored a cursor during a previous request, use itperson_cursor=memcache.get('person_cursor')ifperson_cursor:people.with_cursor(start_cursor=person_cursor)# Iterate over the resultsforpersoninpeople:# Do something# Get updated cursor and store it for next timeperson_cursor=people.cursor()memcache.set('person_cursor',person_cursor)
Einschränkungen von Cursors
Cursors unterliegen folgenden Einschränkungen:
Ein Cursor kann nur von der Anwendung, die die ursprüngliche Abfrage durchgeführt hat, und nur zur Fortsetzung derselben Abfrage verwendet werden. Um den Cursor bei einem nachfolgenden Abrufvorgang zu verwenden, müssen Sie die ursprüngliche Abfrage genau rekonstruieren, einschließlich der Entitätsart, des Ancestor-Filters, der Attributfilter und der Sortierfolgen. Ergebnisse können mit einem Cursor nur abgerufen werden, wenn genau die Abfrage eingerichtet wird, aus der er ursprünglich generiert wurde.
Da die Operatoren != und IN mit mehreren Abfragen implementiert werden, unterstützen Abfragen, die diese Operatoren verwenden, keine Cursor.
Cursors funktionieren häufig nicht wie erwartet bei Abfragen, die einen Ungleichheitsfilter oder eine Sortierfolge bei einem Attribut mit mehreren Werten verwenden. Die Deduplizierungslogik für derartige mehrwertige Attribute bleibt zwischen Abrufvorgängen nicht bestehen, weshalb dasselbe Ergebnis mehr als einmal zurückgegeben werden kann.
Bei neuen App Engine-Versionen können sich die Details der internen Implementierung ändern, sodass Cursors, die davon abhängen, ungültig werden. Wenn eine Anwendung versucht, einen Cursor zu verwenden, der nicht mehr gültig ist, löst Datastore eine BadRequestError-Ausnahme aus.
Cursors und Datenaktualisierungen
Die Position des Cursors ist als die Position in der Ergebnisliste nach dem letzten zurückgegebenen Ergebnis definiert. Ein Cursor ist keine relative Position in der Liste (er ist kein Offset). Er ist eine Markierung, zu der Datastore springen kann, wenn ein Indexscan nach Ergebnissen gestartet wird. Wenn sich die Ergebnisse einer Abfrage zwischen Cursorverwendungen ändern, berücksichtigt die Abfrage nur Änderungen, die in Ergebnissen nach dem Cursor auftreten. Wenn für die Abfrage ein neues Ergebnis vor der Position des Cursors angezeigt wird, wird dieses beim Abrufen der Ergebnisse nach der Position des Cursors nicht zurückgegeben.
Dasselbe gilt, wenn eine Entität kein Ergebnis einer Abfrage mehr ist, aber vor der Position des Cursors angezeigt wurde; in diesem Fall ändern sich die Ergebnisse nach der Position des Cursors nicht. Auch wenn das letzte zurückgegebene Ergebnis aus den Ergebnissen entfernt wird, kann der Cursor die Position des nächsten Ergebnisses bestimmen.
Beim Abrufen von Abfrageergebnissen können Sie einen Start- und einen Endcursor einsetzen, um fortlaufende Ergebnisse aus Datastore zurückzugeben. Wenn Sie beim Abrufen der Ergebnisse einen Start- und einen Endcursor verwenden, ist nicht garantiert, dass der Umfang der Ergebnisse dem Umfang beim Generieren der Cursors entspricht.
Das liegt daran, dass es passieren kann, dass Entitäten in Datastore zwischen dem Zeitpunkt der Generierung der Cursors und deren Verwendung in einer Abfrage hinzugefügt oder gelöscht werden.
[[["Leicht verständlich","easyToUnderstand","thumb-up"],["Mein Problem wurde gelöst","solvedMyProblem","thumb-up"],["Sonstiges","otherUp","thumb-up"]],[["Schwer verständlich","hardToUnderstand","thumb-down"],["Informationen oder Beispielcode falsch","incorrectInformationOrSampleCode","thumb-down"],["Benötigte Informationen/Beispiele nicht gefunden","missingTheInformationSamplesINeed","thumb-down"],["Problem mit der Übersetzung","translationIssue","thumb-down"],["Sonstiges","otherDown","thumb-down"]],["Zuletzt aktualisiert: 2025-09-04 (UTC)."],[[["\u003cp\u003eQuery cursors allow applications to retrieve query results in batches, marking the position of the last retrieved result as an opaque, base64-encoded string.\u003c/p\u003e\n"],["\u003cp\u003eUsing cursors instead of integer offsets is recommended because offsets still retrieve skipped entities internally, affecting latency and incurring read operation costs.\u003c/p\u003e\n"],["\u003cp\u003eCursors can be saved and used later to continue a query from where it left off, allowing applications to get the next batch of results.\u003c/p\u003e\n"],["\u003cp\u003eCursors have limitations, such as only being usable by the same application for the same query and not supporting \u003ccode\u003e!=\u003c/code\u003e and \u003ccode\u003eIN\u003c/code\u003e operators.\u003c/p\u003e\n"],["\u003cp\u003eThe position of a cursor is defined as the location after the last returned result, and while it is not affected by results that come before the cursor, new additions or removals after the cursor can impact results.\u003c/p\u003e\n"]]],[],null,["# Query Cursors\n\n*Query cursors* allow an application to retrieve a query's results in convenient\nbatches, and are recommended over using integer offsets for pagination.\nSee [Queries](/appengine/docs/legacy/standard/python/datastore/queries) for more information on structuring queries for your app.\n\nQuery cursors\n-------------\n\n*Query cursors* allow an application to retrieve a query's results in convenient\nbatches without incurring the overhead of a query offset. After performing a\n[retrieval operation](/appengine/docs/legacy/standard/python/datastore/retrieving-query-results), the application can obtain a\ncursor, which is an opaque base64-encoded string marking the index position of\nthe last result retrieved. The application can save this string, for example in\nDatastore, in Memcache, in a Task Queue task payload, or embedded in\na web page as an HTTP `GET` or `POST` parameter, and can then use the cursor as\nthe starting point for a subsequent retrieval operation to obtain the next batch\nof results from the point where the previous retrieval ended. A retrieval can\nalso specify an end cursor, to limit the extent of the result set returned.\n\nOffsets versus cursors\n----------------------\n\nAlthough Datastore supports integer offsets, you should avoid\nusing them. Instead, use cursors. Using an offset only avoids returning the\nskipped entities to your application, but these entities are still retrieved\ninternally. The skipped entities do affect the latency of the query, and your\napplication is billed for the read operations required to retrieve them. Using\ncursors instead of offsets lets you avoid all these costs.\n\nQuery cursor example\n--------------------\n\nIn Python, an application obtains a cursor after retrieving query results by\ncalling the `Query` object's [`cursor()`](/appengine/docs/legacy/standard/python/datastore/queryclass#Query_cursor) method. To\nretrieve additional results from the point of the cursor, the application\nprepares a similar query with the same entity kind, filters, and sort orders,\nand passes the cursor to the query's\n[`with_cursor()`](/appengine/docs/legacy/standard/python/datastore/queryclass#Query_with_cursor) method before performing the\nretrieval: \n\n from google.appengine.api import memcache\n from google.appengine.ext import db\n\n # class Person(db.Model): ...\n \n # Start a query for all Person entities\n people = Person.all()\n \n # If the application stored a cursor during a previous request, use it\n person_cursor = memcache.get('person_cursor')\n if person_cursor:\n people.with_cursor(start_cursor=person_cursor)\n \n # Iterate over the results\n for person in people:\n # Do something\n \n # Get updated cursor and store it for next time\n person_cursor = people.cursor()\n memcache.set('person_cursor', person_cursor)\n\n| **Note:** Because of the way the iterator interface retrieves results in batches, getting a cursor may result in an additional call to Datastore to position the cursor where the iterator left off. If using only a start cursor, and if you know how many results you need ahead of time, it's faster to use [`fetch()`](/appengine/docs/legacy/standard/python/datastore/queryclass#Query_fetch).\n| **Caution:** Be careful when passing a Datastore cursor to a client, such as in a web form. Although the client cannot change the cursor value to access results outside of the original query, it is possible for it to decode the cursor to expose information about result entities, such as the application ID, entity kind, key name or numeric ID, ancestor keys, and properties used in the query's filters and sort orders. If you don't want users to have access to that information, you can encrypt the cursor, or store it and provide the user with an opaque key.\n\n### Limitations of cursors\n\nCursors are subject to the following limitations:\n\n- A cursor can be used only by the same application that performed the original query, and only to continue the same query. To use the cursor in a subsequent retrieval operation, you must reconstitute the original query exactly, including the same entity kind, ancestor filter, property filters, and sort orders. It is not possible to retrieve results using a cursor without setting up the same query from which it was originally generated.\n- Because the `!=` and `IN` operators are implemented with multiple queries, queries that use them do not support cursors.\n- Cursors don't always work as expected with a query that uses an inequality filter or a sort order on a property with multiple values. The de-duplication logic for such multiple-valued properties does not persist between retrievals, possibly causing the same result to be returned more than once.\n- New App Engine releases might change internal implementation details, invalidating cursors that depend on them. If an application attempts to use a cursor that is no longer valid, Datastore raises a [`BadRequestError`](/appengine/docs/legacy/standard/python/datastore/exceptions#BadRequestError) exception.\n\n### Cursors and data updates\n\nThe cursor's position is defined as the location in the result list after the\nlast result returned. A cursor is not a relative position in the list\n(it's not an offset); it's a marker to which Datastore can jump\nwhen starting an index scan for results. If the results for a query change\nbetween uses of a cursor, the query notices only changes that occur in results\nafter the cursor. If a new result appears before the cursor's position for the\nquery, it will not be returned when the results after the cursor are fetched.\nSimilarly, if an entity is no longer a result for a query but had appeared\nbefore the cursor, the results that appear after the cursor do not change. If\nthe last result returned is removed from the result set, the cursor still knows\nhow to locate the next result.\n\nWhen retrieving query results, you can use both a start cursor and an end cursor\nto return a continuous group of results from Datastore. When\nusing a start and end cursor to retrieve the results, you are not guaranteed\nthat the size of the results will be the same as when you generated the cursors.\nEntities may be added or deleted from Datastore between the\ntime the cursors are generated and when they are used in a query.\n\nWhat's next?\n------------\n\n- [Learn how to specify what a query returns and further control query\n results](/appengine/docs/legacy/standard/python/datastore/retrieving-query-results).\n- Learn the [common restrictions](/appengine/docs/legacy/standard/python/datastore/query-restrictions) for queries on Datastore.\n- [Understand data consistency](/appengine/docs/legacy/standard/python/datastore/data-consistency) and how data consistency works with different types of queries on Datastore.\n- Learn the [basic syntax and structure of queries](/appengine/docs/legacy/standard/python/datastore/queries) for Datastore."]]