-
Notifications
You must be signed in to change notification settings - Fork 598
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nextQuery always returns a value #1260
Comments
I believe there are issues with the emulator. @pcostell Do you think is related to the issue we found before? |
Yes I believe this is the same issue as before. However, even if there are no more results Cloud Datastore will still return an end cursor which may be useful. So I'm re-echoing my concern that nextQuery isn't the right API for when |
What would be the ideal way of handling pagination? Sorry if I missed your explanation before, feel free to link me. |
// just dropping a link to #1237 as that was the issue we discovered the API/emulator mismatch |
Coming back to this: I think we need the following: On more_results = NOT_FINISHED, set next_query. Properly track offset/limit so that this next_query has these set. All of this will apply regardless of the auto_paginate setting. Users that set auto_paginate=False will just have to deal with the case where more_results=NOT_FINISHED, however we'll properly setup the next_query for them. |
I think I'm stuck on why we wouldn't want to provide // @jgeewax |
In the case of |
Woops, forgot about that case. We should definitely run it for them 👍
Makes a lot of sense. We could still return a nextQuery, but make it more straightforward on getting the endCursor from it. It's currently the un-documented I may be wrong here, but I don't think there is harm in leaving the behavior as it is now. I would bet that the "handle everything for me" approach is desirable to a fair amount of users. However, we definitely need to document |
I'm sorry I am not quite following. So with the code I provided would there be a way to modify it to know when there are no more results? Cheers |
The way I see it is that our desired behavior is a streaming API. If a user asks for a limit of N, we will give them a stream of up to N entities. We can then add some helpers on top of that to convert the stream to a list. Cloud Datastore then offers a paging feature, which provides the user a cursor they can use to get extra pages. On top of that, Cloud Datastore has the feature of best effort end condition tracking ( Importantly, Cloud Datastore's paging feature is completely separate from the batching API, which is really just an implementation detail because prior to gRPC we couldn't offer a streaming API. |
@susanlinsfu Sorry I took over this thread a bit to discuss proper Cloud Datastore usage. Right now, the |
I think it would be good to hear from JJ. I'll bring it up in the next meeting (Monday) if we don't hear back sooner. |
I'd like to pop up a level here and look at the situations that users are actually interested in:
Looking at these cases, I see the following:
IMO, Here's what I think we should make things look like: Give me all the results: datastore.runQuery(query).on('data', function(entity) {
// do something with entity
}); Give me results until I change my mind: datastore.runQuery(query).on('data', function(entity) {
// do something with entity
if (iFeelLikeIt) this.end();
}); Limit the number of API calls (because that is the same as $$$): datastore.runQuery(query, {apiCallLimit: 10}).on('data', function(entity) {
// do something with entity
}); Limit the number of entities (because I only want a max of 10 to show in my list): query = query.limit(10);
datastore.runQuery(query).on('data', function(entity) {
// do something with entity
}); Page through things as though I'm showing a UI query = query.limit(10).startCursor(request.get('cursor'));
var pageEntities = [];
datastore.runQuery(query).on('data', function(entity) {
pageEntities.push(entity);
});
render_template('template.html', {entities: entities, cursor: query.nextCursor}); Obviously if we do promises, this gets much happier: query = query.limit(10).startCursor(request.get('cursor'));
datastore.runQuery(query).then(function(entities) {
render_template('template.html', {entities: entities, cursor: query.nextCursor});
}); |
I'd agree here, except for
How many API calls get made is really an implementation detail, and may depend on many other things (like timeouts and constraints on the server). I think this is reflected in Cloud Datastore's new pricing model, which charges per entity returned and doesn't factor in the number of API calls made (starting July 1). |
I think we already do everything you guys agree on :) |
So the takeaway here is that we need to tweak the logic for providing a |
We can add in a
Right now, our Are these the to-do's from this issue? |
Re The take-away for me is:
|
Issue made for point 1 (limiting API calls): #1281 On point 2, I don't think we have to special-case the pagination design throughout our API just for Datastore. Here's what your ideal example looks like: var query = datastore.query('Person').filter('name', '=', 'Patrick');
var itemsPerPage = 10;
var nextPageToken = request.get('nextPage');
query.paginate(itemsPerPage, nextPageToken, function(pageOfEntities, nextPage) {
// nextPage just gets sent back in the following request.
render_template('template.html', {entities: pageOfEntities, nextPage: nextPage});
}); ... and what it looks like today: var query = datastore.query('Person')
.filter('name', 'Patrick')
.limit(10)
.start(request.get('nextPage'));
datastore.runQuery(query, function(err, pageOfEnities, nextQuery) {
// nextPage just gets sent back in the following request.
render_template('template.html', {entities: pageOfEntities, nextPage: nextQuery.startVal});
}); I really think the only thing to mess with is Point 3, regarding examples:
The first example from https://googlecloudplatform.github.io/gcloud-node/#/docs/v0.31.0/datastore?method=runQuery is: var query = datastore.createQuery('Lion');
datastore.runQuery(query, function(err, entities) {}); That uses autoPagination:true by default, so it will collect all of the results. Examples after that go on to show the manual pagination and stream API. We should update these examples to show how to manually paginate from just a start cursor (basically the example from point 2). During that edit, we can also elaborate on how the first example will pull down all of the results available. |
When you say:
Do you mean pagination, or batching? Based on how the whole I think trying to expose this using |
Sorry if I'm about to state the obvious, but I'm not sure how to answer that, regarding the terminology.
* being careful not to call it "pagination" or "batching" |
Opened up #1293 for the documentation improvements and changing We also have #1281 for adding an option to limit the amount of API requests a method can make. I'm going to close this, as it's unclear to me what we should be doing to nextQuery. The only discussion I'm following would either remove features (returning only a cursoring token instead of a Query object where you can get that from) or go against the common use case that @jgeewax wants us to support firstly, which is pull down as many results as we can. If I'm misunderstanding, just re-open and ELI5. |
Sorry, but I am a little confused now. I am really interested in just knowing how would I know there are no more results to fetch in the query? Can I currently do this in any way? |
Using the emulator in its current state, you'll always receive a Just for clarity, this only affects gcloud-node >= 0.30.0, which uses gRPC. You can always downgrade to an older version and run the stable Datastore emulator that is included with the gcloud SDK. |
I wouldn't recommend downgrading to an older version because you'll also switch to using Cloud Datastore API v1beta2, which has much worse latency characteristics than v1beta3. @stephenplusplus -- This should work now if @susanlinsfu just provides autoPaginate(false), right? The query should just return all 42 results. Until the emulator returns the proper Just a note that downgrading shouldn't work. As I understand our setup, the emulator still returned |
@pcostell the bare bones question is:
If the emulator only responds with "MORE_RESULTS_AFTER_LIMIT" or "NOT_FINISHED", but never "NO_MORE_RESULTS", our API always return a *if Thanks for the clarification around the old emulator. |
Ok so here's the issue from my side: You should only auto fetch the next batch if A query with Then, you can get all 42 entities by just issuing the query:
|
Could you please explain when each of the responses is returned? (MORE_RESULTS_AFTER_LIMIT, NOT_FINISHED, NO_MORE_RESULTS) |
NOT_FINISHED: returned when the query is not complete. If the user set a limit, we haven't satisfied the limit. If the user did not, there are likely more results. However, this is best effort: it is still possible that MORE_RESULTS_AFTER_LIMIT: there may be more results after the user specified limit. It's possible for this to be returned even though there are not actually results after the limit because we weren't able to determine if there are more results or not. In MORE_RESULTS_AFTER_CURSOR: there may be more results after the user specified end cursor. NO_MORE_RESULTS: there are no more results. |
Thank you! From the gcloud-node side: If a user runs a query with a limit, we return that many results, as well as a nextQuery for them to get more: runQuery(queryWithLimit, function (err, results, nextQuery) {
results.length <= Limit
if (nextQuery) {
nextQuery.startVal === originalQueryResponse.endCursor
}
}) If a user runs a query without a limit, we fetch until we get runQuery(queryWithoutLimit, function(err, results) {
// all results that match the query (no `nextQuery`)
}) To handle pagination manually: runQuery(queryWithoutLimitAndAutoPaginationDisabled, function(err, results, nextQuery) {
results.length === However many Datastore returned with until `MORE_RESULTS_AFTER_LIMIT`
}) I think this is the case you want us to change-- you're saying we should let the user do all of the pagination manually, as in the last example? Thanks for your patience! |
I'm not sure I'm asking the user to handle pagination manually, but let me restate to hopefully answer the question:
|
I'm going to work on a PR that does that and see if I start to understand what we're going for in the process. |
I have 42 items which should be returned:
The query first gets the first 10 items, then the next 10, then the next 10, then the remaining 2(42 items). The problem then is when I make the last query to get the last 2 items it returns a nextQuery. As a result it looks as if there is more items even though there are none. I have gone over my code and I can't see anything that is causing this so I am thinking it has something to do with gcloud. I am expecting that when the last query is made no nextQuery should be returned by the datastore. Is this correct?
I am using gcd-rpc emulator and gcloud-node 0.31.0.
The text was updated successfully, but these errors were encountered: