in lib/src/datastore_impl.dart [506:608]
static Future<QueryPageImpl> runQuery(api.DatastoreApi api, String project,
api.RunQueryRequest request, int? limit,
{int batchSize = _maxEntitiesPerResponse}) {
if (limit != null && limit < batchSize) {
batchSize = limit;
}
request.query!.limit = batchSize;
return api.projects.runQuery(request, project).then((response) {
var returnedEntities = const <datastore.Entity>[];
final batch = response.batch!;
if (batch.entityResults != null) {
returnedEntities = batch.entityResults!
.map((result) => result.entity!)
.map(DatastoreImpl._convertApi2DatastoreEntity)
.toList();
}
// This check is only necessary for the first request/response pair
// (if offset was supplied).
if (request.query!.offset != null &&
request.query!.offset! > 0 &&
request.query!.offset != batch.skippedResults) {
throw datastore.DatastoreError(
'Server did not skip over the specified ${request.query!.offset} '
'entities.');
}
if (limit != null && returnedEntities.length > limit) {
throw datastore.DatastoreError(
'Server returned more entities then the limit for the request'
'(${request.query!.limit}) was.');
}
// FIXME: TODO: Big hack!
// It looks like Apiary/Atlas is currently broken.
/*
if (limit != null &&
returnedEntities.length < batchSize &&
response.batch.moreResults == 'MORE_RESULTS_AFTER_LIMIT') {
throw new datastore.DatastoreError(
'Server returned response with less entities then the limit was, '
'but signals there are more results after the limit.');
}
*/
// In case a limit was specified, we need to subtraction the number of
// entities we already got.
// (the checks above guarantee that this subtraction is >= 0).
int? remainingEntities;
if (limit != null) {
remainingEntities = limit - returnedEntities.length;
}
// If the server signals there are more entities and we either have no
// limit or our limit has not been reached, we set `moreBatches` to
// `true`.
var moreBatches = (remainingEntities == null || remainingEntities > 0) &&
batch.moreResults == 'MORE_RESULTS_AFTER_LIMIT';
var gotAll = limit != null && remainingEntities == 0;
var noMore = batch.moreResults == 'NO_MORE_RESULTS';
var isLast = gotAll || noMore;
// As a sanity check, we assert that `moreBatches XOR isLast`.
assert(isLast != moreBatches);
// FIXME: TODO: Big hack!
// It looks like Apiary/Atlas is currently broken.
if (moreBatches && returnedEntities.isEmpty) {
print('Warning: Api to Google Cloud Datastore returned bogus response. '
'Trying a workaround.');
isLast = true;
moreBatches = false;
}
if (!isLast && batch.endCursor == null) {
throw datastore.DatastoreError(
'Server did not supply an end cursor, even though the query '
'is not done.');
}
if (isLast) {
return QueryPageImpl(
api, project, request, returnedEntities, true, null);
} else {
// NOTE: We reuse the old RunQueryRequest object here .
// The offset will be 0 from now on, since the first request will have
// skipped over the first `offset` results.
request.query!.offset = 0;
// Furthermore we set the startCursor to the endCursor of the previous
// result batch, so we can continue where we left off.
request.query!.startCursor = batch.endCursor;
return QueryPageImpl(
api, project, request, returnedEntities, false, remainingEntities);
}
});
}