Consider the following 2 predicates filtering through a 5k+ entries store:
predicate1 = [NSPredicate predicateWithFormat:@"hidden == NO AND name BEGINSWITH[cd] %@", searchString];
predicate2 = [NSPredicate predicateWithFormat:@"name BEGINSWITH[cd] %@", searchString];
I turned on -com.apple.CoreData.SQLDebug to see the fetch request times:
predicate1: 0.4728s
predicate2: 0.0867s
Am I missing something? Both columns are indexed. How come adding a simple boolean check slows down the fetch request that much?
EDIT: as requested, the output:
CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZHIDDEN, t0.ZID, t0.ZNAME, t0.ZRANK FROM ZARTISTINDEX t0 WHERE ( t0.ZHIDDEN = ? AND ( NSCoreDataStringSearch( t0.ZNAME, ?, 393, 0) OR NSCoreDataStringSearch( t0.ZNAME, ?, 393, 0))) ORDER BY t0.ZRANK DESC LIMIT 14
That rank column is also indexed. The reason I need this request to be faster than 0.5s is that it’s used for an autocomplete feature. That request is made every time the value of some text field gets changed by the user.
EDIT 2: adding more contextual info:
- (NSArray*)autocompleteSuggestions:(NSString*)searchString {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"ArtistIndex" inManagedObjectContext:self.indexObjectContext];
[request setEntity:entity];
[request setFetchLimit:10];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"hidden == NO AND (name BEGINSWITH[cd] %@ OR name BEGINSWITH[cd] %@)", searchString, [NSString stringWithFormat:@"the %@", searchString]];
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"rank" ascending:NO];
[request setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
NSArray *resultsArray = [self.indexObjectContext executeFetchRequest:request error:nil];
[request release];
return resultsArray;
}
The ArtistIndex entity has the following attributes:
- id (int32)
- name (string, indexed)
- rank (int32, indexed)
- hidden (BOOL, indexed)
Edit 3: here are the full SQL outputs for the slow query (predicate1) and the fast query (predicate2) with com.apple.CoreData.SQLDebug set to 3. More rigorous testing brought me the following test times, which are better, but still have a +2x difference and really make a difference in an autocomplete suggestions context. Or is this a reasonable fetch time difference now?
predicate1: 0.3772s
predicate2: 0.1633s
I ended up taking advice on @pothibo and Ivo Jansch’s answer 2nd approach and do the following:
start with that letter (so basically calling that
autocompleteSuggestions:method but without setting the fetch request’sfetchLimitproperty)NSPredicateto that initially fetched array usingfilteredArrayUsingPredicate:.This results in a slighly slower initial request (although still < 1s) but lightning-fast subsequent fetches.
This is a really clever way to do autocomplete suggestions, since the new set of autocomplete suggestions is always going to be a subset of the previous one. Thanks @pothibo!