static Future runQuery()

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);
      }
    });
  }