-
Notifications
You must be signed in to change notification settings - Fork 64
query cursors
Not applicable to this runtime.
Query cursors allow an application to retrieve a query's results in convenient batches, and are recommended over using integer offsets for pagination. See Queries for more information on structuring queries for your app.
Query cursors allow an application to retrieve a query's results in convenient
batches without incurring the overhead of a query offset. After performing a
retrieval operation, the application can obtain a
cursor, which is an opaque base64-encoded string marking the index position of
the last result retrieved. The application can save this string, for example in
Cloud Datastore, in Memcache, in a Task Queue task payload, or embedded in a web
page as an HTTP GET or POST parameter, and can then use the cursor as the
starting point for a subsequent retrieval operation to obtain the next batch of
results from the point where the previous retrieval ended. A retrieval can also
specify an end cursor, to limit the extent of the result set returned.
Although Datastore supports integer offsets, you should avoid using them. Instead, use cursors. Using an offset only avoids returning the skipped entities to your application, but these entities are still retrieved internally. The skipped entities do affect the latency of the query, and your application is billed for the read operations required to retrieve them. Using cursors instead of offsets lets you avoid all these costs.
In Go, an application obtains a cursor after retrieving query results by calling
the Iterator value's
Cursor
method. To retrieve additional results from the point of the cursor, the
application prepares a similar query with the same entity kind, filters, and
sort orders, and passes the cursor to the query's
Start
method before performing the retrieval:
In the low-level API, the application can use cursors via the
QueryResultList,
QueryResultIterable,
and
QueryResultIterator
interfaces, which are returned by the
PreparedQuery
methods
asQueryResultList(),
asQueryResultIterable(),
and
asQueryResultIterator(),
respectively. Each of these result objects provides a
getCursor()
method, which in turn returns a
Cursor
object. The application can get a web-safe string representing the cursor by
calling the Cursor object's
toWebSafeString()
method, and can later use the static method
Cursor.fromWebSafeString()
to reconstitute the cursor from the string.
The following example demonstrates the use of cursors for pagination:
View ListPeopleServlet.java on GitHub (region:
cursors)
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.
Cursors are subject to the following limitations:
-
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.
-
Because the
NOT_EQUAL!=andINoperators are implemented with multiple queries, queries that use them do not support cursors, nor do composite queries constructed with theCompositeFilterOperator.ormethod. -
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.
-
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
returnsraises
an error an
IllegalArgumentException
(low-level API),
JDOFatalUserException
(JDO), or
PersistenceException
(JPA).
The cursor's position is defined as the location in the result list after the last result returned. A cursor is not a relative position in the list (it's not an offset); it's a marker to which Datastore can jump when starting an index scan for results. If the results for a query change between uses of a cursor, the query notices only changes that occur in results after the cursor. If a new result appears before the cursor's position for the query, it will not be returned when the results after the cursor are fetched. Similarly, if an entity is no longer a result for a query but had appeared before the cursor, the results that appear after the cursor do not change. If the last result returned is removed from the result set, the cursor still knows how to locate the next result.
When retrieving query results, you can use both a start cursor and an end cursor to return a continuous group of results from Datastore. When using a start and end cursor to retrieve the results, you are not guaranteed that the size of the results will be the same as when you generated the cursors. Entities may be added or deleted from Datastore between the time the cursors are generated and when they are used in a query.
- Learn how to specify what a query returns and further control query results. * Learn the common restrictions for queries on Datastore. * Understand data consistency and how data consistency works with different types of queries on Datastore. * Learn the basic syntax and structure of queries for Datastore.
We recommend using the NDB Client Library for Datastore, which has several benefits compared to this client library, such as automatic entity caching via the Memcache API. If you are currently using the older DB Client Library, see the DB to NDB Migration Guide.