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.
Iterative Prompting
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]
(The start
variable is derived from the search bar, and calculated elsewhere.)
SQL
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.
BETWEEN
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.
Expressions
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.