14
votes

I want to fetch objects B which has a to-many relationship with object A (ie. A -->> B). I already have A in memory. I know I could get a NSSet of the B objects from A, but would it be better to make a fetch request of the B objects (especially if I want to sort sections by date in a UITableView)?

How would I make such a request that only gets the B objects belonging to a single object A?

5

5 Answers

6
votes

You must specify a reverse relationship in Class B in order to do what you are asking.

However, I'm not sure why you are objecting (no pun intended) to grabbing the set of B objects already in object A. Even though the objects themselves are lazy loaded, since you already have A in memory, I have a hunch (although an expert would need to verify) that it would be more efficient to simply grab the set from A than to specify a new NSPredicate, and create a completely new Fetch.

Of course, by "more efficient" we're talking milliseconds, even on a device as slow as the iPhone. I'd grab the set from object A because the syntax is cleaner. The fact that it may also be faster is a bonus.

6
votes

If you are already holding an instance of A, just access the related B instances through A's accessor.

If you need to directly fetch all the B's related to a particular A (you don't in this case), you'd build a fetch request for the B entity, with a predicate based on the (inverse) relationship of Bs to A. (The specific syntax will depend on the inverse relationship name, and whether that inverse is a to-one or to-many.)

5
votes

You need to use predicate with your A's objectID, like this:

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

NSEntityDescription *BEntity = [NSEntityDescription entityForName:@"B" inManagedObjectContext:moc];
[fetchRequest setEntity:BEntity];

NSPredicate *predicate = [NSPredicate 
predicateWithFormat:@"(relationship_to_a = %@)", [yourAInstance objectID]];

[fetchRequest setPredicate:predicate];

where relationship_to_a is name of the relationship to A in your B managed object.

Hope this helps.

Btw.: To all those other answers that suggests to use the faults from the relationship set: I have tried this myself, and it was way slower than fetching them, because Core Data apparently fire the faults one after another (not in batch), therefore making it painfully slow for larger sets.

Actually, if you get your A instance by fetching, then you can try to set relationshipKeyPathsForPrefetching in your NSFetchRequest to YES, and then all the objects in the relationship shouldn't be faults. But this didn't work for me, so I stuck with the 'fetch solution'.

2
votes

I am in a similar situation.

I want to use a NSFetchedResultsController to manage B's in the one to many (A-->>B) relationship. Now, one way of doing this is to build a predicate like the one below and apply it to entity B:

NSPredicate *Predicate = [NSPredicate predicateWithFormat:
                                   @"ANY hasParent.label == 'A'"];

But this is a terribly slow way of doing things and should be avoided at all costs. I tried this on 25,000 objects to retrieve about 300 and it took the simulator about 15 seconds. It wouldn't finish the fetch on the iPhone and crashed repeatedly.

The other way would be to do what has already been mentioned, create an NSArray from the set held by A and sort it. If you send allObjects to a set you get an array back. A is an NSManagedObject fetched before.

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
                                   initWithKey:@"Name"
                                   ascending:YES];

NSArray *lotsOfB = [[[A hasRelationsTo]
                   allObjects]
                   sortedArrayUsingDescriptors: sortDescriptors];

This is very fast. No lag in the simulator or on the device. But you cannot use a NSFetchedResultsController sad times :-(

Hope that helps.

0
votes

I am also in a similar situation.

I didn't have as much objects like pingbat does, but it took 15 sec to fetch with "ANY hasParent.label == 'A'"

I do need to use NSFetchedREsultsController, hence I have to re-architecture the model. What I did was to to store the to-many relationship as a string property and construct the predicate "hasParent contains %@".

The lesson I learnt is that having a predicate that transverse a to-many relationship has a BIG performance hit..