Search code examples
iosobjective-cparse-platformpfquerypfobject

Optimize query for Mutual Friends using Parse


Currently, I am attempting to optimize my getMutualFriends method. When I open my 'Friends' view controller, I execute the getMutualFriends method for every friend the user currently has... Which is NOT optimal...but was the easiest solution...

Heres what I did:

[CBUtility queryForFriends:[PFUser currentUser] block:^(NSArray *friends, NSError *error) {
    [self.friendsActivityIndicator stopAnimating];
    if (error) {
        [[[UIAlertView alloc] initWithTitle:@"Error"
                                    message:[error localizedDescription]
                                   delegate:nil cancelButtonTitle:@"Okay"
                          otherButtonTitles:nil]
         show];
        return;
    }

    if ([friends count] == 0 || !friends) {
        [self.friendsTable addSubview:self.friendsEmptyView];
        return;
    }

    self.friends = [NSMutableArray arrayWithArray:friends];

    [self.friendsTable reloadData];
    [self.friendsEmptyView removeFromSuperview];
    int i = 0;

    //
    // THIS IS THE PART THAT SUCKS!!!
    //
    for (PFObject * friendObject in self.friends) {
        [CBUtility queryForMutualFriends:[friendObject objectForKey:kCBFriendToUserKey] block:^(int mutualFriends, NSError *error) {
            if (error) {
                [[[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"Okay" otherButtonTitles:nil] show];
                return;
            }

            CBFriendsCell *cell = (CBFriendsCell *)[self.friendsTable cellForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
            [cell setMutualFriends:mutualFriends];

        }];
        i++;
    }
}];

And heres what +(void)queryForMutualFriends looks like:

+ (void)queryForMutualFriends:(PFUser *)user block:(void (^)(int number, NSError *error))completionBlock
{

    PFQuery *usersFriends = [PFQuery queryWithClassName:kCBFriendClassKey];
    [usersFriends whereKey:kCBFriendFromUserKey equalTo:user];
    [usersFriends whereKey:kCBFriendStatusKey equalTo:kCBFriendStatusFriendKey];

    PFQuery *currentUsersFriends = [PFQuery queryWithClassName:kCBFriendClassKey];
    [currentUsersFriends whereKey:kCBFriendFromUserKey equalTo:[PFUser currentUser]];
    [currentUsersFriends whereKey:kCBFriendStatusKey equalTo:kCBFriendStatusFriendKey];
    [currentUsersFriends whereKey:kCBFriendToUserKey matchesKey:kCBFriendToUserKey inQuery:usersFriends];
    [currentUsersFriends countObjectsInBackgroundWithBlock:^(int number, NSError *error) {
        if (!error) {
            completionBlock(number, error);
            return;
        }
        completionBlock(-1, error);
    }];

}

So instead of running the loop and passing individual PFUser objects into the getMutualFriends method, I'd like to pass an array of friends into the method and return an array of dictionary objects whose keys are 'user' and 'count' with their respective values (e.g. @[@{@"user":somePFUser, @"count":5}, @{@"user":anotherPFUser, @"count":20}];

I mean, this works fine at the moment but takes up way too much API requests...

Anyone got ideas with how to setup the PFQuery?

EDIT:

Here was a link to a SQL query that solves the same problem


Solution

  • No apparently, you cannot... But you can limit the amount of times you query to the server by instead of querying for mutual friends when you retrieve the mutual friends like I did, you instead cache the results into memory...

    I solved this issue by making the query in cellForIndexPath when setting a cells attributes. When the cell is loaded, I check cache first to see if the query has already been made, if it has then I get the cache data... If it hasn't then I make a query to the servers... Only issue I see is that it doesn't update... I figure I can clear cache every minute or so, so the user gets updated automatically instead of pressing a reload button.