Skip to content

Commit c99b237

Browse files
stephenpluspluscallmehiphop
authored andcommitted
datastore: pagination changes (#1295)
* datastore: the changes * manually run runQuery as a stream * add tests * add early stream exit test conditions * move NO_MORE_RESULTS to constant * add other moreResults constants * link constants to constant docs * mock express * fakeexpress mock
1 parent f52d1a8 commit c99b237

File tree

10 files changed

+318
-278
lines changed

10 files changed

+318
-278
lines changed

lib/common/stream-router.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,18 +94,14 @@ streamRouter.parseArguments_ = function(args) {
9494
if (is.number(query.maxResults)) {
9595
// `maxResults` is used API-wide.
9696
maxResults = query.maxResults;
97-
} else if (is.number(query.limitVal)) {
98-
// `limitVal` is part of a Datastore query.
99-
maxResults = query.limitVal;
10097
} else if (is.number(query.pageSize)) {
10198
// `pageSize` is Pub/Sub's `maxResults`.
10299
maxResults = query.pageSize;
103100
}
104101

105102
if (callback &&
106103
(maxResults !== -1 || // The user specified a limit.
107-
query.autoPaginate === false ||
108-
query.autoPaginateVal === false)) {
104+
query.autoPaginate === false)) {
109105
autoPaginate = false;
110106
}
111107
}
@@ -159,8 +155,8 @@ streamRouter.router_ = function(parsedArguments, originalMethod) {
159155
* This method simply calls the nextQuery recursively, emitting results to a
160156
* stream. The stream ends when `nextQuery` is null.
161157
*
162-
* `maxResults` and `limitVal` (from Datastore) will act as a cap for how many
163-
* results are fetched and emitted to the stream.
158+
* `maxResults` will act as a cap for how many results are fetched and emitted
159+
* to the stream.
164160
*
165161
* @param {object=|string=} parsedArguments.query - Query object. This is most
166162
* commonly an object, but to make the API more simple, it can also be a

lib/datastore/index.js

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -192,19 +192,43 @@ var util = require('../common/util.js');
192192
* //-
193193
* // <h3>Paginating Records</h3>
194194
* //
195-
* // By default, all records are returned at once. To control pagination, you
196-
* // can disable this on your query with {module:datastore/query#autoPaginate}.
195+
* // Imagine building a website that allows a user to sift through hundreds of
196+
* // their contacts. You'll likely want to only display a subset of these at
197+
* // once, so you set a limit.
197198
* //-
198-
* query.autoPaginate(false);
199+
* var express = require('express');
200+
* var app = express();
199201
*
200-
* datastore.runQuery(query, function(err, entities, nextQuery) {
201-
* // entities = [...];
202+
* var NUM_RESULTS_PER_PAGE = 15;
202203
*
203-
* if (nextQuery) {
204-
* // Run `datastore.runQuery` again with the prepared query to retrieve the
205-
* // next set of results.
206-
* datastore.runQuery(nextQuery, function(err, entities, nextQuery) {});
204+
* app.get('/contacts', function(req, res) {
205+
* var query = datastore.createQuery('Contacts')
206+
* .limit(NUM_RESULTS_PER_PAGE);
207+
*
208+
* if (req.query.nextPageCursor) {
209+
* query.start(req.query.nextPageCursor);
207210
* }
211+
*
212+
* datastore.runQuery(query, function(err, entities, info) {
213+
* if (err) {
214+
* // Error handling omitted...
215+
* return;
216+
* }
217+
*
218+
* // Respond to the front end with the contacts and the cursoring token
219+
* // from the query we just ran.
220+
* var frontEndResponse = {
221+
* contacts: entities
222+
* };
223+
*
224+
* var moreResultsMayExist = info.moreResults !== datastore.NO_MORE_RESULTS;
225+
*
226+
* if (moreResultsMayExist) {
227+
* frontEndResponse.nextPageCursor = info.endCursor;
228+
* }
229+
*
230+
* res.render('contacts', frontEndResponse);
231+
* });
208232
* });
209233
*
210234
* //-
@@ -374,6 +398,39 @@ Datastore.int = function(value) {
374398
return new entity.Int(value);
375399
};
376400

401+
/**
402+
* This is one of three values which may be returned from
403+
* {module:datastore#runQuery}, {module:transaction#runQuery}, and
404+
* {module:datastore/query#run} as `info.moreResults`.
405+
*
406+
* There *may* be more results after the specified end cursor.
407+
*
408+
* @type {string}
409+
*/
410+
Datastore.MORE_RESULTS_AFTER_CURSOR = 'MORE_RESULTS_AFTER_CURSOR';
411+
412+
/**
413+
* This is one of three values which may be returned from
414+
* {module:datastore#runQuery}, {module:transaction#runQuery}, and
415+
* {module:datastore/query#run} as `info.moreResults`.
416+
*
417+
* There *may* be more results after the specified limit.
418+
*
419+
* @type {string}
420+
*/
421+
Datastore.MORE_RESULTS_AFTER_LIMIT = 'MORE_RESULTS_AFTER_LIMIT';
422+
423+
/**
424+
* This is one of three values which may be returned from
425+
* {module:datastore#runQuery}, {module:transaction#runQuery}, and
426+
* {module:datastore/query#run} as `info.moreResults`.
427+
*
428+
* There are no more results left to query for.
429+
*
430+
* @type {string}
431+
*/
432+
Datastore.NO_MORE_RESULTS = 'NO_MORE_RESULTS';
433+
377434
/**
378435
* Create a query for the specified kind. See {module:datastore/query} for all
379436
* of the available methods.

lib/datastore/query.js

Lines changed: 14 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -67,41 +67,12 @@ function Query(scope, namespace, kinds) {
6767
this.selectVal = [];
6868

6969
// pagination
70-
this.autoPaginateVal = true;
7170
this.startVal = null;
7271
this.endVal = null;
7372
this.limitVal = -1;
7473
this.offsetVal = -1;
7574
}
7675

77-
/**
78-
* @param {boolean=} autoPaginateVal - Have pagination handled automatically.
79-
* Default: true.
80-
* @return {module:datastore/query}
81-
*
82-
* @example
83-
* //-
84-
* // Retrieve a list of people related to "Dave", with auto-pagination
85-
* // disabled.
86-
* //-
87-
* var query = datastore.createQuery('Person')
88-
* .hasAncestor(datastore.key(['Person', 'Dave']))
89-
* .autoPaginate(false);
90-
*
91-
* function callback(err, entities, nextQuery, apiResponse) {
92-
* if (nextQuery) {
93-
* // More results might exist, so we'll manually fetch them.
94-
* datastore.runQuery(nextQuery, callback);
95-
* }
96-
* }
97-
*
98-
* datastore.runQuery(query, callback);
99-
*/
100-
Query.prototype.autoPaginate = function(autoPaginateVal) {
101-
this.autoPaginateVal = autoPaginateVal !== false;
102-
return this;
103-
};
104-
10576
/**
10677
* Datastore allows querying on properties. Supported comparison operators
10778
* are `=`, `<`, `>`, `<=`, and `>=`. "Not equal" and `IN` operators are
@@ -316,35 +287,28 @@ Query.prototype.offset = function(n) {
316287
* stream instance is returned.
317288
* @param {?error} callback.err - An error returned while making this request
318289
* @param {object[]} callback.entities - A list of entities.
319-
* @param {?object} callback.nextQuery - If present, query with this object to
320-
* check for more results.
321-
* @param {object} callback.apiResponse - The full API response.
322-
*
323-
* @example
324-
* query.run(function(err, entities) {});
290+
* @param {object} callback.info - An object useful for pagination.
291+
* @param {?string} callback.info.endCursor - Use this in a follow-up query to
292+
* begin from where these results ended.
293+
* @param {string} callback.info.moreResults - Datastore responds with one of:
325294
*
326-
* //-
327-
* // To control how many API requests are made and page through the results
328-
* // manually, call `autoPaginate(false)` on your query.
329-
* //-
330-
* query.autoPaginate(false);
295+
* - {module:datastore#MORE_RESULTS_AFTER_LIMIT}: There *may* be more
296+
* results after the specified limit.
297+
* - {module:datastore#MORE_RESULTS_AFTER_CURSOR}: There *may* be more
298+
* results after the specified end cursor.
299+
* - {module:datastore#NO_MORE_RESULTS}: There are no more results.
331300
*
332-
* function callback(err, entities, nextQuery, apiResponse) {
333-
* if (nextQuery) {
334-
* // More results might exist.
335-
* nextQuery.run(callback);
336-
* }
337-
* }
338-
*
339-
* query.run(callback);
301+
* @example
302+
* query.run(function(err, entities, info) {});
340303
*
341304
* //-
342-
* // If you omit the callback, `run` will automatically call subsequent queries
343-
* // until no results remain. Entity objects will be pushed as they are found.
305+
* // If you omit the callback, you will get the matching entities in a readable
306+
* // object stream.
344307
* //-
345308
* query.run()
346309
* .on('error', console.error)
347310
* .on('data', function (entity) {})
311+
* .on('info', function(info) {})
348312
* .on('end', function() {
349313
* // All entities retrieved.
350314
* });

0 commit comments

Comments
 (0)