A word about the
BEGINSWITH operator of
NSPredicate: “Caution”! I was using this operator to drive iterative prompting while developing my Full-Text Search (FTS) demo, and found its performance unsatisfactory.
I want to show the user a menu of indexed search terms which share the prefix he has entered in the search box; you can see the desired effect to the right. I accomplish this with a predictable mix of delegates, table views, fetched results controllers, and so on: The bit I want to focus on is the
NSPredicate that selects the keywords to display. Here was my first version of the constructor for that predicate:
[NSPredicate predicateWithFormat:@"keyword BEGINSWITH %@",start]
start variable is derived from the search bar, and calculated elsewhere.)
That predicate threw off SQL like the following:
Select 0, t0.Z_PK From ZKEYWORD t0 Where NSCoreDataStringSearch(t0.ZKEYWORD, ?, 8, 0) Order By t0.ZKEYWORD Collate NSCollateLocaleSensitive
This code was taking between 0.2s and 0.3s to execute on my iPhone 3G; that wasn’t quite responsive enough for real-time behavior. I guessed that the problem was the
NSCoreDataStringSearch() function, which is obviously defined by Core Data, and not something built in to SQLite.
I decided to change my predicate setup code to the following:
NSString* stop = [start stringByAppendingString:@"zzz"]; NSArray* range = [NSArray arrayWithObjects:[NSExpression expressionForConstantValue:start],[NSExpression expressionForConstantValue:stop],nil]; self.keywordResultsController.fetchRequest.predicate = [NSPredicate predicateWithFormat:@"keyword BETWEEN %@",range];
This produced SQL like the following:
Select 0, t0.Z_PK From ZKEYWORD t0 Where (t0.ZKEYWORD Between ? AND ?) Order By t0.ZKEYWORD Collate NSCollateLocaleSensitive
This code typically ran (on my particular device, against my particular test corpus) in between 0.01s and 0.06s; I had one spike to 0.15s, but that seemed to be a startup anomaly. This isn’t a huge change, but it’s the difference between real-time responsiveness and a clunky, stuttering interface.
One word of warning, on a matter which I think the predicate format documentation makes opaque: The
NSArray supplied as the argument to a
BETWEEN clause in an
NSPredicate format string must contain
NSExpressions if you wish to compare strings, and not
NSStrings. (I haven't tested what happens if you're comparing, say, numbers or dates.) If you do supply an array containing
NSStrings, the executable will crash.