0

I need to do a simple query sort with Parse.

I have two classes: Captain and Boat. Every Captain has a Boat pointer object.

I list all of the Captain objects in a table with their attributes, such as name, Boat name etc.

I need to be able to sort the query of Captain objects, via the Boat name. How can I?

I can sort a direct attribute with an NSSortDescriptor sort key such as the Captain name. But I need to sort the Captain objects by the name of their Boat.

halfer
  • 19,824
  • 17
  • 99
  • 186
Josh Kahane
  • 16,765
  • 45
  • 140
  • 253

3 Answers3

4

It would be nice if you could use dot notation on the sort qualifiers, like:

[captainQuery orderByAscending:@"boat.name"];

Unfortunately the attribute dot notation applies only to includeKey:. So the way to do this to fetch, including the related object, then sort locally using the attribute of the related objects:

// ...
[captainQuery includeKey:@"boat"];
[query findObjectsInBackgroundWithBlock:^(NSArray *captains, NSError *error) {
    NSArray *sortedCaptains = [captains sortedArrayUsingComparator: ^(PFObject *capA, PFObject *capB) {
        return [capA[@"boat"][@"name"] compare:capB[@"boat"][@"name"]];
        // or reverse param order for descending
    }];
}];

EDIT - @Logan helpfully points out that this answer doesn't scale well if your query pages through thousands of captains. If the captain count is huge, it might be better to include a boat->captain pointer and do the following:

More roundabout-ly, you could fetch boats, orderByAscending on their name, includeKey their captains (if your boats have a back-pointer to captain), and then map over the returned (sorted) boats for their captains.

danh
  • 62,181
  • 10
  • 95
  • 136
  • Works until too many objects. You wouldn't want to fetch 10000+ objects when all you wanted was boat names starting with say, 'a'. I think your back pointers suggestion is the most ideal. You'd have to set up pointers two ways, but scales much better. – Logan Jan 06 '15 at 18:48
  • 1
    @Logan - not sure I agree. Pulling multiple thousands of objects will be a problem whether or not you sort them locally. My back-pointer idea would be a bad one, too if there were 10k boats. I think we need to arrange our queries to return a reasonable count no matter what. (parse enforces this with a max limit=1000) – danh Jan 06 '15 at 18:56
  • Right, but by querying from the boats, couldn't you do paging? Like query sorted by boatName w/ included key captain. Then you could just get 100 at a time and page as necessary. Otherwise you need to fetch all of the captain objects to ensure they're sorted accurately by boats. Is that correct? – Logan Jan 06 '15 at 18:58
  • Right, I see your point. The data should be designed to match the kind of queries you will do, and if you have 10k boats (a crazy-huge navy) and want to to this kind of query (or lookup a boat's captain), you probably better include a pointer that way. (edited) – danh Jan 06 '15 at 19:10
0

You can use relation query for multiple queries, the query code should looks like below:

PFObject *captain = [PFObject objectWithClassName:@"Captain"];
PFRelation *relation = [captain relationForKey:@"Boat"];
PFQuery *relQuery = [relation query];
[relQuery orderByDescending:@"name"];
// Add other query constraints

[relQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
    if (error) {
       // There was an error
    } else {
       // All the captains sorted by related boat's names
    }
}];

There are some useful links below:

http://blog.parse.com/2012/05/17/new-many-to-many/ https://parse.com/docs/ios_guide#objects-pointers/iOS

Xiaojun
  • 833
  • 6
  • 4
0

There is a scalable solution for this, with rather minimal overhead: Currently you have a table of boats and a table of captains, where each captain contains a pointer to a boat.

You should add a third table - let'c call it 'captains boats', which contains all the boats that are contained in a captain object. You maintain it by adding the boat when a new captain is added and by removing it from the table when a captain is deleted.

In order to sort the captains by their boats:

  1. Query 'CaptainsBoats', sorted by whatever. Notice that even if this table contains a million records, you will get the first X that are relevant to you.
  2. Query 'Captains', use the 'containedIn' method with the boat list from step 1: [captainQuery whereKey:"boat" containedIn:boatList].

  3. Now you have only the captain objects you need (out of a possible million). Sort them locally according to whatever.

Alonzo
  • 773
  • 6
  • 13