Question

I have a "Song" Entity and a "Tag" entity and they have a many to many relationship between them. A Song can have multiple Tags and a Tag can be applied to multiple Songs.

I want to check if a Song has a particular Tag associated with it. If the Song has the Tag associted with it, I want to show a checkmark in the table view.

For a similar logic, in Apple "TaggedLocations" sample code, the following check is made to check for the presence of the relationship.

if ([event.tags containsObject:tag]) {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
}   

This may be inefficient if there are a lot of Tags in the database as this will fetch all of them in the memory. Please correct me if I am wrong here.

Is there a more efficient way to check if the Song is associated with a particular Tag instead of checking in Song.Tags?

Was it helpful?

Solution

It's actually pretty easy to do, if completely undocumented. You want to create a fetch request with a predicate that has a set operation. If we imagine that your Tag model has a property called tagValue, the predicate you care about is "ANY tags.tagValue == 'footag'"

NSString *tagSearch = @"footag";

// However you get your NSManagedObjectContext.  If you use template code, it's from
// the UIApplicationDelegate
NSManagedObjectContext *context = [delegate managedObjectContext];

// Is there no shortcut for this?  Maybe not, seems to be per context...
NSEntityDescription *songEntity = [NSEntityDescription entityForName:@"Song" inManagedObjectContext:context];

NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:songEntity];

// The request looks for this a group with the supplied name
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY tags.tagValue == %@", tagSearch];
[request setPredicate:predicate];

NSError *error = nil;
NSArray *results = [context executeFetchRequest:request error:&error];

[request release];

OTHER TIPS

You are correct, using that code will retrieve the entire set and the object comparison may be quite complex, depending on how many properties and relationship are part of the object's entity.

Anyway, you can not avoid a set comparison for inclusion. Probably, the best you can do is to avoid fetching all of the properties/relationships by asking Core Data to retrieve NSManagedObjectID Objects only.

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
[fetchRequest setEntity:[NSEntityDescription entityForName:@"Tag" inManagedObjectContext:[self managedObjectContext]]]; 
[fetchRequest setResultType:NSManagedObjectIDResultType];

NSManagedObjectID objects are guaranteed to be unique, therefore you can safely use them to check for set inclusion. This should be much more efficient for a performance perspective.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top