Reputation: 42240
I am having trouble understanding how to best use Core Data to solve this problem, including the right terminology to describe the problem. Below is a illustrative sample of the problem (but not my actual objects). Assume you have a music playing system, where artists have songs, and every time a song is played in the system, the time stamp is recorded.
Question: How can I find the count of artists with songs played?
Here are sample NSManagedObject
@interface MYArtist : NSManagedObject
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSSet *songs;
@end
@interface MYSong : NSManagedObject
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) MYArtist *artist;
@property (nonatomic, retain) NSSet *plays;
@end
@interface MYPlay : NSManagedObject
@property (nonatomic, retain) NSDate *playDate;
@property (nonatomic, retain) MYSong *song;
@end
Below is sample data, for Artists, Songs and Plays, include the play date:
A1 <-+-> S1_A1 <---> P1_S1_A1 (2012-08-31)
'-> S2_A1 <-+-> P1_S2_A1 (2012-08-31)
'-> P2_S2_A1 (2012-09-01)
A2 <-+-> S1_A2
'-> S2_A2 <---> P1_S2_A2 (2012-08-31)
A3 <---> S1_A3
Using the code below, I can fetch all the MYPlay
objects and build a set of artists and then find the size of the set at the end. The resulting set from this sample data and code would be [A1, A2] with count = 2. However, I would expect some NSPredicate
syntax or use of countForFetchRequest
to be more efficient, instead of iterating over the objects and faulting them into memory.
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"MYPlay"];
NSArray *results = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
NSMutableSet *set = [[NSMutableSet alloc] init];
for (id result in results) {
MYPlay *play = (MYPlay *)result;
[set addObject:play.song.artist];
}
NSUInteger count = set.count;
Upvotes: 3
Views: 5423
Reputation: 1917
You may try this: Get songs by unique artist name with play count more than 0.
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"MYSong"];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"plays.@count > 0"];
[request setPredicate:predicate];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"MYSong" inManagedObjectContext:context];
NSDictionary *entityProperties = [entity propertiesByName];
[request setReturnsDistinctResults:YES];
[request setPropertiesToFetch:[NSArray arrayWithObject:[entityProperties objectForKey:@"artist"]]];
NSError *error = nil;
NSArray *result = [managedObjectContext executeFetchRequest:request error:&error];
See more for unique data result fetching: Retrieving a unique result set with Core Data
Upvotes: 1
Reputation: 76
The poorly documented SUBQUERY
might be a good solution, and you can nest them so you can check associations of associations.
So:
NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"MYArtist"];
request.predicate = [NSPredicate predicateWithFormat:@"(SUBQUERY(songs,$song,SUBQUERY($song.plays,$play,$play.playDate NOT NULL).@count > 0).@count >0)"]; // I'm assuming that songs with out a play have a nil playDate
NSUInteger count = [context countForFetchRequest:request error:&error];
I haven't tested that subquery string, but that should get you started. There's a few good resources out there if you google for them, but not much in the Apple docs. Documentation for NSExpression states the "string format for a subquery expression is:"
SUBQUERY(collection_expression, variable_expression, predicate);
Upvotes: 4