Parse.com query for 10000 (10K) objects

2019-04-15 08:41发布

I have a parse database with a class named MeetingObject filled with 6000 objects (it will grow by the way...).

Being the parse query limit 1000 I'm trying to get them using the skip query property.

The following code gives me 2000 objects:

NSMutableArray *allObjects = [NSMutableArray array];
NSUInteger limit = 1000;
__block NSUInteger skip = 0;
[query setLimit: limit];
[query setSkip: skip];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    if (!error) {
        [allObjects addObjectsFromArray:objects];
        NSLog(@"%lu", (unsigned long)allObjects.count );

        if (objects.count == limit) {
            // There might be more objects in the table. Update the skip value and execute the query again.
            skip += limit;
            [query setSkip: skip];
            [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
                [allObjects addObjectsFromArray:objects];
                NSLog(@"%lu", (unsigned long)allObjects.count );
            }];
        }

    }  else if (error || [error code] == kPFErrorConnectionFailed) {

        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error!" message:NSLocalizedString(@"The Internet connection appears to be offline.",@"no internet") delegate:self cancelButtonTitle:nil otherButtonTitles:@"Ok", nil];
        self.navigationItem.rightBarButtonItem.enabled = YES;
        self.tableView.userInteractionEnabled = YES;
        [alertView show];
        return;
    }
}];

It is my understanding that if I want to get 1000 more objects I have to add another nested query, then another for the next 1000 and so on like this:

// finding the first 1000 objects
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    if (!error) {
        [allObjects addObjectsFromArray:objects];
        NSLog(@"%lu", (unsigned long)allObjects.count );

        if (objects.count == limit) {
            // finding another 1000 objects
            skip += limit;
            [query setSkip: skip];
            [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
                [allObjects addObjectsFromArray:objects];
                NSLog(@"%lu", (unsigned long)allObjects.count );

                if (objects.count == limit) {
                    // finding another 1000 objects
                    skip += limit;
                    [query setSkip: skip];
                    [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
                        [allObjects addObjectsFromArray:objects];
                        NSLog(@"%lu", (unsigned long)allObjects.count );
                    }];
                }

            }];

        }

but what if I don't know the exact number of objects? I tried using:

while (objects.count == limit) {
        // There might be more objects in the table. Update the skip value and execute the query again.
        skip += limit;
        [query setSkip: skip];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
            [allObjects addObjectsFromArray:objects];
            NSLog(@"%lu", (unsigned long)allObjects.count );
        }];
    }

but I get

* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'This query has an outstanding network connection. You have to wait until it's done.'

because of course the queries are made before the last one is finished...

2条回答
等我变得足够好
2楼-- · 2019-04-15 09:10

although this is a late response, this could help other people who come across this problem. Basically, just like Simon's answer above, step 1 is get a count and then create a group dispatch to loop through each request until all data has been downloaded. This is my (slightly generic) code I used:

- (void)getLargeDataFromParseWithBlock:(void (^)(NSArray *, NSError *))block {
  int max = 1000;

  __block NSError *error;
  PFQuery *query = [PFQuery queryWithClassName:@"<your class>"];
  [query whereKey:@"<field name in class>" equalTo:@"xyz"];
  query.limit = max;
  // get count of ojects first
  [query countObjectsInBackgroundWithBlock:^(int count, NSError *_error) {
    if (!error) {
      // calculate how many requests are need based on count and max
      int APIrequests = (count+max-1)/max;
      // create a group dispatch
      dispatch_group_t downloadGroup = dispatch_group_create();
      for (int i=0; i<APIrequests; i++) {
        // tell dispatch a task is starting
        dispatch_group_enter(downloadGroup);
        PFQuery *dispatchQuery = [PFQuery queryWithClassName:@"<your class>"];
        [dispatchQuery whereKey:@"<field name in class>" equalTo:@"xyz"];
        dispatchQuery.limit = max;
        dispatchQuery.skip = i*max;
        [dispatchQuery findObjectsInBackgroundWithBlock:^(NSArray *arrayResponse, NSError *_error2) {
          if (!_error2) {
            NSLog(@"Successfully retrieved %lu.", (unsigned long)arrayResponse.count);

            // do something with arrayResponse like add to core data or sqlite

            // tell dispatch task has completed
            dispatch_group_leave(downloadGroup);
          } else {
            NSLog(@"Error: %@ %@", _error2, [_error2 userInfo]);
            error = _error2;
            // tell dispatch task has completed - need to cover suuccess and fail scenarios of parse request
            dispatch_group_leave(downloadGroup);
          }
        }];
      }
      // called when no more tasks in dispatch
      dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
        if (block) {
          if (!error) {
            // array could contain combined responses - sourced from core data or sqlite for example
            block(@[], nil);
          } else {
            block(nil, error);
          }
        }
      });
    }else {
      block(nil, _error);
      NSLog(@"Count error: %@ %@", _error, [_error userInfo]);
    }
  }];
}
查看更多
闹够了就滚
3楼-- · 2019-04-15 09:14

what I would do is to fetch the number of objects first, then query using skip method.

PFQuery *query = [PFQuery queryWithClassName:@"GameScore"];
[query whereKey:@"playername" equalTo:@"Sean Plott"];
[query countObjectsInBackgroundWithBlock:^(int count, NSError *error) {
  if (!error) {
     dispatchFindObjectsUsingLimit(count)
  } else {
    // The request failed
  }
}];
查看更多
登录 后发表回答